From 972ed294d307289375cdb7a5e5eede5dbbfba680 Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 15 Sep 2019 13:42:36 +0200 Subject: [PATCH] WIP per block data --- .../java/fr/themode/minestom/data/Data.java | 8 +- .../themode/minestom/data/DataContainer.java | 48 ++---------- .../fr/themode/minestom/entity/Player.java | 20 ++++- .../event/PlayerStartDiggingEvent.java | 9 ++- .../minestom/instance/BlockModifier.java | 21 +++++- .../fr/themode/minestom/instance/Chunk.java | 75 +++++++++++++++---- .../minestom/instance/ChunkLoaderIO.java | 40 ++-------- .../themode/minestom/instance/Instance.java | 6 ++ .../minestom/instance/InstanceContainer.java | 9 ++- .../minestom/instance/SharedInstance.java | 9 ++- .../minestom/instance/batch/BlockBatch.java | 46 ++++++------ .../minestom/instance/batch/ChunkBatch.java | 40 +++++----- .../instance/demo/ChunkGeneratorDemo.java | 5 +- .../fr/themode/minestom/io/ChunkReader.java | 65 ++++++++++++++++ .../fr/themode/minestom/io/DataReader.java | 61 +++++++++++++++ .../listener/PlayerDiggingListener.java | 2 +- .../packet/server/play/ChunkDataPacket.java | 4 + .../fr/themode/minestom/utils/Position.java | 2 +- .../minestom/utils/PrimitiveConversion.java | 20 +++++ 19 files changed, 341 insertions(+), 149 deletions(-) create mode 100644 src/main/java/fr/themode/minestom/io/ChunkReader.java create mode 100644 src/main/java/fr/themode/minestom/io/DataReader.java diff --git a/src/main/java/fr/themode/minestom/data/Data.java b/src/main/java/fr/themode/minestom/data/Data.java index 4c16fed29..069833646 100644 --- a/src/main/java/fr/themode/minestom/data/Data.java +++ b/src/main/java/fr/themode/minestom/data/Data.java @@ -1,6 +1,7 @@ package fr.themode.minestom.data; import fr.themode.minestom.Main; +import fr.themode.minestom.utils.PrimitiveConversion; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -10,13 +11,14 @@ import java.util.concurrent.ConcurrentHashMap; public class Data { - private DataManager dataManager = Main.getDataManager(); + private static final DataManager DATA_MANAGER = Main.getDataManager(); + // TODO replace maps for something more memory-friendly private ConcurrentHashMap data = new ConcurrentHashMap(); private ConcurrentHashMap dataType = new ConcurrentHashMap<>(); public void set(String key, T value, Class type) { - if (dataManager.getDataType(type) == null) { + if (DATA_MANAGER.getDataType(type) == null) { throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); } this.data.put(key, value); @@ -41,7 +43,7 @@ public class Data { Object value = entry.getValue(); DataType dataType = Main.getDataManager().getDataType(type); - byte[] encodedType = type.getName().getBytes(); // Data type + byte[] encodedType = PrimitiveConversion.getObjectClassString(type.getName()).getBytes(); // Data type (fix for primitives) dos.writeShort(encodedType.length); dos.write(encodedType); diff --git a/src/main/java/fr/themode/minestom/data/DataContainer.java b/src/main/java/fr/themode/minestom/data/DataContainer.java index aa38122e6..cbf292d99 100644 --- a/src/main/java/fr/themode/minestom/data/DataContainer.java +++ b/src/main/java/fr/themode/minestom/data/DataContainer.java @@ -1,10 +1,13 @@ package fr.themode.minestom.data; -import fr.themode.minestom.Main; +import fr.themode.minestom.io.DataReader; import fr.themode.minestom.io.IOManager; import fr.themode.minestom.utils.CompressionUtils; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.nio.file.Files; import java.util.function.Consumer; @@ -58,46 +61,7 @@ public interface DataContainer { return; } - DataInputStream stream = new DataInputStream(new ByteArrayInputStream(CompressionUtils.getDecompressedData(array))); - - Data data = new Data(); - try { - while (true) { - short typeLength = stream.readShort(); - - if (typeLength == 0xff) { - // End of data - break; - } - - byte[] typeCache = new byte[typeLength]; - for (int i = 0; i < typeLength; i++) { - typeCache[i] = stream.readByte(); - } - - short nameLength = stream.readShort(); - byte[] nameCache = new byte[nameLength]; - for (int i = 0; i < nameLength; i++) { - nameCache[i] = stream.readByte(); - } - - int valueLength = stream.readInt(); - byte[] valueCache = new byte[valueLength]; - for (int i = 0; i < valueLength; i++) { - valueCache[i] = stream.readByte(); - } - - Class type = Class.forName(new String(typeCache)); - String name = new String(nameCache); - Object value = Main.getDataManager().getDataType(type).decode(valueCache); - - data.set(name, value, type); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } + Data data = DataReader.readData(array, true); setData(data); if (callback != null) diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index 25d6c14f7..e872f7190 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -6,6 +6,7 @@ import fr.themode.minestom.Main; import fr.themode.minestom.bossbar.BossBar; import fr.themode.minestom.chat.Chat; import fr.themode.minestom.collision.BoundingBox; +import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.property.Attribute; import fr.themode.minestom.event.*; import fr.themode.minestom.instance.Chunk; @@ -24,6 +25,7 @@ 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; @@ -51,8 +53,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; @@ -133,6 +135,20 @@ 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"); + return; + } + sendMessage("BLOCK DATA: " + data.get("x")); }); setEventCallback(PickupItemEvent.class, event -> { diff --git a/src/main/java/fr/themode/minestom/event/PlayerStartDiggingEvent.java b/src/main/java/fr/themode/minestom/event/PlayerStartDiggingEvent.java index e52516c7b..009b55e45 100644 --- a/src/main/java/fr/themode/minestom/event/PlayerStartDiggingEvent.java +++ b/src/main/java/fr/themode/minestom/event/PlayerStartDiggingEvent.java @@ -1,15 +1,22 @@ package fr.themode.minestom.event; import fr.themode.minestom.instance.block.CustomBlock; +import fr.themode.minestom.utils.BlockPosition; public class PlayerStartDiggingEvent extends CancellableEvent { + private BlockPosition blockPosition; private CustomBlock customBlock; - public PlayerStartDiggingEvent(CustomBlock customBlock) { + public PlayerStartDiggingEvent(BlockPosition blockPosition, CustomBlock customBlock) { + this.blockPosition = blockPosition; this.customBlock = customBlock; } + public BlockPosition getBlockPosition() { + return blockPosition; + } + public CustomBlock getBlock() { return customBlock; } diff --git a/src/main/java/fr/themode/minestom/instance/BlockModifier.java b/src/main/java/fr/themode/minestom/instance/BlockModifier.java index eed73ef94..4a6126b9d 100644 --- a/src/main/java/fr/themode/minestom/instance/BlockModifier.java +++ b/src/main/java/fr/themode/minestom/instance/BlockModifier.java @@ -1,6 +1,7 @@ package fr.themode.minestom.instance; import fr.themode.minestom.Main; +import fr.themode.minestom.data.Data; import fr.themode.minestom.instance.block.BlockManager; import fr.themode.minestom.instance.block.CustomBlock; import fr.themode.minestom.utils.BlockPosition; @@ -10,9 +11,17 @@ public interface BlockModifier { BlockManager BLOCK_MANAGER = Main.getBlockManager(); - void setBlock(int x, int y, int z, short blockId); + void setBlock(int x, int y, int z, short blockId, Data data); - void setCustomBlock(int x, int y, int z, short blockId); + void setCustomBlock(int x, int y, int z, short blockId, Data data); + + default void setBlock(int x, int y, int z, short blockId) { + setBlock(x, y, z, blockId, null); + } + + default void setCustomBlock(int x, int y, int z, short blockId) { + setCustomBlock(x, y, z, blockId, null); + } default void setBlock(BlockPosition blockPosition, short blockId) { setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId); @@ -22,9 +31,13 @@ public interface BlockModifier { setBlock(position.toBlockPosition(), blockId); } - default void setCustomBlock(int x, int y, int z, String blockId) { + default void setCustomBlock(int x, int y, int z, String blockId, Data data) { CustomBlock customBlock = BLOCK_MANAGER.getBlock(blockId); - setCustomBlock(x, y, z, customBlock.getId()); + setCustomBlock(x, y, z, customBlock.getId(), data); + } + + default void setCustomBlock(int x, int y, int z, String blockId) { + setCustomBlock(x, y, z, blockId, null); } default void setCustomBlock(BlockPosition blockPosition, String blockId) { diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 1763f80ff..a9fc9c7c8 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -12,6 +12,8 @@ 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 java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -31,6 +33,16 @@ public class Chunk implements Viewable { private Biome biome; private int chunkX, chunkZ; + + // Int represent the chunk coord of the block + // value is: 2 bytes -> blockId | 2 bytes -> customBlockId (filled with 0 if isn't) + private Int2IntMap blocks = new Int2IntOpenHashMap(16 * 16 * 16); // Start with the size of a full chunk section + + // Used to get all blocks with data (no null) + // Key is still chunk coord + // 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 + protected volatile boolean packetUpdated; // Block entities @@ -41,9 +53,6 @@ public class Chunk implements Viewable { // Cache private Set viewers = new CopyOnWriteArraySet<>(); private Packet fullDataPacket; - // Int represent the chunk coord of the block - // value is: 2 bytes -> blockId | 2 bytes -> customBlockId (filled with 0 if isn't) - private Int2IntMap blocks = new Int2IntOpenHashMap(16 * 16 * 16); // Start with the size of a full chunk section public Chunk(Biome biome, int chunkX, int chunkZ) { this.biome = biome; @@ -51,19 +60,35 @@ public class Chunk implements Viewable { this.chunkZ = chunkZ; } - public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId) { - setBlock(x, y, z, blockId, (short) 0); + public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId, Data data) { + setBlock(x, y, z, blockId, (short) 0, data); } - public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId) { + public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId) { + UNSAFE_setBlock(x, y, z, blockId, null); + } + + public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId, Data data) { CustomBlock customBlock = BLOCK_MANAGER.getBlock(customBlockId); if (customBlock == null) throw new IllegalArgumentException("The custom block " + customBlockId + " does not exist or isn't registered"); - setCustomBlock(x, y, z, customBlock); + setCustomBlock(x, y, z, customBlock, data); } - private void setBlock(byte x, byte y, byte z, short blockType, short customId) { + public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId) { + UNSAFE_setCustomBlock(x, y, z, customBlockId, null); + } + + 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); + } + + private void setBlock(byte x, byte y, byte z, short blockType, short customId, Data data) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); if (blockType != 0 || customId != 0) { int value = (blockType << 16 | customId & 0xFFFF); @@ -73,6 +98,13 @@ public class Chunk implements Viewable { this.blocks.remove(index); } + // Set the new data (or remove from the map if is null) + if (data != null) { + this.blocksData.put(index, data); + } else { + this.blocksData.remove(index); + } + if (isBlockEntity(blockType)) { this.blockEntities.add(index); } else { @@ -82,12 +114,13 @@ public class Chunk implements Viewable { this.packetUpdated = false; } - private void setCustomBlock(byte x, byte y, byte z, CustomBlock customBlock) { - if (customBlock.hasUpdate()) { - Consumer test = customBlock::update; - // TODO add update callback + public void setBlockData(byte x, byte y, byte z, Data data) { + int index = SerializerUtils.chunkCoordToIndex(x, y, z); + if (data != null) { + this.blocksData.put(index, data); + } else { + this.blocksData.remove(index); } - setBlock(x, y, z, customBlock.getType(), customBlock.getId()); } public short getBlockId(byte x, byte y, byte z) { @@ -103,6 +136,11 @@ public class Chunk implements Viewable { return id != 0 ? BLOCK_MANAGER.getBlock(id) : null; } + public Data getData(byte x, byte y, byte z) { + int index = SerializerUtils.chunkCoordToIndex(x, y, z); + return blocksData.get(index); + } + public void updateBlocks() { /** * TODO blocks' update: @@ -161,9 +199,20 @@ public class Chunk implements Viewable { boolean isCustomBlock = customBlockId != 0; short id = isCustomBlock ? customBlockId : blockId; + Data data = blocksData.get(index); + boolean hasData = data != null; + dos.writeInt(index); // Chunk coord dos.writeBoolean(isCustomBlock); // Determine the type of the ID dos.writeShort(id); + + dos.writeBoolean(hasData); + if (hasData) { + byte[] d = data.getSerializedData(); + dos.writeInt(d.length); + dos.write(d); + } + } byte[] result = output.toByteArray(); diff --git a/src/main/java/fr/themode/minestom/instance/ChunkLoaderIO.java b/src/main/java/fr/themode/minestom/instance/ChunkLoaderIO.java index e3c087c2a..d726fc3e0 100644 --- a/src/main/java/fr/themode/minestom/instance/ChunkLoaderIO.java +++ b/src/main/java/fr/themode/minestom/instance/ChunkLoaderIO.java @@ -1,11 +1,13 @@ package fr.themode.minestom.instance; -import fr.themode.minestom.instance.batch.ChunkBatch; +import fr.themode.minestom.io.ChunkReader; import fr.themode.minestom.io.IOManager; import fr.themode.minestom.utils.CompressionUtils; -import fr.themode.minestom.utils.SerializerUtils; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.nio.file.Files; import java.util.function.Consumer; @@ -53,37 +55,7 @@ public class ChunkLoaderIO { return; } - DataInputStream stream = new DataInputStream(new ByteArrayInputStream(CompressionUtils.getDecompressedData(array))); - - ChunkBatch chunkBatch = null; - try { - Biome biome = Biome.fromId(stream.readByte()); - Chunk chunk = new Chunk(biome, chunkX, chunkZ); - - chunkBatch = instance.createChunkBatch(chunk); - - while (true) { - // TODO block data - int index = stream.readInt(); - boolean isCustomBlock = stream.readBoolean(); - short blockId = stream.readShort(); - - byte[] chunkPos = SerializerUtils.indexToChunkPosition(index); - byte x = chunkPos[0]; - byte y = chunkPos[1]; - byte z = chunkPos[2]; - if (isCustomBlock) { - chunkBatch.setCustomBlock(x, y, z, blockId); - } else { - chunkBatch.setBlock(x, y, z, blockId); - } - } - } catch (EOFException e) { - } catch (IOException e) { - e.printStackTrace(); - } - - chunkBatch.flush(c -> callback.accept(c)); // Success, null if file isn't properly encoded + ChunkReader.readChunk(array, instance, chunkX, chunkZ, true, callback); }); } diff --git a/src/main/java/fr/themode/minestom/instance/Instance.java b/src/main/java/fr/themode/minestom/instance/Instance.java index d9bb1bcfe..4157de87a 100644 --- a/src/main/java/fr/themode/minestom/instance/Instance.java +++ b/src/main/java/fr/themode/minestom/instance/Instance.java @@ -2,6 +2,7 @@ package fr.themode.minestom.instance; import com.github.simplenet.packet.Packet; import fr.themode.minestom.Main; +import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.*; import fr.themode.minestom.instance.batch.BlockBatch; import fr.themode.minestom.instance.batch.ChunkBatch; @@ -147,6 +148,11 @@ public abstract class Instance implements BlockModifier { return chunk.getCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16)); } + public Data getBlockData(int x, int y, int z) { + Chunk chunk = getChunkAt(x, z); + return chunk.getData((byte) (x % 16), (byte) y, (byte) (z % 16)); + } + public Chunk getChunkAt(double x, double z) { int chunkX = Math.floorDiv((int) x, 16); int chunkZ = Math.floorDiv((int) z, 16); diff --git a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java index 467f81e78..3e57ca1b8 100644 --- a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java +++ b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java @@ -1,6 +1,7 @@ package fr.themode.minestom.instance; import com.github.simplenet.packet.Packet; +import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.Player; import fr.themode.minestom.event.PlayerBlockBreakEvent; import fr.themode.minestom.instance.batch.BlockBatch; @@ -37,25 +38,25 @@ public class InstanceContainer extends Instance { } @Override - public synchronized void setBlock(int x, int y, int z, short blockId) { + public synchronized void setBlock(int x, int y, int z, short blockId, Data data) { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { byte chunkX = (byte) (x % 16); byte chunkY = (byte) y; byte chunkZ = (byte) (z % 16); - chunk.UNSAFE_setBlock(chunkX, chunkY, chunkZ, blockId); + chunk.UNSAFE_setBlock(chunkX, chunkY, chunkZ, blockId, data); sendBlockChange(chunk, x, y, z, blockId); } } @Override - public synchronized void setCustomBlock(int x, int y, int z, short blockId) { + public synchronized void setCustomBlock(int x, int y, int z, short blockId, Data data) { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { byte chunkX = (byte) (x % 16); byte chunkY = (byte) y; byte chunkZ = (byte) (z % 16); - chunk.UNSAFE_setCustomBlock(chunkX, chunkY, chunkZ, blockId); + chunk.UNSAFE_setCustomBlock(chunkX, chunkY, chunkZ, blockId, data); short id = BLOCK_MANAGER.getBlock(blockId).getType(); sendBlockChange(chunk, x, y, z, id); } diff --git a/src/main/java/fr/themode/minestom/instance/SharedInstance.java b/src/main/java/fr/themode/minestom/instance/SharedInstance.java index b0c59f1cf..8377fdcd4 100644 --- a/src/main/java/fr/themode/minestom/instance/SharedInstance.java +++ b/src/main/java/fr/themode/minestom/instance/SharedInstance.java @@ -1,5 +1,6 @@ package fr.themode.minestom.instance; +import fr.themode.minestom.data.Data; import fr.themode.minestom.entity.Player; import fr.themode.minestom.instance.batch.BlockBatch; import fr.themode.minestom.instance.batch.ChunkBatch; @@ -118,13 +119,13 @@ public class SharedInstance extends Instance { } @Override - public void setBlock(int x, int y, int z, short blockId) { - instanceContainer.setBlock(x, y, z, blockId); + public void setBlock(int x, int y, int z, short blockId, Data data) { + instanceContainer.setBlock(x, y, z, blockId, data); } @Override - public void setCustomBlock(int x, int y, int z, short blockId) { - instanceContainer.setBlock(x, y, z, blockId); + public void setCustomBlock(int x, int y, int z, short blockId, Data data) { + instanceContainer.setBlock(x, y, z, blockId, data); } public InstanceContainer getContainer() { diff --git a/src/main/java/fr/themode/minestom/instance/batch/BlockBatch.java b/src/main/java/fr/themode/minestom/instance/batch/BlockBatch.java index d9ee2a5da..799e198d6 100644 --- a/src/main/java/fr/themode/minestom/instance/batch/BlockBatch.java +++ b/src/main/java/fr/themode/minestom/instance/batch/BlockBatch.java @@ -1,5 +1,6 @@ package fr.themode.minestom.instance.batch; +import fr.themode.minestom.data.Data; import fr.themode.minestom.instance.BlockModifier; import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.instance.InstanceContainer; @@ -20,36 +21,38 @@ public class BlockBatch implements IBatch, BlockModifier { } @Override - public synchronized void setBlock(int x, int y, int z, short blockId) { + public synchronized void setBlock(int x, int y, int z, short blockId, Data data) { Chunk chunk = this.instance.getChunkAt(x, z); - List blockData = this.data.getOrDefault(chunk, new ArrayList<>()); + List blocksData = this.data.getOrDefault(chunk, new ArrayList<>()); - BlockData data = new BlockData(); - data.x = x % 16; - data.y = y; - data.z = z % 16; - data.blockId = blockId; + BlockData blockData = new BlockData(); + blockData.x = x % 16; + blockData.y = y; + blockData.z = z % 16; + blockData.blockId = blockId; + blockData.data = data; - blockData.add(data); + blocksData.add(blockData); - this.data.put(chunk, blockData); + this.data.put(chunk, blocksData); } @Override - public void setCustomBlock(int x, int y, int z, short blockId) { + public void setCustomBlock(int x, int y, int z, short blockId, Data data) { Chunk chunk = this.instance.getChunkAt(x, z); - List blockData = this.data.getOrDefault(chunk, new ArrayList<>()); + List blocksData = this.data.getOrDefault(chunk, new ArrayList<>()); - BlockData data = new BlockData(); - data.x = x % 16; - data.y = y; - data.z = z % 16; - data.isCustomBlock = true; - data.blockId = blockId; + BlockData blockData = new BlockData(); + blockData.x = x % 16; + blockData.y = y; + blockData.z = z % 16; + blockData.isCustomBlock = true; + blockData.blockId = blockId; + blockData.data = data; - blockData.add(data); + blocksData.add(blockData); - this.data.put(chunk, blockData); + this.data.put(chunk, blocksData); } public void flush(Runnable callback) { @@ -81,12 +84,13 @@ public class BlockBatch implements IBatch, BlockModifier { private int x, y, z; private boolean isCustomBlock; private short blockId; + private Data data; public void apply(Chunk chunk) { if (!isCustomBlock) { - chunk.UNSAFE_setBlock((byte) x, (byte) y, (byte) z, blockId); + chunk.UNSAFE_setBlock((byte) x, (byte) y, (byte) z, blockId, data); } else { - chunk.UNSAFE_setCustomBlock((byte) x, (byte) y, (byte) z, blockId); + chunk.UNSAFE_setCustomBlock((byte) x, (byte) y, (byte) z, blockId, data); } } diff --git a/src/main/java/fr/themode/minestom/instance/batch/ChunkBatch.java b/src/main/java/fr/themode/minestom/instance/batch/ChunkBatch.java index 4cb7e4297..4e09db4d0 100644 --- a/src/main/java/fr/themode/minestom/instance/batch/ChunkBatch.java +++ b/src/main/java/fr/themode/minestom/instance/batch/ChunkBatch.java @@ -1,5 +1,6 @@ package fr.themode.minestom.instance.batch; +import fr.themode.minestom.data.Data; import fr.themode.minestom.instance.BlockModifier; import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.instance.ChunkGenerator; @@ -25,27 +26,29 @@ public class ChunkBatch implements IBatch, BlockModifier { } @Override - public void setBlock(int x, int y, int z, short blockId) { - BlockData data = new BlockData(); - data.x = (byte) x; - data.y = (byte) y; - data.z = (byte) z; - data.isCustomBlock = false; - data.blockId = blockId; + public void setBlock(int x, int y, int z, short blockId, Data data) { + BlockData blockData = new BlockData(); + blockData.x = (byte) x; + blockData.y = (byte) y; + blockData.z = (byte) z; + blockData.isCustomBlock = false; + blockData.blockId = blockId; + blockData.data = data; - this.dataList.add(data); + this.dataList.add(blockData); } @Override - public void setCustomBlock(int x, int y, int z, short blockId) { - BlockData data = new BlockData(); - data.x = (byte) x; - data.y = (byte) y; - data.z = (byte) z; - data.isCustomBlock = true; - data.blockId = blockId; + public void setCustomBlock(int x, int y, int z, short blockId, Data data) { + BlockData blockData = new BlockData(); + blockData.x = (byte) x; + blockData.y = (byte) y; + blockData.z = (byte) z; + blockData.isCustomBlock = true; + blockData.blockId = blockId; + blockData.data = data; - this.dataList.add(data); + this.dataList.add(blockData); } public void flushChunkGenerator(ChunkGenerator chunkGenerator, Consumer callback) { @@ -79,12 +82,13 @@ public class ChunkBatch implements IBatch, BlockModifier { private byte x, y, z; private boolean isCustomBlock; private short blockId; + private Data data; public void apply(Chunk chunk) { if (!isCustomBlock) { - chunk.UNSAFE_setBlock(x, y, z, blockId); + chunk.UNSAFE_setBlock(x, y, z, blockId, data); } else { - chunk.UNSAFE_setCustomBlock(x, y, z, blockId); + chunk.UNSAFE_setCustomBlock(x, y, z, blockId, 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 fb8d21460..f131b7f4d 100644 --- a/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java +++ b/src/main/java/fr/themode/minestom/instance/demo/ChunkGeneratorDemo.java @@ -1,5 +1,6 @@ package fr.themode.minestom.instance.demo; +import fr.themode.minestom.data.Data; import fr.themode.minestom.instance.Biome; import fr.themode.minestom.instance.ChunkGenerator; import fr.themode.minestom.instance.batch.ChunkBatch; @@ -15,7 +16,9 @@ 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++) { - batch.setCustomBlock(x, y, z, "custom_block"); + Data data = new Data(); + data.set("x", (int) x, int.class); + batch.setCustomBlock(x, y, z, "custom_block", data); } } } diff --git a/src/main/java/fr/themode/minestom/io/ChunkReader.java b/src/main/java/fr/themode/minestom/io/ChunkReader.java new file mode 100644 index 000000000..9f5d01330 --- /dev/null +++ b/src/main/java/fr/themode/minestom/io/ChunkReader.java @@ -0,0 +1,65 @@ +package fr.themode.minestom.io; + +import fr.themode.minestom.data.Data; +import fr.themode.minestom.instance.Biome; +import fr.themode.minestom.instance.Chunk; +import fr.themode.minestom.instance.Instance; +import fr.themode.minestom.instance.batch.ChunkBatch; +import fr.themode.minestom.utils.CompressionUtils; +import fr.themode.minestom.utils.SerializerUtils; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +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)); + + ChunkBatch chunkBatch = null; + try { + Biome biome = Biome.fromId(stream.readByte()); + Chunk chunk = new Chunk(biome, chunkX, chunkZ); + + chunkBatch = instance.createChunkBatch(chunk); + + while (true) { + // TODO block data + int index = stream.readInt(); + boolean isCustomBlock = stream.readBoolean(); + short blockId = stream.readShort(); + boolean hasData = stream.readBoolean(); + Data data = null; + + // Data deserializer + if (hasData) { + int dataLength = stream.readInt(); + byte[] dataArray = stream.readNBytes(dataLength); + data = DataReader.readData(dataArray, false); + } + + byte[] chunkPos = SerializerUtils.indexToChunkPosition(index); + byte x = chunkPos[0]; + byte y = chunkPos[1]; + byte z = chunkPos[2]; + if (isCustomBlock) { + chunkBatch.setCustomBlock(x, y, z, blockId, data); + } else { + chunkBatch.setBlock(x, y, z, blockId, data); + } + } + } catch (EOFException e) { + } catch (IOException e) { + e.printStackTrace(); + } + + chunkBatch.flush(c -> callback.accept(c)); // Success, null if file isn't properly encoded + } + +} diff --git a/src/main/java/fr/themode/minestom/io/DataReader.java b/src/main/java/fr/themode/minestom/io/DataReader.java new file mode 100644 index 000000000..8538e1ed3 --- /dev/null +++ b/src/main/java/fr/themode/minestom/io/DataReader.java @@ -0,0 +1,61 @@ +package fr.themode.minestom.io; + +import fr.themode.minestom.Main; +import fr.themode.minestom.data.Data; +import fr.themode.minestom.utils.CompressionUtils; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +public class DataReader { + + public static Data readData(byte[] b, boolean shouldDecompress) { + b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b; + + DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b)); + + Data data = new Data(); + try { + while (true) { + short typeLength = stream.readShort(); + + if (typeLength == 0xff) { + // End of data + break; + } + + byte[] typeCache = new byte[typeLength]; + for (int i = 0; i < typeLength; i++) { + typeCache[i] = stream.readByte(); + } + + short nameLength = stream.readShort(); + byte[] nameCache = new byte[nameLength]; + for (int i = 0; i < nameLength; i++) { + nameCache[i] = stream.readByte(); + } + + int valueLength = stream.readInt(); + byte[] valueCache = new byte[valueLength]; + for (int i = 0; i < valueLength; i++) { + valueCache[i] = stream.readByte(); + } + + Class type = Class.forName(new String(typeCache)); + + String name = new String(nameCache); + Object value = Main.getDataManager().getDataType(type).decode(valueCache); + + data.set(name, value, type); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + return data; + } + +} diff --git a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java index e1c2152ce..16af55f08 100644 --- a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java +++ b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java @@ -32,7 +32,7 @@ public class PlayerDiggingListener { if (instance != null) { CustomBlock customBlock = instance.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); if (customBlock != null) { - PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(customBlock); + PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(blockPosition, customBlock); player.callEvent(PlayerStartDiggingEvent.class, playerStartDiggingEvent); if (!playerStartDiggingEvent.isCancelled()) { player.refreshTargetBlock(customBlock, blockPosition); diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java index 3beabb243..4f55ed469 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java @@ -41,7 +41,11 @@ public class ChunkDataPacket implements ServerPacket { if (section != null) { // section contains at least one block mask |= 1 << i; Utils.writeBlocks(blocks, section, BITS_PER_ENTRY); + } else { + mask |= 0 << i; } + } else { + mask |= 0 << i; } } diff --git a/src/main/java/fr/themode/minestom/utils/Position.java b/src/main/java/fr/themode/minestom/utils/Position.java index 34f2ec087..99a5ae5c4 100644 --- a/src/main/java/fr/themode/minestom/utils/Position.java +++ b/src/main/java/fr/themode/minestom/utils/Position.java @@ -140,7 +140,7 @@ public class Position { } public BlockPosition toBlockPosition() { - return new BlockPosition((int) Math.ceil(getX()), (int) Math.ceil(getY()), (int) Math.ceil(getZ())); + return new BlockPosition((int) Math.ceil(x), (int) Math.ceil(y), (int) Math.ceil(z)); } @Override diff --git a/src/main/java/fr/themode/minestom/utils/PrimitiveConversion.java b/src/main/java/fr/themode/minestom/utils/PrimitiveConversion.java index ed523c7fb..dd0fd570a 100644 --- a/src/main/java/fr/themode/minestom/utils/PrimitiveConversion.java +++ b/src/main/java/fr/themode/minestom/utils/PrimitiveConversion.java @@ -22,4 +22,24 @@ public class PrimitiveConversion { return clazz; } + public static String getObjectClassString(String clazz) { + if (clazz == "boolean") + return "java.lang.Boolean"; + if (clazz == "byte") + return "java.lang.Byte"; + if (clazz == "char") + return "java.lang.Character"; + if (clazz == "short") + return "java.lang.Short"; + if (clazz == "int") + return "java.lang.Integer"; + if (clazz == "long") + return "java.lang.Long"; + if (clazz == "float") + return "java.lang.Float"; + if (clazz == "double") + return "java.lang.Double"; + return clazz; + } + }