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);
}
@Override
public Class<BetterModelBlockEntityElement> elementClass() {
return BetterModelBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")

View File

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

View File

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

View File

@@ -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<ItemDisplayBlockEntityElement> elementClass() {
return ItemDisplayBlockEntityElement.class;
}
public Item<?> item(Player player) {
return this.item.apply(player);
}

View File

@@ -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;
}
}

View File

@@ -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<TextDisplayBlockEntityElement> elementClass() {
return TextDisplayBlockEntityElement.class;
}
public Component text(Player player) {
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.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()) {

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 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) {
if (element != null) {
element.show(player);
}
}
}
public void hide(Player player) {
for (BlockEntityElement element : this.elements) {
if (element != null) {
element.hide(player);
}
}
}
public void deactivate() {
for (BlockEntityElement element : this.elements) {
if (element != null) {
element.deactivate();
}
}
}
public void activate() {
for (BlockEntityElement element : this.elements) {
if (element != null) {
element.activate();
}
}
}
public BlockEntityElement[] elements() {
return this.elements;
}
}

View File

@@ -10,6 +10,8 @@ public interface BlockEntityElement {
void hide(Player player);
default void transform(Player player) {}
default void deactivate() {}
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.World;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public interface BlockEntityElementConfig<E extends BlockEntityElement> {
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.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<? extends BlockEntityElement>[] renderers = state.constantRenderers();
if (renderers != null && renderers.length > 0) {
BlockEntityElement[] elements = new BlockEntityElement[renderers.length];
ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements);
World wrappedWorld = this.world.world();
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);
}
ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements);
for (Player player : getTrackedBy()) {
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) {
if (hide) {
for (Player player : getTrackedBy()) {
removed.hide(player);
}
}
}
return removed;
} finally {
this.renderLock.writeLock().unlock();
}