Added SerializableData

This commit is contained in:
Felix Cravic 2020-04-28 00:24:10 +02:00
parent 754821f447
commit f544f090ae
7 changed files with 84 additions and 58 deletions

View File

@ -1,28 +1,20 @@
package net.minestom.server.data; package net.minestom.server.data;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.utils.PrimitiveConversion;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class Data { public class Data {
private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager(); protected static final DataManager DATA_MANAGER = MinecraftServer.getDataManager();
// TODO replace maps to something more memory-friendly protected ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
private ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
private ConcurrentHashMap<String, Class> dataType = new ConcurrentHashMap<>();
public <T> void set(String key, T value, Class<T> type) { public <T> void set(String key, T value, Class<T> type) {
if (DATA_MANAGER.getDataType(type) == null) { if (DATA_MANAGER.getDataType(type) == null) {
throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType"); throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType");
} }
this.data.put(key, value); this.data.put(key, value);
this.dataType.put(key, type);
} }
public <T> T get(String key) { public <T> T get(String key) {
@ -36,36 +28,7 @@ public class Data {
public Data clone() { public Data clone() {
Data data = new Data(); Data data = new Data();
data.data = new ConcurrentHashMap<>(this.data); data.data = new ConcurrentHashMap<>(this.data);
data.dataType = new ConcurrentHashMap<>(dataType);
return data; return data;
} }
public byte[] getSerializedData() throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(output);
for (Map.Entry<String, Object> entry : data.entrySet()) {
String key = entry.getKey();
Class type = dataType.get(key);
Object value = entry.getValue();
DataType dataType = DATA_MANAGER.getDataType(type);
byte[] encodedType = PrimitiveConversion.getObjectClassString(type.getName()).getBytes(); // Data type (fix for primitives)
dos.writeShort(encodedType.length);
dos.write(encodedType);
byte[] encodedName = key.getBytes(); // Data name
dos.writeShort(encodedName.length);
dos.write(encodedName);
byte[] encodedValue = dataType.encode(value); // Data
dos.writeInt(encodedValue.length);
dos.write(encodedValue);
}
dos.writeShort(0xff); // End of data object
return output.toByteArray();
}
} }

View File

@ -22,9 +22,12 @@ public interface DataContainer {
Data data = getData(); Data data = getData();
if (data == null) if (data == null)
throw new NullPointerException("You cannot save null data!"); throw new NullPointerException("You cannot save null data!");
if (!(data instanceof SerializableData))
throw new IllegalArgumentException("Only SerializableData can be serialized");
SerializableData serializableData = (SerializableData) data;
try (FileOutputStream fos = new FileOutputStream(file)) { try (FileOutputStream fos = new FileOutputStream(file)) {
byte[] serializedData = data.getSerializedData(); byte[] serializedData = serializableData.getSerializedData();
fos.write(CompressionUtils.getCompressedData(serializedData)); fos.write(CompressionUtils.getCompressedData(serializedData));
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
@ -59,7 +62,7 @@ public interface DataContainer {
return; return;
} }
Data data = DataReader.readData(array, true); SerializableData data = DataReader.readData(array, true);
setData(data); setData(data);
if (callback != null) if (callback != null)

View File

@ -23,7 +23,7 @@ public class DataManager {
registerType(String.class, new StringData()); registerType(String.class, new StringData());
registerType(Data.class, new DataData()); registerType(SerializableData.class, new SerializableDataData());
} }
public <T> void registerType(Class<T> clazz, DataType<T> dataType) { public <T> void registerType(Class<T> clazz, DataType<T> dataType) {

View File

@ -0,0 +1,56 @@
package net.minestom.server.data;
import net.minestom.server.utils.PrimitiveConversion;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SerializableData extends Data {
private ConcurrentHashMap<String, Class> dataType = new ConcurrentHashMap<>();
@Override
public <T> void set(String key, T value, Class<T> type) {
super.set(key, value, type);
this.dataType.put(key, type);
}
@Override
public Data clone() {
SerializableData cloned = (SerializableData) super.clone();
cloned.dataType = new ConcurrentHashMap<>(dataType);
return super.clone();
}
public byte[] getSerializedData() throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(output);
for (Map.Entry<String, Object> entry : data.entrySet()) {
String key = entry.getKey();
Class type = dataType.get(key);
Object value = entry.getValue();
DataType dataType = DATA_MANAGER.getDataType(type);
byte[] encodedType = PrimitiveConversion.getObjectClassString(type.getName()).getBytes(); // Data type (fix for primitives)
dos.writeShort(encodedType.length);
dos.write(encodedType);
byte[] encodedName = key.getBytes(); // Data name
dos.writeShort(encodedName.length);
dos.write(encodedName);
byte[] encodedValue = dataType.encode(value); // Data
dos.writeInt(encodedValue.length);
dos.write(encodedValue);
}
dos.writeShort(0xff); // End of data object
return output.toByteArray();
}
}

View File

@ -1,15 +1,16 @@
package net.minestom.server.data.type; package net.minestom.server.data.type;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataType; import net.minestom.server.data.DataType;
import net.minestom.server.data.SerializableData;
import net.minestom.server.io.DataReader; import net.minestom.server.io.DataReader;
import java.io.IOException; import java.io.IOException;
// Pretty weird name huh? // Pretty weird name huh?
public class DataData extends DataType<Data> { public class SerializableDataData extends DataType<SerializableData> {
@Override @Override
public byte[] encode(Data value) { public byte[] encode(SerializableData value) {
try { try {
return value.getSerializedData(); return value.getSerializedData();
} catch (IOException e) { } catch (IOException e) {
@ -19,7 +20,7 @@ public class DataData extends DataType<Data> {
} }
@Override @Override
public Data decode(byte[] value) { public SerializableData decode(byte[] value) {
return DataReader.readData(value, false); return DataReader.readData(value, false);
} }
} }

View File

@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.*;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.Viewable; import net.minestom.server.Viewable;
import net.minestom.server.data.Data; import net.minestom.server.data.Data;
import net.minestom.server.data.SerializableData;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.BlockManager;
@ -287,15 +288,17 @@ public class Chunk implements Viewable {
dos.writeBoolean(isCustomBlock); // Determine the type of the ID dos.writeBoolean(isCustomBlock); // Determine the type of the ID
dos.writeShort(id); dos.writeShort(id);
if (data instanceof SerializableData) {
dos.writeBoolean(hasData); dos.writeBoolean(hasData);
if (hasData) { if (hasData) {
byte[] d = data.getSerializedData(); byte[] d = ((SerializableData) data).getSerializedData();
dos.writeInt(d.length); dos.writeInt(d.length);
dos.write(d); dos.write(d);
} }
} }
} }
} }
}
byte[] result = output.toByteArray(); byte[] result = output.toByteArray();
return result; return result;

View File

@ -1,8 +1,8 @@
package net.minestom.server.io; package net.minestom.server.io;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataManager; import net.minestom.server.data.DataManager;
import net.minestom.server.data.SerializableData;
import net.minestom.server.utils.CompressionUtils; import net.minestom.server.utils.CompressionUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -13,12 +13,12 @@ public class DataReader {
private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager(); private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager();
public static Data readData(byte[] b, boolean shouldDecompress) { public static SerializableData readData(byte[] b, boolean shouldDecompress) {
b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b; b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b;
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b)); DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b));
Data data = new Data(); SerializableData data = new SerializableData();
try { try {
while (true) { while (true) {
short typeLength = stream.readShort(); short typeLength = stream.readShort();