From 3a3083a12a542e25039f2a4cee1cef23914da872 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 29 Apr 2025 22:13:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B9=E5=9D=97=E5=AE=9E?= =?UTF-8?q?=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 1 - .../plugin/injector/BukkitInjector.java | 2 +- .../core/block/BlockEntityState.java | 17 +++++++ .../model/special/BannerSpecialModel.java | 1 - .../pack/model/special/ChestSpecialModel.java | 2 - .../model/special/ShulkerBoxSpecialModel.java | 1 - .../pack/model/special/SignSpecialModel.java | 1 - .../craftengine/core/world/chunk/CEChunk.java | 19 +++++--- .../DefaultBlockEntitySerializer.java | 48 +++++++++++++++++++ .../serialization/DefaultChunkSerializer.java | 10 ++-- .../DefaultSectionSerializer.java | 2 +- 11 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 49515e11d..05e035db4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -169,7 +169,6 @@ public class BukkitBlockManager extends AbstractBlockManager { this.tempVanillaBlockStateModels.clear(); } - @Nullable public Object getMinecraftBlockHolder(int stateId) { return stateId2BlockHolder.get(stateId); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 697399b11..585cd3be9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java @@ -388,7 +388,7 @@ public class BukkitInjector { // } // } - public static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, CEChunk chunk, SectionPos pos) { + public synchronized static void injectLevelChunkSection(Object targetSection, CESection ceSection, CEWorld ceWorld, CEChunk chunk, SectionPos pos) { try { Object container = FastNMS.INSTANCE.field$LevelChunkSection$states(targetSection); if (!clazz$InjectedPalettedContainer.isInstance(container)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java new file mode 100644 index 000000000..8e19578b0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockEntityState.java @@ -0,0 +1,17 @@ +package net.momirealms.craftengine.core.block; + +import net.momirealms.sparrow.nbt.CompoundTag; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class BlockEntityState { + private final CompoundTag nbt; + + public BlockEntityState(CompoundTag nbt) { + this.nbt = nbt.deepClone(); + } + + public CompoundTag nbt() { + return nbt; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java index 05442f8a8..c19570798 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import net.momirealms.craftengine.core.util.Key; import java.util.Map; -import java.util.Objects; public class BannerSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java index 23d8a969c..feb049fd9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java @@ -4,10 +4,8 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import software.amazon.awssdk.services.s3.endpoints.internal.Value; import java.util.Map; -import java.util.Objects; public class ChestSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java index 355e802fb..f8d68f58e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.Locale; import java.util.Map; -import java.util.Objects; public class ShulkerBoxSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index e6eeebf63..ac7561545 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import net.momirealms.craftengine.core.util.Key; import java.util.Map; -import java.util.Objects; public class SignSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index cfa239665..1ae57e38b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -1,13 +1,14 @@ package net.momirealms.craftengine.core.world.chunk; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.BlockEntityState; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.world.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; public class CEChunk { private boolean loaded; @@ -15,7 +16,7 @@ public class CEChunk { private final ChunkPos chunkPos; private final CESection[] sections; private final WorldHeight worldHeightAccessor; - private final List entities; + private final Map blockEntities; private boolean dirty; public CEChunk(CEWorld world, ChunkPos chunkPos) { @@ -23,14 +24,14 @@ public class CEChunk { this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.entities = new ArrayList<>(); + this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, List entities) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map blockEntities) { this.world = world; this.chunkPos = chunkPos; - this.entities = entities; + this.blockEntities = blockEntities; this.worldHeightAccessor = world.worldHeight(); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; @@ -45,6 +46,10 @@ public class CEChunk { this.fillEmptySection(); } + public Map blockEntities() { + return blockEntities; + } + public boolean dirty() { return dirty; } @@ -54,7 +59,7 @@ public class CEChunk { } public boolean isEmpty() { - if (!this.entities.isEmpty()) return false; + if (!this.blockEntities.isEmpty()) return false; for (CESection section : this.sections) { if (section != null && !section.statesContainer().isEmpty()) { return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java new file mode 100644 index 000000000..8f8be31d9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -0,0 +1,48 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.BlockEntityState; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Map; + +public final class DefaultBlockEntitySerializer { + + @ApiStatus.Experimental + public static ListTag serialize(Map tiles) { + ListTag result = new ListTag(); + Map nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); + for (Map.Entry entry : tiles.entrySet()) { + int pos = entry.getKey(); + CompoundTag tag = entry.getValue().nbt(); + int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos}); + int[] newPoses = new int[previous.length + 1]; + System.arraycopy(previous, 0, newPoses, 0, previous.length); + newPoses[previous.length] = pos; + nbtToPosMap.put(tag, newPoses); + } + for (Map.Entry entry : nbtToPosMap.entrySet()) { + CompoundTag blockEntityTag = new CompoundTag(); + blockEntityTag.put("data", entry.getKey()); + blockEntityTag.putIntArray("pos", entry.getValue()); + result.add(blockEntityTag); + } + return result; + } + + @ApiStatus.Experimental + public static Map deserialize(ListTag tag) { + Map result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f); + for (int i = 0; i < tag.size(); i++) { + CompoundTag blockEntityTag = tag.getCompound(i); + CompoundTag data = blockEntityTag.getCompound("data"); + int[] pos = blockEntityTag.getIntArray("pos"); + for (int j = 0; j < pos.length; j++) { + result.put(j, new BlockEntityState(data)); + } + } + return result; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 62dacbd48..1a44e0efc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -9,9 +9,9 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.Optional; -public class DefaultChunkSerializer { +public final class DefaultChunkSerializer { @Nullable public static CompoundTag serialize(@NotNull CEChunk chunk) { @@ -28,7 +28,7 @@ public class DefaultChunkSerializer { if (sections.isEmpty()) return null; CompoundTag chunkNbt = new CompoundTag(); chunkNbt.put("sections", sections); - chunkNbt.put("entities", new ListTag()); + chunkNbt.put("block_entities", DefaultBlockEntitySerializer.serialize(chunk.blockEntities())); return chunkNbt; } @@ -46,7 +46,7 @@ public class DefaultChunkSerializer { } } } - ListTag entities = chunkNbt.getList("entities"); - return new CEChunk(world, pos, sectionArray, List.of()); + ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); + return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java index 5ccdf2a3f..9e6b41426 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultSectionSerializer.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.LongStream; -public class DefaultSectionSerializer { +public final class DefaultSectionSerializer { @Nullable public static CompoundTag serialize(@NotNull CESection section) {