From 6017a09a03a3b16d5a5482f66e22b0321ff49908 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 22 Dec 2024 08:05:20 -0800 Subject: [PATCH 1/7] 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 2/7] 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 class ZNPCSPlusHook extends Hook { NpcType type = npc.getType(); for (EntityProperty property : npc.getAppliedProperties()) try { - property.toString(); PropertySerializer serializer = propertyRegistry .getSerializer(((EntityPropertyImpl) property).getType()); if (serializer == null) { From 180ccd9f5eaf373a0026babcab640383eaec3e79 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 09:17:07 -0800 Subject: [PATCH 3/7] 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.BlueprintCreatureSpawner; 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 class BlueprintClipboard { } 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 @@ public class BlueprintClipboard { // 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 @@ public class BlueprintPaster { 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 @@ package world.bentobox.bentobox.hooks; 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 lol.pyr.znpcsplus.util.NpcLocation; 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 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; */ 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 class ZNPCsPlusHook extends Hook { String result = NpcApiProvider.get().getNpcSerializerRegistry().getSerializer(YamlConfiguration.class) .serialize(entry) .saveToString(); - BentoBox.getInstance().logDebug(result); return result; } @@ -48,6 +54,7 @@ public class ZNPCsPlusHook extends Hook { NpcLocation loc = new NpcLocation(pos); entry.getNpc().setLocation(loc); NpcApiProvider.get().getNpcRegistry().register(entry); + return true; } @@ -56,7 +63,9 @@ public class ZNPCsPlusHook extends 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 class ZNPCsPlusHook extends 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.blueprints.dataobjects.BlueprintEntity; 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 class DefaultPasteUtil { 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 class DefaultPasteUtil { * @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 @@ public class DefaultPasteUtil { // 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 @@ public class DefaultPasteUtil { // 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 class Util { 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 4/7] 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 class DefaultPasteUtilTest { 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 5/7] 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 @@ package world.bentobox.bentobox.hooks; 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 6/7] 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 class ZNPCsPlusHook extends 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.World; 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 class MythicMobsHookTest { 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 7/7] 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 class UtilTest { 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