From 180ccd9f5eaf373a0026babcab640383eaec3e79 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 26 Dec 2024 09:17:07 -0800 Subject: [PATCH] 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}