From d1444642ae2948354eb1c0273b164277a388b677 Mon Sep 17 00:00:00 2001 From: bridge Date: Mon, 1 Dec 2025 00:05:46 +0100 Subject: [PATCH] fix: 1.21.9+ client crash and add copper golem support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix skinLayersIndex for 1.21.9+ (index 16 instead of 17) - Fix shoulderIndex offset for 1.21.9+ - Refactor Viewable to use shared ExecutorService - Add CopperGolemState and WeatheringCopperState enums - Register copper_golem entity type for 1.21.9+ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../pyr/znpcsplus/util/CopperGolemState.java | 9 ++++ .../znpcsplus/util/WeatheringCopperState.java | 8 ++++ .../java/lol/pyr/znpcsplus/ZNpcsPlus.java | 9 ++-- .../entity/EntityPropertyRegistryImpl.java | 12 ++++- .../znpcsplus/npc/NpcTypeRegistryImpl.java | 6 +++ .../java/lol/pyr/znpcsplus/util/Viewable.java | 44 ++++++------------- 6 files changed, 52 insertions(+), 36 deletions(-) create mode 100644 api/src/main/java/lol/pyr/znpcsplus/util/CopperGolemState.java create mode 100644 api/src/main/java/lol/pyr/znpcsplus/util/WeatheringCopperState.java diff --git a/api/src/main/java/lol/pyr/znpcsplus/util/CopperGolemState.java b/api/src/main/java/lol/pyr/znpcsplus/util/CopperGolemState.java new file mode 100644 index 0000000..acce9af --- /dev/null +++ b/api/src/main/java/lol/pyr/znpcsplus/util/CopperGolemState.java @@ -0,0 +1,9 @@ +package lol.pyr.znpcsplus.util; + +public enum CopperGolemState { + IDLE, + GETTING_ITEM, + GETTING_NO_ITEM, + DROPPING_ITEM, + DROPPING_NO_ITEM +} diff --git a/api/src/main/java/lol/pyr/znpcsplus/util/WeatheringCopperState.java b/api/src/main/java/lol/pyr/znpcsplus/util/WeatheringCopperState.java new file mode 100644 index 0000000..a4bd5af --- /dev/null +++ b/api/src/main/java/lol/pyr/znpcsplus/util/WeatheringCopperState.java @@ -0,0 +1,8 @@ +package lol.pyr.znpcsplus.util; + +public enum WeatheringCopperState { + UNAFFECTED, + EXPOSED, + WEATHERED, + OXIDIZED +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index 425a2a1..5c33612 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -66,10 +66,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; +import java.util.*; public class ZNpcsPlus { private final LegacyComponentSerializer textSerializer = LegacyComponentSerializer.builder() @@ -98,6 +95,7 @@ public class ZNpcsPlus { NpcPropertyRegistryProvider.register(bootstrap, propertyRegistry); shutdownTasks.add(NpcPropertyRegistryProvider::unregister); + shutdownTasks.add(Viewable::shutdownExecutor); } private void log(String str) { @@ -215,6 +213,7 @@ public class ZNpcsPlus { public void onDisable() { NpcApiProvider.unregister(); + Collections.reverse(shutdownTasks); for (Runnable runnable : shutdownTasks) try { runnable.run(); } catch (Throwable throwable) { @@ -297,6 +296,8 @@ public class ZNpcsPlus { registerEnumParser(manager, WoldVariant.class, incorrectUsageMessage); registerEnumParser(manager, NpcStorageType.class, incorrectUsageMessage); registerEnumParser(manager, SkeletonType.class, incorrectUsageMessage); + registerEnumParser(manager, CopperGolemState.class, incorrectUsageMessage); + registerEnumParser(manager, WeatheringCopperState.class, incorrectUsageMessage); manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root")) .addSubcommand("center", new CenterCommand(npcRegistry)) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index 28053e6..6b5db12 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -93,6 +93,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerEnumSerializer(ArmadilloState.class); registerEnumSerializer(WoldVariant.class); registerEnumSerializer(SkeletonType.class); + registerEnumSerializer(CopperGolemState.class); + registerEnumSerializer(WeatheringCopperState.class); registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.class); @@ -201,7 +203,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { // Player register(new DummyProperty<>("skin", SkinDescriptor.class, false)); final int skinLayersIndex; - if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) skinLayersIndex = 17; + if (ver.isNewerThanOrEquals(ServerVersion.V_1_21_9)) skinLayersIndex = 16; + else if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) skinLayersIndex = 17; else if (ver.isNewerThanOrEquals(ServerVersion.V_1_16)) skinLayersIndex = 16; else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) skinLayersIndex = 15; else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) skinLayersIndex = 13; @@ -561,6 +564,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { return compound; }; int shoulderIndex = skinLayersIndex+2; + if (ver.isNewerThanOrEquals(ServerVersion.V_1_21_9)) shoulderIndex += 1; register(new NBTProperty<>("shoulder_entity_left", ParrotVariant.class, shoulderIndex++, parrotVariantDecoder, true)); register(new NBTProperty<>("shoulder_entity_right", ParrotVariant.class, shoulderIndex, parrotVariantDecoder, true)); @@ -734,6 +738,12 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { // Creaking register(new BooleanProperty("creaking_crumbling", 18, false, legacyBooleans)); + + if (!ver.isNewerThanOrEquals(ServerVersion.V_1_21_9)) return; + + // Copper Golem + register(new EncodedIntegerProperty<>("weathering_copper_state", WeatheringCopperState.UNAFFECTED, 16, Enum::ordinal)); + register(new EncodedIntegerProperty<>("copper_golem_state", CopperGolemState.IDLE, 17, Enum::ordinal)); } private void registerSerializer(PropertySerializer serializer) { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java index dbc76bb..dff206b 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeRegistryImpl.java @@ -402,6 +402,12 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { register(builder(p, "creaking", EntityTypes.CREAKING) .setHologramOffset(0.725) .addProperties("creaking_active")); + + if (!version.isNewerThanOrEquals(ServerVersion.V_1_21_9)) return; + + register(builder(p, "copper_golem", EntityTypes.COPPER_GOLEM) + .setHologramOffset(-0.995) + .addProperties("weathering_copper_state", "copper_golem_state")); } public Collection getAll() { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/util/Viewable.java b/plugin/src/main/java/lol/pyr/znpcsplus/util/Viewable.java index cc222a8..333819c 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/util/Viewable.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/util/Viewable.java @@ -5,9 +5,7 @@ import org.bukkit.entity.Player; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.*; import java.util.stream.Collectors; public abstract class Viewable { @@ -22,34 +20,20 @@ public abstract class Viewable { } } - private boolean queueRunning = false; - private final Queue visibilityTaskQueue = new ConcurrentLinkedQueue<>(); + private final static ExecutorService visibilityExecutor = Executors.newSingleThreadExecutor(); + + public static void shutdownExecutor() { + visibilityExecutor.shutdown(); + } + private final Set viewers = ConcurrentHashMap.newKeySet(); public Viewable() { all.add(new WeakReference<>(this)); } - private void tryRunQueue() { - if (visibilityTaskQueue.isEmpty() || queueRunning) return; - queueRunning = true; - FutureUtil.exceptionPrintingRunAsync(() -> { - while (!visibilityTaskQueue.isEmpty()) try { - visibilityTaskQueue.remove().run(); - } catch (Exception e) { - e.printStackTrace(); - } - queueRunning = false; - }); - } - - private void queueVisibilityTask(Runnable runnable) { - visibilityTaskQueue.add(runnable); - tryRunQueue(); - } - public void delete() { - queueVisibilityTask(() -> { + visibilityExecutor.submit(() -> { UNSAFE_hideAll(); viewers.clear(); synchronized (all) { @@ -60,10 +44,9 @@ public abstract class Viewable { public CompletableFuture respawn() { CompletableFuture future = new CompletableFuture<>(); - queueVisibilityTask(() -> { + visibilityExecutor.submit(() -> { UNSAFE_hideAll(); - UNSAFE_showAll().join(); - future.complete(null); + UNSAFE_showAll().thenRun(() -> future.complete(null)); }); return future; } @@ -75,20 +58,19 @@ public abstract class Viewable { public CompletableFuture show(Player player) { CompletableFuture future = new CompletableFuture<>(); - queueVisibilityTask(() -> { + visibilityExecutor.submit(() -> { if (viewers.contains(player)) { future.complete(null); return; } viewers.add(player); - UNSAFE_show(player).join(); - future.complete(null); + UNSAFE_show(player).thenRun(() -> future.complete(null)); }); return future; } public void hide(Player player) { - queueVisibilityTask(() -> { + visibilityExecutor.submit(() -> { if (!viewers.contains(player)) return; viewers.remove(player); UNSAFE_hide(player);