diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 2928766a2..2ac3aaaf4 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -1,8 +1,6 @@ package net.minestom.server.instance; import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.pathfinding.PFBlock; @@ -11,15 +9,14 @@ import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.world.biomes.Biome; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.lang.ref.SoftReference; import java.util.Map; +import java.util.Objects; import java.util.TreeMap; -import java.util.concurrent.TimeUnit; /** * Represents a {@link Chunk} which store each individual block in memory. @@ -31,8 +28,8 @@ public class DynamicChunk extends Chunk { protected final TreeMap sectionMap = new TreeMap<>(); // Key = ChunkUtils#getBlockIndex - protected final Int2ObjectOpenHashMap entries = new Int2ObjectOpenHashMap<>(); - protected final Int2ObjectOpenHashMap tickableMap = new Int2ObjectOpenHashMap<>(); + protected final Int2ObjectOpenHashMap entries = new Int2ObjectOpenHashMap<>(); + protected final Int2ObjectOpenHashMap tickableMap = new Int2ObjectOpenHashMap<>(); private long lastChangeTime; @@ -60,13 +57,13 @@ public class DynamicChunk extends Chunk { final BlockHandler handler = block.handler(); final NBTCompound nbt = block.nbt(); if (handler != null || nbt != null) { - this.entries.put(index, new BlockEntry(handler, nbt)); + this.entries.put(index, block); } else { this.entries.remove(index); } // Tickable if (handler != null && handler.isTickable()) { - this.tickableMap.put(index, handler); + this.tickableMap.put(index, block); } else { this.tickableMap.remove(index); } @@ -88,41 +85,33 @@ public class DynamicChunk extends Chunk { return; for (var entry : tickableMap.int2ObjectEntrySet()) { final int index = entry.getIntKey(); - - final int x = ChunkUtils.blockIndexToChunkPositionX(index); - final int y = ChunkUtils.blockIndexToChunkPositionY(index); - final int z = ChunkUtils.blockIndexToChunkPositionZ(index); - final Vec blockPosition = new Vec(x, y, z); - - final Block block = getBlock(blockPosition); - entry.getValue().tick(new BlockHandler.Tick(block, instance, blockPosition)); + final Block block = entry.getValue(); + final var handler = block.handler(); + if (handler != null) { + final int x = ChunkUtils.blockIndexToChunkPositionX(index); + final int y = ChunkUtils.blockIndexToChunkPositionY(index); + final int z = ChunkUtils.blockIndexToChunkPositionZ(index); + final Vec blockPosition = new Vec(x, y, z); + handler.tick(new BlockHandler.Tick(block, instance, blockPosition)); + } } } @Override public @NotNull Block getBlock(int x, int y, int z) { + // Verify if the block object is present + final int index = ChunkUtils.getBlockIndex(x, y, z); + final var entry = entries.get(index); + if (entry != null) { + return entry; + } + // Retrieve the block from state id final Section section = retrieveSection(y); final short blockStateId = section.getBlockAt(x, y, z); if (blockStateId == -1) { return Block.AIR; } - Block block = Block.fromStateId(blockStateId); - if (block == null) { - return Block.AIR; - } - final int index = ChunkUtils.getBlockIndex(x, y, z); - final var entry = entries.get(index); - if (entry != null) { - final BlockHandler handler = entry.handler; - final NBTCompound nbt = entry.nbtCompound; - if (handler != null) { - block = block.withHandler(handler); - } - if (nbt != null) { - block = block.withNbt(nbt); - } - } - return block; + return Objects.requireNonNullElse(Block.fromStateId(blockStateId), Block.AIR); } @Override @@ -170,28 +159,4 @@ public class DynamicChunk extends Chunk { final int sectionIndex = ChunkUtils.getSectionAt(y); return getSection(sectionIndex); } - - @ApiStatus.Internal - public static class BlockEntry { - private static final Cache NBT_CACHE = Caffeine.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .weakValues() - .build(); - - private final BlockHandler handler; - private final NBTCompound nbtCompound; - - public BlockEntry(BlockHandler handler, NBTCompound nbtCompound) { - this.handler = handler; - this.nbtCompound = nbtCompound != null ? NBT_CACHE.get(nbtCompound, compound -> nbtCompound) : null; - } - - public BlockHandler handler() { - return handler; - } - - public NBTCompound nbtCompound() { - return nbtCompound; - } - } } diff --git a/src/main/java/net/minestom/server/instance/block/BlockImpl.java b/src/main/java/net/minestom/server/instance/block/BlockImpl.java index d471b1ac1..980d41cb0 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -1,5 +1,7 @@ package net.minestom.server.instance.block; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import net.minestom.server.registry.Registry; import net.minestom.server.tag.Tag; import org.jetbrains.annotations.NotNull; @@ -10,8 +12,14 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; class BlockImpl implements Block { + private static final Cache NBT_CACHE = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .weakValues() + .build(); + private final Registry.BlockEntry registry; private final Map properties; private final NBTCompound nbt; @@ -48,7 +56,7 @@ class BlockImpl implements Block { public @NotNull Block withTag(@NotNull Tag tag, @Nullable T value) { var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new); tag.write(compound, value); - return new BlockImpl(registry, properties, compound, handler); + return new BlockImpl(registry, properties, NBT_CACHE.get(compound, c -> compound), handler); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index 74b596723..c1a554016 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minestom.server.MinecraftServer; import net.minestom.server.instance.DynamicChunk; import net.minestom.server.instance.Section; +import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -35,7 +36,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket { public int chunkX, chunkZ; public Map sections = new HashMap<>(); - public Map entries = new HashMap<>(); + public Map entries = new HashMap<>(); private static final byte CHUNK_SECTION_COUNT = 16; private static final int MAX_BITS_PER_ENTRY = 16; @@ -130,14 +131,15 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket { List compounds = new ArrayList<>(); for (var entry : entries.entrySet()) { final int index = entry.getKey(); - final var blockEntry = entry.getValue(); - final BlockHandler handler = blockEntry.handler(); - final var blockEntityTags = handler.getBlockEntityTags(); - if (blockEntityTags.isEmpty()) + final var block = entry.getValue(); + final BlockHandler handler = block.handler(); + if(handler == null) continue; - final var blockNbt = Objects.requireNonNullElseGet(blockEntry.nbtCompound(), NBTCompound::new); + final var blockEntityTags = handler.getBlockEntityTags(); + if (blockEntityTags.isEmpty()) // Verify if the block should be sent as block entity to client + continue; + final var blockNbt = Objects.requireNonNullElseGet(block.nbt(), NBTCompound::new); final var resultNbt = new NBTCompound(); - for (Tag tag : blockEntityTags) { final var value = tag.read(blockNbt); if (value != null) {