diff --git a/src/main/java/fr/themode/demo/generator/NoiseTestGenerator.java b/src/main/java/fr/themode/demo/generator/NoiseTestGenerator.java index 23b8b41bf..6fd985725 100644 --- a/src/main/java/fr/themode/demo/generator/NoiseTestGenerator.java +++ b/src/main/java/fr/themode/demo/generator/NoiseTestGenerator.java @@ -4,6 +4,7 @@ import de.articdive.jnoise.JNoise; import de.articdive.jnoise.interpolation.InterpolationType; import net.minestom.server.MinecraftServer; import net.minestom.server.data.SerializableData; +import net.minestom.server.data.SerializableDataImpl; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.ChunkGenerator; import net.minestom.server.instance.ChunkPopulator; @@ -46,7 +47,7 @@ public class NoiseTestGenerator implements ChunkGenerator { batch.setBlock(x, y, z, Block.GRASS_BLOCK); } else if (y > height - 7) { // Data for debugging purpose - SerializableData serializableData = new SerializableData(); + SerializableData serializableData = new SerializableDataImpl(); serializableData.set("test", 55, Integer.class); batch.setBlockStateId(x, y, z, Block.DIRT.getBlockId(), serializableData); } else { diff --git a/src/main/java/net/minestom/server/data/Data.java b/src/main/java/net/minestom/server/data/Data.java index 86b15a406..d2f62861a 100644 --- a/src/main/java/net/minestom/server/data/Data.java +++ b/src/main/java/net/minestom/server/data/Data.java @@ -2,14 +2,12 @@ package net.minestom.server.data; import java.util.Collections; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -public class Data { +public interface Data { - public static final Data EMPTY = new Data() { + Data EMPTY = new Data() { @Override - public void set(String key, T value, Class type) { - } + public void set(String key, T value, Class type) { } @Override public T get(String key) { @@ -21,14 +19,27 @@ public class Data { return false; } + @Override + public Set getKeys() { + return Collections.EMPTY_SET; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Data clone() { + return this; + } + @Override public T getOrDefault(String key, T defaultValue) { return defaultValue; } }; - protected final ConcurrentHashMap data = new ConcurrentHashMap<>(); - /** * Set a value to a specific key * @@ -37,21 +48,16 @@ public class Data { * @param type the value type * @param the value generic */ - public void set(String key, T value, Class type) { - this.data.put(key, value); - } + void set(String key, T value, Class type); /** * Retrieve a value based on its key * * @param key the key * @param the value type - * @return the data associated with the key - * @throws NullPointerException if the key is not found + * @return the data associated with the key or null */ - public T get(String key) { - return (T) data.get(key); - } + T get(String key); /** * Retrieve a value based on its key, give a default value if not found @@ -61,9 +67,7 @@ public class Data { * @param the value type * @return {@link #get(String)} if found, {@code defaultValue} otherwise */ - public T getOrDefault(String key, T defaultValue) { - return (T) data.getOrDefault(key, defaultValue); - } + T getOrDefault(String key, T defaultValue); /** * Get if the data has a key @@ -71,37 +75,27 @@ public class Data { * @param key the key to check * @return true if the data contains the key */ - public boolean hasKey(String key) { - return data.containsKey(key); - } + boolean hasKey(String key); /** * Get the list of data keys * * @return an unmodifiable set containing all keys */ - public Set getKeys() { - return Collections.unmodifiableSet(data.keySet()); - } + Set getKeys(); /** * Get if the data is empty or not * * @return true if the data does not contain anything, false otherwise */ - public boolean isEmpty() { - return data.isEmpty(); - } + boolean isEmpty(); /** * Clone this data * * @return a cloned data object */ - public Data clone() { - Data data = new Data(); - data.data.putAll(this.data); - return data; - } + Data clone(); } diff --git a/src/main/java/net/minestom/server/data/DataImpl.java b/src/main/java/net/minestom/server/data/DataImpl.java new file mode 100644 index 000000000..13a2744fe --- /dev/null +++ b/src/main/java/net/minestom/server/data/DataImpl.java @@ -0,0 +1,48 @@ +package net.minestom.server.data; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class DataImpl implements Data { + + protected final ConcurrentHashMap data = new ConcurrentHashMap<>(); + + @Override + public void set(String key, T value, Class type) { + this.data.put(key, value); + } + + @Override + public T get(String key) { + return (T) data.get(key); + } + + @Override + public T getOrDefault(String key, T defaultValue) { + return (T) data.getOrDefault(key, defaultValue); + } + + @Override + public boolean hasKey(String key) { + return data.containsKey(key); + } + + @Override + public Set getKeys() { + return Collections.unmodifiableSet(data.keySet()); + } + + @Override + public boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public Data clone() { + DataImpl data = new DataImpl(); + data.data.putAll(this.data); + return data; + } + +} diff --git a/src/main/java/net/minestom/server/data/SerializableData.java b/src/main/java/net/minestom/server/data/SerializableData.java index ae6ab7ed3..fe970ed16 100644 --- a/src/main/java/net/minestom/server/data/SerializableData.java +++ b/src/main/java/net/minestom/server/data/SerializableData.java @@ -1,52 +1,14 @@ package net.minestom.server.data; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.objects.Object2ShortMap; -import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import net.minestom.server.MinecraftServer; import net.minestom.server.reader.DataReader; -import net.minestom.server.utils.PrimitiveConversion; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +public interface SerializableData extends Data { -public class SerializableData extends Data { - - private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager(); - - private ConcurrentHashMap dataType = new ConcurrentHashMap<>(); - - /** - * Set a value to a specific key - *

- * WARNING: the type needs to be registered in {@link DataManager} - * - * @param key the key - * @param value the value object - * @param type the value type - * @param the value generic - * @throws UnsupportedOperationException if {@code type} is not registered in {@link DataManager} - */ - @Override - public void set(String key, T value, Class type) { - if (DATA_MANAGER.getDataType(type) == null) { - throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); - } - - super.set(key, value, type); - this.dataType.put(key, type); - } - - @Override - public Data clone() { - SerializableData data = new SerializableData(); - data.data.putAll(this.data); - data.dataType.putAll(this.dataType); - return data; - } + DataManager DATA_MANAGER = MinecraftServer.getDataManager(); /** * Serialize the data into an array of bytes @@ -60,61 +22,7 @@ public class SerializableData extends Data { * @param indexed true to add the types index in the header * @return the array representation of this data object */ - public byte[] getSerializedData(Object2ShortMap typeToIndexMap, boolean indexed) { - // Get the current max index, it supposes that the index keep being incremented by 1 - short lastIndex = (short) typeToIndexMap.size(); - - // Main buffer containing the data - BinaryWriter binaryWriter = new BinaryWriter(); - for (Map.Entry entry : data.entrySet()) { - final String key = entry.getKey(); - final Object value = entry.getValue(); - - final Class type = dataType.get(key); - final short typeIndex; - { - // Find the type name - final String encodedType = PrimitiveConversion.getObjectClassString(type.getName()); // Data type (fix for primitives) - - // Find the type index - if (typeToIndexMap.containsKey(encodedType)) { - // Get index - typeIndex = typeToIndexMap.getShort(encodedType); - } else { - // Create new index - typeToIndexMap.put(encodedType, ++lastIndex); - // Set index - typeIndex = lastIndex; - } - } - - - // Write the data type index - binaryWriter.writeShort(typeIndex); - - // Write the data key - binaryWriter.writeSizedString(key); - - // Write the data (no length) - final DataType dataType = DATA_MANAGER.getDataType(type); - dataType.encode(binaryWriter, value); - } - - binaryWriter.writeShort((short) 0); // End of data object - - // Header for type indexes - if (indexed) { - // The buffer containing all the index info (class name to class index) - BinaryWriter indexWriter = new BinaryWriter(); - writeDataIndexHeader(indexWriter, typeToIndexMap); - // Merge the index buffer & the main data buffer - final ByteBuf finalBuffer = Unpooled.wrappedBuffer(indexWriter.getBuffer(), binaryWriter.getBuffer()); - // Change the main writer buffer, so it contains both the indexes and the data - binaryWriter.setBuffer(finalBuffer); - } - - return binaryWriter.toByteArray(); - } + byte[] getSerializedData(Object2ShortMap typeToIndexMap, boolean indexed); /** * Serialize the data into an array of bytes @@ -126,9 +34,7 @@ public class SerializableData extends Data { * * @return the array representation of this data object */ - public byte[] getIndexedSerializedData() { - return getSerializedData(new Object2ShortOpenHashMap<>(), true); - } + byte[] getIndexedSerializedData(); /** * Get the index info (class name -> class index) @@ -137,7 +43,7 @@ public class SerializableData extends Data { * * @param typeToIndexMap the data index map */ - public static void writeDataIndexHeader(BinaryWriter indexWriter, Object2ShortMap typeToIndexMap) { + static void writeDataIndexHeader(BinaryWriter indexWriter, Object2ShortMap typeToIndexMap) { // Write the size of the following index list (class name-> class index) indexWriter.writeVarInt(typeToIndexMap.size()); diff --git a/src/main/java/net/minestom/server/data/SerializableDataImpl.java b/src/main/java/net/minestom/server/data/SerializableDataImpl.java new file mode 100644 index 000000000..29b272205 --- /dev/null +++ b/src/main/java/net/minestom/server/data/SerializableDataImpl.java @@ -0,0 +1,108 @@ +package net.minestom.server.data; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.objects.Object2ShortMap; +import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; +import net.minestom.server.utils.PrimitiveConversion; +import net.minestom.server.utils.binary.BinaryWriter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SerializableDataImpl extends DataImpl implements SerializableData { + + private ConcurrentHashMap dataType = new ConcurrentHashMap<>(); + + /** + * Set a value to a specific key + *

+ * WARNING: the type needs to be registered in {@link DataManager} + * + * @param key the key + * @param value the value object + * @param type the value type + * @param the value generic + * @throws UnsupportedOperationException if {@code type} is not registered in {@link DataManager} + */ + @Override + public void set(String key, T value, Class type) { + if (DATA_MANAGER.getDataType(type) == null) { + throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); + } + + super.set(key, value, type); + this.dataType.put(key, type); + } + + @Override + public Data clone() { + SerializableDataImpl data = new SerializableDataImpl(); + data.data.putAll(this.data); + data.dataType.putAll(this.dataType); + return data; + } + + @Override + public byte[] getSerializedData(Object2ShortMap typeToIndexMap, boolean indexed) { + // Get the current max index, it supposes that the index keep being incremented by 1 + short lastIndex = (short) typeToIndexMap.size(); + + // Main buffer containing the data + BinaryWriter binaryWriter = new BinaryWriter(); + for (Map.Entry entry : data.entrySet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + + final Class type = dataType.get(key); + final short typeIndex; + { + // Find the type name + final String encodedType = PrimitiveConversion.getObjectClassString(type.getName()); // Data type (fix for primitives) + + // Find the type index + if (typeToIndexMap.containsKey(encodedType)) { + // Get index + typeIndex = typeToIndexMap.getShort(encodedType); + } else { + // Create new index + typeToIndexMap.put(encodedType, ++lastIndex); + // Set index + typeIndex = lastIndex; + } + } + + + // Write the data type index + binaryWriter.writeShort(typeIndex); + + // Write the data key + binaryWriter.writeSizedString(key); + + // Write the data (no length) + final DataType dataType = DATA_MANAGER.getDataType(type); + dataType.encode(binaryWriter, value); + } + + binaryWriter.writeShort((short) 0); // End of data object + + // Header for type indexes + if (indexed) { + // The buffer containing all the index info (class name to class index) + BinaryWriter indexWriter = new BinaryWriter(); + SerializableData.writeDataIndexHeader(indexWriter, typeToIndexMap); + // Merge the index buffer & the main data buffer + final ByteBuf finalBuffer = Unpooled.wrappedBuffer(indexWriter.getBuffer(), binaryWriter.getBuffer()); + // Change the main writer buffer, so it contains both the indexes and the data + binaryWriter.setBuffer(finalBuffer); + } + + return binaryWriter.toByteArray(); + } + + @Override + public byte[] getIndexedSerializedData() { + return getSerializedData(new Object2ShortOpenHashMap<>(), true); + } + +} diff --git a/src/main/java/net/minestom/server/reader/DataReader.java b/src/main/java/net/minestom/server/reader/DataReader.java index ee9dcdf11..99bbcbd9e 100644 --- a/src/main/java/net/minestom/server/reader/DataReader.java +++ b/src/main/java/net/minestom/server/reader/DataReader.java @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import net.minestom.server.MinecraftServer; import net.minestom.server.data.DataManager; import net.minestom.server.data.SerializableData; +import net.minestom.server.data.SerializableDataImpl; import net.minestom.server.utils.binary.BinaryReader; import java.util.concurrent.ConcurrentHashMap; @@ -43,7 +44,7 @@ public class DataReader { } } - SerializableData data = new SerializableData(); + SerializableData data = new SerializableDataImpl(); while (true) { // Get the class index final short typeIndex = reader.readShort(); diff --git a/src/main/java/net/minestom/server/storage/StorageLocation.java b/src/main/java/net/minestom/server/storage/StorageLocation.java index 4da40ef66..587baf670 100644 --- a/src/main/java/net/minestom/server/storage/StorageLocation.java +++ b/src/main/java/net/minestom/server/storage/StorageLocation.java @@ -5,6 +5,7 @@ import net.minestom.server.data.DataContainer; import net.minestom.server.data.DataManager; import net.minestom.server.data.DataType; import net.minestom.server.data.SerializableData; +import net.minestom.server.data.SerializableDataImpl; import net.minestom.server.reader.DataReader; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; @@ -152,7 +153,7 @@ public class StorageLocation { if (bytes != null) { data = DataReader.readIndexedData(new BinaryReader(bytes)); } else { - data = new SerializableData(); + data = new SerializableDataImpl(); } dataContainer.setData(data); @@ -183,7 +184,7 @@ public class StorageLocation { if (bytes != null) { data = DataReader.readIndexedData(new BinaryReader(bytes)); } else { - data = new SerializableData(); + data = new SerializableDataImpl(); } dataContainer.setData(data); diff --git a/src/test/java/loottables/TestLootTables.java b/src/test/java/loottables/TestLootTables.java index 51acce6db..d84a8f714 100644 --- a/src/test/java/loottables/TestLootTables.java +++ b/src/test/java/loottables/TestLootTables.java @@ -1,6 +1,7 @@ package loottables; import net.minestom.server.data.Data; +import net.minestom.server.data.DataImpl; import net.minestom.server.gamedata.conditions.SurvivesExplosionCondition; import net.minestom.server.gamedata.loottables.LootTable; import net.minestom.server.gamedata.loottables.LootTableManager; @@ -94,7 +95,7 @@ public class TestLootTables { @Test public void simpleGenerate() throws FileNotFoundException { LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button")); - Data arguments = new Data(); + Data arguments = new DataImpl(); List stacks = lootTable.generate(arguments); Assertions.assertEquals(1, stacks.size()); Assertions.assertEquals(Material.ACACIA_BUTTON, stacks.get(0).getMaterial()); @@ -103,7 +104,7 @@ public class TestLootTables { @Test public void testExplosion() throws FileNotFoundException { LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button")); - Data arguments = new Data(); + Data arguments = new DataImpl(); // negative value will force the condition to fail arguments.set("explosionPower", -1.0, Double.class); List stacks = lootTable.generate(arguments);