From d83396341466c93f318485f4a222ee3e75e303ea Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 15 Sep 2019 16:30:38 +0200 Subject: [PATCH] WIP blocks update --- .../java/fr/themode/minestom/data/Data.java | 2 +- .../fr/themode/minestom/entity/Player.java | 13 ++-- .../fr/themode/minestom/instance/Chunk.java | 70 +++++++++++++------ .../minestom/instance/block/BlockManager.java | 6 +- .../minestom/instance/block/CustomBlock.java | 6 +- .../instance/block/UpdateConsumer.java | 12 ++++ .../instance/demo/ChunkGeneratorDemo.java | 8 ++- .../minestom/instance/demo/StoneBlock.java | 13 +++- .../fr/themode/minestom/io/ChunkReader.java | 3 +- 9 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 src/main/java/fr/themode/minestom/instance/block/UpdateConsumer.java diff --git a/src/main/java/fr/themode/minestom/data/Data.java b/src/main/java/fr/themode/minestom/data/Data.java index 069833646..3da2d8b08 100644 --- a/src/main/java/fr/themode/minestom/data/Data.java +++ b/src/main/java/fr/themode/minestom/data/Data.java @@ -13,7 +13,7 @@ public class Data { private static final DataManager DATA_MANAGER = Main.getDataManager(); - // TODO replace maps for something more memory-friendly + // TODO replace maps to something more memory-friendly private ConcurrentHashMap data = new ConcurrentHashMap(); private ConcurrentHashMap dataType = new ConcurrentHashMap<>(); diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index e872f7190..f9d74e4bb 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -25,7 +25,6 @@ import fr.themode.minestom.utils.*; import fr.themode.minestom.world.Dimension; import fr.themode.minestom.world.LevelType; -import java.io.File; import java.util.Collections; import java.util.Random; import java.util.Set; @@ -53,8 +52,8 @@ public class Player extends LivingEntity { static { ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo(); - instanceContainer = Main.getInstanceManager().createInstanceContainer(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data")); - //instanceContainer = Main.getInstanceManager().createInstanceContainer(); + //instanceContainer = Main.getInstanceManager().createInstanceContainer(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data")); + instanceContainer = Main.getInstanceManager().createInstanceContainer(); instanceContainer.enableAutoChunkLoad(true); instanceContainer.setChunkGenerator(chunkGeneratorDemo); int loopStart = -2; @@ -135,20 +134,16 @@ public class Player extends LivingEntity { if (player != this) player.teleport(getPosition()); } - - getInstance().saveToFolder(() -> { - sendMessage("SAVED"); - }); }); setEventCallback(PlayerStartDiggingEvent.class, event -> { BlockPosition blockPosition = event.getBlockPosition(); Data data = getInstance().getBlockData(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); if (data == null) { - sendMessage("DATA NULL"); + sendMessage("DATA IS NULL"); return; } - sendMessage("BLOCK DATA: " + data.get("x")); + sendMessage("BLOCK DATA: " + data.get("value")); }); setEventCallback(PickupItemEvent.class, event -> { diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index a9fc9c7c8..362ed1e4d 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -7,13 +7,12 @@ import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.Player; import fr.themode.minestom.instance.block.BlockManager; import fr.themode.minestom.instance.block.CustomBlock; +import fr.themode.minestom.instance.block.UpdateConsumer; import fr.themode.minestom.net.packet.server.play.ChunkDataPacket; +import fr.themode.minestom.utils.BlockPosition; import fr.themode.minestom.utils.PacketUtils; import fr.themode.minestom.utils.SerializerUtils; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.*; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -21,7 +20,6 @@ import java.io.IOException; import java.util.Collections; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.function.Consumer; public class Chunk implements Viewable { @@ -43,6 +41,9 @@ public class Chunk implements Viewable { // TODO shouldn't take Data object (too much memory overhead) private Int2ObjectMap blocksData = new Int2ObjectOpenHashMap<>(16 * 16); // Start with the size of a single row + // Contains CustomBlocks' index which are updatable + private IntSet updatableBlocks = new IntOpenHashSet(); + protected volatile boolean packetUpdated; // Block entities @@ -61,7 +62,7 @@ public class Chunk implements Viewable { } public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId, Data data) { - setBlock(x, y, z, blockId, (short) 0, data); + setBlock(x, y, z, blockId, (short) 0, data, null); } public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId) { @@ -81,14 +82,11 @@ public class Chunk implements Viewable { } private void setCustomBlock(byte x, byte y, byte z, CustomBlock customBlock, Data data) { - if (customBlock.hasUpdate()) { - Consumer test = customBlock::update; - // TODO add update callback - } - setBlock(x, y, z, customBlock.getType(), customBlock.getId(), data); + UpdateConsumer updateConsumer = customBlock.hasUpdate() ? customBlock::update : null; + setBlock(x, y, z, customBlock.getType(), customBlock.getId(), data, updateConsumer); } - private void setBlock(byte x, byte y, byte z, short blockType, short customId, Data data) { + private void setBlock(byte x, byte y, byte z, short blockType, short customId, Data data, UpdateConsumer updateConsumer) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); if (blockType != 0 || customId != 0) { int value = (blockType << 16 | customId & 0xFFFF); @@ -105,6 +103,13 @@ public class Chunk implements Viewable { this.blocksData.remove(index); } + // Set update consumer + if (updateConsumer != null) { + this.updatableBlocks.add(index); + } else { + this.updatableBlocks.rem(index); + } + if (isBlockEntity(blockType)) { this.blockEntities.add(index); } else { @@ -125,31 +130,50 @@ public class Chunk implements Viewable { public short getBlockId(byte x, byte y, byte z) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); - int value = this.blocks.get(index); + int value = getBlockValue(index); return (short) (value >>> 16); } public CustomBlock getCustomBlock(byte x, byte y, byte z) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); - int value = this.blocks.get(index); + return getCustomBlock(index); + } + + private CustomBlock getCustomBlock(int index) { + int value = getBlockValue(index); short id = (short) (value & 0xffff); return id != 0 ? BLOCK_MANAGER.getBlock(id) : null; } + private int getBlockValue(int index) { + return blocks.get(index); + } + public Data getData(byte x, byte y, byte z) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); + return getData(index); + } + + private Data getData(int index) { return blocksData.get(index); } - public void updateBlocks() { - /** - * TODO blocks' update: - * - get all custom blocks - * - check if they have an update method - * - check if they should be updated - * - get custom block's data - * - call update method - */ + public void updateBlocks(long time, Instance instance) { + synchronized (this) { + IntIterator iterator = updatableBlocks.iterator(); + while (iterator.hasNext()) { + int index = iterator.nextInt(); + byte[] blockPos = SerializerUtils.indexToChunkPosition(index); + byte x = blockPos[0]; + byte y = blockPos[1]; + byte z = blockPos[2]; + CustomBlock customBlock = getCustomBlock(x, y, z); + BlockPosition blockPosition = new BlockPosition(x * chunkX, y, z * chunkZ); + Data data = getData(index); + // TODO should customBlock be updated? + customBlock.update(instance, blockPosition, data); + } + } } public Biome getBiome() { diff --git a/src/main/java/fr/themode/minestom/instance/block/BlockManager.java b/src/main/java/fr/themode/minestom/instance/block/BlockManager.java index 37ec1ba80..e8da9b697 100644 --- a/src/main/java/fr/themode/minestom/instance/block/BlockManager.java +++ b/src/main/java/fr/themode/minestom/instance/block/BlockManager.java @@ -27,10 +27,12 @@ public class BlockManager { } public void update() { + long time = System.currentTimeMillis(); + // TODO another thread pool for (Instance instance : instanceManager.getInstances()) { - // TODO only InstanceContainer? + // FIXME: only InstanceContainer? for (Chunk chunk : instance.getChunks()) { - chunk.updateBlocks(); + chunk.updateBlocks(time, instance); } } } diff --git a/src/main/java/fr/themode/minestom/instance/block/CustomBlock.java b/src/main/java/fr/themode/minestom/instance/block/CustomBlock.java index bb013bf2a..cfab27912 100644 --- a/src/main/java/fr/themode/minestom/instance/block/CustomBlock.java +++ b/src/main/java/fr/themode/minestom/instance/block/CustomBlock.java @@ -2,12 +2,14 @@ package fr.themode.minestom.instance.block; import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.Player; +import fr.themode.minestom.instance.Instance; +import fr.themode.minestom.utils.BlockPosition; import java.util.concurrent.atomic.AtomicInteger; /** * TODO - * - option to set the global as "global breaking" meaning that multiple players mining the same block will break it faster (cumulation) + * - option to set the global as "global breaking" meaning that multiple players mining the same block will break it faster (cumulation) */ public abstract class CustomBlock { @@ -19,7 +21,7 @@ public abstract class CustomBlock { this.id = (short) idCounter.incrementAndGet(); } - public void update(Data data) { + public void update(Instance instance, BlockPosition blockPosition, Data data) { throw new UnsupportedOperationException("Update method not overriden"); } diff --git a/src/main/java/fr/themode/minestom/instance/block/UpdateConsumer.java b/src/main/java/fr/themode/minestom/instance/block/UpdateConsumer.java new file mode 100644 index 000000000..7108ad3d0 --- /dev/null +++ b/src/main/java/fr/themode/minestom/instance/block/UpdateConsumer.java @@ -0,0 +1,12 @@ +package fr.themode.minestom.instance.block; + +import fr.themode.minestom.data.Data; +import fr.themode.minestom.instance.Instance; +import fr.themode.minestom.utils.BlockPosition; + +@FunctionalInterface +public interface UpdateConsumer { + + void update(Instance instance, BlockPosition blockPosition, Data data); + +} diff --git a/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java b/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java index f131b7f4d..407d1c0c2 100644 --- a/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java +++ b/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java @@ -16,9 +16,11 @@ public class ChunkGeneratorDemo extends ChunkGenerator { for (byte x = 0; x < 16; x++) for (byte z = 0; z < 16; z++) { for (byte y = 0; y < 65; y++) { - Data data = new Data(); - data.set("x", (int) x, int.class); - batch.setCustomBlock(x, y, z, "custom_block", data); + if (random.nextInt(100) > 90) { + batch.setCustomBlock(x, y, z, "custom_block", new Data()); + } else { + batch.setBlock(x, y, z, (short) 10); + } } } } diff --git a/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java index 20a5d49a8..9aa105494 100644 --- a/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java +++ b/src/main/java/fr/themode/minestom/instance/demo/StoneBlock.java @@ -2,22 +2,31 @@ package fr.themode.minestom.instance.demo; import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.Player; +import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.block.CustomBlock; import fr.themode.minestom.instance.block.UpdateOption; import fr.themode.minestom.timer.TimeUnit; +import fr.themode.minestom.utils.BlockPosition; + +import java.util.concurrent.atomic.AtomicInteger; public class StoneBlock extends CustomBlock { private static final UpdateOption UPDATE_OPTION = new UpdateOption(1, TimeUnit.TICK); + private final AtomicInteger counter = new AtomicInteger(); + @Override public UpdateOption getUpdateOption() { return UPDATE_OPTION; } @Override - public void update(Data data) { - System.out.println("BLOCK HAS BEEN UPDATED"); + public void update(Instance instance, BlockPosition blockPosition, Data data) { + if (data == null) + return; + + data.set("value", counter.incrementAndGet(), int.class); } @Override diff --git a/src/main/java/fr/themode/minestom/io/ChunkReader.java b/src/main/java/fr/themode/minestom/io/ChunkReader.java index 9f5d01330..7decfb03b 100644 --- a/src/main/java/fr/themode/minestom/io/ChunkReader.java +++ b/src/main/java/fr/themode/minestom/io/ChunkReader.java @@ -17,7 +17,6 @@ import java.util.function.Consumer; public class ChunkReader { public static void readChunk(byte[] b, Instance instance, int chunkX, int chunkZ, boolean shouldDecompress, Consumer callback) { - b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b; DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b)); @@ -30,7 +29,6 @@ public class ChunkReader { chunkBatch = instance.createChunkBatch(chunk); while (true) { - // TODO block data int index = stream.readInt(); boolean isCustomBlock = stream.readBoolean(); short blockId = stream.readShort(); @@ -55,6 +53,7 @@ public class ChunkReader { } } } catch (EOFException e) { + // Finished reading } catch (IOException e) { e.printStackTrace(); }