From 4f6a574fcb48410a5db7f43001d2848218c2751d Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 21 Dec 2024 14:30:37 -0800 Subject: [PATCH 01/24] Try parallel class tests --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index ffb166b74..f4b64ccfc 100644 --- a/pom.xml +++ b/pom.xml @@ -457,6 +457,8 @@ 3.0.0-M5 + classes + 4 ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED From fab7f08473fcd7197111c8c0e13999f0b9db7991 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 21 Dec 2024 14:31:08 -0800 Subject: [PATCH 02/24] Version 3.1.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4b64ccfc..360d19dc0 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ -LOCAL - 3.1.1 + 3.1.2 bentobox-world https://sonarcloud.io ${project.basedir}/lib From b3b0d71af6becc05259b841de21b9171602d174e Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 21 Dec 2024 15:56:08 -0800 Subject: [PATCH 03/24] Update modrinth-publish.yml --- .github/workflows/modrinth-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/modrinth-publish.yml b/.github/workflows/modrinth-publish.yml index 8244fe239..9f8b505a8 100644 --- a/.github/workflows/modrinth-publish.yml +++ b/.github/workflows/modrinth-publish.yml @@ -21,8 +21,8 @@ jobs: cache: maven # This step will take the version tag from the release and replace it in `pom.xml` before building. - - name: Set version from release tag - run: mvn -B versions:set -DnewVersion=${{ github.event.release.tag_name }} -DgenerateBackupPoms=false + #- name: Set version from release tag + # run: mvn -B versions:set -DnewVersion=${{ github.event.release.tag_name }} -DgenerateBackupPoms=false - name: Build and package with Maven run: mvn -B clean package --file pom.xml From 4231bcfe35cc2dcb711f5b551ff0c6014870d45f Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 21 Dec 2024 18:58:08 -0800 Subject: [PATCH 04/24] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dff7a8f18..47e1c74cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ on: push: branches: - develop + - master pull_request: types: [opened, synchronize, reopened] jobs: From 6017a09a03a3b16d5a5482f66e22b0321ff49908 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 08:05:20 -0800 Subject: [PATCH 05/24] WIP --- pom.xml | 22 ++- .../bentobox/hooks/ZNPCSPlusHook.java | 131 ++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java diff --git a/pom.xml b/pom.xml index 360d19dc0..854b0e192 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,12 @@ FancyPlugins Repository https://repo.fancyplugins.de/releases + + + pyr-snapshots + Pyr's Repo + https://repo.pyr.lol/snapshots + @@ -406,6 +412,18 @@ 2.4.0 provided + + + lol.pyr + znpcsplus-api + 2.0.0-SNAPSHOT + provided + + + lol.pyr + ZNPCsPlus + 1.0.7 + @@ -454,11 +472,11 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.5.2 classes - 4 + 8 ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java new file mode 100644 index 000000000..cc080cdbb --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java @@ -0,0 +1,131 @@ +package world.bentobox.bentobox.hooks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import lol.pyr.znpcsplus.ZNPCsPlus; +import lol.pyr.znpcsplus.api.entity.EntityProperty; +import lol.pyr.znpcsplus.api.interaction.InteractionAction; +import lol.pyr.znpcsplus.api.npc.Npc; +import lol.pyr.znpcsplus.api.npc.NpcType; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.hooks.Hook; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; + +/** + * Provides copy and pasting of ZNPCS Plus in blueprints https://github.com/Pyrbu/ZNPCsPlus + * + * @author tastybento + * @since 3.2.0 + */ +public class ZNPCSPlusHook extends Hook { + + private ZNPCsPlus plugin; + + public ZNPCSPlusHook() { + super("ZNPCsPlus", Material.PLAYER_HEAD); + + } + + public String serializeNPC(Npc npc, Vector origin) { + if (npc == null) { + throw new IllegalArgumentException("NPC cannot be null."); + } + YamlConfiguration config = new YamlConfiguration(); + NpcType type = npc.getType(); + for (EntityProperty property : npc.getAppliedProperties()) + try { + property.toString(); + PropertySerializer serializer = propertyRegistry + .getSerializer(((EntityPropertyImpl) property).getType()); + if (serializer == null) { + BentoBox.getInstance().logWarning("Unknown serializer for property '" + property.getName() + + "' for npc '" + npc.getUuid() + "'. skipping ..."); + continue; + } + config.set("properties." + property.getName(), serializer.UNSAFE_serialize(npc.getProperty(property))); + } catch (Exception exception) { + BentoBox.getInstance().logWarning( + "Failed to serialize property " + property.getName() + " for npc with id " + npc.getUuid()); + exception.printStackTrace(); + } + + lol.pyr.znpcsplus.api.hologram.Hologram hologram = npc.getHologram(); + if (hologram.getRefreshDelay() != -1) + config.set("hologram.refresh-delay", hologram.getRefreshDelay()); + List lines = new ArrayList<>(npc.getHologram().lineCount()); + for (int i = 0; i < npc.getHologram().lineCount(); i++) { + lines.add(hologram.getLine(i)); + } + config.set("hologram.lines", lines); + config.set("actions", npc.getActions().stream().map(InteractionAction::toString).filter(Objects::nonNull) + .collect(Collectors.toList())); + return config.saveToString(); + } + + public boolean spawnNpc(String yaml, Location pos) throws InvalidConfigurationException { + YamlConfiguration npcConfig = new YamlConfiguration(); + npcConfig.loadFromString(yaml); + + String name = UUID.randomUUID().toString(); // Create a unique name + + UUID creator = UUID.randomUUID(); // Random creator + + + return true; + } + + @Override + public boolean hook() { + boolean hooked = this.isPluginAvailable(); + if (!hooked) { + BentoBox.getInstance().logError("Could not hook into FancyNpcs"); + } + return hooked; // The hook process shouldn't fail + } + + @Override + public String getFailureCause() { + return null; // The hook process shouldn't fail + } + + public Map> getNpcsInArea(World world, List vectorsToCopy, + @Nullable Vector origin) { + Map> bpEntities = new HashMap<>(); + for (Npc npc : FancyNpcsPlugin.get().getNpcManager().getAllNpcs()) { + Location npcLocation = npc.getData().getLocation(); + Vector spot = new Vector(npcLocation.getBlockX(), npcLocation.getBlockY(), npcLocation.getBlockZ()); + if (npcLocation.getWorld().equals(world) && vectorsToCopy.contains(spot)) { + BlueprintEntity cit = new BlueprintEntity(); + cit.setType(npc.getData().getType()); + cit.setNpc(this.serializeNPC(npc, origin)); + // Retrieve or create the list, then add the entity + List entities = bpEntities.getOrDefault(spot, new ArrayList<>()); + entities.add(cit); + // Create position + Vector origin2 = origin == null ? new Vector(0, 0, 0) : origin; + int x = spot.getBlockX() - origin2.getBlockX(); + int y = spot.getBlockY() - origin2.getBlockY(); + int z = spot.getBlockZ() - origin2.getBlockZ(); + Vector pos = new Vector(x, y, z); + // Store + bpEntities.put(pos, entities); // Update the map + } + } + return bpEntities; + } +} From 8346405ab327a9415637a2478f97b81c99bc412a Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 08:05:47 -0800 Subject: [PATCH 06/24] WIP --- src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java index cc080cdbb..d00e9e468 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCSPlusHook.java @@ -49,7 +49,6 @@ public String serializeNPC(Npc npc, Vector origin) { NpcType type = npc.getType(); for (EntityProperty property : npc.getAppliedProperties()) try { - property.toString(); PropertySerializer serializer = propertyRegistry .getSerializer(((EntityPropertyImpl) property).getType()); if (serializer == null) { From 2dbb522eb708696cdcd37482adc22c809c204c0c Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 08:48:50 -0800 Subject: [PATCH 07/24] Updated JavaDoc to avoid error. --- src/main/java/world/bentobox/bentobox/database/Database.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/database/Database.java b/src/main/java/world/bentobox/bentobox/database/Database.java index eca983ac2..8b0c4aea3 100644 --- a/src/main/java/world/bentobox/bentobox/database/Database.java +++ b/src/main/java/world/bentobox/bentobox/database/Database.java @@ -167,8 +167,8 @@ public static Set> getDataobjects() { } /** - * Load all objects async - * @return CompletableFuture> + * Load all objects asynchronously. + * @return {@code CompletableFuture>} */ public @NonNull CompletableFuture> loadObjectsASync() { return handler.loadObjectsASync(); From 60f0110547a850f6087c1e3746d3e406fdc2c0a1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 09:27:41 -0800 Subject: [PATCH 08/24] Remove the parallel test running --- pom.xml | 2 -- .../bentobox/bentobox/panels/customizable/IslandHomesPanel.java | 1 - 2 files changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index 360d19dc0..faf1b955c 100644 --- a/pom.xml +++ b/pom.xml @@ -457,8 +457,6 @@ 3.0.0-M5 - classes - 4 ${argLine} --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/src/main/java/world/bentobox/bentobox/panels/customizable/IslandHomesPanel.java b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandHomesPanel.java index c760a21d8..84e9898a3 100644 --- a/src/main/java/world/bentobox/bentobox/panels/customizable/IslandHomesPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandHomesPanel.java @@ -60,7 +60,6 @@ public class IslandHomesPanel extends AbstractPanel * * @param command CompositeCommand * @param user User who opens panel - * @param islandMap map of island names and IslandInfo */ private IslandHomesPanel(@NonNull CompositeCommand command, @NonNull User user) { From 212166ebe806fbe4c4201b1ca3e68628e072b7c8 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 09:28:18 -0800 Subject: [PATCH 09/24] Update surefire plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index faf1b955c..d932b91ca 100644 --- a/pom.xml +++ b/pom.xml @@ -454,7 +454,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.5.2 From 180ccd9f5eaf373a0026babcab640383eaec3e79 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 09:17:07 -0800 Subject: [PATCH 10/24] Tidied up and working --- .../blueprints/BlueprintClipboard.java | 10 +++- .../bentobox/blueprints/BlueprintPaster.java | 2 +- .../bentobox/hooks/ZNPCsPlusHook.java | 41 ++++++++++------ .../bentobox/util/DefaultPasteUtil.java | 24 ++++++++-- .../world/bentobox/bentobox/util/Util.java | 48 +++++++++++++++++++ src/main/resources/plugin.yml | 1 + 6 files changed, 106 insertions(+), 20 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index dc10da175..6d678bd08 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -45,6 +45,7 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.MythicMobsHook; +import world.bentobox.bentobox.hooks.ZNPCsPlusHook; /** * The clipboard provides the holding spot for an active blueprint that is being @@ -71,6 +72,7 @@ public class BlueprintClipboard { private final BentoBox plugin = BentoBox.getInstance(); private Optional mmh; private Optional npc; + private Optional znpc; /** * Create a clipboard for blueprint @@ -82,12 +84,15 @@ public BlueprintClipboard(@NonNull Blueprint blueprint) { } public BlueprintClipboard() { - // Citizens Hook + // Fancy NPCs Hook npc = plugin.getHooks().getHook("FancyNpcs").filter(FancyNpcsHook.class::isInstance) .map(FancyNpcsHook.class::cast); // MythicMobs Hook mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance) .map(MythicMobsHook.class::cast); + // ZNPCs Plus Hook + znpc = plugin.getHooks().getHook("ZNPCsPlus").filter(ZNPCsPlusHook.class::isInstance) + .map(ZNPCsPlusHook.class::cast); } /** @@ -143,6 +148,9 @@ private void copyAsync(World world, User user, List vectorsToCopy, int s // Add all the citizens for the area in one go. This is pretty fast. bpEntities.putAll(npc.get().getNpcsInArea(world, vectorsToCopy, origin)); } + if (znpc.isPresent()) { + bpEntities.putAll(znpc.get().getNpcsInArea(world, vectorsToCopy, origin)); + } // Repeating copy task copyTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java index 8ba1cb498..06a53e40a 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java @@ -240,7 +240,7 @@ private void pasteEntities(Bits bits, int count, Optional owner, int paste 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)); + Location center = new Location(world, x, y, z).add(new Vector(0.5, 0D, 0.5)); List entities = entry.getValue(); entityMap.put(center, entities); count++; diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java index a4c99d990..d72df0847 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java @@ -3,13 +3,17 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.World; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; @@ -19,6 +23,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.hooks.Hook; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; +import world.bentobox.bentobox.util.Util; /** * Provides copy and pasting of ZNPCS Plus in blueprints https://github.com/Pyrbu/ZNPCsPlus @@ -28,6 +33,8 @@ */ public class ZNPCsPlusHook extends Hook { + private static final String VERSION = "2.0.0-SNAPSHOT"; // Minimum version required + public ZNPCsPlusHook() { super("ZNPCsPlus", Material.PLAYER_HEAD); } @@ -36,7 +43,6 @@ public String serializeNPC(NpcEntry entry, Vector origin) { String result = NpcApiProvider.get().getNpcSerializerRegistry().getSerializer(YamlConfiguration.class) .serialize(entry) .saveToString(); - BentoBox.getInstance().logDebug(result); return result; } @@ -48,6 +54,7 @@ public boolean spawnNpc(String yaml, Location pos) throws InvalidConfigurationEx NpcLocation loc = new NpcLocation(pos); entry.getNpc().setLocation(loc); NpcApiProvider.get().getNpcRegistry().register(entry); + return true; } @@ -56,7 +63,9 @@ public boolean hook() { boolean hooked = this.isPluginAvailable(); // Check version String version = this.getPlugin().getDescription().getVersion(); - BentoBox.getInstance().logDebug("ZNPCsPlus version = " + version); + if (!Util.isVersionCompatible(version, VERSION)) { + return false; + } if (!hooked) { BentoBox.getInstance().logError("Could not hook into FancyNpcs"); } @@ -65,28 +74,30 @@ public boolean hook() { @Override public String getFailureCause() { - return null; // The hook process shouldn't fail + // The only failure is wrong version + return "ZNPCsPlus version " + VERSION + " required or later. You are running " + + this.getPlugin().getDescription().getVersion(); } public Map> getNpcsInArea(World world, List vectorsToCopy, @Nullable Vector origin) { Map> bpEntities = new HashMap<>(); - for (NpcEntry npc : NpcApiProvider.get().getNpcRegistry().getAll()) { - NpcLocation npcLocation = npc.getNpc().getLocation(); - Vector spot = new Vector(npcLocation.getBlockX(), npcLocation.getBlockY(), npcLocation.getBlockZ()); - if (npc.getNpc().getWorld().equals(world) && vectorsToCopy.contains(spot)) { + for (NpcEntry npcEntry : NpcApiProvider.get().getNpcRegistry().getAll()) { + NpcLocation npcLocation = npcEntry.getNpc().getLocation(); + Vector loc = new Vector(npcLocation.getBlockX(), npcLocation.getBlockY(), npcLocation.getBlockZ()); + if (npcEntry.getNpc().getWorld().equals(world) && vectorsToCopy.contains(loc)) { + // Put the NPC into a BlueprintEntity - serialize it BlueprintEntity cit = new BlueprintEntity(); - //cit.setType(npc.getNpc().getType()); - cit.setNpc(this.serializeNPC(npc, origin)); - // Retrieve or create the list, then add the entity - List entities = bpEntities.getOrDefault(spot, new ArrayList<>()); + cit.setNpc(this.serializeNPC(npcEntry, origin)); + // Retrieve or create the list of entities and add this one + List entities = bpEntities.getOrDefault(loc, new ArrayList<>()); entities.add(cit); - // Create position + // Create the position where this entity will be pasted relative to the location Vector origin2 = origin == null ? new Vector(0, 0, 0) : origin; - int x = spot.getBlockX() - origin2.getBlockX(); - int y = spot.getBlockY() - origin2.getBlockY(); - int z = spot.getBlockZ() - origin2.getBlockZ(); + int x = loc.getBlockX() - origin2.getBlockX(); + int y = loc.getBlockY() - origin2.getBlockY(); + int z = loc.getBlockZ() - origin2.getBlockZ(); Vector pos = new Vector(x, y, z); // Store bpEntities.put(pos, entities); // Update the map diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 86ee2e070..2e6083026 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -36,6 +36,7 @@ import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.MythicMobsHook; +import world.bentobox.bentobox.hooks.ZNPCsPlusHook; import world.bentobox.bentobox.nms.PasteHandler; /** @@ -176,8 +177,8 @@ public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner public static CompletableFuture setEntity(Island island, Location location, List list) { World world = location.getWorld(); assert world != null; - return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null) - .forEach(k -> spawnBlueprintEntity(k, location, island))); + return Util.getChunkAtAsync(location) + .thenRun(() -> list.stream().forEach(k -> spawnBlueprintEntity(k, location, island))); } /** @@ -188,7 +189,7 @@ public static CompletableFuture setEntity(Island island, Location location * @return true if Bukkit entity spawned, false another plugin entity spawned */ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) { - // Npc entity + // FancyNpc entity if (k.getNpc() != null && plugin.getHooks().getHook("FancyNpcs").filter(mmh -> mmh instanceof FancyNpcsHook).map(mmh -> { try { @@ -201,6 +202,19 @@ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island // Npc has spawned. return false; } + // ZNPCsPlus + if (k.getNpc() != null + && plugin.getHooks().getHook("ZNPCsPlus").filter(mmh -> mmh instanceof ZNPCsPlusHook).map(znpch -> { + try { + return ((ZNPCsPlusHook) znpch).spawnNpc(k.getNpc(), location); + } catch (InvalidConfigurationException e) { + plugin.logError("ZNPCsPlus loading failed in blueprint."); + return false; + } + }).orElse(false)) { + // Npc has spawned. + return false; + } // Mythic Mobs entity if (k.getMythicMobsRecord() != null && plugin.getHooks().getHook("MythicMobs") @@ -210,6 +224,10 @@ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island // MythicMob has spawned. return false; } + if (k.getType() == null) { + // Nothing + return false; + } LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType()); if (k.getCustomName() != null) { String customName = k.getCustomName(); diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index 00434d260..3cf2fac55 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -514,6 +514,54 @@ public static int getMinecraftPatchVersion() { return PaperLib.getMinecraftPatchVersion(); } + /** + * Checks if the given version is compatible with the required version. + * + *

+ * A version is considered compatible if: + *

    + *
  • The major, minor, and patch components of the given version are greater than or equal to those of the required version.
  • + *
  • If the numeric components are equal, the absence of "-SNAPSHOT" in the given version takes precedence (i.e., release versions are considered more compatible than SNAPSHOT versions).
  • + *
+ *

+ * + * @param version the version to check, in the format "major.minor.patch[-SNAPSHOT]". + * @param requiredVersion the required version, in the format "major.minor.patch[-SNAPSHOT]". + * @return {@code true} if the given version is compatible with the required version; {@code false} otherwise. + * + *

+ * Examples: + *

    + *
  • {@code isVersionCompatible("2.1.0", "2.0.0-SNAPSHOT")} returns {@code true}
  • + *
  • {@code isVersionCompatible("2.0.0", "2.0.0-SNAPSHOT")} returns {@code true}
  • + *
  • {@code isVersionCompatible("2.0.0-SNAPSHOT", "2.0.0")} returns {@code false}
  • + *
  • {@code isVersionCompatible("1.9.9", "2.0.0-SNAPSHOT")} returns {@code false}
  • + *
+ *

+ */ + public static boolean isVersionCompatible(String version, String requiredVersion) { + String[] versionParts = version.replace("-SNAPSHOT", "").split("\\."); + String[] requiredVersionParts = requiredVersion.replace("-SNAPSHOT", "").split("\\."); + + for (int i = 0; i < Math.max(versionParts.length, requiredVersionParts.length); i++) { + int vPart = i < versionParts.length ? Integer.parseInt(versionParts[i]) : 0; + int rPart = i < requiredVersionParts.length ? Integer.parseInt(requiredVersionParts[i]) : 0; + + if (vPart > rPart) { + return true; + } else if (vPart < rPart) { + return false; + } + } + + // If numeric parts are equal, prioritize SNAPSHOT as lower precedence + boolean isVersionSnapshot = version.contains("-SNAPSHOT"); + boolean isRequiredSnapshot = requiredVersion.contains("-SNAPSHOT"); + + // If required version is a full release but current version is SNAPSHOT, it's incompatible + return !(!isRequiredSnapshot && isVersionSnapshot); + } + /** * Check if the server has access to the Spigot API * @return True for Spigot and Paper environments diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fcb054854..9ddec656a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -24,6 +24,7 @@ softdepend: - LuckPerms - EconomyPlus - MythicMobs + - ZNPCsPlus libraries: - mysql:mysql-connector-java:${mysql.version} From d6c11e8098478cc02b2c980a5e6f98dcf435bdd0 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 09:28:18 -0800 Subject: [PATCH 11/24] Fix tests --- .../world/bentobox/bentobox/util/DefaultPasteUtilTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/world/bentobox/bentobox/util/DefaultPasteUtilTest.java b/src/test/java/world/bentobox/bentobox/util/DefaultPasteUtilTest.java index 38135c66d..768b1ad32 100644 --- a/src/test/java/world/bentobox/bentobox/util/DefaultPasteUtilTest.java +++ b/src/test/java/world/bentobox/bentobox/util/DefaultPasteUtilTest.java @@ -141,6 +141,9 @@ public void setUp() throws Exception { when(plugin.getHooks()).thenReturn(hooksManager); when(plugin.getPlayers()).thenReturn(pm); + + // Blueprint Entity + when(blueprintEntity.getType()).thenReturn(EntityType.PLAYER); } /** From 2edad796b446241283a4ca7fefd4cf5d0cc614a5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 09:28:50 -0800 Subject: [PATCH 12/24] Remove unused import --- .../java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java index d72df0847..33c1fb186 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java @@ -3,17 +3,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.World; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.EntityType; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; From 00ce92b9af2024f9517712c9db55c5c3ab8442f0 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 10:09:33 -0800 Subject: [PATCH 13/24] Improved tests --- .../bentobox/hooks/ZNPCsPlusHook.java | 2 +- .../bentobox/hooks/MythicMobsHookTest.java | 8 - .../bentobox/hooks/ZNPCsPlusHookTest.java | 174 ++++++++++++++++++ 3 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/hooks/ZNPCsPlusHookTest.java diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java index 33c1fb186..d9a31dfef 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java @@ -65,7 +65,7 @@ public boolean hook() { if (!hooked) { BentoBox.getInstance().logError("Could not hook into FancyNpcs"); } - return hooked; // The hook process shouldn't fail + return hooked; } @Override diff --git a/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java b/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java index 87fe88631..c9dc48437 100644 --- a/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java +++ b/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java @@ -21,7 +21,6 @@ import org.bukkit.entity.Entity; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -98,13 +97,6 @@ public void setUp() throws Exception { hook = new MythicMobsHook(); } - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - } - /** * Test method for {@link world.bentobox.bentobox.hooks.MythicMobsHook#hook()}. */ diff --git a/src/test/java/world/bentobox/bentobox/hooks/ZNPCsPlusHookTest.java b/src/test/java/world/bentobox/bentobox/hooks/ZNPCsPlusHookTest.java new file mode 100644 index 000000000..cdce3e51e --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/hooks/ZNPCsPlusHookTest.java @@ -0,0 +1,174 @@ +package world.bentobox.bentobox.hooks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.util.Vector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import lol.pyr.znpcsplus.api.NpcApi; +import lol.pyr.znpcsplus.api.NpcApiProvider; +import lol.pyr.znpcsplus.api.npc.Npc; +import lol.pyr.znpcsplus.api.npc.NpcEntry; +import lol.pyr.znpcsplus.api.npc.NpcRegistry; +import lol.pyr.znpcsplus.api.serialization.NpcSerializer; +import lol.pyr.znpcsplus.api.serialization.NpcSerializerRegistry; +import lol.pyr.znpcsplus.util.NpcLocation; +import world.bentobox.bentobox.BentoBox; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ BentoBox.class, Bukkit.class, NpcApiProvider.class }) +public class ZNPCsPlusHookTest { + + @Mock + private BentoBox plugin; + @Mock + private PluginManager pim; + @Mock + private Plugin mythicMobs; + @Mock + private Location location; + @Mock + private World world; + @Mock + private Plugin npcPlugin; + private ZNPCsPlusHook hook; + @Mock + private NpcEntry entry; + @Mock + private NpcApi npcApi; + @Mock + private NpcSerializerRegistry npcSerReg; + @Mock + private NpcSerializer ser; + @Mock + private NpcRegistry registry; + @Mock + private Npc npc; + @Mock + private NpcLocation npcLoc; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(Bukkit.getPluginManager()).thenReturn(pim); + when(npcPlugin.getDescription()).thenReturn(new PluginDescriptionFile("ZNPCsPlus", "2.0.0-SNAPSHOT", "main")); + when(pim.getPlugin("ZNPCsPlus")).thenReturn(npcPlugin); + // Location + when(world.getName()).thenReturn("bskyblock"); + when(location.getWorld()).thenReturn(world); + // NpcApiProvider + PowerMockito.mockStatic(NpcApiProvider.class, Mockito.RETURNS_MOCKS); + when(NpcApiProvider.get()).thenReturn(npcApi); + + when(registry.getAll()).thenAnswer(invocation -> List.of(entry)); + + when(npcLoc.getBlockX()).thenReturn(0); + when(npcLoc.getBlockY()).thenReturn(0); + when(npcLoc.getBlockZ()).thenReturn(0); + when(npc.getWorld()).thenReturn(world); + + when(npc.getLocation()).thenReturn(npcLoc); + + when(npcApi.getNpcRegistry()).thenReturn(registry); + when(npcApi.getNpcSerializerRegistry()).thenReturn(npcSerReg); + when(npcSerReg.getSerializer(any())).thenReturn(ser); + YamlConfiguration yaml = new YamlConfiguration(); + yaml.set("test", "test"); + when(ser.serialize(any())).thenReturn(yaml); + when(entry.getNpc()).thenReturn(npc); + when(ser.deserialize(any())).thenReturn(entry); + + + hook = new ZNPCsPlusHook(); + } + + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#hook()}. + */ + @Test + public void testHook() { + // Not hooked + assertFalse(hook.hook()); + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#getFailureCause()}. + */ + @Test + public void testGetFailureCause() { + when(npcPlugin.getDescription()).thenReturn(new PluginDescriptionFile("ZNPCsPlus", "1.0.0", "main")); + assertEquals("ZNPCsPlus version 2.0.0-SNAPSHOT required or later. You are running 1.0.0", + hook.getFailureCause()); + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#ZNPCsPlusHook()}. + */ + @Test + public void testZNPCsPlusHook() { + assertNotNull(hook); + assertEquals(Material.PLAYER_HEAD, hook.getIcon()); + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#serializeNPC(lol.pyr.znpcsplus.api.npc.NpcEntry, org.bukkit.util.Vector)}. + */ + @Test + public void testSerializeNPC() { + assertEquals("test: test\n", hook.serializeNPC(entry, new Vector(1, 1, 1))); + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#spawnNpc(java.lang.String, org.bukkit.Location)}. + */ + @Test + public void testSpawnNpc() { + try { + assertTrue(hook.spawnNpc("", location)); + } catch (InvalidConfigurationException e) { + e.printStackTrace(); + } + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.ZNPCsPlusHook#getNpcsInArea(org.bukkit.World, java.util.List, org.bukkit.util.Vector)}. + */ + @Test + public void testGetNpcsInArea() { + hook.getNpcsInArea(world, List.of(new Vector(0, 0, 0)), new Vector(0, 0, 0)); + } + +} From 945670be04105e002b40775b968c20819eb95370 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 10:18:11 -0800 Subject: [PATCH 14/24] Add tests for Util.isVersionCompatible method --- .../bentobox/bentobox/util/UtilTest.java | 110 +++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/src/test/java/world/bentobox/bentobox/util/UtilTest.java b/src/test/java/world/bentobox/bentobox/util/UtilTest.java index 4745b6ce3..d6162354d 100644 --- a/src/test/java/world/bentobox/bentobox/util/UtilTest.java +++ b/src/test/java/world/bentobox/bentobox/util/UtilTest.java @@ -494,4 +494,112 @@ public void testTranslateColorCodesHex() { assertEquals("§x§f§f§0§0§0§0full hex", Util.translateColorCodes("&#ff0000 full hex")); assertEquals("&#ggg outside hex range", Util.translateColorCodes("&#ggg outside hex range")); } -} + + /** + * Tests if the method returns true for identical versions without SNAPSHOT. + */ + @Test + public void testVersionIsCompatible_SameVersion() { + assertTrue("Same versions should be compatible", Util.isVersionCompatible("2.0.0", "2.0.0")); + } + + /** + * Tests if the method returns true for identical SNAPSHOT versions. + */ + @Test + public void testVersionIsCompatible_SnapshotToSnapshot() { + assertTrue("Same SNAPSHOT versions should be compatible", + Util.isVersionCompatible("2.0.0-SNAPSHOT", "2.0.0-SNAPSHOT")); + } + + /** + * Tests if the method considers release versions compatible with their SNAPSHOT equivalents. + */ + @Test + public void testVersionIsCompatible_ReleaseGreaterThanSnapshot() { + assertTrue("Release version should be compatible with SNAPSHOT of the same version", + Util.isVersionCompatible("2.0.0", "2.0.0-SNAPSHOT")); + } + + /** + * Tests if the method considers SNAPSHOT versions less compatible than release versions. + */ + @Test + public void testVersionIsCompatible_SnapshotLessThanRelease() { + assertFalse("SNAPSHOT version should not be compatible with release of the same version", + Util.isVersionCompatible("2.0.0-SNAPSHOT", "2.0.0")); + } + + /** + * Tests if the method correctly identifies compatibility for a higher major version. + */ + @Test + public void testVersionIsCompatible_MajorVersionGreater() { + assertTrue("Higher major version should be compatible", Util.isVersionCompatible("3.0.0", "2.0.0")); + } + + /** + * Tests if the method correctly identifies incompatibility for a lower major version. + */ + @Test + public void testVersionIsCompatible_MajorVersionLower() { + assertFalse("Lower major version should not be compatible", Util.isVersionCompatible("1.9.9", "2.0.0")); + } + + /** + * Tests if the method correctly identifies compatibility for a higher minor version. + */ + @Test + public void testVersionIsCompatible_MinorVersionGreater() { + assertTrue("Higher minor version should be compatible", Util.isVersionCompatible("2.1.0", "2.0.0")); + } + + /** + * Tests if the method correctly identifies incompatibility for a lower minor version. + */ + @Test + public void testVersionIsCompatible_MinorVersionLower() { + assertFalse("Lower minor version should not be compatible", Util.isVersionCompatible("2.0.0", "2.1.0")); + } + + /** + * Tests if the method correctly identifies compatibility for a higher patch version. + */ + @Test + public void testVersionIsCompatible_PatchVersionGreater() { + assertTrue("Higher patch version should be compatible", Util.isVersionCompatible("2.0.1", "2.0.0")); + } + + /** + * Tests if the method correctly identifies incompatibility for a lower patch version. + */ + @Test + public void testVersionIsCompatible_PatchVersionLower() { + assertFalse("Lower patch version should not be compatible", Util.isVersionCompatible("2.0.0", "2.0.1")); + } + + /** + * Tests if the method correctly handles compatibility when both versions have a SNAPSHOT suffix. + */ + @Test + public void testVersionIsCompatible_HandlesSnapshotSuffix() { + assertTrue("Higher patch version (SNAPSHOT) should be compatible with lower patch version (SNAPSHOT)", + Util.isVersionCompatible("2.0.1-SNAPSHOT", "2.0.0-SNAPSHOT")); + } + + /** + * Tests if the method throws an exception for an empty version string. + */ + @Test(expected = NumberFormatException.class) + public void testVersionIsCompatible_EmptyVersion() { + Util.isVersionCompatible("", "2.0.0"); + } + + /** + * Tests if the method throws an exception for a null version string. + */ + @Test(expected = NullPointerException.class) + public void testVersionIsCompatible_NullVersion() { + Util.isVersionCompatible(null, "2.0.0"); + } +} \ No newline at end of file From 64fd48ead6a57f9f21f830ad7d14f86977cd2203 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 27 Dec 2024 08:36:13 -0800 Subject: [PATCH 15/24] WIP - store data on Display Entities --- pom.xml | 7 + .../blueprints/BlueprintClipboard.java | 6 + .../dataobjects/BlueprintEntity.java | 147 ++++++++++++++++++ .../json/adapters/MaterialTypeAdapter.java | 2 +- .../bentobox/hooks/FancyHologramsHook.java | 35 +++++ .../bentobox/util/DefaultPasteUtil.java | 2 + src/main/resources/plugin.yml | 2 + 7 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java diff --git a/pom.xml b/pom.xml index e29d7d061..3198bfad0 100644 --- a/pom.xml +++ b/pom.xml @@ -419,6 +419,13 @@ 2.0.0-SNAPSHOT provided + + + de.oliver + FancyHolograms + 2.4.1 + provided + diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index 6d678bd08..a5ae6ad1b 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -22,6 +22,7 @@ import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; import org.bukkit.entity.ChestedHorse; +import org.bukkit.entity.Display; import org.bukkit.entity.Entity; import org.bukkit.entity.Horse; import org.bukkit.entity.Player; @@ -356,6 +357,11 @@ private List setEntities(List ents) { mmh.filter(mm -> mm.isMythicMob(entity)).map(mm -> mm.getMythicMob(entity)) .ifPresent(bpe::setMythicMobsRecord); + // Display entities + if (entity instanceof Display disp) { + BentoBox.getInstance().logDebug(disp.getAsString()); + bpe.storeDisplay(disp); + } bpEnts.add(bpe); } return bpEnts; diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index 2ce285fee..b37bc3fe5 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -2,19 +2,32 @@ import java.util.Map; +import org.bukkit.Color; import org.bukkit.DyeColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; +import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.ChestedHorse; +import org.bukkit.entity.Display; +import org.bukkit.entity.Display.Billboard; +import org.bukkit.entity.Display.Brightness; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Horse; import org.bukkit.entity.Horse.Style; +import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Tameable; +import org.bukkit.entity.TextDisplay; +import org.bukkit.entity.TextDisplay.TextAlignment; import org.bukkit.entity.Villager; import org.bukkit.entity.Villager.Profession; import org.bukkit.inventory.ItemStack; import org.bukkit.material.Colorable; +import org.bukkit.util.Transformation; import com.google.gson.annotations.Expose; @@ -342,4 +355,138 @@ public String toString() { + (villagerType != null ? "villagerType=" + villagerType : "") + "]"; } + + + + @Expose + public DisplayRec displayRec; + @Expose + public TextDisplayRec textDisp; + @Expose + public BlockData blockDisp; + @Expose + public ItemStack itemDisp; + + public record DisplayRec(@Expose Billboard billboard, @Expose Brightness brightness, @Expose float height, + @Expose float width, @Expose Color glowColorOverride, @Expose int interpolationDelay, + @Expose int interpolationDuration, @Expose float shadowRadius, @Expose float shadowStrength, + @Expose int teleportDuration, @Expose Transformation transformation, @Expose float range) { + } + + public record TextDisplayRec(@Expose String text, @Expose TextAlignment alignment, @Expose Color bgColor, + @Expose BlockFace face, @Expose int lWidth, @Expose byte opacity, @Expose boolean isShadowed, + @Expose boolean isSeeThrough, @Expose boolean isDefaultBg) { + } + + /** + * BlockDisplay, ItemDisplay, TextDisplay + * @param disp display entity + */ + public void storeDisplay(Display disp) { + // Generic items + displayRec = new DisplayRec(disp.getBillboard(), disp.getBrightness(), disp.getDisplayHeight(), + disp.getDisplayWidth(), disp.getGlowColorOverride(), disp.getInterpolationDelay(), + disp.getInterpolationDuration(), disp.getShadowRadius(), disp.getShadowStrength(), + disp.getTeleportDuration(), disp.getTransformation(), disp.getViewRange()); + // Class specific items + if (disp instanceof BlockDisplay bd) { + this.blockDisp = bd.getBlock(); + } else if (disp instanceof ItemDisplay id) { + itemDisp = id.getItemStack(); + } else if (disp instanceof TextDisplay td) { + textDisp = new TextDisplayRec(td.getText(), td.getAlignment(), td.getBackgroundColor(), + td.getFacing(), td.getLineWidth(), td.getTextOpacity(), td.isShadowed(), td.isSeeThrough(), + td.isDefaultBackground()); + } + + // , getBrightness, getDisplayHeight, getDisplayWidth, getGlowColorOverride, getInterpolationDelay, getInterpolationDuration, + //getShadowRadius, getShadowStrength, getTeleportDuration, getTransformation, getViewRange, setBillboard, setBrightness, + // setDisplayHeight, setDisplayWidth, setGlowColorOverride, setInterpolationDelay, setInterpolationDuration, setShadowRadius, setShadowStrength, setTeleportDuration, setTransformation, setTransformationMatrix, setViewRange + } + + /** + * Sets any display entity properties to the location, e.g. holograms + * @param pos location + */ + public void setDisplay(Location pos) { + World world = pos.getWorld(); + Display d = null; + if (this.blockDisp != null) { + // Block Display + d = world.spawn(pos, BlockDisplay.class); + ((BlockDisplay) d).setBlock(this.blockDisp); + } else if (this.itemDisp != null) { + // Item Display + d = world.spawn(pos, ItemDisplay.class); + ((ItemDisplay) d).setItemStack(itemDisp); + } else if (this.textDisp != null) { + // Block Display + d = world.spawn(pos, TextDisplay.class); + ((TextDisplay) d).setText(textDisp.text()); + ((TextDisplay) d).setAlignment(textDisp.alignment()); + ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); + ((TextDisplay) d).setLineWidth(textDisp.lWidth()); + ((TextDisplay) d).setTextOpacity(textDisp.opacity()); + ((TextDisplay) d).setShadowed(textDisp.isShadowed()); + ((TextDisplay) d).setSeeThrough(textDisp.isSeeThrough()); + ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); + } + if (d != null && this.displayRec != null) { + d.setBillboard(displayRec.billboard()); + d.setBrightness(displayRec.brightness()); + d.setDisplayHeight(displayRec.height()); + d.setDisplayWidth(displayRec.width()); + d.setGlowColorOverride(displayRec.glowColorOverride()); + d.setInterpolationDelay(displayRec.interpolationDelay()); + d.setInterpolationDuration(displayRec.interpolationDuration()); + d.setShadowRadius(displayRec.shadowRadius()); + d.setShadowStrength(displayRec.shadowStrength()); + d.setTeleportDuration(displayRec.teleportDuration()); + d.setTransformation(displayRec.transformation()); + d.setViewRange(displayRec.range()); + } + } + + /** + * @return the displayRec + */ + public DisplayRec getDisplayRec() { + return displayRec; + } + + /** + * @param displayRec the displayRec to set + */ + public void setDisplayRec(DisplayRec displayRec) { + this.displayRec = displayRec; + } + + /** + * @return the blockDisp + */ + public BlockData getBlockDisp() { + return blockDisp; + } + + /** + * @param blockDisp the blockDisp to set + */ + public void setBlockDisp(BlockData blockDisp) { + this.blockDisp = blockDisp; + } + + /** + * @return the itemDisp + */ + public ItemStack getItemDisp() { + return itemDisp; + } + + /** + * @param itemDisp the itemDisp to set + */ + public void setItemDisp(ItemStack itemDisp) { + this.itemDisp = itemDisp; + } + } diff --git a/src/main/java/world/bentobox/bentobox/database/json/adapters/MaterialTypeAdapter.java b/src/main/java/world/bentobox/bentobox/database/json/adapters/MaterialTypeAdapter.java index e5efa70a9..13e5d3401 100644 --- a/src/main/java/world/bentobox/bentobox/database/json/adapters/MaterialTypeAdapter.java +++ b/src/main/java/world/bentobox/bentobox/database/json/adapters/MaterialTypeAdapter.java @@ -17,7 +17,7 @@ /** * Minecraft 1.20 changed GRASS to SHORT_GRASS. This class provides and backwards compatibility when loading - * databased files stored with previous versions. It can be extended in the future if further enum changes are made. + * database files stored with previous versions. It can be extended in the future if further enum changes are made. * @author tastybento * @since 2.0.0 */ diff --git a/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java b/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java new file mode 100644 index 000000000..650e39133 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java @@ -0,0 +1,35 @@ +package world.bentobox.bentobox.hooks; + +import org.bukkit.Material; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.hooks.Hook; + +/** + * Provides copy and pasting of FancyHolograms in blueprints + * + * @author tastybento + * @since 3.2.0 + */ +public class FancyHologramsHook extends Hook { + + public FancyHologramsHook() { + super("FancyHolograms", Material.END_PORTAL); + } + + + @Override + public boolean hook() { + boolean hooked = this.isPluginAvailable(); + if (!hooked) { + BentoBox.getInstance().logError("Could not hook into FancyHolograms"); + } + return hooked; // The hook process shouldn't fail + } + + @Override + public String getFailureCause() { + return null; // The hook process shouldn't fail + } + +} diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 2e6083026..2de5ae600 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -189,6 +189,8 @@ public static CompletableFuture setEntity(Island island, Location location * @return true if Bukkit entity spawned, false another plugin entity spawned */ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) { + // Display Entity (holograms, etc.) + k.setDisplay(location); // FancyNpc entity if (k.getNpc() != null && plugin.getHooks().getHook("FancyNpcs").filter(mmh -> mmh instanceof FancyNpcsHook).map(mmh -> { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9ddec656a..d314626f3 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -25,6 +25,8 @@ softdepend: - EconomyPlus - MythicMobs - ZNPCsPlus + - FancyNpcs + - FancyHolograms libraries: - mysql:mysql-connector-java:${mysql.version} From 62a4f63040b9deae520b907c34d13c1f772fe96e Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Dec 2024 09:06:28 -0800 Subject: [PATCH 16/24] Working with debug --- .../blueprints/BlueprintClipboard.java | 22 +-- .../bentobox/blueprints/DisplayListener.java | 51 +++++++ .../dataobjects/BlueprintEntity.java | 139 +++++++----------- .../bentobox/managers/BlueprintsManager.java | 3 + .../bentobox/util/DefaultPasteUtil.java | 1 + 5 files changed, 124 insertions(+), 92 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index a5ae6ad1b..f590469c7 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -18,6 +18,7 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; +import org.bukkit.block.data.Attachable; import org.bukkit.block.sign.Side; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; @@ -30,7 +31,6 @@ import org.bukkit.entity.Villager; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import org.bukkit.material.Attachable; import org.bukkit.material.Colorable; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.BoundingBox; @@ -163,9 +163,8 @@ private void copyAsync(World world, User user, List vectorsToCopy, int s List ents = world.getEntities().stream() .filter(Objects::nonNull) .filter(e -> !(e instanceof Player)) - .filter(e -> new Vector(Math.rint(e.getLocation().getX()), - Math.rint(e.getLocation().getY()), - Math.rint(e.getLocation().getZ())).equals(v)) + .filter(e -> new Vector(e.getLocation().getBlockX(), e.getLocation().getBlockY(), + e.getLocation().getBlockZ()).equals(v)) .toList(); if (copyBlock(v.toLocation(world), copyAir, copyBiome, ents)) { count++; @@ -231,7 +230,7 @@ private boolean copyBlock(Location l, boolean copyAir, boolean copyBiome, List()); @@ -285,11 +287,11 @@ private BlueprintBlock bluePrintBlock(Vector pos, Block block, boolean copyBiome } } } - + BentoBox.getInstance().logDebug("Spawner"); if (blockState instanceof CreatureSpawner spawner) { b.setCreatureSpawner(getSpawner(spawner)); } - + BentoBox.getInstance().logDebug("Banners"); // Banners if (blockState instanceof Banner banner) { b.setBannerPatterns(banner.getPatterns()); @@ -359,7 +361,7 @@ private List setEntities(List ents) { // Display entities if (entity instanceof Display disp) { - BentoBox.getInstance().logDebug(disp.getAsString()); + BentoBox.getInstance().logDebug("Storing display: " + disp.getAsString()); bpe.storeDisplay(disp); } bpEnts.add(bpe); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java b/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java new file mode 100644 index 000000000..c893e19b4 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java @@ -0,0 +1,51 @@ +package world.bentobox.bentobox.blueprints; + +import java.util.UUID; + +import org.bukkit.NamespacedKey; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Display; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.persistence.PersistentDataType; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; + +/** + * Provides a listener for the Display Objects pasted when a hologram is interacted with + * https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/player/PlayerInteractAtEntityEvent.html + */ +public class DisplayListener implements Listener { + + @EventHandler + public void onPlayerInteractEntity(PlayerInteractAtEntityEvent event) { + BentoBox.getInstance().logDebug(event.getEventName()); + if (event.getRightClicked() instanceof ArmorStand) { + ArmorStand armorStand = (ArmorStand) event.getRightClicked(); + NamespacedKey key = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); + + if (armorStand.getPersistentDataContainer().has(key, PersistentDataType.STRING)) { + String displayEntityUUID = armorStand.getPersistentDataContainer().get(key, PersistentDataType.STRING); + + // Fetch the associated DisplayEntity by UUID + World world = armorStand.getWorld(); + world.getEntitiesByClass(Display.class).stream() + .filter(e -> e.getUniqueId().equals(UUID.fromString(displayEntityUUID))).findFirst() + .ifPresent(e -> { + User user = User.getInstance(event.getPlayer()); + user.sendRawMessage("You interacted with a DisplayEntity! "); + event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, + 1F); + e.remove(); + + }); + // Perform actions related to the DisplayEntity + + } + } + } +} diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index b37bc3fe5..804cb165a 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -2,14 +2,17 @@ import java.util.Map; +import org.bukkit.Bukkit; import org.bukkit.Color; import org.bukkit.DyeColor; import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; +import org.bukkit.entity.ArmorStand; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.Display; @@ -20,6 +23,7 @@ import org.bukkit.entity.Horse; import org.bukkit.entity.Horse.Style; import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.ItemDisplay.ItemDisplayTransform; import org.bukkit.entity.Tameable; import org.bukkit.entity.TextDisplay; import org.bukkit.entity.TextDisplay.TextAlignment; @@ -27,10 +31,16 @@ import org.bukkit.entity.Villager.Profession; import org.bukkit.inventory.ItemStack; import org.bukkit.material.Colorable; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.Transformation; +import org.bukkit.util.Vector; import com.google.gson.annotations.Expose; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.ItemDispRec.DisplayRec; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.ItemDispRec.TextDisplayRec; + /** * @author tastybento * @since 1.5.0 @@ -81,8 +91,16 @@ public record MythicMobRecord(String type, String displayName, double level, flo private Integer experience; @Expose private Villager.Type villagerType; + // Position within the block + @Expose + private double x; + @Expose + private double y; + @Expose + private double z; /** + * Adjusts the entity according to how it was stored * @since 1.8.0 */ public void configureEntity(Entity e) { @@ -115,6 +133,11 @@ public void configureEntity(Entity e) { if (style != null && e instanceof Horse horse) { horse.setStyle(style); } + // Shift to the in-block location + Vector add = new Vector(x, y, z); + BentoBox.getInstance().logDebug("entity is at " + e.getLocation().toVector() + " and adding " + add); + e.getLocation().add(add); + BentoBox.getInstance().logDebug("entity is now at " + e.getLocation().toVector()); } /** @@ -330,42 +353,22 @@ public String getNpc() { } /** - * @param citizen the citizen to set + * @param npc the citizen to set */ - public void setNpc(String citizen) { - this.npc = citizen; - } - - @Override - public String toString() { - return "BlueprintEntity [" + (npc != null ? "npc=" + npc + ", " : "") - + (MMtype != null ? "MMtype=" + MMtype + ", " : "") - + (MMLevel != null ? "MMLevel=" + MMLevel + ", " : "") - + (MMStance != null ? "MMStance=" + MMStance + ", " : "") - + (MMpower != null ? "MMpower=" + MMpower + ", " : "") + (color != null ? "color=" + color + ", " : "") - + (type != null ? "type=" + type + ", " : "") - + (customName != null ? "customName=" + customName + ", " : "") - + (tamed != null ? "tamed=" + tamed + ", " : "") + (chest != null ? "chest=" + chest + ", " : "") - + (adult != null ? "adult=" + adult + ", " : "") - + (domestication != null ? "domestication=" + domestication + ", " : "") - + (inventory != null ? "inventory=" + inventory + ", " : "") - + (style != null ? "style=" + style + ", " : "") + (level != null ? "level=" + level + ", " : "") - + (profession != null ? "profession=" + profession + ", " : "") - + (experience != null ? "experience=" + experience + ", " : "") - + (villagerType != null ? "villagerType=" + villagerType : "") + "]"; + public void setNpc(String npc) { + this.npc = npc; } - - - @Expose public DisplayRec displayRec; @Expose public TextDisplayRec textDisp; @Expose - public BlockData blockDisp; + public BlueprintBlock blockDisp; @Expose - public ItemStack itemDisp; + public ItemDispRec itemDisp; + + public record ItemDispRec(@Expose ItemStack item, @Expose ItemDisplayTransform itemDispTrans) {} public record DisplayRec(@Expose Billboard billboard, @Expose Brightness brightness, @Expose float height, @Expose float width, @Expose Color glowColorOverride, @Expose int interpolationDelay, @@ -390,18 +393,18 @@ public void storeDisplay(Display disp) { disp.getTeleportDuration(), disp.getTransformation(), disp.getViewRange()); // Class specific items if (disp instanceof BlockDisplay bd) { - this.blockDisp = bd.getBlock(); + this.blockDisp = new BlueprintBlock(bd.getBlock().getAsString()); } else if (disp instanceof ItemDisplay id) { - itemDisp = id.getItemStack(); + itemDisp = new ItemDispRec(id.getItemStack(), id.getItemDisplayTransform()); } else if (disp instanceof TextDisplay td) { textDisp = new TextDisplayRec(td.getText(), td.getAlignment(), td.getBackgroundColor(), td.getFacing(), td.getLineWidth(), td.getTextOpacity(), td.isShadowed(), td.isSeeThrough(), td.isDefaultBackground()); } - - // , getBrightness, getDisplayHeight, getDisplayWidth, getGlowColorOverride, getInterpolationDelay, getInterpolationDuration, - //getShadowRadius, getShadowStrength, getTeleportDuration, getTransformation, getViewRange, setBillboard, setBrightness, - // setDisplayHeight, setDisplayWidth, setGlowColorOverride, setInterpolationDelay, setInterpolationDuration, setShadowRadius, setShadowStrength, setTeleportDuration, setTransformation, setTransformationMatrix, setViewRange + // Store location within block + x = disp.getLocation().getX() - disp.getLocation().getBlockX(); + y = disp.getLocation().getY() - disp.getLocation().getBlockY(); + z = disp.getLocation().getZ() - disp.getLocation().getBlockZ(); } /** @@ -410,18 +413,22 @@ public void storeDisplay(Display disp) { */ public void setDisplay(Location pos) { World world = pos.getWorld(); + Location newPos = pos.clone().add(new Vector(x - 0.5D, y, z - 0.5D)); Display d = null; if (this.blockDisp != null) { // Block Display - d = world.spawn(pos, BlockDisplay.class); - ((BlockDisplay) d).setBlock(this.blockDisp); + d = world.spawn(newPos, BlockDisplay.class); + BlockData bd = Bukkit.createBlockData(this.blockDisp.getBlockData()); + ((BlockDisplay) d).setBlock(bd); } else if (this.itemDisp != null) { // Item Display - d = world.spawn(pos, ItemDisplay.class); - ((ItemDisplay) d).setItemStack(itemDisp); + d = world.spawn(newPos, ItemDisplay.class); + ((ItemDisplay) d).setItemStack(itemDisp.item()); + ((ItemDisplay) d).setItemDisplayTransform(itemDisp.itemDispTrans()); } else if (this.textDisp != null) { - // Block Display - d = world.spawn(pos, TextDisplay.class); + BentoBox.getInstance().logDebug("Text display - " + textDisp.text()); + // Text Display + d = world.spawn(newPos, TextDisplay.class); ((TextDisplay) d).setText(textDisp.text()); ((TextDisplay) d).setAlignment(textDisp.alignment()); ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); @@ -432,6 +439,8 @@ public void setDisplay(Location pos) { ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); } if (d != null && this.displayRec != null) { + BentoBox.getInstance().logDebug("General display"); + d.setCustomName(getCustomName()); d.setBillboard(displayRec.billboard()); d.setBrightness(displayRec.brightness()); d.setDisplayHeight(displayRec.height()); @@ -445,48 +454,14 @@ public void setDisplay(Location pos) { d.setTransformation(displayRec.transformation()); d.setViewRange(displayRec.range()); } + // Spawn an armor stand here so that we have a way to detect if a player interacts with the item + ArmorStand armorStand = (ArmorStand) world.spawnEntity(newPos, EntityType.ARMOR_STAND); + armorStand.setSmall(true); // Reduces size + armorStand.setGravity(false); // Prevents falling + //armorStand.setInvisible(true); + //armorStand.setMarker(true); // No hitbox + NamespacedKey key = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); + armorStand.getPersistentDataContainer().set(key, PersistentDataType.STRING, d.getUniqueId().toString()); + BentoBox.getInstance().logDebug("display set done"); } - - /** - * @return the displayRec - */ - public DisplayRec getDisplayRec() { - return displayRec; - } - - /** - * @param displayRec the displayRec to set - */ - public void setDisplayRec(DisplayRec displayRec) { - this.displayRec = displayRec; - } - - /** - * @return the blockDisp - */ - public BlockData getBlockDisp() { - return blockDisp; - } - - /** - * @param blockDisp the blockDisp to set - */ - public void setBlockDisp(BlockData blockDisp) { - this.blockDisp = blockDisp; - } - - /** - * @return the itemDisp - */ - public ItemStack getItemDisp() { - return itemDisp; - } - - /** - * @param itemDisp the itemDisp to set - */ - public void setItemDisp(ItemStack itemDisp) { - this.itemDisp = itemDisp; - } - } diff --git a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java index d13332ed5..ca6a6cef6 100644 --- a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java @@ -44,6 +44,7 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.Blueprint; import world.bentobox.bentobox.blueprints.BlueprintPaster; +import world.bentobox.bentobox.blueprints.DisplayListener; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.database.json.BentoboxTypeAdapterFactory; @@ -113,6 +114,8 @@ public BlueprintsManager(@NonNull BentoBox plugin) { gson = builder.create(); // Loaded tracker blueprintsLoaded = new HashSet<>(); + // Register Display listeners + Bukkit.getPluginManager().registerEvents(new DisplayListener(), plugin); } /** diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 2de5ae600..f688cb9de 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -189,6 +189,7 @@ public static CompletableFuture setEntity(Island island, Location location * @return true if Bukkit entity spawned, false another plugin entity spawned */ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) { + BentoBox.getInstance().logDebug("spawn blueprint entiy " + k + " at " + location); // Display Entity (holograms, etc.) k.setDisplay(location); // FancyNpc entity From 30d3fad81f68588381163c6a83f70c49bdf6afc2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Dec 2024 10:16:59 -0800 Subject: [PATCH 17/24] Fix pasting of other display entities. Remove debug. --- .../blueprints/BlueprintClipboard.java | 78 +-- .../bentobox/blueprints/DisplayListener.java | 6 - .../dataobjects/BlueprintEntity.java | 601 ++++++++++++------ .../bentobox/util/DefaultPasteUtil.java | 1 - 4 files changed, 402 insertions(+), 284 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index f590469c7..5522ea867 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -12,6 +12,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.block.Banner; import org.bukkit.block.Block; @@ -20,18 +21,11 @@ import org.bukkit.block.Sign; import org.bukkit.block.data.Attachable; import org.bukkit.block.sign.Side; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Ageable; -import org.bukkit.entity.ChestedHorse; -import org.bukkit.entity.Display; import org.bukkit.entity.Entity; -import org.bukkit.entity.Horse; import org.bukkit.entity.Player; -import org.bukkit.entity.Tameable; -import org.bukkit.entity.Villager; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import org.bukkit.material.Colorable; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; @@ -58,6 +52,10 @@ */ public class BlueprintClipboard { + /** + * Used to filter out hidden DisplayEntity armor stands when copying + */ + private static final NamespacedKey KEY = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); private @Nullable Blueprint blueprint; private @Nullable Location pos1; private @Nullable Location pos2; @@ -75,6 +73,7 @@ public class BlueprintClipboard { private Optional npc; private Optional znpc; + /** * Create a clipboard for blueprint * @param blueprint - the blueprint to load into the clipboard @@ -149,6 +148,7 @@ private void copyAsync(World world, User user, List vectorsToCopy, int s // Add all the citizens for the area in one go. This is pretty fast. bpEntities.putAll(npc.get().getNpcsInArea(world, vectorsToCopy, origin)); } + // ZNPCsPlus NPCs if (znpc.isPresent()) { bpEntities.putAll(znpc.get().getNpcsInArea(world, vectorsToCopy, origin)); } @@ -163,6 +163,7 @@ private void copyAsync(World world, User user, List vectorsToCopy, int s List ents = world.getEntities().stream() .filter(Objects::nonNull) .filter(e -> !(e instanceof Player)) + .filter(e -> !e.getPersistentDataContainer().has(KEY, PersistentDataType.STRING)) // Do not copy hidden display entities .filter(e -> new Vector(e.getLocation().getBlockX(), e.getLocation().getBlockY(), e.getLocation().getBlockZ()).equals(v)) .toList(); @@ -230,7 +231,6 @@ private boolean copyBlock(Location l, boolean copyAir, boolean copyBiome, List()); @@ -287,11 +283,9 @@ private BlueprintBlock bluePrintBlock(Vector pos, Block block, boolean copyBiome } } } - BentoBox.getInstance().logDebug("Spawner"); if (blockState instanceof CreatureSpawner spawner) { b.setCreatureSpawner(getSpawner(spawner)); } - BentoBox.getInstance().logDebug("Banners"); // Banners if (blockState instanceof Banner banner) { b.setBannerPatterns(banner.getPatterns()); @@ -320,67 +314,15 @@ private BlueprintCreatureSpawner getSpawner(CreatureSpawner spawner) { private List setEntities(List ents) { List bpEnts = new ArrayList<>(); for (Entity entity : ents) { - BlueprintEntity bpe = new BlueprintEntity(); - - bpe.setType(entity.getType()); - bpe.setCustomName(entity.getCustomName()); - if (entity instanceof Villager villager) { - setVillager(villager, bpe); - } - if (entity instanceof Colorable c && c.getColor() != null) { - bpe.setColor(c.getColor()); - } - if (entity instanceof Tameable tameable) { - bpe.setTamed(tameable.isTamed()); - } - if (entity instanceof ChestedHorse chestedHorse) { - bpe.setChest(chestedHorse.isCarryingChest()); - } - // Only set if child. Most animals are adults - if (entity instanceof Ageable ageable && !ageable.isAdult()) { - bpe.setAdult(false); - } - if (entity instanceof AbstractHorse horse) { - bpe.setDomestication(horse.getDomestication()); - bpe.setInventory(new HashMap<>()); - for (int i = 0; i < horse.getInventory().getSize(); i++) { - ItemStack item = horse.getInventory().getItem(i); - if (item != null) { - bpe.getInventory().put(i, item); - } - } - } - - if (entity instanceof Horse horse) { - bpe.setStyle(horse.getStyle()); - } - + BlueprintEntity bpe = new BlueprintEntity(entity); // Mythic mob check mmh.filter(mm -> mm.isMythicMob(entity)).map(mm -> mm.getMythicMob(entity)) .ifPresent(bpe::setMythicMobsRecord); - - // Display entities - if (entity instanceof Display disp) { - BentoBox.getInstance().logDebug("Storing display: " + disp.getAsString()); - bpe.storeDisplay(disp); - } bpEnts.add(bpe); } return bpEnts; } - /** - * Set the villager stats - * @param v - villager - * @param bpe - Blueprint Entity - */ - private void setVillager(Villager v, BlueprintEntity bpe) { - bpe.setExperience(v.getVillagerExperience()); - bpe.setLevel(v.getVillagerLevel()); - bpe.setProfession(v.getProfession()); - bpe.setVillagerType(v.getVillagerType()); - } - /** * @return the origin */ diff --git a/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java b/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java index c893e19b4..efa1dccf3 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/DisplayListener.java @@ -13,7 +13,6 @@ import org.bukkit.persistence.PersistentDataType; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.user.User; /** * Provides a listener for the Display Objects pasted when a hologram is interacted with @@ -23,7 +22,6 @@ public class DisplayListener implements Listener { @EventHandler public void onPlayerInteractEntity(PlayerInteractAtEntityEvent event) { - BentoBox.getInstance().logDebug(event.getEventName()); if (event.getRightClicked() instanceof ArmorStand) { ArmorStand armorStand = (ArmorStand) event.getRightClicked(); NamespacedKey key = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); @@ -36,15 +34,11 @@ public void onPlayerInteractEntity(PlayerInteractAtEntityEvent event) { world.getEntitiesByClass(Display.class).stream() .filter(e -> e.getUniqueId().equals(UUID.fromString(displayEntityUUID))).findFirst() .ifPresent(e -> { - User user = User.getInstance(event.getPlayer()); - user.sendRawMessage("You interacted with a DisplayEntity! "); event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F); e.remove(); }); - // Perform actions related to the DisplayEntity - } } } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index 804cb165a..e41ebcc9b 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -1,5 +1,6 @@ package world.bentobox.bentobox.blueprints.dataobjects; +import java.util.HashMap; import java.util.Map; import org.bukkit.Bukkit; @@ -38,8 +39,6 @@ import com.google.gson.annotations.Expose; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.ItemDispRec.DisplayRec; -import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.ItemDispRec.TextDisplayRec; /** * @author tastybento @@ -47,48 +46,82 @@ */ public class BlueprintEntity { - // Npc storage - @Expose - private String npc; - // MythicMobs storage public record MythicMobRecord(String type, String displayName, double level, float power, String stance) { } - // GSON can serialize records, but the record class needs to be know in advance. So this breaks out the record entries - @Expose - String MMtype; - @Expose - Double MMLevel; + /** + * Item Display Entity store + * @since 3.2.0 + */ + public record ItemDispRec(@Expose ItemStack item, @Expose ItemDisplayTransform itemDispTrans) {} + + /** + * Display Entity store + * @since 3.2.0 + */ + public record DisplayRec(@Expose Billboard billboard, @Expose Brightness brightness, @Expose float height, + @Expose float width, @Expose Color glowColorOverride, @Expose int interpolationDelay, + @Expose int interpolationDuration, @Expose float shadowRadius, @Expose float shadowStrength, + @Expose int teleportDuration, @Expose Transformation transformation, @Expose float range) { + } + + /** + * TextDisplay entity store + * @since 3.2.0 + */ + public record TextDisplayRec(@Expose String text, @Expose TextAlignment alignment, @Expose Color bgColor, + @Expose BlockFace face, @Expose int lWidth, @Expose byte opacity, @Expose boolean isShadowed, + @Expose boolean isSeeThrough, @Expose boolean isDefaultBg) { + } @Expose - String MMStance; + private Boolean adult; @Expose - Float MMpower; + public BlueprintBlock blockDisp; @Expose - private DyeColor color; + private Boolean chest; @Expose - private EntityType type; + private DyeColor color; @Expose private String customName; @Expose - private Boolean tamed; - @Expose - private Boolean chest; - @Expose - private Boolean adult; + public DisplayRec displayRec; @Expose private Integer domestication; @Expose + private Integer experience; + @Expose private Map inventory; @Expose - private Style style; + public ItemDispRec itemDisp; @Expose private Integer level; @Expose + Double MMLevel; + @Expose + Float MMpower; + @Expose + String MMStance; + // GSON can serialize records, but the record class needs to be know in advance. So this breaks out the record entries + @Expose + String MMtype; + // Npc storage + @Expose + private String npc; + @Expose private Profession profession; @Expose - private Integer experience; + private Style style; + + @Expose + private Boolean tamed; + + @Expose + public TextDisplayRec textDisp; + + @Expose + private EntityType type; @Expose private Villager.Type villagerType; // Position within the block @@ -98,12 +131,104 @@ public record MythicMobRecord(String type, String displayName, double level, flo private double y; @Expose private double z; + @Expose + private boolean glowing; + @Expose + private boolean gravity; + @Expose + private boolean visualFire; + @Expose + private boolean silent; + @Expose + private boolean invulnerable; + @Expose + private int fireTicks; + + /** + * Serializes an entity to a Blueprint Entity + * @param entity entity to serialize + * @since 3.2.0 + */ + public BlueprintEntity(Entity entity) { + this.setType(entity.getType()); + this.setCustomName(entity.getCustomName()); + this.setGlowing(entity.isGlowing()); + this.setGravity(entity.hasGravity()); + this.setVisualFire(entity.isVisualFire()); + this.setSilent(entity.isSilent()); + this.setInvulnerable(entity.isInvulnerable()); + this.setFireTicks(entity.getFireTicks()); + + if (entity instanceof Villager villager) { + configVillager(villager); + } + if (entity instanceof Colorable c && c.getColor() != null) { + this.setColor(c.getColor()); + } + if (entity instanceof Tameable tameable) { + this.setTamed(tameable.isTamed()); + } + if (entity instanceof ChestedHorse chestedHorse) { + this.setChest(chestedHorse.isCarryingChest()); + } + // Only set if child. Most animals are adults + if (entity instanceof Ageable ageable && !ageable.isAdult()) { + this.setAdult(false); + } + if (entity instanceof AbstractHorse horse) { + this.setDomestication(horse.getDomestication()); + this.setInventory(new HashMap<>()); + for (int i = 0; i < horse.getInventory().getSize(); i++) { + ItemStack item = horse.getInventory().getItem(i); + if (item != null) { + this.getInventory().put(i, item); + } + } + } + + if (entity instanceof Horse horse) { + this.setStyle(horse.getStyle()); + } + + // Display entities + if (entity instanceof Display disp) { + this.storeDisplay(disp); + } + + } + + /** + * Makes a blank BlueprintEntity + */ + public BlueprintEntity() { + // Blank constructor + } + + /** + * Set the villager stats + * @param v - villager + * @param bpe - Blueprint Entity + */ + private void configVillager(Villager v) { + this.setExperience(v.getVillagerExperience()); + this.setLevel(v.getVillagerLevel()); + this.setProfession(v.getProfession()); + this.setVillagerType(v.getVillagerType()); + } /** * Adjusts the entity according to how it was stored * @since 1.8.0 */ public void configureEntity(Entity e) { + // Set the general states + e.setGlowing(glowing); + e.setGravity(gravity); + e.setVisualFire(visualFire); + e.setSilent(silent); + e.setInvulnerable(invulnerable); + e.setFireTicks(fireTicks); + if (e instanceof Villager villager) { setVillager(villager); } @@ -133,47 +258,26 @@ public void configureEntity(Entity e) { if (style != null && e instanceof Horse horse) { horse.setStyle(style); } - // Shift to the in-block location - Vector add = new Vector(x, y, z); - BentoBox.getInstance().logDebug("entity is at " + e.getLocation().toVector() + " and adding " + add); - e.getLocation().add(add); - BentoBox.getInstance().logDebug("entity is now at " + e.getLocation().toVector()); - } - - /** - * @param v - villager - * @since 1.16.0 - */ - private void setVillager(Villager v) { - v.setProfession(profession == null ? Profession.NONE : profession); - v.setVillagerExperience(experience == null ? 0 : experience); - v.setVillagerLevel(level == null ? 0 : level); - v.setVillagerType(villagerType == null ? Villager.Type.PLAINS : villagerType); - } - - /** - * @return the color - */ - public DyeColor getColor() { - return color; + // Shift to the in-block location (remove the 0.5 that the location serializer used) + e.getLocation().add(new Vector(x - 0.5D, y, z - 0.5D)); } /** - * @param color the color to set + * @return the adult */ - public void setColor(DyeColor color) { - this.color = color; + public Boolean getAdult() { + return adult; } /** - * @return the type + * @return the chest */ - public EntityType getType() { - return type; + public Boolean getChest() { + return chest; } /** - * @param type the type to set + * @return the color */ - public void setType(EntityType type) { - this.type = type; + public DyeColor getColor() { + return color; } /** * @return the customName @@ -182,117 +286,173 @@ public String getCustomName() { return customName; } /** - * @param customName the customName to set + * @return the domestication */ - public void setCustomName(String customName) { - this.customName = customName; + public Integer getDomestication() { + return domestication; } /** - * @return the tamed + * @return the experience */ - public Boolean getTamed() { - return tamed; + public Integer getExperience() { + return experience; } /** - * @param tamed the tamed to set + * @return the inventory */ - public void setTamed(Boolean tamed) { - this.tamed = tamed; + public Map getInventory() { + return inventory; } /** - * @return the chest + * @return the level */ - public Boolean getChest() { - return chest; + public Integer getLevel() { + return level; } /** - * @param chest the chest to set + * @return the mythicMobsRecord */ - public void setChest(Boolean chest) { - this.chest = chest; + public MythicMobRecord getMythicMobsRecord() { + if (this.MMtype == null || this.MMLevel == null || this.MMpower == null || this.MMStance == null) { + return null; + } + return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance); } /** - * @return the adult + * @return the npc */ - public Boolean getAdult() { - return adult; + public String getNpc() { + return npc; } /** - * @param adult the adult to set + * @return the profession */ - public void setAdult(Boolean adult) { - this.adult = adult; + public Profession getProfession() { + return profession; } /** - * @return the domestication + * @return the style */ - public Integer getDomestication() { - return domestication; + public Style getStyle() { + return style; } + /** - * @param domestication the domestication to set + * @return the tamed */ - public void setDomestication(int domestication) { - this.domestication = domestication; + public Boolean getTamed() { + return tamed; } + /** - * @return the inventory + * @return the type */ - public Map getInventory() { - return inventory; + public EntityType getType() { + return type; } + /** - * @param inventory the inventory to set + * @return the villagerType */ - public void setInventory(Map inventory) { - this.inventory = inventory; + public Villager.Type getVillagerType() { + return villagerType; } + /** - * @return the style + * @param adult the adult to set */ - public Style getStyle() { - return style; + public void setAdult(Boolean adult) { + this.adult = adult; } + /** - * @param style the style to set + * @param chest the chest to set */ - public void setStyle(Style style) { - this.style = style; + public void setChest(Boolean chest) { + this.chest = chest; } /** - * @return the level + * @param color the color to set */ - public Integer getLevel() { - return level; + public void setColor(DyeColor color) { + this.color = color; } /** - * @param level the level to set + * @param customName the customName to set */ - public void setLevel(Integer level) { - this.level = level; + public void setCustomName(String customName) { + this.customName = customName; } /** - * @return the profession + * Sets any display entity properties to the location, e.g. holograms + * @param pos location */ - public Profession getProfession() { - return profession; + public void setDisplay(Location pos) { + World world = pos.getWorld(); + Location newPos = pos.clone().add(new Vector(x - 0.5D, y, z - 0.5D)); + Display d = null; + if (this.blockDisp != null) { + // Block Display + d = world.spawn(newPos, BlockDisplay.class); + BlockData bd = Bukkit.createBlockData(this.blockDisp.getBlockData()); + ((BlockDisplay) d).setBlock(bd); + } else if (this.itemDisp != null) { + // Item Display + d = world.spawn(newPos, ItemDisplay.class); + ((ItemDisplay) d).setItemStack(itemDisp.item()); + ((ItemDisplay) d).setItemDisplayTransform(itemDisp.itemDispTrans()); + } else if (this.textDisp != null) { + // Text Display + d = world.spawn(newPos, TextDisplay.class); + ((TextDisplay) d).setText(textDisp.text()); + ((TextDisplay) d).setAlignment(textDisp.alignment()); + ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); + ((TextDisplay) d).setLineWidth(textDisp.lWidth()); + ((TextDisplay) d).setTextOpacity(textDisp.opacity()); + ((TextDisplay) d).setShadowed(textDisp.isShadowed()); + ((TextDisplay) d).setSeeThrough(textDisp.isSeeThrough()); + ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); + } + if (d != null && this.displayRec != null) { + d.setCustomName(getCustomName()); + d.setBillboard(displayRec.billboard()); + d.setBrightness(displayRec.brightness()); + d.setDisplayHeight(displayRec.height()); + d.setDisplayWidth(displayRec.width()); + d.setGlowColorOverride(displayRec.glowColorOverride()); + d.setInterpolationDelay(displayRec.interpolationDelay()); + d.setInterpolationDuration(displayRec.interpolationDuration()); + d.setShadowRadius(displayRec.shadowRadius()); + d.setShadowStrength(displayRec.shadowStrength()); + d.setTeleportDuration(displayRec.teleportDuration()); + d.setTransformation(displayRec.transformation()); + d.setViewRange(displayRec.range()); + + // Spawn an armor stand here so that we have a way to detect if a player interacts with the item + ArmorStand armorStand = (ArmorStand) world.spawnEntity(newPos, EntityType.ARMOR_STAND); + armorStand.setSmall(true); // Reduces size + armorStand.setGravity(false); // Prevents falling + armorStand.setInvisible(true); + NamespacedKey key = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); + armorStand.getPersistentDataContainer().set(key, PersistentDataType.STRING, d.getUniqueId().toString()); + } } /** - * @param profession the profession to set + * @param domestication the domestication to set */ - public void setProfession(Profession profession) { - this.profession = profession; + public void setDomestication(int domestication) { + this.domestication = domestication; } /** - * @return the experience + * @param domestication the domestication to set */ - public Integer getExperience() { - return experience; + public void setDomestication(Integer domestication) { + this.domestication = domestication; } /** @@ -303,34 +463,17 @@ public void setExperience(Integer experience) { } /** - * @return the villagerType - */ - public Villager.Type getVillagerType() { - return villagerType; - } - - /** - * @param villagerType the villagerType to set - */ - public void setVillagerType(Villager.Type villagerType) { - this.villagerType = villagerType; - } - - /** - * @param domestication the domestication to set + * @param inventory the inventory to set */ - public void setDomestication(Integer domestication) { - this.domestication = domestication; + public void setInventory(Map inventory) { + this.inventory = inventory; } /** - * @return the mythicMobsRecord + * @param level the level to set */ - public MythicMobRecord getMythicMobsRecord() { - if (this.MMtype == null || this.MMLevel == null || this.MMpower == null || this.MMStance == null) { - return null; - } - return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance); + public void setLevel(Integer level) { + this.level = level; } /** @@ -344,41 +487,55 @@ public void setMythicMobsRecord(MythicMobRecord mmr) { this.MMStance = mmr.stance(); this.MMpower = mmr.power(); } - /** - * @return the npc + * @param npc the citizen to set */ - public String getNpc() { - return npc; + public void setNpc(String npc) { + this.npc = npc; + } + /** + * @param profession the profession to set + */ + public void setProfession(Profession profession) { + this.profession = profession; + } + /** + * @param style the style to set + */ + public void setStyle(Style style) { + this.style = style; } /** - * @param npc the citizen to set + * @param tamed the tamed to set */ - public void setNpc(String npc) { - this.npc = npc; + public void setTamed(Boolean tamed) { + this.tamed = tamed; } - @Expose - public DisplayRec displayRec; - @Expose - public TextDisplayRec textDisp; - @Expose - public BlueprintBlock blockDisp; - @Expose - public ItemDispRec itemDisp; - - public record ItemDispRec(@Expose ItemStack item, @Expose ItemDisplayTransform itemDispTrans) {} + /** + * @param type the type to set + */ + public void setType(EntityType type) { + this.type = type; + } - public record DisplayRec(@Expose Billboard billboard, @Expose Brightness brightness, @Expose float height, - @Expose float width, @Expose Color glowColorOverride, @Expose int interpolationDelay, - @Expose int interpolationDuration, @Expose float shadowRadius, @Expose float shadowStrength, - @Expose int teleportDuration, @Expose Transformation transformation, @Expose float range) { + /** + * @param v - villager + * @since 1.16.0 + */ + private void setVillager(Villager v) { + v.setProfession(profession == null ? Profession.NONE : profession); + v.setVillagerExperience(experience == null ? 0 : experience); + v.setVillagerLevel(level == null ? 0 : level); + v.setVillagerType(villagerType == null ? Villager.Type.PLAINS : villagerType); } - public record TextDisplayRec(@Expose String text, @Expose TextAlignment alignment, @Expose Color bgColor, - @Expose BlockFace face, @Expose int lWidth, @Expose byte opacity, @Expose boolean isShadowed, - @Expose boolean isSeeThrough, @Expose boolean isDefaultBg) { + /** + * @param villagerType the villagerType to set + */ + public void setVillagerType(Villager.Type villagerType) { + this.villagerType = villagerType; } /** @@ -408,60 +565,86 @@ public void storeDisplay(Display disp) { } /** - * Sets any display entity properties to the location, e.g. holograms - * @param pos location + * @return the glowing */ - public void setDisplay(Location pos) { - World world = pos.getWorld(); - Location newPos = pos.clone().add(new Vector(x - 0.5D, y, z - 0.5D)); - Display d = null; - if (this.blockDisp != null) { - // Block Display - d = world.spawn(newPos, BlockDisplay.class); - BlockData bd = Bukkit.createBlockData(this.blockDisp.getBlockData()); - ((BlockDisplay) d).setBlock(bd); - } else if (this.itemDisp != null) { - // Item Display - d = world.spawn(newPos, ItemDisplay.class); - ((ItemDisplay) d).setItemStack(itemDisp.item()); - ((ItemDisplay) d).setItemDisplayTransform(itemDisp.itemDispTrans()); - } else if (this.textDisp != null) { - BentoBox.getInstance().logDebug("Text display - " + textDisp.text()); - // Text Display - d = world.spawn(newPos, TextDisplay.class); - ((TextDisplay) d).setText(textDisp.text()); - ((TextDisplay) d).setAlignment(textDisp.alignment()); - ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); - ((TextDisplay) d).setLineWidth(textDisp.lWidth()); - ((TextDisplay) d).setTextOpacity(textDisp.opacity()); - ((TextDisplay) d).setShadowed(textDisp.isShadowed()); - ((TextDisplay) d).setSeeThrough(textDisp.isSeeThrough()); - ((TextDisplay) d).setBackgroundColor(textDisp.bgColor()); - } - if (d != null && this.displayRec != null) { - BentoBox.getInstance().logDebug("General display"); - d.setCustomName(getCustomName()); - d.setBillboard(displayRec.billboard()); - d.setBrightness(displayRec.brightness()); - d.setDisplayHeight(displayRec.height()); - d.setDisplayWidth(displayRec.width()); - d.setGlowColorOverride(displayRec.glowColorOverride()); - d.setInterpolationDelay(displayRec.interpolationDelay()); - d.setInterpolationDuration(displayRec.interpolationDuration()); - d.setShadowRadius(displayRec.shadowRadius()); - d.setShadowStrength(displayRec.shadowStrength()); - d.setTeleportDuration(displayRec.teleportDuration()); - d.setTransformation(displayRec.transformation()); - d.setViewRange(displayRec.range()); - } - // Spawn an armor stand here so that we have a way to detect if a player interacts with the item - ArmorStand armorStand = (ArmorStand) world.spawnEntity(newPos, EntityType.ARMOR_STAND); - armorStand.setSmall(true); // Reduces size - armorStand.setGravity(false); // Prevents falling - //armorStand.setInvisible(true); - //armorStand.setMarker(true); // No hitbox - NamespacedKey key = new NamespacedKey(BentoBox.getInstance(), "associatedDisplayEntity"); - armorStand.getPersistentDataContainer().set(key, PersistentDataType.STRING, d.getUniqueId().toString()); - BentoBox.getInstance().logDebug("display set done"); + public boolean isGlowing() { + return glowing; + } + + /** + * @param glowing the glowing to set + */ + public void setGlowing(boolean glowing) { + this.glowing = glowing; + } + + /** + * @return the gravity + */ + public boolean isGravity() { + return gravity; + } + + /** + * @param gravity the gravity to set + */ + public void setGravity(boolean gravity) { + this.gravity = gravity; + } + + /** + * @return the visualFire + */ + public boolean isVisualFire() { + return visualFire; + } + + /** + * @param visualFire the visualFire to set + */ + public void setVisualFire(boolean visualFire) { + this.visualFire = visualFire; + } + + /** + * @return the silent + */ + public boolean isSilent() { + return silent; + } + + /** + * @param silent the silent to set + */ + public void setSilent(boolean silent) { + this.silent = silent; + } + + /** + * @return the invulnerable + */ + public boolean isInvulnerable() { + return invulnerable; + } + + /** + * @param invulnerable the invulnerable to set + */ + public void setInvulnerable(boolean invulnerable) { + this.invulnerable = invulnerable; + } + + /** + * @return the fireTicks + */ + public int getFireTicks() { + return fireTicks; + } + + /** + * @param fireTicks the fireTicks to set + */ + public void setFireTicks(int fireTicks) { + this.fireTicks = fireTicks; } } diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index f688cb9de..2de5ae600 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -189,7 +189,6 @@ public static CompletableFuture setEntity(Island island, Location location * @return true if Bukkit entity spawned, false another plugin entity spawned */ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) { - BentoBox.getInstance().logDebug("spawn blueprint entiy " + k + " at " + location); // Display Entity (holograms, etc.) k.setDisplay(location); // FancyNpc entity From df814cce0f67587330990c84226241b3321fb1d2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Dec 2024 14:21:49 -0800 Subject: [PATCH 18/24] Fixed tests --- .../bentobox/bentobox/api/hooks/NPCHook.java | 41 +++++++++++++++++++ .../bentobox/hooks/FancyNpcsHook.java | 29 +++++++++++-- .../bentobox/hooks/ZNPCsPlusHook.java | 34 +++++++++++++-- .../bentobox/nms/CopyWorldRegenerator.java | 34 ++++++++++++--- .../AdminBlueprintCopyCommandTest.java | 10 +++-- .../AdminBlueprintLoadCommandTest.java | 2 + .../AdminBlueprintSaveCommandTest.java | 6 ++- .../blueprints/BlueprintClipboardTest.java | 2 + .../BlueprintClipboardManagerTest.java | 12 ++++-- 9 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java diff --git a/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java b/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java new file mode 100644 index 000000000..6a848215f --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java @@ -0,0 +1,41 @@ +package world.bentobox.bentobox.api.hooks; + +import java.util.List; +import java.util.Map; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import de.oliver.fancynpcs.api.Npc; +import lol.pyr.znpcsplus.api.npc.NpcEntry; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; + +/** + * NPC Hooks + * @author tastybento + * @since 3.2.0 + */ +public abstract class NPCHook extends Hook { + + protected NPCHook(@NonNull String pluginName, @NonNull Material icon) { + super(pluginName, icon); + } + + public abstract boolean spawnNpc(String yaml, Location pos) throws InvalidConfigurationException; + + public abstract Map> getNpcsInArea(World world, + List vectorsToCopy, @Nullable Vector origin); + + /** + * Remove all NPCs in chunk + * @param chunk chunk + */ + public abstract void removeNPCsInChunk(Chunk chunk); + +} diff --git a/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java index acc5ba7d8..c8c375f12 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java @@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -29,9 +30,10 @@ import de.oliver.fancynpcs.api.actions.NpcAction; import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; import de.oliver.fancynpcs.api.utils.SkinFetcher; +import lol.pyr.znpcsplus.api.npc.NpcEntry; import net.kyori.adventure.text.format.NamedTextColor; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.hooks.Hook; +import world.bentobox.bentobox.api.hooks.NPCHook; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; /** @@ -40,13 +42,13 @@ * @author tastybento * @since 3.1.0 */ -public class FancyNpcsHook extends Hook { +public class FancyNpcsHook extends NPCHook { public FancyNpcsHook() { super("FancyNpcs", Material.PLAYER_HEAD); } - public String serializeNPC(Npc npc, Vector origin) { + String serializeNPC(Npc npc, Vector origin) { if (npc == null) { throw new IllegalArgumentException("NPC cannot be null."); } @@ -265,6 +267,26 @@ public String getFailureCause() { return null; // The hook process shouldn't fail } + /** + * Return all NPCs in the chunk + * @param chunk chunk + * @return list of NPCs + */ + public List getNPCsInChunk(Chunk chunk) { + return FancyNpcsPlugin.get().getNpcManager().getAllNpcs().stream() + .filter(npc -> npc.getData().getLocation().getChunk().equals(chunk)).toList(); + } + + /** + * Remove all NPCs in chunk + * @param chunk chunk + */ + @Override + public void removeNPCsInChunk(Chunk chunk) { + getNPCsInChunk(chunk).forEach(npc -> npc.removeForAll()); + } + + @Override public Map> getNpcsInArea(World world, List vectorsToCopy, @Nullable Vector origin) { Map> bpEntities = new HashMap<>(); @@ -290,4 +312,5 @@ public String getFailureCause() { } return bpEntities; } + } diff --git a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java index d9a31dfef..9a538d7a5 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/ZNPCsPlusHook.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -17,7 +18,7 @@ import lol.pyr.znpcsplus.api.npc.NpcEntry; import lol.pyr.znpcsplus.util.NpcLocation; import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.hooks.Hook; +import world.bentobox.bentobox.api.hooks.NPCHook; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; import world.bentobox.bentobox.util.Util; @@ -27,7 +28,7 @@ * @author tastybento * @since 3.2.0 */ -public class ZNPCsPlusHook extends Hook { +public class ZNPCsPlusHook extends NPCHook { private static final String VERSION = "2.0.0-SNAPSHOT"; // Minimum version required @@ -35,13 +36,20 @@ public ZNPCsPlusHook() { super("ZNPCsPlus", Material.PLAYER_HEAD); } - public String serializeNPC(NpcEntry entry, Vector origin) { + /** + * Serialize a NpcEntry + * @param entry NPC entry + * @param origin origin point of blueprint + * @return string serializing the NPC Entry + */ + String serializeNPC(NpcEntry entry, Vector origin) { String result = NpcApiProvider.get().getNpcSerializerRegistry().getSerializer(YamlConfiguration.class) .serialize(entry) .saveToString(); return result; } + @Override public boolean spawnNpc(String yaml, Location pos) throws InvalidConfigurationException { YamlConfiguration yaml2 = new YamlConfiguration(); yaml2.loadFromString(yaml); @@ -75,6 +83,7 @@ public String getFailureCause() { + this.getPlugin().getDescription().getVersion(); } + @Override public Map> getNpcsInArea(World world, List vectorsToCopy, @Nullable Vector origin) { Map> bpEntities = new HashMap<>(); @@ -101,4 +110,23 @@ public String getFailureCause() { } return bpEntities; } + + /** + * Get a list of all the NPC IDs in this chunk + * @param chunk chunk + * @return list of NPC IDs + */ + public List getNPCsInChunk(Chunk chunk) { + return NpcApiProvider.get().getNpcRegistry().getAll().stream() + .filter(npc -> npc.getNpc().getWorld().equals(chunk.getWorld())) // Only NPCs in this world + .filter(npc -> npc.getNpc().getLocation().toBukkitLocation(chunk.getWorld()).getChunk().equals(chunk)) // Only in this chunk + .map(npc -> npc.getId()) // IDs + .toList(); + } + + @Override + public void removeNPCsInChunk(Chunk chunk) { + getNPCsInChunk(chunk).forEach(NpcApiProvider.get().getNpcRegistry()::delete); + } + } diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java index c3ac17183..4be154439 100644 --- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java +++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java @@ -43,8 +43,10 @@ import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.hooks.Hook; import world.bentobox.bentobox.database.objects.IslandDeletion; +import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.ItemsAdderHook; import world.bentobox.bentobox.hooks.SlimefunHook; +import world.bentobox.bentobox.hooks.ZNPCsPlusHook; import world.bentobox.bentobox.util.MyBiomeGrid; /** @@ -56,9 +58,18 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { private final BentoBox plugin; + private Optional npc; + private Optional znpc; protected CopyWorldRegenerator() { this.plugin = BentoBox.getInstance(); + // Fancy NPCs Hook + npc = plugin.getHooks().getHook("FancyNpcs").filter(FancyNpcsHook.class::isInstance) + .map(FancyNpcsHook.class::cast); + // ZNPCs Plus Hook + znpc = plugin.getHooks().getHook("ZNPCsPlus").filter(ZNPCsPlusHook.class::isInstance) + .map(ZNPCsPlusHook.class::cast); + } /** @@ -179,11 +190,20 @@ private CompletableFuture cleanChunk(CompletableFuture chunkFuture, ); // Similarly, when the chunk is loaded, remove all the entities in the chunk apart from players - CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> - // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above - Arrays.stream(chunk.getEntities()) - .filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())) - .forEach(Entity::remove)); + CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> { + // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above + Arrays.stream(chunk.getEntities()) + .filter(e -> !(e instanceof Player) + && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())) + .forEach(Entity::remove); + // Remove any NPCs + // Fancy NPCs Hook + npc.ifPresent(hook -> hook.removeNPCsInChunk(chunk)); + // ZNPCs Plus Hook + znpc.ifPresent(hook -> hook.removeNPCsInChunk(chunk)); + + }); + return CompletableFuture.allOf(invFuture, entitiesFuture); } @@ -310,6 +330,10 @@ private void writeSign(Sign fromSign, Sign toSign, Side side) { public CompletableFuture regenerateSimple(GameModeAddon gm, IslandDeletion di, World world) { CompletableFuture bigFuture = new CompletableFuture<>(); + if (world == null) { + bigFuture.complete(null); + return bigFuture; + } new BukkitRunnable() { private int chunkX = di.getMinXChunk(); private int chunkZ = di.getMinZChunk(); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommandTest.java index cdcbe9bea..af3d086cd 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommandTest.java @@ -47,13 +47,15 @@ @PrepareForTest({Bukkit.class, BentoBox.class, User.class }) public class AdminBlueprintCopyCommandTest { + @Mock + private BentoBox plugin; @Mock private AdminBlueprintCommand ac; @Mock private GameModeAddon addon; @Mock private User user; - @Mock + private BlueprintClipboard clip; private UUID uuid = UUID.randomUUID(); @Mock @@ -64,10 +66,12 @@ public class AdminBlueprintCopyCommandTest { */ @Before public void setUp() throws Exception { - // Set up plugin - BentoBox plugin = mock(BentoBox.class); + // Set up plugin // Set up plugin + // Required for NamespacedKey + when(plugin.getName()).thenReturn("BentoBox"); Whitebox.setInternalState(BentoBox.class, "instance", plugin); + clip = mock(BlueprintClipboard.class); // Blueprints Manager when(plugin.getBlueprintsManager()).thenReturn(bm); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommandTest.java index 284028a36..64c5963bc 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommandTest.java @@ -76,6 +76,8 @@ public static void beforeClass() { @Before public void setUp() throws Exception { + // Required for NamespacedKey + when(plugin.getName()).thenReturn("BentoBox"); // Set up plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommandTest.java index ac5386a6c..a8d382fc4 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommandTest.java @@ -55,6 +55,8 @@ @PrepareForTest({Bukkit.class, BentoBox.class, User.class }) public class AdminBlueprintSaveCommandTest { + @Mock + private BentoBox plugin; private AdminBlueprintSaveCommand absc; @Mock private AdminBlueprintCommand ac; @@ -76,8 +78,8 @@ public static void beforeClass() { @Before public void setUp() throws Exception { - // Set up plugin - BentoBox plugin = mock(BentoBox.class); + // Required for NamespacedKey + when(plugin.getName()).thenReturn("BentoBox"); Whitebox.setInternalState(BentoBox.class, "instance", plugin); // Hooks HooksManager hooksManager = mock(HooksManager.class); diff --git a/src/test/java/world/bentobox/bentobox/blueprints/BlueprintClipboardTest.java b/src/test/java/world/bentobox/bentobox/blueprints/BlueprintClipboardTest.java index 861b0f8be..b951aa4e8 100644 --- a/src/test/java/world/bentobox/bentobox/blueprints/BlueprintClipboardTest.java +++ b/src/test/java/world/bentobox/bentobox/blueprints/BlueprintClipboardTest.java @@ -58,6 +58,8 @@ public class BlueprintClipboardTest { */ @Before public void setUp() throws Exception { + // Required for NamespacedKey + when(plugin.getName()).thenReturn("BentoBox"); // Set up plugin Whitebox.setInternalState(BentoBox.class, "instance", plugin); // Hooks diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java index d6e4fc354..390257852 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java @@ -57,7 +57,7 @@ public class BlueprintClipboardManagerTest { @Mock private BentoBox plugin; - @Mock + private BlueprintClipboard clipboard; private File blueprintFolder; @@ -129,15 +129,19 @@ private void zip(File targetFile) throws IOException { */ @Before public void setUp() throws Exception { + // Set up plugin + // Required for NamespacedKey + when(plugin.getName()).thenReturn("BentoBox"); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + clipboard = mock(BlueprintClipboard.class); + server = ServerMocks.newServer(); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); blueprintFolder = new File("blueprints"); // Clear any residual files tearDown(); - // Set up plugin - BentoBox plugin = mock(BentoBox.class); - Whitebox.setInternalState(BentoBox.class, "instance", plugin); // Hooks HooksManager hooksManager = mock(HooksManager.class); when(hooksManager.getHook(anyString())).thenReturn(Optional.empty()); From f4060254962c459d246ff4d1b2499d4ad70f352c Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 28 Dec 2024 15:03:38 -0800 Subject: [PATCH 19/24] Remove this for now --- .../bentobox/hooks/FancyHologramsHook.java | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java diff --git a/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java b/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java deleted file mode 100644 index 650e39133..000000000 --- a/src/main/java/world/bentobox/bentobox/hooks/FancyHologramsHook.java +++ /dev/null @@ -1,35 +0,0 @@ -package world.bentobox.bentobox.hooks; - -import org.bukkit.Material; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.hooks.Hook; - -/** - * Provides copy and pasting of FancyHolograms in blueprints - * - * @author tastybento - * @since 3.2.0 - */ -public class FancyHologramsHook extends Hook { - - public FancyHologramsHook() { - super("FancyHolograms", Material.END_PORTAL); - } - - - @Override - public boolean hook() { - boolean hooked = this.isPluginAvailable(); - if (!hooked) { - BentoBox.getInstance().logError("Could not hook into FancyHolograms"); - } - return hooked; // The hook process shouldn't fail - } - - @Override - public String getFailureCause() { - return null; // The hook process shouldn't fail - } - -} From c7d0095905646b5d99757eb2fecf30a60d497e90 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 29 Dec 2024 09:15:37 -0800 Subject: [PATCH 20/24] Update to Java 21 --- pom.xml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 3198bfad0..d5e5030f5 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ UTF-8 UTF-8 - 17 + 21 2.0.9 @@ -161,12 +161,6 @@ papermc https://repo.papermc.io/repository/maven-public/ - - - maven-snapshots - https://repository.apache.org/content/repositories/snapshots/ - minecraft-repo https://libraries.minecraft.net/ @@ -548,7 +542,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.4.0 + 3.6.0 true ${project.build.directory}/dependency-reduced-pom.xml From f718b9be4b2d36d0d59e6097eebffa639afe2655 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 29 Dec 2024 09:17:09 -0800 Subject: [PATCH 21/24] Version 3.2.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5e5030f5..e580a33b8 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ -LOCAL - 3.1.2 + 3.2.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib From 00d0735079477e62dbc88ea6d7fd9ef06d994642 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 29 Dec 2024 09:29:59 -0800 Subject: [PATCH 22/24] Abstract out all PaperLib so that it only exists in Util class. --- .../listeners/flags/protection/LockAndBanListener.java | 4 ++-- .../world/bentobox/bentobox/managers/IslandsManager.java | 5 ++--- .../world/bentobox/bentobox/nms/CopyWorldRegenerator.java | 7 ++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java index 6a41cb2ca..9295eb8a7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java @@ -13,11 +13,11 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; -import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.util.Util; /** * Listener for the lock flag @@ -164,7 +164,7 @@ private void eject(Player player) { // We'll try to teleport him to the spawn... Location l = player.getWorld().getSpawnLocation(); if (l != null) { - PaperLib.teleportAsync(player, l); + Util.teleportAsync(player, l); } // Switch him back to the default gamemode. He may die, sorry :( diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 53c25b4ba..0e63adaa9 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -40,7 +40,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; @@ -1080,7 +1079,7 @@ private CompletableFuture homeTeleportAsync(@NonNull World world, @NonN .ifFail(() -> goingHome.remove(user.getUniqueId())).buildFuture().thenAccept(result::complete); return; } - PaperLib.teleportAsync(Objects.requireNonNull(player), home).thenAccept(b -> { + Util.teleportAsync(Objects.requireNonNull(player), home).thenAccept(b -> { // Only run the commands if the player is successfully teleported if (Boolean.TRUE.equals(b)) { teleported(world, user, name, newIsland, island); @@ -1469,7 +1468,7 @@ public void removePlayersFromIsland(Island island) { } else { // Move player to spawn getSpawn(w).map(i -> i.getSpawnPoint(w.getEnvironment())).filter(Objects::nonNull) - .ifPresentOrElse(sp -> PaperLib.teleportAsync(p, sp), + .ifPresentOrElse(sp -> Util.teleportAsync(p, sp), () -> plugin.logWarning("Spawn exists but its location is null!")); } diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java index 4be154439..5affe938b 100644 --- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java +++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java @@ -48,6 +48,7 @@ import world.bentobox.bentobox.hooks.SlimefunHook; import world.bentobox.bentobox.hooks.ZNPCsPlusHook; import world.bentobox.bentobox.util.MyBiomeGrid; +import world.bentobox.bentobox.util.Util; /** * Regenerates by using a seed world. The seed world is created using the same generator as the game @@ -148,7 +149,7 @@ private CompletableFuture regenerateChunk(@Nullable IslandDeletion di, @No CompletableFuture seedWorldFuture = getSeedWorldChunk(world, chunkX, chunkZ); // Set up a future to get the chunk requests using Paper's Lib. If Paper is used, this should be done async - CompletableFuture chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ); + CompletableFuture chunkFuture = Util.getChunkAtAsync(world, chunkX, chunkZ); // If there is no island, do not clean chunk CompletableFuture cleanFuture = di != null ? cleanChunk(chunkFuture, di) : CompletableFuture.completedFuture(null); @@ -172,7 +173,7 @@ private CompletableFuture regenerateChunk(@Nullable IslandDeletion di, @No private CompletableFuture getSeedWorldChunk(World world, int chunkX, int chunkZ) { World seed = Bukkit.getWorld(world.getName() + "/bentobox"); if (seed == null) return CompletableFuture.completedFuture(null); - return PaperLib.getChunkAtAsync(seed, chunkX, chunkZ); + return Util.getChunkAtAsync(seed, chunkX, chunkZ); } /** @@ -374,7 +375,7 @@ private boolean isEnded(int chunkX) { @SuppressWarnings("deprecation") private CompletableFuture regenerateChunk(GameModeAddon gm, IslandDeletion di, @Nonnull World world, int chunkX, int chunkZ) { - CompletableFuture chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ); + CompletableFuture chunkFuture = Util.getChunkAtAsync(world, chunkX, chunkZ); CompletableFuture invFuture = chunkFuture.thenAccept(chunk -> Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance) .filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ())) From 1fa2849053055424fe3e3a7c71a89f258b7cf514 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 29 Dec 2024 09:36:09 -0800 Subject: [PATCH 23/24] Adjust POM --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index e580a33b8..1e46541fd 100644 --- a/pom.xml +++ b/pom.xml @@ -233,6 +233,13 @@ 4.2.2 test + + + io.papermc.paper + paper-api + ${paper.version} + provided + org.spigotmc @@ -357,13 +364,6 @@ org.eclipse.jdt.annotation 2.2.600 - - - io.papermc - paperlib - 1.0.6 - compile - com.github.apachezy From 4a55ea790eddb96f8f59e6a47c113a3d06e4df49 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 29 Dec 2024 13:40:10 -0800 Subject: [PATCH 24/24] Works on Paper and Spigot. Tests are a mess and won't compile or run TODO: Get test working in Paper-land. Relates to ##2577 --- .../bentobox/bentobox/api/hooks/NPCHook.java | 2 - .../bentobox/hooks/FancyNpcsHook.java | 1 - .../bentobox/managers/IslandsManager.java | 1 - .../bentobox/managers/island/NewIsland.java | 2 +- .../bentobox/nms/CopyWorldRegenerator.java | 2 +- .../world/bentobox/bentobox/util/Util.java | 131 +++++++----------- .../team/IslandTeamInviteCommandTest.java | 3 +- .../adapters/ItemStackTypeAdapterTest.java | 3 +- .../protection/BreakBlocksListenerTest.java | 2 +- .../InvincibleVisitorsListenerTest.java | 2 +- .../bentobox/managers/IslandsManagerTest.java | 12 +- .../bentobox/managers/PlayersManagerTest.java | 2 +- .../bentobox/bentobox/mocks/ServerMocks.java | 19 ++- .../panels/BlueprintManagementPanelTest.java | 2 +- .../customizable/IslandCreationPanelTest.java | 10 +- .../customizable/LanguagePanelTest.java | 2 +- 16 files changed, 88 insertions(+), 108 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java b/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java index 6a848215f..fdcbca181 100644 --- a/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java +++ b/src/main/java/world/bentobox/bentobox/api/hooks/NPCHook.java @@ -12,8 +12,6 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import de.oliver.fancynpcs.api.Npc; -import lol.pyr.znpcsplus.api.npc.NpcEntry; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; /** diff --git a/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java index c8c375f12..528289e24 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java @@ -30,7 +30,6 @@ import de.oliver.fancynpcs.api.actions.NpcAction; import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; import de.oliver.fancynpcs.api.utils.SkinFetcher; -import lol.pyr.znpcsplus.api.npc.NpcEntry; import net.kyori.adventure.text.format.NamedTextColor; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.hooks.NPCHook; diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 0e63adaa9..30037e739 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -1191,7 +1191,6 @@ public void spawnTeleport(@NonNull World world, @NonNull Player player) { * * @param player player */ - @SuppressWarnings("deprecation") private void readyPlayer(@NonNull Player player) { // Stop any gliding player.setGliding(false); diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index 198e8950c..59aec708d 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -15,9 +15,9 @@ import world.bentobox.bentobox.api.events.island.IslandCreateEvent; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; +import world.bentobox.bentobox.api.events.island.IslandResetEvent; import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.api.logs.LogEntry.LogType; -import world.bentobox.bentobox.api.events.island.IslandResetEvent; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.BlueprintsManager; diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java index 5affe938b..dec80a8e3 100644 --- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java +++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java @@ -38,7 +38,6 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.hooks.Hook; @@ -248,6 +247,7 @@ private void copyChunkDataToChunk(Chunk toChunk, Chunk fromChunk, BoundingBox li Arrays.stream(fromChunk.getTileEntities()).forEach(bs -> processTileEntity(bs.getBlock(), bs.getLocation().toVector().toLocation(toChunk.getWorld()).getBlock())); } + @SuppressWarnings("deprecation") private void processEntity(Entity entity, Location location) { Entity bpe = location.getWorld().spawnEntity(location, entity.getType()); bpe.setCustomName(entity.getCustomName()); diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index 3cf2fac55..4aa1ccc1f 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -1,6 +1,7 @@ package world.bentobox.bentobox.util; import java.io.IOException; +import java.lang.reflect.Method; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -26,7 +27,6 @@ import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.attribute.Attribute; -import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Allay; import org.bukkit.entity.Animals; @@ -51,8 +51,6 @@ import com.google.common.base.Enums; import com.google.common.base.Optional; -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; @@ -382,7 +380,7 @@ public static boolean isTamableEntity(Entity entity) { */ @NonNull public static CompletableFuture teleportAsync(@Nonnull Entity entity, @Nonnull Location location) { - return PaperLib.teleportAsync(entity, location); + return teleportAsync(entity, location, TeleportCause.UNKNOWN); } /** @@ -392,12 +390,26 @@ public static CompletableFuture teleportAsync(@Nonnull Entity entity, @ * @param cause The cause for the teleportation * @return Future that completes with the result of the teleport */ + @SuppressWarnings("unchecked") @NonNull public static CompletableFuture teleportAsync(@Nonnull Entity entity, @Nonnull Location location, TeleportCause cause) { - return PaperLib.teleportAsync(entity, location, cause); + try { + // Use reflection to check if the method exists + Method method = Entity.class.getMethod("teleportAsync", Location.class, TeleportCause.class); + if (method != null) { + // Invoke the method using reflection on the entity instance + return (CompletableFuture) method.invoke(entity, location, cause); + } + } catch (NoSuchMethodException e) { + // Method does not exist, fallback to Spigot behavior + } catch (Exception e) { + plugin.logStacktrace(e); // Report other exceptions + } + // Fallback for Spigot servers + entity.teleport(location, cause); + return CompletableFuture.completedFuture(true); } - /** * Gets the chunk at the target location, loading it asynchronously if needed. * @param loc Location to get chunk for @@ -440,9 +452,24 @@ public static CompletableFuture getChunkAtAsync(@Nonnull World world, int * @param gen Should the chunk generate or not. Only respected on some MC versions, 1.13 for CB, 1.12 for Paper * @return Future that completes with the chunk, or null if the chunk did not exists and generation was not requested. */ + @SuppressWarnings("unchecked") @NonNull public static CompletableFuture getChunkAtAsync(@Nonnull World world, int x, int z, boolean gen) { - return PaperLib.getChunkAtAsync(world, x, z, gen); + try { + // Use reflection to check if the method exists + Method method = World.class.getMethod("getChunkAtAsync", int.class, int.class, boolean.class); + if (method != null) { + // Invoke the method using reflection + return (CompletableFuture) method.invoke(world, x, z, gen); + } + } catch (NoSuchMethodException e) { + // Method does not exist, fallback to Spigot behavior + } catch (Exception e) { + e.printStackTrace(); // Handle other exceptions (optional) + } + // Fallback for Spigot servers + return CompletableFuture.completedFuture(world.getChunkAt(x, z, gen)); + } /** @@ -462,56 +489,7 @@ public static boolean isChunkGenerated(@NonNull Location loc) { * @return If the chunk is generated or not */ public static boolean isChunkGenerated(@Nonnull World world, int x, int z) { - return PaperLib.isChunkGenerated(world, x, z); - } - - /** - * Get's a BlockState, optionally not using a snapshot - * @param block The block to get a State of - * @param useSnapshot Whether or not to use a snapshot when supported - * @return The BlockState - */ - @NonNull - public static BlockStateSnapshotResult getBlockState(@Nonnull Block block, boolean useSnapshot) { - return PaperLib.getBlockState(block, useSnapshot); - } - - /** - * Detects if the current MC version is at least the following version. - *

- * Assumes 0 patch version. - * - * @param minor Min Minor Version - * @return Meets the version requested - */ - public static boolean isVersion(int minor) { - return PaperLib.isVersion(minor); - } - - /** - * Detects if the current MC version is at least the following version. - * @param minor Min Minor Version - * @param patch Min Patch Version - * @return Meets the version requested - */ - public static boolean isVersion(int minor, int patch) { - return PaperLib.isVersion(minor, patch); - } - - /** - * Gets the current Minecraft Minor version. IE: 1.13.1 returns 13 - * @return The Minor Version - */ - public static int getMinecraftVersion() { - return PaperLib.getMinecraftVersion(); - } - - /** - * Gets the current Minecraft Patch version. IE: 1.13.1 returns 1 - * @return The Patch Version - */ - public static int getMinecraftPatchVersion() { - return PaperLib.getMinecraftPatchVersion(); + return world.isChunkGenerated(x, z); } /** @@ -562,42 +540,26 @@ public static boolean isVersionCompatible(String version, String requiredVersion return !(!isRequiredSnapshot && isVersionSnapshot); } - /** - * Check if the server has access to the Spigot API - * @return True for Spigot and Paper environments - */ - public static boolean isSpigot() { - return PaperLib.isSpigot(); - } - /** * Check if the server has access to the Paper API * @return True for Paper environments */ public static boolean isPaper() { - return !isJUnitTest() && PaperLib.isPaper(); - } - - /** - * I don't like doing this, but otherwise we need to set a flag in every test - */ - private static boolean isJUnitTest() { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - for (StackTraceElement element : stackTrace) { - if (element.getClassName().startsWith("org.junit.")) { - return true; - } + try { + Class.forName("com.destroystokyo.paper.PaperConfig"); + return true; // Paper-specific class exists + } catch (ClassNotFoundException e) { + return false; // Not a Paper server } - return false; } - /** * This method translates color codes in given string and strips whitespace after them. * This code parses both: hex and old color codes. * @param textToColor Text which color codes must be parsed. * @return String text with parsed colors and stripped whitespaces after them. */ + @SuppressWarnings("deprecation") @NonNull public static String translateColorCodes(@NonNull String textToColor) { // Use matcher to find hex patterns in given text. @@ -760,8 +722,14 @@ public static void runCommands(User user, String ownerName, @NonNull List