diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java index a3be6906c..2bbee20cc 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java @@ -1,49 +1,27 @@ package world.bentobox.bentobox.blueprints; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -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.data.BlockData; -import org.bukkit.block.data.type.Sign; -import org.bukkit.block.data.type.WallSign; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; - -import com.google.common.collect.ImmutableMap; - 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; import world.bentobox.bentobox.util.Util; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; + /** * This class pastes the clipboard it is given * @author tastybento @@ -68,11 +46,9 @@ public class BlueprintPaster { */ private static long chunkLoadTime = 0; - private static final String MINECRAFT = "minecraft:"; - - private static final Map BLOCK_CONVERSION = ImmutableMap.of("sign", "oak_sign", "wall_sign", "oak_wall_sign"); - private final BentoBox plugin; + private final PasteHandler paster = Util.getPasteHandler(); + private final World world; // The minimum block position (x,y,z) private Location pos1; // The maximum block position (x,y,z) @@ -80,6 +56,7 @@ public class BlueprintPaster { private PasteState pasteState; private BukkitTask pastingTask; private BlueprintClipboard clipboard; + private CompletableFuture currentTask = CompletableFuture.completedFuture(null); /** * The Blueprint to paste. @@ -111,6 +88,7 @@ public class BlueprintPaster { // Calculate location for pasting this.blueprint = Objects.requireNonNull(clipboard.getBlueprint(), "Clipboard cannot have a null Blueprint"); this.location = location; + this.world = location.getWorld(); this.island = null; // Paste @@ -128,6 +106,7 @@ public class BlueprintPaster { this.plugin = plugin; this.blueprint = bp; this.island = island; + this.world = world; // Offset due to bedrock Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0); // Calculate location for pasting @@ -157,7 +136,8 @@ public class BlueprintPaster { // If this is an island OVERWORLD paste, get the island owner. final Optional owner = Optional.ofNullable(island) .filter(i -> location.getWorld().getEnvironment().equals(World.Environment.NORMAL)) - .map(i -> User.getInstance(i.getOwner())); + .map(Island::getOwner) + .map(User::getInstance); // Tell the owner we're pasting blocks and how much time it might take owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed())); Bits bits = new Bits(blocks, attached, entities, @@ -169,6 +149,8 @@ public class BlueprintPaster { } private void pasterTask(CompletableFuture result, Optional owner, Bits bits) { + if (!currentTask.isDone()) return; + final int pasteSpeed = plugin.getSettings().getPasteSpeed(); long timer = System.currentTimeMillis(); @@ -176,7 +158,7 @@ public class BlueprintPaster { if (pasteState.equals(PasteState.CHUNK_LOAD)) { pasteState = PasteState.CHUNK_LOADING; // Load chunk - Util.getChunkAtAsync(location).thenRun(() -> { + currentTask = Util.getChunkAtAsync(location).thenRun(() -> { pasteState = PasteState.BLOCKS; long duration = System.currentTimeMillis() - timer; if (duration > chunkLoadTime) { @@ -184,34 +166,65 @@ public class BlueprintPaster { } }); } - while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && bits.it.hasNext()) { - pasteBlock(location, bits.it.next()); - count++; - } - while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && bits.it2.hasNext()) { - pasteBlock(location, bits.it2.next()); - count++; - } - while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && bits.it3.hasNext()) { - pasteEntity(location, bits.it3.next()); - count++; - } - // STATE SHIFT - if (pasteState.equals(PasteState.BLOCKS) && !bits.it.hasNext()) { - // Blocks done - // Next paste attachments - pasteState = PasteState.ATTACHMENTS; - } - else if (pasteState.equals(PasteState.ATTACHMENTS) && !bits.it2.hasNext()) { - // Attachments done. Next paste entities - pasteState = PasteState.ENTITIES; - if (bits.entities.size() != 0) { - owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size()))); + else if (pasteState.equals(PasteState.BLOCKS) || pasteState.equals(PasteState.ATTACHMENTS)) { + Iterator> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2; + if (it.hasNext()) { + Map blockMap = new HashMap<>(); + // Paste blocks + while (count < pasteSpeed) { + if (!it.hasNext()) { + break; + } + Entry entry = it.next(); + Location pasteTo = location.clone().add(entry.getKey()); + // pos1 and pos2 update + updatePos(pasteTo); + + BlueprintBlock block = entry.getValue(); + blockMap.put(pasteTo, block); + count++; + } + if (!blockMap.isEmpty()) { + currentTask = paster.pasteBlocks(island, world, blockMap); + } + } else { + if (pasteState.equals(PasteState.BLOCKS)) { + // Blocks done + // Next paste attachments + pasteState = PasteState.ATTACHMENTS; + } else { + // Attachments done. Next paste entities + pasteState = PasteState.ENTITIES; + if (bits.entities.size() != 0) { + owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size()))); + } + } } } - else if (pasteState.equals(PasteState.ENTITIES) && !bits.it3.hasNext()) { - pasteState = PasteState.DONE; - owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done")); + else if (pasteState.equals(PasteState.ENTITIES)) { + if (bits.it3().hasNext()) { + Map> entityMap = new HashMap<>(); + // Paste entities + while (count < pasteSpeed) { + if (!bits.it3().hasNext()) { + break; + } + Entry> entry = bits.it3().next(); + int x = location.getBlockX() + entry.getKey().getBlockX(); + int y = location.getBlockY() + entry.getKey().getBlockY(); + int z = location.getBlockZ() + entry.getKey().getBlockZ(); + Location center = new Location(world, x, y, z).add(new Vector(0.5, 0.5, 0.5)); + List entities = entry.getValue(); + entityMap.put(center, entities); + count++; + } + if (!entityMap.isEmpty()) { + currentTask = paster.pasteEntities(island, world, entityMap); + } + } else { + pasteState = PasteState.DONE; + owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done")); + } } else if (pasteState.equals(PasteState.DONE)) { // All done. Cancel task @@ -238,135 +251,6 @@ public class BlueprintPaster { user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize)); } - private void pasteBlock(Location location, Entry entry) { - World world = location.getWorld(); - Location pasteTo = location.clone().add(entry.getKey()); - BlueprintBlock bpBlock = entry.getValue(); - Util.getChunkAtAsync(pasteTo).thenRun(() -> { - Block block = pasteTo.getBlock(); - // Set the block data - default is AIR - BlockData bd; - try { - bd = Bukkit.createBlockData(bpBlock.getBlockData()); - } catch (Exception e) { - bd = convertBlockData(world, bpBlock); - } - block.setBlockData(bd, false); - setBlockState(block, bpBlock); - // Set biome - if (bpBlock.getBiome() != null) { - block.setBiome(bpBlock.getBiome()); - } - // pos1 and pos2 update - updatePos(block.getLocation()); - }); - } - - /** - * Tries to convert the BlockData to a newer version, and logs a warning if it fails to do so. - * @return the converted BlockData or a default AIR BlockData. - * @since 1.6.0 - */ - private BlockData convertBlockData(World world, BlueprintBlock block) { - BlockData blockData = Bukkit.createBlockData(Material.AIR); - try { - for (Entry 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("World: " + world.getName() + "; Failed block data: " + block.getBlockData()); - } - return blockData; - } - - private void pasteEntity(Location location, Entry> entry) { - int x = location.getBlockX() + entry.getKey().getBlockX(); - int y = location.getBlockY() + entry.getKey().getBlockY(); - int z = location.getBlockZ() + entry.getKey().getBlockZ(); - setEntity(new Location(location.getWorld(), x, y, z), entry.getValue()); - } - - /** - * Handles signs, chests and mob spawner blocks - * @param block - block - * @param bpBlock - config - */ - private void setBlockState(Block block, BlueprintBlock bpBlock) { - // Get the block state - BlockState bs = block.getState(); - // Signs - if (bs instanceof org.bukkit.block.Sign sign) { - writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText()); - } - // Chests, in general - 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 - if (bs instanceof CreatureSpawner spawner) { - setSpawner(spawner, bpBlock.getCreatureSpawner()); - } - // Banners - if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) { - bpBlock.getBannerPatterns().removeIf(Objects::isNull); - banner.setPatterns(bpBlock.getBannerPatterns()); - banner.update(true, false); - } - } - - private 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); - } - - /** - * Sets any entity that is in this location - * @param location - location - * @param list - list of entities to paste - */ - private void setEntity(Location location, List list) { - list.stream().filter(k -> k.getType() != null).forEach(k -> { - // Center, and just a bit high - Location center = location.add(new Vector(0.5, 0.5, 0.5)); - Util.getChunkAtAsync(center).thenRun(() -> { - LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, 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) - Player owner = User.getInstance(island.getOwner()).getPlayer(); - if (owner != null) { - // Parse for the player's name first (in case placeholders might need it) - customName = customName.replace(TextVariables.NAME, owner.getName()); - // Now parse the placeholders - customName = plugin.getPlaceholdersManager().replacePlaceholders(owner, customName); - } - } - - // Actually set the custom name - e.setCustomName(customName); - } - k.configureEntity(e); - }); - }); - } - /** * Tracks the minimum and maximum block positions * @param l - location of block pasted @@ -397,50 +281,4 @@ public class BlueprintPaster { pos2.setZ(l.getBlockZ()); } } - - private void writeSign(final Block block, final List lines, boolean glow) { - BlockFace bf; - if (block.getType().name().contains("WALL_SIGN")) { - WallSign wallSign = (WallSign)block.getBlockData(); - bf = wallSign.getFacing(); - } else { - Sign sign = (Sign)block.getBlockData(); - bf = sign.getRotation(); - } - // Handle spawn sign - if (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 - org.bukkit.block.Sign s = (org.bukkit.block.Sign)block.getState(); - // 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(""); - if (island.getOwner() != null) { - for (int i = 0; i < 4; i++) { - s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()), - addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name))); - } - } - } else { - // Just paste - for (int i = 0; i < 4; i++) { - s.setLine(i, lines.get(i)); - } - } - s.setGlowingText(glow); - // Update the sign - s.update(); - } } diff --git a/src/main/java/world/bentobox/bentobox/nms/PasteHandler.java b/src/main/java/world/bentobox/bentobox/nms/PasteHandler.java new file mode 100644 index 000000000..33537aa7a --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/nms/PasteHandler.java @@ -0,0 +1,36 @@ +package world.bentobox.bentobox.nms; + +import org.bukkit.Location; +import org.bukkit.World; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; +import world.bentobox.bentobox.database.objects.Island; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +/** + * A helper class for {@link world.bentobox.bentobox.blueprints.BlueprintPaster} + */ +public interface PasteHandler { + /** + * Create a future to paste the blocks + * + * @param island the island + * @param world the world + * @param blockMap the block map + * @return the future + */ + CompletableFuture pasteBlocks(Island island, World world, Map blockMap); + + /** + * Create a future to paste the entities + * + * @param island the island + * @param world the world + * @param entityMap the entities map + * @return the future + */ + CompletableFuture pasteEntities(Island island, World world, Map> entityMap); +} diff --git a/src/main/java/world/bentobox/bentobox/nms/fallback/PasteHandlerImpl.java b/src/main/java/world/bentobox/bentobox/nms/fallback/PasteHandlerImpl.java new file mode 100644 index 000000000..64e6df247 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/nms/fallback/PasteHandlerImpl.java @@ -0,0 +1,27 @@ +package world.bentobox.bentobox.nms.fallback; + +import org.bukkit.Location; +import org.bukkit.World; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.nms.PasteHandler; +import world.bentobox.bentobox.util.DefaultPasteUtil; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class PasteHandlerImpl implements PasteHandler { + @Override + public CompletableFuture pasteBlocks(Island island, World world, Map blockMap) { + blockMap.forEach((location, block) -> DefaultPasteUtil.setBlock(island, location, block)); + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture pasteEntities(Island island, World world, Map> entityMap) { + entityMap.forEach((location, blueprintEntities) -> DefaultPasteUtil.setEntity(island, location, blueprintEntities)); + return CompletableFuture.completedFuture(null); + } +} diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java new file mode 100644 index 000000000..cf1e4db33 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -0,0 +1,237 @@ +package world.bentobox.bentobox.util; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.*; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.WallSign; +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; + +import java.util.*; + +/** + * A utility class for {@link PasteHandler} + * + * @author tastybento + */ +public class DefaultPasteUtil { + private static final String MINECRAFT = "minecraft:"; + private static final Map BLOCK_CONVERSION = Map.of("sign", "oak_sign", "wall_sign", "oak_wall_sign"); + private static final BentoBox plugin; + + static { + plugin = BentoBox.getInstance(); + } + + /** + * Set the block to the location + * + * @param island - island + * @param location - location + * @param bpBlock - blueprint block + */ + public static void setBlock(Island island, Location location, BlueprintBlock bpBlock) { + 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 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) { + writeSign(island, block, bpBlock.getSignLines(), bpBlock.isGlowingText()); + } + // 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 void setEntity(Island island, Location location, List list) { + World world = location.getWorld(); + assert world != null; + 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 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 lines - lines + * @param glow - is sign glowing? + */ + public static void writeSign(Island island, final Block block, final List lines, boolean glow) { + 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 (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 + org.bukkit.block.Sign s = (org.bukkit.block.Sign) block.getState(); + // 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 = Optional.ofNullable(island.getOwner()).map(User::getInstance); + if (user.isPresent()) { + for (int i = 0; i < 4; i++) { + s.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++) { + s.setLine(i, lines.get(i)); + } + } + s.setGlowingText(glow); + // Update the sign + s.update(); + } +} diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index c0a049119..e0b9cb8d2 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -47,8 +47,10 @@ import io.papermc.lib.PaperLib; import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.nms.PasteHandler; import world.bentobox.bentobox.nms.WorldRegenerator; + /** * A set of utility methods * @@ -64,6 +66,7 @@ public class Util { private static final String THE_END = "_the_end"; private static String serverVersion = null; private static BentoBox plugin = BentoBox.getInstance(); + private static PasteHandler pasteHandler = null; private static WorldRegenerator regenerator = null; private Util() {} @@ -722,6 +725,25 @@ public class Util { return regenerator; } + /** + * Set the paste handler the plugin will use + * @param pasteHandler the NMS paster + */ + public static void setPasteHandler(PasteHandler pasteHandler) { + Util.pasteHandler = pasteHandler; + } + + /** + * Get the paste handler the plugin will use + * @return an NMS accelerated class for this server + */ + public static PasteHandler getPasteHandler() { + if (pasteHandler == null) { + setPasteHandler(new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl()); + } + return pasteHandler; + } + /** * Broadcast a localized message to all players with the permission {@link Server#BROADCAST_CHANNEL_USERS} *