9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

优化方块实体切换

This commit is contained in:
XiaoMoMi
2025-11-04 22:13:43 +08:00
parent 7389318731
commit bb305343d3
11 changed files with 211 additions and 28 deletions

View File

@@ -44,6 +44,11 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
return new BetterModelBlockEntityElement(world, pos, this); return new BetterModelBlockEntityElement(world, pos, this);
} }
@Override
public Class<BetterModelBlockEntityElement> elementClass() {
return BetterModelBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory { public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -44,6 +44,11 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo
return new ModelEngineBlockEntityElement(world, pos, this); return new ModelEngineBlockEntityElement(world, pos, this);
} }
@Override
public Class<ModelEngineBlockEntityElement> elementClass() {
return ModelEngineBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory { public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -13,13 +13,16 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
public class ItemDisplayBlockEntityElement implements BlockEntityElement { public class ItemDisplayBlockEntityElement implements BlockEntityElement {
private final ItemDisplayBlockEntityElementConfig config; public final ItemDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket; public final Object cachedSpawnPacket;
private final Object cachedDespawnPacket; public final Object cachedDespawnPacket;
private final int entityId; public final int entityId;
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) { 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(); Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, 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) { public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); 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);
}
} }

View File

@@ -78,6 +78,19 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new ItemDisplayBlockEntityElement(this, pos); 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<ItemDisplayBlockEntityElement> elementClass() {
return ItemDisplayBlockEntityElement.class;
}
public Item<?> item(Player player) { public Item<?> item(Player player) {
return this.item.apply(player); return this.item.apply(player);
} }

View File

@@ -13,13 +13,16 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
public class TextDisplayBlockEntityElement implements BlockEntityElement { public class TextDisplayBlockEntityElement implements BlockEntityElement {
private final TextDisplayBlockEntityElementConfig config; public final TextDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket; public final Object cachedSpawnPacket;
private final Object cachedDespawnPacket; public final Object cachedDespawnPacket;
private final int entityId; public final int entityId;
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) { 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(); Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, 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) { public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true); 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;
}
} }

View File

@@ -66,6 +66,19 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new TextDisplayBlockEntityElement(this, pos); 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<TextDisplayBlockEntityElement> elementClass() {
return TextDisplayBlockEntityElement.class;
}
public Component text(Player player) { public Component text(Player player) {
return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers()); return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers());
} }

View File

@@ -19,6 +19,8 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState;
import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity; 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.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.ReflectionUtils;
@@ -226,7 +228,10 @@ public final class WorldStorageInjector {
// 处理 自定义块到自定义块或原版块到自定义块 // 处理 自定义块到自定义块或原版块到自定义块
CEChunk chunk = holder.ceChunk(); CEChunk chunk = holder.ceChunk();
chunk.setDirty(true); chunk.setDirty(true);
ConstantBlockEntityRenderer previousRenderer = null;
// 如果两个方块没有相同的主人 且 旧方块有方块实体 // 如果两个方块没有相同的主人 且 旧方块有方块实体
if (!previousImmutableBlockState.isEmpty()) { if (!previousImmutableBlockState.isEmpty()) {
if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) { 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); 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()) { if (previousImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); 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()) { if (newImmutableBlockState.hasBlockEntity()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
BlockEntity blockEntity = chunk.getBlockEntity(pos, false); BlockEntity blockEntity = chunk.getBlockEntity(pos, false);
@@ -264,10 +271,13 @@ public final class WorldStorageInjector {
chunk.createDynamicBlockEntityRenderer(blockEntity); chunk.createDynamicBlockEntityRenderer(blockEntity);
} }
} }
// 处理新老entity renderer更替
if (newImmutableBlockState.hasConstantBlockEntityRenderer()) { if (newImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); 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 (Config.enableLightSystem()) {
if (previousImmutableBlockState.isEmpty()) { if (previousImmutableBlockState.isEmpty()) {

View File

@@ -4,6 +4,8 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import java.util.Collection;
@ApiStatus.Experimental @ApiStatus.Experimental
public class ConstantBlockEntityRenderer { public class ConstantBlockEntityRenderer {
private final BlockEntityElement[] elements; private final BlockEntityElement[] elements;
@@ -14,25 +16,37 @@ public class ConstantBlockEntityRenderer {
public void show(Player player) { public void show(Player player) {
for (BlockEntityElement element : this.elements) { for (BlockEntityElement element : this.elements) {
element.show(player); if (element != null) {
element.show(player);
}
} }
} }
public void hide(Player player) { public void hide(Player player) {
for (BlockEntityElement element : this.elements) { for (BlockEntityElement element : this.elements) {
element.hide(player); if (element != null) {
element.hide(player);
}
} }
} }
public void deactivate() { public void deactivate() {
for (BlockEntityElement element : this.elements) { for (BlockEntityElement element : this.elements) {
element.deactivate(); if (element != null) {
element.deactivate();
}
} }
} }
public void activate() { public void activate() {
for (BlockEntityElement element : this.elements) { for (BlockEntityElement element : this.elements) {
element.activate(); if (element != null) {
element.activate();
}
} }
} }
public BlockEntityElement[] elements() {
return this.elements;
}
} }

View File

@@ -10,6 +10,8 @@ public interface BlockEntityElement {
void hide(Player player); void hide(Player player);
default void transform(Player player) {}
default void deactivate() {} default void deactivate() {}
default void activate() {} default void activate() {}

View File

@@ -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.BlockPos;
import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.World;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public interface BlockEntityElementConfig<E extends BlockEntityElement> { public interface BlockEntityElementConfig<E extends BlockEntityElement> {
E create(World world, BlockPos pos); E create(World world, BlockPos pos);
default E create(World world, BlockPos pos, E previous) {
return null;
}
Class<E> elementClass();
} }

View File

@@ -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.block.entity.tick.*;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.logger.Debugger; 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.*;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer;
@@ -115,40 +116,131 @@ public class CEChunk {
} }
} }
public void addConstantBlockEntityRenderer(BlockPos pos) { public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos) {
this.addConstantBlockEntityRenderer(pos, this.getBlockState(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<? extends BlockEntityElement>[] renderers = state.constantRenderers(); BlockEntityElementConfig<? extends BlockEntityElement>[] renderers = state.constantRenderers();
if (renderers != null && renderers.length > 0) { if (renderers != null && renderers.length > 0) {
BlockEntityElement[] elements = new BlockEntityElement[renderers.length]; 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); ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements);
for (Player player : getTrackedBy()) { World wrappedWorld = this.world.world();
renderer.show(player); List<Player> 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<? extends BlockEntityElement> 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<? extends BlockEntityElement> 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 { try {
this.renderLock.writeLock().lock(); this.renderLock.writeLock().lock();
this.constantBlockEntityRenderers.put(pos, renderer); this.constantBlockEntityRenderers.put(pos, renderer);
return renderer;
} finally { } finally {
this.renderLock.writeLock().unlock(); 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 { try {
this.renderLock.writeLock().lock(); this.renderLock.writeLock().lock();
ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos); ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos);
if (removed != null) { if (removed != null) {
for (Player player : getTrackedBy()) { if (hide) {
removed.hide(player); for (Player player : getTrackedBy()) {
removed.hide(player);
}
} }
} }
return removed;
} finally { } finally {
this.renderLock.writeLock().unlock(); this.renderLock.writeLock().unlock();
} }