From 2285c9622a7edaed17b8b1a64d36662e1054db98 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 15 Nov 2020 08:03:33 +0100 Subject: [PATCH] If the garbage collector is happy, everyone is - TheMode --- .../net/minestom/server/entity/Entity.java | 3 +- .../server/instance/batch/ChunkBatch.java | 112 ++++++++++++------ .../minestom/server/inventory/Inventory.java | 1 - .../server/utils/chunk/ChunkUtils.java | 14 +-- src/test/java/demo/Main.java | 3 +- 5 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 425ec4e40..1e130ad9b 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -597,10 +597,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P /** * Each entity has an unique id (server-wide) which will change after a restart. - *

- * All entities can be retrieved by calling {@link Entity#getEntity(int)}. * * @return the unique entity id + * @see Entity#getEntity(int) to retrive an entity based on its id */ public int getEntityId() { return id; diff --git a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java index 16384213e..f85493e24 100644 --- a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java @@ -1,11 +1,18 @@ package net.minestom.server.instance.batch; -import kotlin.collections.ArrayDeque; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; import net.minestom.server.data.Data; import net.minestom.server.instance.*; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.utils.block.CustomBlockUtils; +import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; +import net.minestom.server.utils.chunk.ChunkUtils; +import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -21,48 +28,68 @@ import java.util.List; */ public class ChunkBatch implements InstanceBatch { - private static final int INITIAL_SIZE = (Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Y * Chunk.CHUNK_SIZE_Z) / 2; - private final InstanceContainer instance; private final Chunk chunk; // Need to be synchronized manually - private final ArrayDeque dataList = new ArrayDeque<>(INITIAL_SIZE); + // Format: blockIndex/blockStateId/customBlockId (32/16/16 bits) + private final LongList blocks = new LongArrayList(); - public ChunkBatch(InstanceContainer instance, Chunk chunk) { + // Need to be synchronized manually + // block index - data + private final Int2ObjectMap blockDataMap = new Int2ObjectOpenHashMap<>(); + + public ChunkBatch(@NotNull InstanceContainer instance, @NotNull Chunk chunk) { this.instance = instance; this.chunk = chunk; } @Override - public void setBlockStateId(int x, int y, int z, short blockStateId, Data data) { + public void setBlockStateId(int x, int y, int z, short blockStateId, @Nullable Data data) { addBlockData((byte) x, y, (byte) z, blockStateId, (short) 0, data); } @Override - public void setCustomBlock(int x, int y, int z, short customBlockId, Data data) { + public void setCustomBlock(int x, int y, int z, short customBlockId, @Nullable Data data) { final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); + Check.notNull(customBlock, "The custom block with the id " + customBlockId + " does not exist!"); addBlockData((byte) x, y, (byte) z, customBlock.getDefaultBlockStateId(), customBlockId, data); } @Override - public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, Data data) { + public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) { addBlockData((byte) x, y, (byte) z, blockStateId, customBlockId, data); } - private void addBlockData(byte x, int y, byte z, short blockStateId, short customBlockId, Data data) { - // TODO store a single long with bitwise operators (xyz;boolean,short,short,boolean) with the data in a map - final BlockData blockData = new BlockData(x, y, z, blockStateId, customBlockId, data); - synchronized (dataList) { - this.dataList.add(blockData); + private void addBlockData(byte x, int y, byte z, short blockStateId, short customBlockId, @Nullable Data data) { + final int index = ChunkUtils.getBlockIndex(x, y, z); + + if (data != null) { + synchronized (blockDataMap) { + this.blockDataMap.put(index, data); + } + } + + long value = index; + value = (value << 16) | blockStateId; + value = (value << 16) | customBlockId; + + synchronized (blocks) { + this.blocks.add(value); } } - public void flushChunkGenerator(ChunkGenerator chunkGenerator, @Nullable ChunkCallback callback) { - BLOCK_BATCH_POOL.execute(() -> { - final List populators = chunkGenerator.getPopulators(); - final boolean hasPopulator = populators != null && !populators.isEmpty(); + public void flushChunkGenerator(@NotNull ChunkGenerator chunkGenerator, @Nullable ChunkCallback callback) { + final List populators = chunkGenerator.getPopulators(); + final boolean hasPopulator = populators != null && !populators.isEmpty(); + // Check if there is anything to process + if (blocks.isEmpty() && !hasPopulator) { + OptionalCallback.execute(callback, chunk); + return; + } + + BLOCK_BATCH_POOL.execute(() -> { chunkGenerator.generateChunkData(this, chunk.getChunkX(), chunk.getChunkZ()); singleThreadFlush(hasPopulator ? null : callback, true); @@ -104,8 +131,8 @@ public class ChunkBatch implements InstanceBatch { * Resets the chunk batch by removing all the entries. */ public void clearData() { - synchronized (dataList) { - this.dataList.clear(); + synchronized (blocks) { + this.blocks.clear(); } } @@ -116,13 +143,18 @@ public class ChunkBatch implements InstanceBatch { * @param safeCallback true to run the callback in the instance update thread, otherwise run in the current one */ private void singleThreadFlush(@Nullable ChunkCallback callback, boolean safeCallback) { + if (blocks.isEmpty()) { + OptionalCallback.execute(callback, chunk); + return; + } + synchronized (chunk) { if (!chunk.isLoaded()) return; - synchronized (dataList) { - for (BlockData data : dataList) { - data.apply(chunk); + synchronized (blocks) { + for (long block : blocks) { + apply(chunk, block); } } @@ -141,25 +173,29 @@ public class ChunkBatch implements InstanceBatch { } } - private static class BlockData { + /** + * Places a block which is encoded in a long. + * + * @param chunk the chunk to place the block on + * @param value the block data + */ + private void apply(@NotNull Chunk chunk, long value) { + final short customBlockId = (short) (value & 0xFF); + final short blockId = (short) (value >> 16 & 0xFF); + final int index = (int) (value >> 32 & 0xFFFF); - private final int x, y, z; - private final short blockStateId; - private final short customBlockId; - private final Data data; - - private BlockData(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) { - this.x = x; - this.y = y; - this.z = z; - this.blockStateId = blockStateId; - this.customBlockId = customBlockId; - this.data = data; + Data data = null; + if (!blockDataMap.isEmpty()) { + synchronized (blockDataMap) { + data = blockDataMap.get(index); + } } - public void apply(Chunk chunk) { - chunk.UNSAFE_setBlock(x, y, z, blockStateId, customBlockId, data, CustomBlockUtils.hasUpdate(customBlockId)); - } + chunk.UNSAFE_setBlock(ChunkUtils.blockIndexToChunkPositionX(index), + ChunkUtils.blockIndexToChunkPositionY(index), + ChunkUtils.blockIndexToChunkPositionZ(index), + blockId, customBlockId, data, CustomBlockUtils.hasUpdate(customBlockId)); + } diff --git a/src/main/java/net/minestom/server/inventory/Inventory.java b/src/main/java/net/minestom/server/inventory/Inventory.java index 1d64a7511..52c3e8a15 100644 --- a/src/main/java/net/minestom/server/inventory/Inventory.java +++ b/src/main/java/net/minestom/server/inventory/Inventory.java @@ -186,7 +186,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View setItemStackInternal(i, ItemStack.getAirItem()); } // Send the cleared inventory to viewers - // TODO cached packet with empty content update(); } diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java index 56a3d6275..7b014f713 100644 --- a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java +++ b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java @@ -196,9 +196,7 @@ public final class ChunkUtils { * @return the X coordinate of the block index */ public static int blockIndexToPositionX(int index, int chunkX) { - int x = (byte) (index & 0xF); - x += Chunk.CHUNK_SIZE_X * chunkX; - return x; + return (int) blockIndexToChunkPositionX(index) + Chunk.CHUNK_SIZE_X * chunkX; } /** @@ -219,9 +217,7 @@ public final class ChunkUtils { * @return the Z coordinate of the block index */ public static int blockIndexToPositionZ(int index, int chunkZ) { - int z = (byte) (index >> 12 & 0xF); - z += Chunk.CHUNK_SIZE_Z * chunkZ; - return z; + return (int) blockIndexToChunkPositionZ(index) + Chunk.CHUNK_SIZE_Z * chunkZ; } /** @@ -231,7 +227,7 @@ public final class ChunkUtils { * @return the chunk position X (O-15) of the specified index */ public static byte blockIndexToChunkPositionX(int index) { - return (byte) blockIndexToPositionX(index, 0); + return (byte) (index & 0xF); } /** @@ -241,7 +237,7 @@ public final class ChunkUtils { * @return the chunk position Y (O-255) of the specified index */ public static short blockIndexToChunkPositionY(int index) { - return (short) blockIndexToPositionY(index); + return (short) (index >>> 4 & 0xFF); } /** @@ -251,7 +247,7 @@ public final class ChunkUtils { * @return the chunk position Z (O-15) of the specified index */ public static byte blockIndexToChunkPositionZ(int index) { - return (byte) blockIndexToPositionZ(index, 0); + return (byte) (index >> 12 & 0xF); } } diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index d48d58588..0979c7b54 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -6,7 +6,6 @@ import demo.blocks.UpdatableBlockDemo; import demo.commands.*; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; -import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.extras.optifine.OptifineSupport; import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule; @@ -53,7 +52,7 @@ public class Main { OptifineSupport.enable(); //VelocityProxy.enable("rBeJJ79W4MVU"); - BungeeCordProxy.enable(); + //BungeeCordProxy.enable(); // MojangAuth.init();