From 81ac75adb05ca46f66b50d90f07308b33d22fd97 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Mar 2025 01:49:37 +0800 Subject: [PATCH] add slime world support --- .../slimeworld/SlimeFormatStorageAdaptor.java | 27 +++++++++++++++-- .../slimeworld/SlimeWorldDataStorage.java | 30 ++++++++++--------- .../bukkit/world/BukkitCEWorld.java | 5 ++++ .../bukkit/world/BukkitWorldManager.java | 21 +++++++++++++ .../craftengine/core/world/CEWorld.java | 8 +++++ .../craftengine/core/world/WorldManager.java | 5 ++++ 6 files changed, 80 insertions(+), 16 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java index b5ce5a6e6..b01a6e1c1 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeFormatStorageAdaptor.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.slimeworld; import com.infernalsuite.asp.api.AdvancedSlimePaperAPI; import com.infernalsuite.asp.api.events.LoadSlimeWorldEvent; import com.infernalsuite.asp.api.world.SlimeWorld; +import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldManager; import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor; @@ -12,13 +13,18 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Method; + public class SlimeFormatStorageAdaptor extends DefaultStorageAdaptor implements Listener { private final WorldManager worldManager; + private final Class byteArrayTagClass = ReflectionUtils.getClazz("net{}kyori{}adventure{}nbt{}ByteArrayBinaryTag".replace("{}", ".")); + private final Method method$ByteArrayBinaryTag$byteArrayBinaryTag = ReflectionUtils.getStaticMethod(byteArrayTagClass, byteArrayTagClass, byte.class.arrayType()); + private final Method method$ByteArrayBinaryTag$value = ReflectionUtils.getMethod(byteArrayTagClass, byte.class.arrayType()); @EventHandler public void onWorldLoad(LoadSlimeWorldEvent event) { org.bukkit.World world = Bukkit.getWorld(event.getSlimeWorld().getName()); - this.worldManager.loadWorld(this.worldManager.wrap(world)); + this.worldManager.loadWorld(this.worldManager.createWorld(this.worldManager.wrap(world), new SlimeWorldDataStorage(event.getSlimeWorld(), this))); } public SlimeFormatStorageAdaptor(WorldManager worldManager) { @@ -29,12 +35,29 @@ public class SlimeFormatStorageAdaptor extends DefaultStorageAdaptor implements return AdvancedSlimePaperAPI.instance().getLoadedWorld(name); } + // 请注意,在加载事件的时候,无法通过AdvancedSlimePaperAPI.instance().getLoadedWorld来判断是否为slime世界 @Override public @NotNull WorldDataStorage adapt(@NotNull World world) { SlimeWorld slimeWorld = getWorld(world.name()); if (slimeWorld == null) { return super.adapt(world); } - return new SlimeWorldDataStorage(slimeWorld); + return new SlimeWorldDataStorage(slimeWorld, this); + } + + public byte[] byteArrayTagToBytes(Object byteArrayTag) { + try { + return (byte[]) method$ByteArrayBinaryTag$value.invoke(byteArrayTag); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to convert byte array tag to byte[]", e); + } + } + + public Object bytesToByteArrayTag(byte[] bytes) { + try { + return method$ByteArrayBinaryTag$byteArrayBinaryTag.invoke(null, (Object) bytes); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to convert byte array tag to byte[]", e); + } } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java index b9b63ded4..feeb6505f 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/slimeworld/SlimeWorldDataStorage.java @@ -2,8 +2,6 @@ package net.momirealms.craftengine.bukkit.compatibility.slimeworld; import com.infernalsuite.asp.api.world.SlimeChunk; import com.infernalsuite.asp.api.world.SlimeWorld; -import net.kyori.adventure.nbt.BinaryTag; -import net.kyori.adventure.nbt.ByteArrayBinaryTag; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import net.momirealms.sparrow.nbt.CompoundTag; @@ -14,12 +12,15 @@ import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.lang.ref.WeakReference; +import java.util.Map; public class SlimeWorldDataStorage implements WorldDataStorage { private final WeakReference slimeWorld; + private final SlimeFormatStorageAdaptor adaptor; - public SlimeWorldDataStorage(SlimeWorld slimeWorld) { + public SlimeWorldDataStorage(SlimeWorld slimeWorld, SlimeFormatStorageAdaptor adaptor) { this.slimeWorld = new WeakReference<>(slimeWorld); + this.adaptor = adaptor; } public SlimeWorld getWorld() { @@ -31,16 +32,16 @@ public class SlimeWorldDataStorage implements WorldDataStorage { public CompoundTag readChunkTagAt(ChunkPos pos) { SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); if (slimeChunk == null) return null; - BinaryTag tag = slimeChunk.getExtraData().get("craftengine"); + Object tag = slimeChunk.getExtraData().get("craftengine"); if (tag == null) return null; - ByteArrayBinaryTag byteArrayBinaryTag = (ByteArrayBinaryTag) tag; try { - return NBT.readCompound(new DataInputStream(new ByteArrayInputStream(byteArrayBinaryTag.value()))); - } catch (IOException e) { + return NBT.readCompound(new DataInputStream(new ByteArrayInputStream(adaptor.byteArrayTagToBytes(tag)))); + } catch (Exception e) { throw new RuntimeException("Failed to read chunk tag from slime world. " + pos, e); } } + @SuppressWarnings("unchecked") @Override public void writeChunkTagAt(ChunkPos pos, @Nullable CompoundTag nbt) { SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z); @@ -48,13 +49,14 @@ public class SlimeWorldDataStorage implements WorldDataStorage { if (nbt == null) { slimeChunk.getExtraData().remove("craftengine"); } else { - slimeChunk.getExtraData().computeIfAbsent("craftengine", l -> { - try { - return ByteArrayBinaryTag.byteArrayBinaryTag(NBT.toBytes(nbt)); - } catch (IOException e) { - throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e); - } - }); + try { + Object tag = adaptor.bytesToByteArrayTag(NBT.toBytes(nbt)); + Map data1 = slimeChunk.getExtraData(); + Map data2 = (Map) data1; + data2.put("craftengine", tag); + } catch (Exception e) { + throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java index fb4f0e365..73ca854da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.util.SectionPosUtils; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; public class BukkitCEWorld extends CEWorld { @@ -13,6 +14,10 @@ public class BukkitCEWorld extends CEWorld { super(world, adaptor); } + public BukkitCEWorld(World world, WorldDataStorage dataStorage) { + super(world, dataStorage); + } + @Override public void tick() { if (ConfigManager.enableLightSystem()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index 623540d88..001dc1754 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.world.chunk.CESection; import net.momirealms.craftengine.core.world.chunk.serialization.ChunkSerializer; import net.momirealms.craftengine.core.world.chunk.storage.DefaultStorageAdaptor; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Bukkit; import org.bukkit.Chunk; @@ -168,6 +169,26 @@ public class BukkitWorldManager implements WorldManager, Listener { } } + @Override + public void loadWorld(CEWorld world) { + this.worldMapLock.writeLock().lock(); + try { + if (this.worlds.containsKey(world.world().uuid())) return; + this.worlds.put(world.world().uuid(), world); + this.resetWorldArray(); + for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) { + handleChunkLoad(world, chunk); + } + } finally { + this.worldMapLock.writeLock().unlock(); + } + } + + @Override + public CEWorld createWorld(net.momirealms.craftengine.core.world.World world, WorldDataStorage storage) { + return new BukkitCEWorld(world, storage); + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onWorldUnload(WorldUnloadEvent event) { unloadWorld(new BukkitWorld(event.getWorld())); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index f604df05d..59a4978f3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -33,6 +33,14 @@ public abstract class CEWorld { this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } + public CEWorld(World world, WorldDataStorage dataStorage) { + this.world = world; + this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f); + this.worldDataStorage = dataStorage; + this.worldHeightAccessor = world.worldHeight(); + this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; + } + public World world() { return world; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java index ee5ab39a0..58db10caf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldManager.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.plugin.Reloadable; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; +import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -16,6 +17,10 @@ public interface WorldManager extends Reloadable { void loadWorld(World world); + void loadWorld(CEWorld world); + + CEWorld createWorld(World world, WorldDataStorage storage); + void unloadWorld(World world); World wrap(T world);