Proposer serialization of itemStack and other Serializable stuff in Bukkit API

This commit is contained in:
Marc Baloup 2023-10-07 19:42:26 +02:00
parent da1ee9d882
commit fdcfaf071a
3 changed files with 71 additions and 15 deletions

View File

@ -0,0 +1,49 @@
package fr.pandacube.lib.paper.json;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.bind.TreeTypeAdapter;
import com.google.gson.reflect.TypeToken;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
/* package */ class ConfigurationSerializableAdapter implements JsonSerializer<ConfigurationSerializable>, JsonDeserializer<ConfigurationSerializable> {
public static final TypeAdapterFactory FACTORY = TreeTypeAdapter.newTypeHierarchyFactory(ConfigurationSerializable.class, new ConfigurationSerializableAdapter());
private static final TypeToken<Map<String, Object>> MAP_STR_OBJ_TYPE = new TypeToken<>() { };
private boolean isItemStack(Map<String, Object> deserializedMap) {
return deserializedMap.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)
&& deserializedMap.get(ConfigurationSerialization.SERIALIZED_TYPE_KEY) instanceof String serializedType
&& ItemStack.class.equals(ConfigurationSerialization.getClassByAlias(serializedType));
}
@Override
public ConfigurationSerializable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map<String, Object> deserializedMap = context.deserialize(json, MAP_STR_OBJ_TYPE.getType());
if (isItemStack(deserializedMap)) {
ItemStackAdapter.fixDeserializationVersion(deserializedMap);
}
return ConfigurationSerialization.deserializeObject(deserializedMap);
}
@Override
public JsonElement serialize(ConfigurationSerializable src, Type typeOfSrc, JsonSerializationContext context) {
Map<String, Object> values = new LinkedHashMap<>();
values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(src.getClass()));
values.putAll(src.serialize());
return context.serialize(values, MAP_STR_OBJ_TYPE.getType());
}
}

View File

@ -25,21 +25,7 @@ import java.util.Map;
@Override @Override
public ItemStack deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public ItemStack deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Map<String, Object> deserializedMap = context.deserialize(json, MAP_STR_OBJ_TYPE.getType()); Map<String, Object> deserializedMap = context.deserialize(json, MAP_STR_OBJ_TYPE.getType());
int itemStackVersion = deserializedMap.containsKey("v") ? ((Number)deserializedMap.get("v")).intValue() : -1; fixDeserializationVersion(deserializedMap);
if (itemStackVersion >= 0) {
@SuppressWarnings("deprecation")
int currentDataVersion = Bukkit.getUnsafe().getDataVersion();
if (itemStackVersion > currentDataVersion) {
/* The itemStack we are deserializing is from a newer MC version, so Bukkit will refuse it.
* We decide to ignore the provided version and consider that the received item stack is from current
* version. We let Bukkit handles the deserialization with the data it can interpret, throwing an error
* only if it can't.
*/
deserializedMap.put("v", currentDataVersion);
return ItemStack.deserialize(deserializedMap);
}
}
return ItemStack.deserialize(deserializedMap); return ItemStack.deserialize(deserializedMap);
} }
@ -47,4 +33,24 @@ import java.util.Map;
public JsonElement serialize(ItemStack src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(ItemStack src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.serialize(), MAP_STR_OBJ_TYPE.getType()); return context.serialize(src.serialize(), MAP_STR_OBJ_TYPE.getType());
} }
/* package */ static void fixDeserializationVersion(Map<String, Object> deserializedMap) {
if (!deserializedMap.containsKey("v"))
return;
int itemStackVersion = ((Number)deserializedMap.get("v")).intValue();
if (itemStackVersion >= 0) {
@SuppressWarnings("deprecation")
int currentDataVersion = Bukkit.getUnsafe().getDataVersion();
if (itemStackVersion > currentDataVersion) {
/* Here, the itemStack we are deserializing is from a newer MC version, so Bukkit will refuse it.
* We decide to ignore the provided version and consider that the received item stack is from current
* version. We let Bukkit handles the deserialization with the data it can interpret, throwing an error
* only if it can't.
*/
deserializedMap.put("v", currentDataVersion);
}
}
}
} }

View File

@ -12,5 +12,6 @@ public class PaperJson {
*/ */
public static void init() { public static void init() {
Json.registerTypeAdapterFactory(ItemStackAdapter.FACTORY); Json.registerTypeAdapterFactory(ItemStackAdapter.FACTORY);
Json.registerTypeAdapterFactory(ConfigurationSerializableAdapter.FACTORY);
} }
} }