diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java index 0385b0b32..3e778bd32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java @@ -6,4 +6,4 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityTypes; public class BukkitBlockEntityTypes extends BlockEntityTypes { public static final BlockEntityType SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new); -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java index b9ec60521..26541c363 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SimpleStorageBlockEntity.java @@ -24,7 +24,6 @@ import org.bukkit.GameEvent; import org.bukkit.GameMode; import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; @@ -200,6 +199,4 @@ public class SimpleStorageBlockEntity extends BlockEntity { } this.inventory.clear(); } - - } 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 81fdda00b..50fafbd48 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 @@ -237,6 +237,10 @@ public final class WorldStorageInjector { chunk.removeBlockEntity(pos); } } + if (previous.hasBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.removeBlockEntityRenderer(pos); + } if (Config.enableLightSystem()) { // 自定义块到原版块,只需要判断旧块是否和客户端一直 BlockStateWrapper wrapper = previous.vanillaBlockState(); @@ -266,6 +270,10 @@ public final class WorldStorageInjector { chunk.removeBlockEntity(pos); } } + if (previousImmutableBlockState.hasBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.removeBlockEntityRenderer(pos); + } } if (newImmutableBlockState.hasBlockEntity()) { BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); @@ -285,6 +293,10 @@ public final class WorldStorageInjector { chunk.replaceOrCreateTickingBlockEntity(blockEntity); } } + if (newImmutableBlockState.hasBlockEntityRenderer()) { + BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z); + chunk.addBlockEntityRenderer(pos, newImmutableBlockState); + } // 如果新方块的光照属性和客户端认为的不同 if (Config.enableLightSystem()) { if (previousImmutableBlockState.isEmpty()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 740564e0a..cd2cb926b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -37,6 +37,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -64,6 +65,7 @@ import net.momirealms.craftengine.core.plugin.network.*; import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import net.momirealms.craftengine.core.world.chunk.Palette; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; @@ -261,14 +263,24 @@ public class PacketConsumers { public static final BiConsumer FORGET_LEVEL_CHUNK = (user, event) -> { try { + BukkitServerPlayer player = (BukkitServerPlayer) user; FriendlyByteBuf buf = event.getBuffer(); + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); if (VersionHelper.isOrAbove1_20_2()) { long chunkPos = buf.readLong(); user.removeTrackedChunk(chunkPos); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos); + if (ceChunk != null) { + ceChunk.despawnBlockEntities(player); + } } else { int x = buf.readInt(); int y = buf.readInt(); user.removeTrackedChunk(ChunkPos.asLong(x, y)); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(x, y); + if (ceChunk != null) { + ceChunk.despawnBlockEntities(player); + } } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e); @@ -401,6 +413,12 @@ public class PacketConsumers { ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); // 记录加载的区块 player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); + + CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos.longKey); + if (ceChunk != null) { + ceChunk.spawnBlockEntities(player); + } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index e62760b77..4ec9a9a20 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -8,7 +8,6 @@ import io.netty.channel.ChannelHandler; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder; -import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; @@ -48,7 +47,6 @@ import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.block.Block; -import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; 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 a3f56861a..6d5d0cae4 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 @@ -1,8 +1,11 @@ package net.momirealms.craftengine.bukkit.world; import net.momirealms.craftengine.bukkit.util.LightUtils; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRenderer; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRendererConfig; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.SectionPosUtils; +import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.SectionPos; import net.momirealms.craftengine.core.world.World; @@ -39,4 +42,9 @@ public class BukkitCEWorld extends CEWorld { ); } } + + @Override + public BlockEntityRenderer createBlockEntityRenderer(BlockEntityRendererConfig config, BlockPos pos) { + return null; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index cfa5ebb2d..9a3a2b65d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRendererConfig; import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.Player; @@ -30,6 +31,8 @@ public final class ImmutableBlockState extends BlockStateHolder { private Integer hashCode; private BlockSettings settings; private BlockEntityType blockEntityType; + @Nullable + private BlockEntityRendererConfig rendererConfig; ImmutableBlockState( Holder owner, @@ -66,6 +69,11 @@ public final class ImmutableBlockState extends BlockStateHolder { return this == EmptyBlock.STATE; } + @Nullable + public BlockEntityRendererConfig entityRenderer() { + return this.rendererConfig; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -85,6 +93,10 @@ public final class ImmutableBlockState extends BlockStateHolder { return this.blockEntityType != null; } + public boolean hasBlockEntityRenderer() { + return this.rendererConfig != null; + } + public BlockStateWrapper customBlockState() { return this.customBlockState; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java index 5d969518d..cb452fd4d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypeKeys.java @@ -5,5 +5,6 @@ import net.momirealms.craftengine.core.util.Key; public final class BlockEntityTypeKeys { private BlockEntityTypeKeys() {} + public static final Key UNSAFE_COMPOSITE = Key.of("craftengine:unsafe_composite"); public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRenderer.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRenderer.java new file mode 100644 index 000000000..6414fb396 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRenderer.java @@ -0,0 +1,23 @@ +package net.momirealms.craftengine.core.block.entity.render; + +import net.momirealms.craftengine.core.entity.player.Player; + +public abstract class BlockEntityRenderer { + private final int entityId; + + public BlockEntityRenderer(int entityId) { + this.entityId = entityId; + } + + public int entityId() { + return this.entityId; + } + + public abstract void spawn(); + + public abstract void despawn(); + + public abstract void spawn(Player player); + + public abstract void despawn(Player player); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRendererConfig.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRendererConfig.java new file mode 100644 index 000000000..be4cdffbe --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/BlockEntityRendererConfig.java @@ -0,0 +1,88 @@ +package net.momirealms.craftengine.core.block.entity.render; + +import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.item.Item; + +public class BlockEntityRendererConfig { + private final float yRot; + private final float xRot; + private final ItemDisplayContext displayContext; + private final Item item; + private final float scale; + + public BlockEntityRendererConfig(ItemDisplayContext displayContext, + float yRot, + float xRot, + Item item, + float scale) { + this.displayContext = displayContext; + this.yRot = yRot; + this.xRot = xRot; + this.item = item; + this.scale = scale; + } + + public ItemDisplayContext displayContext() { + return displayContext; + } + + public Item item() { + return item; + } + + public float xRot() { + return xRot; + } + + public float yRot() { + return yRot; + } + + public float scale() { + return scale; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private ItemDisplayContext displayContext = ItemDisplayContext.NONE; + private Item item; + private float xRot; + private float yRot; + private float scale = 1f; + + public Builder() { + } + + public Builder displayContext(ItemDisplayContext displayContext) { + this.displayContext = displayContext; + return this; + } + + public Builder item(Item item) { + this.item = item; + return this; + } + + public Builder xRot(float xRot) { + this.xRot = xRot; + return this; + } + + public Builder yRot(float yRot) { + this.yRot = yRot; + return this; + } + + public Builder scale(float scale) { + this.scale = scale; + return this; + } + + public BlockEntityRendererConfig build() { + return new BlockEntityRendererConfig(this.displayContext, this.yRot, this.xRot, this.item, this.scale); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java index 066e3216a..51a74c794 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/PlaceholderTag.java @@ -7,7 +7,6 @@ import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.PlayerContext; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.AdventureHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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 9d68a0e1c..b3c183f1b 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 @@ -4,6 +4,8 @@ import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTabl import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRenderer; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRendererConfig; import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; @@ -110,6 +112,11 @@ public abstract class CEWorld { return getChunkAtIfLoaded(ChunkPos.asLong(x, z)); } + @Nullable + public CEChunk getChunkAtIfLoaded(ChunkPos chunkPos) { + return getChunkAtIfLoaded(chunkPos.longKey); + } + @Nullable public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) { CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4); @@ -208,4 +215,6 @@ public abstract class CEWorld { this.tickingBlockEntities.removeAll(toRemove); this.isTickingBlockEntities = false; } + + public abstract BlockEntityRenderer createBlockEntityRenderer(BlockEntityRendererConfig config, BlockPos pos); } 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 6b8acd1a7..4dbea6f15 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 @@ -4,15 +4,20 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 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.BlockEntityRenderer; +import net.momirealms.craftengine.core.block.entity.render.BlockEntityRendererConfig; 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.world.*; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class CEChunk { public final CEWorld world; @@ -20,23 +25,25 @@ public class CEChunk { public final CESection[] sections; public final WorldHeight worldHeightAccessor; public final Map blockEntities; + public final Map blockEntityRenderers; + private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock(); private volatile boolean dirty; private volatile boolean loaded; - protected final Map tickingBlockEntitiesByPos = new HashMap<>(); + protected final Map tickingBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); public CEChunk(CEWorld world, ChunkPos chunkPos) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.blockEntities = new Object2ObjectOpenHashMap<>(16, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.blockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, ListTag blockEntitiesTag) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag itemDisplayBlockRenders) { this.world = world; this.chunkPos = chunkPos; - this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 16), 0.5f); this.worldHeightAccessor = world.worldHeight(); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; @@ -49,9 +56,75 @@ public class CEChunk { } } this.fillEmptySection(); - List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); - for (BlockEntity blockEntity : blockEntities) { - this.setBlockEntity(blockEntity); + if (blockEntitiesTag != null) { + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 10), 0.5f); + List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); + for (BlockEntity blockEntity : blockEntities) { + this.setBlockEntity(blockEntity); + } + } else { + this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + } + if (itemDisplayBlockRenders != null) { + this.blockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(itemDisplayBlockRenders.size(), 10), 0.5f); + List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, itemDisplayBlockRenders); + for (BlockPos pos : blockEntityRendererPoses) { + this.addBlockEntityRenderer(pos); + } + } else { + this.blockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + } + } + + public void spawnBlockEntities(Player player) { + try { + this.renderLock.readLock().lock(); + for (BlockEntityRenderer renderer : this.blockEntityRenderers.values()) { + renderer.spawn(player); + } + } finally { + this.renderLock.readLock().unlock(); + } + } + + public void despawnBlockEntities(Player player) { + try { + this.renderLock.readLock().lock(); + for (BlockEntityRenderer renderer : this.blockEntityRenderers.values()) { + renderer.despawn(player); + } + } finally { + this.renderLock.readLock().unlock(); + } + } + + public void addBlockEntityRenderer(BlockPos pos) { + this.addBlockEntityRenderer(pos, this.getBlockState(pos)); + } + + public void addBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) { + BlockEntityRendererConfig config = state.entityRenderer(); + if (config != null) { + BlockEntityRenderer renderer = this.world.createBlockEntityRenderer(config, pos); + renderer.spawn(); + try { + this.renderLock.writeLock().lock(); + this.blockEntityRenderers.put(pos, renderer); + } finally { + this.renderLock.writeLock().unlock(); + } + } + } + + public void removeBlockEntityRenderer(BlockPos pos) { + try { + this.renderLock.writeLock().lock(); + BlockEntityRenderer removed = this.blockEntityRenderers.remove(pos); + if (removed != null) { + removed.despawn(); + } + } finally { + this.renderLock.writeLock().unlock(); } } @@ -152,8 +225,17 @@ public class CEChunk { return Objects.requireNonNull(blockState.behavior().getEntityBehavior()).createBlockEntity(pos, blockState); } - public Map blockEntities() { - return Collections.unmodifiableMap(this.blockEntities); + public Collection blockEntities() { + return Collections.unmodifiableCollection(this.blockEntities.values()); + } + + public List blockEntityRenderers() { + try { + this.renderLock.readLock().lock(); + return new ArrayList<>(this.blockEntityRenderers.keySet()); + } finally { + this.renderLock.readLock().unlock(); + } } public boolean dirty() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java new file mode 100644 index 000000000..cf89f584e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntityRendererSerializer.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; + +import java.util.ArrayList; +import java.util.List; + +public final class DefaultBlockEntityRendererSerializer { + + public static List deserialize(ChunkPos chunkPos, ListTag blockEntitiesTag) { + List blockEntities = new ArrayList<>(blockEntitiesTag.size()); + for (int i = 0; i < blockEntitiesTag.size(); i++) { + CompoundTag tag = blockEntitiesTag.getCompound(i); + BlockPos blockPos = BlockEntity.readPosAndVerify(tag, chunkPos); + blockEntities.add(blockPos); + } + return blockEntities; + } + + public static ListTag serialize(List poses) { + ListTag listTag = new ListTag(); + for (BlockPos pos : poses) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.x()); + tag.putInt("y", pos.y()); + tag.putInt("z", pos.z()); + listTag.add(tag); + } + return listTag; + } +} 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 index 0e1d59602..6dfc48961 100644 --- 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 @@ -12,15 +12,14 @@ import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Map; public final class DefaultBlockEntitySerializer { - public static ListTag serialize(Map tiles) { + public static ListTag serialize(Collection entities) { ListTag result = new ListTag(); - for (Map.Entry entry : tiles.entrySet()) { - BlockEntity entity = entry.getValue(); + for (BlockEntity entity : entities) { if (entity.isValid()) { result.add(entity.saveAsTag()); } 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 fafceb99c..4ef8a7955 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,8 +9,6 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Optional; - public final class DefaultChunkSerializer { @Nullable @@ -28,7 +26,14 @@ public final class DefaultChunkSerializer { if (sections.isEmpty()) return null; CompoundTag chunkNbt = new CompoundTag(); chunkNbt.put("sections", sections); - chunkNbt.put("block_entities", DefaultBlockEntitySerializer.serialize(chunk.blockEntities())); + ListTag blockEntities = DefaultBlockEntitySerializer.serialize(chunk.blockEntities()); + if (!blockEntities.isEmpty()) { + chunkNbt.put("block_entities", blockEntities); + } + ListTag blockEntityRenders = DefaultBlockEntityRendererSerializer.serialize(chunk.blockEntityRenderers()); + if (!blockEntityRenders.isEmpty()) { + chunkNbt.put("block_entity_renders", blockEntityRenders); + } return chunkNbt; } @@ -46,7 +51,8 @@ public final class DefaultChunkSerializer { } } } - ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); - return new CEChunk(world, pos, sectionArray, blockEntities); + ListTag blockEntities = chunkNbt.getList("block_entities"); + ListTag itemDisplayBlockRenders = chunkNbt.getList("block_entity_renderers"); + return new CEChunk(world, pos, sectionArray, blockEntities, itemDisplayBlockRenders); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index 8121e3b64..9a837a003 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.world.collision; import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.MCUtils; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d;