259 lines
10 KiB
Java
259 lines
10 KiB
Java
package world.bentobox.bentobox.util;
|
|
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.World;
|
|
import org.bukkit.block.Banner;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.block.BlockFace;
|
|
import org.bukkit.block.BlockState;
|
|
import org.bukkit.block.CreatureSpawner;
|
|
import org.bukkit.block.Sign;
|
|
import org.bukkit.block.data.BlockData;
|
|
import org.bukkit.block.data.type.WallSign;
|
|
import org.bukkit.block.sign.Side;
|
|
import org.bukkit.block.sign.SignSide;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.inventory.Inventory;
|
|
import org.bukkit.inventory.InventoryHolder;
|
|
|
|
import world.bentobox.bentobox.BentoBox;
|
|
import world.bentobox.bentobox.api.localization.TextVariables;
|
|
import world.bentobox.bentobox.api.user.User;
|
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
|
|
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
|
import world.bentobox.bentobox.database.objects.Island;
|
|
import world.bentobox.bentobox.nms.PasteHandler;
|
|
|
|
/**
|
|
* A utility class for {@link PasteHandler}
|
|
*
|
|
* @author tastybento
|
|
*/
|
|
public class DefaultPasteUtil {
|
|
private static final String MINECRAFT = "minecraft:";
|
|
private static final Map<String, String> BLOCK_CONVERSION = Map.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
|
|
private static final BentoBox plugin;
|
|
|
|
static {
|
|
plugin = BentoBox.getInstance();
|
|
}
|
|
|
|
private DefaultPasteUtil() {} // private constructor to hide the implicit public one.
|
|
|
|
/**
|
|
* Set the block to the location
|
|
*
|
|
* @param island - island
|
|
* @param location - location
|
|
* @param bpBlock - blueprint block
|
|
*/
|
|
public static CompletableFuture<Void> setBlock(Island island, Location location, BlueprintBlock bpBlock) {
|
|
return Util.getChunkAtAsync(location).thenRun(() -> {
|
|
Block block = location.getBlock();
|
|
// Set the block data - default is AIR
|
|
BlockData bd = createBlockData(bpBlock);
|
|
block.setBlockData(bd, false);
|
|
setBlockState(island, block, bpBlock);
|
|
// Set biome
|
|
if (bpBlock.getBiome() != null) {
|
|
block.setBiome(bpBlock.getBiome());
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a block data from the blueprint
|
|
*
|
|
* @param block - blueprint block
|
|
* @return the block data
|
|
*/
|
|
public static BlockData createBlockData(BlueprintBlock block) {
|
|
try {
|
|
return Bukkit.createBlockData(block.getBlockData());
|
|
} catch (Exception e) {
|
|
return convertBlockData(block);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert the blueprint to block data
|
|
*
|
|
* @param block - the blueprint block
|
|
* @return the block data
|
|
*/
|
|
public static BlockData convertBlockData(BlueprintBlock block) {
|
|
BlockData blockData = Bukkit.createBlockData(Material.AIR);
|
|
try {
|
|
for (Map.Entry<String, String> en : BLOCK_CONVERSION.entrySet()) {
|
|
if (block.getBlockData().startsWith(MINECRAFT + en.getKey())) {
|
|
blockData = Bukkit.createBlockData(block.getBlockData().replace(MINECRAFT + en.getKey(), MINECRAFT + en.getValue()));
|
|
break;
|
|
}
|
|
}
|
|
} catch (IllegalArgumentException e) {
|
|
// This may happen if the block type is no longer supported by the server
|
|
plugin.logWarning("Blueprint references materials not supported on this server version.");
|
|
plugin.logWarning("Load blueprint manually, check and save to fix for this server version.");
|
|
plugin.logWarning("Failed block data: " + block.getBlockData());
|
|
}
|
|
return blockData;
|
|
}
|
|
|
|
/**
|
|
* Handles signs, chests and mob spawner blocks
|
|
*
|
|
* @param island - island
|
|
* @param block - block
|
|
* @param bpBlock - config
|
|
*/
|
|
public static void setBlockState(Island island, Block block, BlueprintBlock bpBlock) {
|
|
// Get the block state
|
|
BlockState bs = block.getState();
|
|
// Signs
|
|
if (bs instanceof Sign) {
|
|
for (Side side : Side.values()) {
|
|
writeSign(island, block, bpBlock, side);
|
|
}
|
|
}
|
|
// Chests, in general
|
|
else if (bs instanceof InventoryHolder holder) {
|
|
Inventory ih = holder.getInventory();
|
|
// Double chests are pasted as two blocks so inventory is filled twice.
|
|
// This code stops over-filling for the first block.
|
|
bpBlock.getInventory().forEach(ih::setItem);
|
|
}
|
|
// Mob spawners
|
|
else if (bs instanceof CreatureSpawner spawner) {
|
|
setSpawner(spawner, bpBlock.getCreatureSpawner());
|
|
}
|
|
// Banners
|
|
else if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
|
|
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
|
|
banner.setPatterns(bpBlock.getBannerPatterns());
|
|
banner.update(true, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the spawner setting from the blueprint
|
|
*
|
|
* @param spawner - spawner
|
|
* @param s - blueprint spawner
|
|
*/
|
|
public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
|
|
spawner.setSpawnedType(s.getSpawnedType());
|
|
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
|
|
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
|
|
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
|
|
spawner.setDelay(s.getDelay());
|
|
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
|
|
spawner.setSpawnRange(s.getSpawnRange());
|
|
spawner.update(true, false);
|
|
}
|
|
|
|
/**
|
|
* Spawn the blueprint entities to the location
|
|
*
|
|
* @param island - island
|
|
* @param location - location
|
|
* @param list - blueprint entities
|
|
*/
|
|
public static CompletableFuture<Void> setEntity(Island island, Location location, List<BlueprintEntity> list) {
|
|
World world = location.getWorld();
|
|
assert world != null;
|
|
return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null).forEach(k -> {
|
|
LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType());
|
|
if (k.getCustomName() != null) {
|
|
String customName = k.getCustomName();
|
|
|
|
if (island != null) {
|
|
// Parse any placeholders in the entity's name, if the owner's connected (he should)
|
|
Optional<Player> owner = Optional.ofNullable(island.getOwner())
|
|
.map(User::getInstance)
|
|
.map(User::getPlayer);
|
|
if (owner.isPresent()) {
|
|
// Parse for the player's name first (in case placeholders might need it)
|
|
customName = customName.replace(TextVariables.NAME, owner.get().getName());
|
|
// Now parse the placeholders
|
|
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner.get(), customName);
|
|
}
|
|
}
|
|
|
|
// Actually set the custom name
|
|
e.setCustomName(customName);
|
|
}
|
|
k.configureEntity(e);
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Write the lines to the sign at the block
|
|
*
|
|
* @param island - island
|
|
* @param block - block
|
|
* @param bpSign - BlueprintBlock that is the sign
|
|
* @param side - the side being written
|
|
*/
|
|
public static void writeSign(Island island, final Block block, BlueprintBlock bpSign, Side side) {
|
|
List<String> lines = bpSign.getSignLines(side);
|
|
boolean glow = bpSign.isGlowingText(side);
|
|
|
|
BlockFace bf;
|
|
if (block.getType().name().contains("WALL_SIGN")) {
|
|
WallSign wallSign = (WallSign) block.getBlockData();
|
|
bf = wallSign.getFacing();
|
|
} else {
|
|
org.bukkit.block.data.type.Sign sign = (org.bukkit.block.data.type.Sign) block.getBlockData();
|
|
bf = sign.getRotation();
|
|
}
|
|
// Handle spawn sign
|
|
if (side == Side.FRONT && island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) {
|
|
block.setType(Material.AIR);
|
|
// Orient to face same direction as sign
|
|
Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(),
|
|
block.getZ() + 0.5D, Util.blockFaceToFloat(bf.getOppositeFace()), 30F);
|
|
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
|
return;
|
|
}
|
|
// Get the name of the player
|
|
String name = "";
|
|
if (island != null) {
|
|
name = plugin.getPlayers().getName(island.getOwner());
|
|
}
|
|
// Handle locale text for starting sign
|
|
Sign s = (org.bukkit.block.Sign) block.getState();
|
|
SignSide signSide = s.getSide(side);
|
|
// Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file
|
|
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) {
|
|
// Get the addon that is operating in this world
|
|
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
|
|
Optional<User> user = Optional.ofNullable(island.getOwner()).map(User::getInstance);
|
|
if (user.isPresent()) {
|
|
for (int i = 0; i < 4; i++) {
|
|
signSide.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(),
|
|
addonName + ".sign.line" + i, "").replace(TextVariables.NAME, name)));
|
|
}
|
|
}
|
|
} else {
|
|
// Just paste
|
|
for (int i = 0; i < 4; i++) {
|
|
signSide.setLine(i, lines.get(i));
|
|
}
|
|
}
|
|
signSide.setGlowingText(glow);
|
|
// Update the sign
|
|
s.update();
|
|
}
|
|
}
|