From bb305343d33b946c703437dfc7bf515dc377023c Mon Sep 17 00:00:00 2001 From: XiaoMoMi <972454774@qq.com> Date: Tue, 4 Nov 2025 22:13:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=B9=E5=9D=97=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BetterModelBlockEntityElementConfig.java | 5 + .../ModelEngineBlockEntityElementConfig.java | 5 + .../ItemDisplayBlockEntityElement.java | 18 ++- .../ItemDisplayBlockEntityElementConfig.java | 13 ++ .../TextDisplayBlockEntityElement.java | 22 +++- .../TextDisplayBlockEntityElementConfig.java | 13 ++ .../plugin/injector/WorldStorageInjector.java | 14 ++- .../render/ConstantBlockEntityRenderer.java | 22 +++- .../render/element/BlockEntityElement.java | 2 + .../element/BlockEntityElementConfig.java | 9 ++ .../craftengine/core/world/chunk/CEChunk.java | 116 ++++++++++++++++-- 11 files changed, 211 insertions(+), 28 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java index 0b31cf939..7127e4966 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/bettermodel/BetterModelBlockEntityElementConfig.java @@ -44,6 +44,11 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo return new BetterModelBlockEntityElement(world, pos, this); } + @Override + public Class elementClass() { + return BetterModelBlockEntityElement.class; + } + public static class Factory implements BlockEntityElementConfigFactory { @SuppressWarnings("unchecked") diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java index a8dbd2643..0a4c5c41c 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/model/modelengine/ModelEngineBlockEntityElementConfig.java @@ -44,6 +44,11 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo return new ModelEngineBlockEntityElement(world, pos, this); } + @Override + public Class elementClass() { + return ModelEngineBlockEntityElement.class; + } + public static class Factory implements BlockEntityElementConfigFactory { @SuppressWarnings("unchecked") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java index 074bc87c5..1f4760aef 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElement.java @@ -13,13 +13,16 @@ import java.util.List; import java.util.UUID; public class ItemDisplayBlockEntityElement implements BlockEntityElement { - private final ItemDisplayBlockEntityElementConfig config; - private final Object cachedSpawnPacket; - private final Object cachedDespawnPacket; - private final int entityId; + public final ItemDisplayBlockEntityElementConfig config; + public final Object cachedSpawnPacket; + public final Object cachedDespawnPacket; + public final int entityId; public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) { - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet()); + } + + public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos, int entityId) { Vector3f position = config.position(); this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, @@ -39,4 +42,9 @@ public class ItemDisplayBlockEntityElement implements BlockEntityElement { public void show(Player player) { player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); } + + @Override + public void transform(Player player) { + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java index 53320d84d..5fc5a399c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemDisplayBlockEntityElementConfig.java @@ -78,6 +78,19 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo return new ItemDisplayBlockEntityElement(this, pos); } + @Override + public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) { + if (previous.config.yRot != this.yRot || !previous.config.position.equals(this.position)) { + return null; + } + return new ItemDisplayBlockEntityElement(this, pos, previous.entityId); + } + + @Override + public Class elementClass() { + return ItemDisplayBlockEntityElement.class; + } + public Item item(Player player) { return this.item.apply(player); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java index 45a1c6c9d..b0d604745 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElement.java @@ -13,13 +13,16 @@ import java.util.List; import java.util.UUID; public class TextDisplayBlockEntityElement implements BlockEntityElement { - private final TextDisplayBlockEntityElementConfig config; - private final Object cachedSpawnPacket; - private final Object cachedDespawnPacket; - private final int entityId; + public final TextDisplayBlockEntityElementConfig config; + public final Object cachedSpawnPacket; + public final Object cachedDespawnPacket; + public final int entityId; public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) { - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet()); + } + + public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos, int entityId) { Vector3f position = config.position(); this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, @@ -39,4 +42,13 @@ public class TextDisplayBlockEntityElement implements BlockEntityElement { public void show(Player player) { player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); } + + @Override + public void transform(Player player) { + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false); + } + + public int entityId() { + return entityId; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java index e707023ee..eaef23d0b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/TextDisplayBlockEntityElementConfig.java @@ -66,6 +66,19 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo return new TextDisplayBlockEntityElement(this, pos); } + @Override + public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) { + if (previous.config.yRot != this.yRot || !previous.config.position.equals(this.position)) { + return null; + } + return new TextDisplayBlockEntityElement(this, pos, previous.entityId); + } + + @Override + public Class elementClass() { + return TextDisplayBlockEntityElement.class; + } + public Component text(Player player) { return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers()); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java index b1e7d7ff1..59ec1b225 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java @@ -19,6 +19,8 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.ReflectionUtils; @@ -226,7 +228,10 @@ public final class WorldStorageInjector { // 处理 自定义块到自定义块或原版块到自定义块 CEChunk chunk = holder.ceChunk(); chunk.setDirty(true); + + ConstantBlockEntityRenderer previousRenderer = null; // 如果两个方块没有相同的主人 且 旧方块有方块实体 + if (!previousImmutableBlockState.isEmpty()) { if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) { BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); @@ -242,9 +247,11 @@ public final class WorldStorageInjector { } if (previousImmutableBlockState.hasConstantBlockEntityRenderer()) { BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); - chunk.removeConstantBlockEntityRenderer(pos); + // 如果新状态没有entity renderer,那么直接移除,否则暂存 + previousRenderer = chunk.removeConstantBlockEntityRenderer(pos, !newImmutableBlockState.hasConstantBlockEntityRenderer()); } } + if (newImmutableBlockState.hasBlockEntity()) { BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); BlockEntity blockEntity = chunk.getBlockEntity(pos, false); @@ -264,10 +271,13 @@ public final class WorldStorageInjector { chunk.createDynamicBlockEntityRenderer(blockEntity); } } + + // 处理新老entity renderer更替 if (newImmutableBlockState.hasConstantBlockEntityRenderer()) { BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); - chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState); + chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState, previousRenderer); } + // 如果新方块的光照属性和客户端认为的不同 if (Config.enableLightSystem()) { if (previousImmutableBlockState.isEmpty()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java index f28ab579f..82c9fdeae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/ConstantBlockEntityRenderer.java @@ -4,6 +4,8 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.entity.player.Player; import org.jetbrains.annotations.ApiStatus; +import java.util.Collection; + @ApiStatus.Experimental public class ConstantBlockEntityRenderer { private final BlockEntityElement[] elements; @@ -14,25 +16,37 @@ public class ConstantBlockEntityRenderer { public void show(Player player) { for (BlockEntityElement element : this.elements) { - element.show(player); + if (element != null) { + element.show(player); + } } } public void hide(Player player) { for (BlockEntityElement element : this.elements) { - element.hide(player); + if (element != null) { + element.hide(player); + } } } public void deactivate() { for (BlockEntityElement element : this.elements) { - element.deactivate(); + if (element != null) { + element.deactivate(); + } } } public void activate() { for (BlockEntityElement element : this.elements) { - element.activate(); + if (element != null) { + element.activate(); + } } } + + public BlockEntityElement[] elements() { + return this.elements; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java index eaf5d605c..a0c0fb321 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElement.java @@ -10,6 +10,8 @@ public interface BlockEntityElement { void hide(Player player); + default void transform(Player player) {} + default void deactivate() {} default void activate() {} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java index a61f0b030..c5c0f59db 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfig.java @@ -3,7 +3,16 @@ package net.momirealms.craftengine.core.block.entity.render.element; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.World; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + public interface BlockEntityElementConfig { E create(World world, BlockPos pos); + + default E create(World world, BlockPos pos, E previous) { + return null; + } + + Class elementClass(); } 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 d48e09472..67e355a05 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 @@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.block.entity.tick.*; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.BlockEntityTickersList; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; @@ -115,40 +116,131 @@ public class CEChunk { } } - public void addConstantBlockEntityRenderer(BlockPos pos) { - this.addConstantBlockEntityRenderer(pos, this.getBlockState(pos)); + public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos) { + return this.addConstantBlockEntityRenderer(pos, this.getBlockState(pos), null); } - public void addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) { + public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) { + return this.addConstantBlockEntityRenderer(pos, state, null); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state, @Nullable ConstantBlockEntityRenderer previous) { BlockEntityElementConfig[] renderers = state.constantRenderers(); if (renderers != null && renderers.length > 0) { BlockEntityElement[] elements = new BlockEntityElement[renderers.length]; - World wrappedWorld = this.world.world(); - for (int i = 0; i < elements.length; i++) { - elements[i] = renderers[i].create(wrappedWorld, pos); - } ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements); - for (Player player : getTrackedBy()) { - renderer.show(player); + World wrappedWorld = this.world.world(); + List trackedBy = getTrackedBy(); + boolean hasTrackedBy = trackedBy != null && !trackedBy.isEmpty(); + // 处理旧到新的转换 + if (previous != null) { + // 由于entity-render的体量基本都很小,所以考虑一个特殊情况,即前后都是1个renderer,对此情况进行简化和优化 + BlockEntityElement[] previousElements = previous.elements().clone(); + if (previousElements.length == 1 && renderers.length == 1) { + BlockEntityElement previousElement = previousElements[0]; + BlockEntityElementConfig config = renderers[0]; + outer: { + if (config.elementClass().isInstance(previousElement)) { + BlockEntityElement element = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement); + if (element != null) { + elements[0] = element; + if (hasTrackedBy) { + for (Player player : trackedBy) { + element.transform(player); + } + } + break outer; + } + } + BlockEntityElement element = config.create(wrappedWorld, pos); + elements[0] = element; + if (hasTrackedBy) { + for (Player player : trackedBy) { + previousElement.hide(player); + element.show(player); + } + } + } + } else { + outer: for (int i = 0; i < elements.length; i++) { + BlockEntityElementConfig config = renderers[i]; + { + for (int j = 0; j < previousElements.length; j++) { + BlockEntityElement previousElement = previousElements[j]; + if (previousElement != null && config.elementClass().isInstance(previousElement)) { + BlockEntityElement newElement = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement); + if (newElement != null) { + previousElements[i] = null; + elements[i] = newElement; + if (hasTrackedBy) { + for (Player player : trackedBy) { + newElement.transform(player); + } + } + continue outer; + } + } + } + BlockEntityElement newElement = config.create(wrappedWorld, pos); + elements[i] = newElement; + if (hasTrackedBy) { + for (Player player : trackedBy) { + newElement.show(player); + } + } + } + } + if (hasTrackedBy) { + for (int i = 0; i < previousElements.length; i++) { + BlockEntityElement previousElement = previousElements[i]; + if (previousElement != null) { + for (Player player : trackedBy) { + previousElement.hide(player); + } + } + } + } + } + } else { + for (int i = 0; i < elements.length; i++) { + elements[i] = renderers[i].create(wrappedWorld, pos); + } + if (hasTrackedBy) { + for (Player player : trackedBy) { + renderer.show(player); + } + } } try { this.renderLock.writeLock().lock(); this.constantBlockEntityRenderers.put(pos, renderer); + return renderer; } finally { this.renderLock.writeLock().unlock(); } } + return null; } - public void removeConstantBlockEntityRenderer(BlockPos pos) { + @Nullable + public ConstantBlockEntityRenderer removeConstantBlockEntityRenderer(BlockPos pos) { + return this.removeConstantBlockEntityRenderer(pos, true); + } + + @Nullable + public ConstantBlockEntityRenderer removeConstantBlockEntityRenderer(BlockPos pos, boolean hide) { try { this.renderLock.writeLock().lock(); ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos); if (removed != null) { - for (Player player : getTrackedBy()) { - removed.hide(player); + if (hide) { + for (Player player : getTrackedBy()) { + removed.hide(player); + } } } + return removed; } finally { this.renderLock.writeLock().unlock(); }