fix: 1.21.9+ client crash and add copper golem support

- 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 <noreply@anthropic.com>
This commit is contained in:
bridge 2025-12-01 00:05:46 +01:00
parent b6f878bdc0
commit d1444642ae
6 changed files with 52 additions and 36 deletions

View file

@ -0,0 +1,9 @@
package lol.pyr.znpcsplus.util;
public enum CopperGolemState {
IDLE,
GETTING_ITEM,
GETTING_NO_ITEM,
DROPPING_ITEM,
DROPPING_NO_ITEM
}

View file

@ -0,0 +1,8 @@
package lol.pyr.znpcsplus.util;
public enum WeatheringCopperState {
UNAFFECTED,
EXPOSED,
WEATHERED,
OXIDIZED
}

View file

@ -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))

View file

@ -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) {

View file

@ -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<NpcType> getAll() {

View file

@ -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<Runnable> visibilityTaskQueue = new ConcurrentLinkedQueue<>();
private final static ExecutorService visibilityExecutor = Executors.newSingleThreadExecutor();
public static void shutdownExecutor() {
visibilityExecutor.shutdown();
}
private final Set<Player> 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<Void> respawn() {
CompletableFuture<Void> 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<Void> show(Player player) {
CompletableFuture<Void> 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);