Paper config files
== AT == public org.spigotmc.SpigotWorldConfig getBoolean(Ljava/lang/String;Z)Z public org.spigotmc.SpigotWorldConfig getDouble(Ljava/lang/String;)D public org.spigotmc.SpigotWorldConfig getDouble(Ljava/lang/String;D)D public org.spigotmc.SpigotWorldConfig getInt(Ljava/lang/String;)I public org.spigotmc.SpigotWorldConfig getInt(Ljava/lang/String;I)I public org.spigotmc.SpigotWorldConfig getList(Ljava/lang/String;Ljava/lang/Object;)Ljava/util/List; public org.spigotmc.SpigotWorldConfig getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; public net.minecraft.server.dedicated.DedicatedServerProperties reload(Lnet/minecraft/core/RegistryAccess;Ljava/util/Properties;Ljoptsimple/OptionSet;)Lnet/minecraft/server/dedicated/DedicatedServerProperties; public net.minecraft.world.level.NaturalSpawner SPAWNING_CATEGORIES
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ComponentSerializer extends ScalarSerializer<Component> {
|
||||
|
||||
public ComponentSerializer() {
|
||||
super(Component.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component deserialize(Type type, Object obj) throws SerializationException {
|
||||
return MiniMessage.miniMessage().deserialize(obj.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object serialize(Component component, Predicate<Class<?>> typeSupported) {
|
||||
return MiniMessage.miniMessage().serialize(component);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import io.papermc.paper.configuration.type.EngineMode;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class EngineModeSerializer extends ScalarSerializer<EngineMode> {
|
||||
|
||||
public EngineModeSerializer() {
|
||||
super(EngineMode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EngineMode deserialize(Type type, Object obj) throws SerializationException {
|
||||
if (obj instanceof Integer id) {
|
||||
try {
|
||||
return EngineMode.valueOf(id);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SerializationException(id + " is not a valid id for type " + type + " for this node");
|
||||
}
|
||||
}
|
||||
|
||||
throw new SerializationException(obj + " is not of a valid type " + type + " for this node");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object serialize(EngineMode item, Predicate<Class<?>> typeSupported) {
|
||||
return item.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.util.EnumLookup;
|
||||
|
||||
import static io.leangen.geantyref.GenericTypeReflector.erase;
|
||||
|
||||
/**
|
||||
* Enum serializer that lists options if fails and accepts `-` as `_`.
|
||||
*/
|
||||
public class EnumValueSerializer extends ScalarSerializer<Enum<?>> {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
public EnumValueSerializer() {
|
||||
super(new TypeToken<Enum<?>>() {});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public @Nullable Enum<?> deserialize(final Type type, final Object obj) throws SerializationException {
|
||||
final String enumConstant = obj.toString();
|
||||
final Class<? extends Enum> typeClass = erase(type).asSubclass(Enum.class);
|
||||
Enum<?> ret = EnumLookup.lookupEnum(typeClass, enumConstant);
|
||||
if (ret == null) {
|
||||
ret = EnumLookup.lookupEnum(typeClass, enumConstant.replace("-", "_"));
|
||||
}
|
||||
if (ret == null) {
|
||||
boolean longer = typeClass.getEnumConstants().length > 10;
|
||||
List<String> options = Arrays.stream(typeClass.getEnumConstants()).limit(10L).map(Enum::name).toList();
|
||||
LOGGER.error("Invalid enum constant provided, expected one of [{}{}], but got {}", String.join(", ", options), longer ? ", ..." : "", enumConstant);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(final Enum<?> item, final Predicate<Class<?>> typeSupported) {
|
||||
return item.name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import com.destroystokyo.paper.util.SneakyThrow;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.commands.arguments.NbtPathArgument;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public class NbtPathSerializer extends ScalarSerializer<NbtPathArgument.NbtPath> {
|
||||
|
||||
public static final NbtPathSerializer SERIALIZER = new NbtPathSerializer();
|
||||
private static final NbtPathArgument DUMMY_ARGUMENT = new NbtPathArgument();
|
||||
|
||||
private NbtPathSerializer() {
|
||||
super(NbtPathArgument.NbtPath.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtPathArgument.NbtPath deserialize(final Type type, final Object obj) throws SerializationException {
|
||||
return fromString(obj.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object serialize(final NbtPathArgument.NbtPath item, final Predicate<Class<?>> typeSupported) {
|
||||
return item.toString();
|
||||
}
|
||||
|
||||
public static List<NbtPathArgument.NbtPath> fromString(final List<String> tags) {
|
||||
List<NbtPathArgument.NbtPath> paths = new ArrayList<>();
|
||||
try {
|
||||
for (final String tag : tags) {
|
||||
paths.add(fromString(tag));
|
||||
}
|
||||
} catch (SerializationException ex) {
|
||||
SneakyThrow.sneaky(ex);
|
||||
}
|
||||
return List.copyOf(paths);
|
||||
}
|
||||
|
||||
private static NbtPathArgument.NbtPath fromString(final String tag) throws SerializationException {
|
||||
try {
|
||||
return DUMMY_ARGUMENT.parse(new StringReader(tag));
|
||||
} catch (CommandSyntaxException e) {
|
||||
throw new SerializationException(NbtPathArgument.NbtPath.class, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
|
||||
import io.papermc.paper.util.ObfHelper;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
@SuppressWarnings("Convert2Diamond")
|
||||
public final class PacketClassSerializer extends ScalarSerializer<Class<? extends Packet<?>>> implements MapSerializer.WriteBack {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
private static final TypeToken<Class<? extends Packet<?>>> TYPE = new TypeToken<Class<? extends Packet<?>>>() {};
|
||||
private static final List<String> SUBPACKAGES = List.of("game", "handshake", "login", "status");
|
||||
private static final BiMap<String, String> MOJANG_TO_OBF;
|
||||
|
||||
static {
|
||||
final ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder();
|
||||
final @Nullable Map<String, ObfHelper.ClassMapping> classMappingMap = ObfHelper.INSTANCE.mappingsByMojangName();
|
||||
if (classMappingMap != null) {
|
||||
classMappingMap.forEach((mojMap, classMapping) -> {
|
||||
if (mojMap.startsWith("net.minecraft.network.protocol.")) {
|
||||
builder.put(classMapping.mojangName(), classMapping.obfName());
|
||||
}
|
||||
});
|
||||
}
|
||||
MOJANG_TO_OBF = builder.build();
|
||||
}
|
||||
|
||||
public PacketClassSerializer() {
|
||||
super(TYPE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<? extends Packet<?>> deserialize(final Type type, final Object obj) throws SerializationException {
|
||||
Class<?> packetClass = null;
|
||||
for (final String subpackage : SUBPACKAGES) {
|
||||
final String fullClassName = "net.minecraft.network.protocol." + subpackage + "." + obj;
|
||||
try {
|
||||
packetClass = Class.forName(fullClassName);
|
||||
break;
|
||||
} catch (final ClassNotFoundException ex) {
|
||||
final String spigotClassName = MOJANG_TO_OBF.get(fullClassName);
|
||||
if (spigotClassName != null) {
|
||||
try {
|
||||
packetClass = Class.forName(spigotClassName);
|
||||
} catch (final ClassNotFoundException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packetClass == null || !Packet.class.isAssignableFrom(packetClass)) {
|
||||
throw new SerializationException("Could not deserialize a packet from " + obj);
|
||||
}
|
||||
return (Class<? extends Packet<?>>) packetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object serialize(final Class<? extends Packet<?>> packetClass, final Predicate<Class<?>> typeSupported) {
|
||||
final String name = packetClass.getName();
|
||||
@Nullable String mojName = ObfHelper.INSTANCE.mappingsByMojangName() == null ? name : MOJANG_TO_OBF.inverse().get(name); // if the mappings are null, running on moj-mapped server
|
||||
if (mojName == null && MOJANG_TO_OBF.containsKey(name)) {
|
||||
mojName = name;
|
||||
}
|
||||
if (mojName != null) {
|
||||
int pos = mojName.lastIndexOf('.');
|
||||
if (pos != -1 && pos != mojName.length() - 1) {
|
||||
return mojName.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.error("Could not serialize {} into a mojang-mapped packet class name", packetClass);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public final class StringRepresentableSerializer extends ScalarSerializer<StringRepresentable> {
|
||||
private static final Map<Type, Function<String, StringRepresentable>> TYPES = Collections.synchronizedMap(Map.ofEntries(
|
||||
createEntry(MobCategory.class)
|
||||
));
|
||||
|
||||
public StringRepresentableSerializer() {
|
||||
super(StringRepresentable.class);
|
||||
}
|
||||
|
||||
public static boolean isValidFor(final Type type) {
|
||||
return TYPES.containsKey(type);
|
||||
}
|
||||
|
||||
private static <E extends Enum<E> & StringRepresentable> Map.Entry<Type, Function<String, @Nullable StringRepresentable>> createEntry(Class<E> type) {
|
||||
return Map.entry(type, s -> {
|
||||
for (E value : type.getEnumConstants()) {
|
||||
if (value.getSerializedName().equals(s)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringRepresentable deserialize(Type type, Object obj) throws SerializationException {
|
||||
Function<String, StringRepresentable> function = TYPES.get(type);
|
||||
if (function == null) {
|
||||
throw new SerializationException(type + " isn't registered");
|
||||
}
|
||||
return function.apply(obj.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object serialize(StringRepresentable item, Predicate<Class<?>> typeSupported) {
|
||||
return item.getSerializedName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package io.papermc.paper.configuration.serializer.collections;
|
||||
|
||||
import io.leangen.geantyref.GenericTypeReflector;
|
||||
import io.leangen.geantyref.TypeFactory;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedParameterizedType;
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public abstract class FastutilMapSerializer<M extends Map<?, ?>> implements TypeSerializer.Annotated<M> {
|
||||
|
||||
private final Function<? super Map, ? extends M> factory;
|
||||
|
||||
protected FastutilMapSerializer(final Function<? super Map, ? extends M> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public M deserialize(final AnnotatedType annotatedType, final ConfigurationNode node) throws SerializationException {
|
||||
final Map map = (Map) node.get(this.createAnnotatedMapType((AnnotatedParameterizedType) annotatedType));
|
||||
return this.factory.apply(map == null ? Collections.emptyMap() : map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final AnnotatedType annotatedType, final @Nullable M obj, final ConfigurationNode node) throws SerializationException {
|
||||
if (obj == null || obj.isEmpty()) {
|
||||
node.raw(null);
|
||||
} else {
|
||||
final AnnotatedType baseMapType = this.createAnnotatedMapType((AnnotatedParameterizedType) annotatedType);
|
||||
node.set(baseMapType, obj);
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotatedType createAnnotatedMapType(final AnnotatedParameterizedType type) {
|
||||
final Type baseType = this.createBaseMapType((ParameterizedType) type.getType());
|
||||
return GenericTypeReflector.annotate(baseType, type.getAnnotations());
|
||||
}
|
||||
|
||||
protected abstract Type createBaseMapType(final ParameterizedType type);
|
||||
|
||||
public static final class SomethingToPrimitive<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
|
||||
|
||||
private final Type primitiveType;
|
||||
|
||||
public SomethingToPrimitive(final Function<Map, ? extends M> factory, final Type primitiveType) {
|
||||
super(factory);
|
||||
this.primitiveType = primitiveType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type createBaseMapType(final ParameterizedType type) {
|
||||
return TypeFactory.parameterizedClass(Map.class, type.getActualTypeArguments()[0], GenericTypeReflector.box(this.primitiveType));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class PrimitiveToSomething<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
|
||||
|
||||
private final Type primitiveType;
|
||||
|
||||
public PrimitiveToSomething(final Function<Map, ? extends M> factory, final Type primitiveType) {
|
||||
super(factory);
|
||||
this.primitiveType = primitiveType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type createBaseMapType(final ParameterizedType type) {
|
||||
return TypeFactory.parameterizedClass(Map.class, GenericTypeReflector.box(this.primitiveType), type.getActualTypeArguments()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SomethingToSomething<M extends Map<?, ?>> extends FastutilMapSerializer<M> {
|
||||
|
||||
public SomethingToSomething(final Function<? super Map, ? extends M> factory) {
|
||||
super(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Type createBaseMapType(final ParameterizedType type) {
|
||||
return TypeFactory.parameterizedClass(Map.class, type.getActualTypeArguments()[0], type.getActualTypeArguments()[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package io.papermc.paper.configuration.serializer.collections;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.BasicConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationOptions;
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializerCollection;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Map serializer that does not throw errors on individual entry serialization failures.
|
||||
*/
|
||||
public class MapSerializer implements TypeSerializer.Annotated<Map<?, ?>> {
|
||||
|
||||
public static final TypeToken<Map<?, ?>> TYPE = new TypeToken<Map<?, ?>>() {};
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
private final boolean clearInvalids;
|
||||
private final TypeSerializer<Map<?, ?>> fallback;
|
||||
|
||||
public MapSerializer(boolean clearInvalids) {
|
||||
this.clearInvalids = clearInvalids;
|
||||
this.fallback = requireNonNull(TypeSerializerCollection.defaults().get(TYPE), "Could not find default Map<?, ?> serializer");
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ThrowExceptions {}
|
||||
|
||||
@Override
|
||||
public Map<?, ?> deserialize(AnnotatedType annotatedType, ConfigurationNode node) throws SerializationException {
|
||||
if (annotatedType.isAnnotationPresent(ThrowExceptions.class)) {
|
||||
return this.fallback.deserialize(annotatedType, node);
|
||||
}
|
||||
final Map<Object, Object> map = new LinkedHashMap<>();
|
||||
final Type type = annotatedType.getType();
|
||||
if (node.isMap()) {
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new SerializationException(type, "Raw types are not supported for collections");
|
||||
}
|
||||
if (parameterizedType.getActualTypeArguments().length != 2) {
|
||||
throw new SerializationException(type, "Map expected two type arguments!");
|
||||
}
|
||||
final Type key = parameterizedType.getActualTypeArguments()[0];
|
||||
final Type value = parameterizedType.getActualTypeArguments()[1];
|
||||
final @Nullable TypeSerializer<?> keySerializer = node.options().serializers().get(key);
|
||||
final @Nullable TypeSerializer<?> valueSerializer = node.options().serializers().get(value);
|
||||
if (keySerializer == null) {
|
||||
throw new SerializationException(type, "No type serializer available for key type " + key);
|
||||
}
|
||||
if (valueSerializer == null) {
|
||||
throw new SerializationException(type, "No type serializer available for value type " + value);
|
||||
}
|
||||
|
||||
final BasicConfigurationNode keyNode = BasicConfigurationNode.root(node.options());
|
||||
final Set<Object> keysToClear = new HashSet<>();
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> ent : node.childrenMap().entrySet()) {
|
||||
final @Nullable Object deserializedKey = deserialize(key, keySerializer, "key", keyNode.set(ent.getKey()), node.path());
|
||||
final @Nullable Object deserializedValue = deserialize(value, valueSerializer, "value", ent.getValue(), ent.getValue().path());
|
||||
if (deserializedKey == null || deserializedValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (keySerializer instanceof WriteBack) {
|
||||
if (serialize(key, keySerializer, deserializedKey, "key", keyNode, node.path()) && !ent.getKey().equals(requireNonNull(keyNode.raw(), "Key must not be null!"))) {
|
||||
keysToClear.add(ent.getKey());
|
||||
}
|
||||
}
|
||||
map.put(deserializedKey, deserializedValue);
|
||||
}
|
||||
if (keySerializer instanceof WriteBack) { // supports cleaning keys which deserialize to the same value
|
||||
for (Object keyToClear : keysToClear) {
|
||||
node.node(keyToClear).raw(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private @Nullable Object deserialize(Type type, TypeSerializer<?> serializer, String mapPart, ConfigurationNode node, NodePath path) {
|
||||
try {
|
||||
return serializer.deserialize(type, node);
|
||||
} catch (SerializationException ex) {
|
||||
ex.initPath(node::path);
|
||||
LOGGER.error("Could not deserialize {} {} into {} at {}: {}", mapPart, node.raw(), type, path, ex.rawMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(AnnotatedType annotatedType, @Nullable Map<?, ?> obj, ConfigurationNode node) throws SerializationException {
|
||||
if (annotatedType.isAnnotationPresent(ThrowExceptions.class)) {
|
||||
this.fallback.serialize(annotatedType, obj, node);
|
||||
return;
|
||||
}
|
||||
final Type type = annotatedType.getType();
|
||||
if (!(type instanceof ParameterizedType parameterizedType)) {
|
||||
throw new SerializationException(type, "Raw types are not supported for collections");
|
||||
}
|
||||
if (parameterizedType.getActualTypeArguments().length != 2) {
|
||||
throw new SerializationException(type, "Map expected two type arguments!");
|
||||
}
|
||||
final Type key = parameterizedType.getActualTypeArguments()[0];
|
||||
final Type value = parameterizedType.getActualTypeArguments()[1];
|
||||
final @Nullable TypeSerializer<?> keySerializer = node.options().serializers().get(key);
|
||||
final @Nullable TypeSerializer<?> valueSerializer = node.options().serializers().get(value);
|
||||
|
||||
if (keySerializer == null) {
|
||||
throw new SerializationException(type, "No type serializer available for key type " + key);
|
||||
}
|
||||
|
||||
if (valueSerializer == null) {
|
||||
throw new SerializationException(type, "No type serializer available for value type " + value);
|
||||
}
|
||||
|
||||
if (obj == null || obj.isEmpty()) {
|
||||
node.set(Collections.emptyMap());
|
||||
} else {
|
||||
final Set<Object> unvisitedKeys;
|
||||
if (node.empty()) {
|
||||
node.raw(Collections.emptyMap());
|
||||
unvisitedKeys = Collections.emptySet();
|
||||
} else {
|
||||
unvisitedKeys = new HashSet<>(node.childrenMap().keySet());
|
||||
}
|
||||
final BasicConfigurationNode keyNode = BasicConfigurationNode.root(node.options());
|
||||
for (Map.Entry<?, ?> ent : obj.entrySet()) {
|
||||
if (!serialize(key, keySerializer, ent.getKey(), "key", keyNode, node.path())) {
|
||||
continue;
|
||||
}
|
||||
final Object keyObj = requireNonNull(keyNode.raw(), "Key must not be null!");
|
||||
final ConfigurationNode child = node.node(keyObj);
|
||||
serialize(value, valueSerializer, ent.getValue(), "value", child, child.path());
|
||||
unvisitedKeys.remove(keyObj);
|
||||
}
|
||||
if (this.clearInvalids) {
|
||||
for (Object unusedChild : unvisitedKeys) {
|
||||
node.removeChild(unusedChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private boolean serialize(Type type, TypeSerializer serializer, Object object, String mapPart, ConfigurationNode node, NodePath path) {
|
||||
try {
|
||||
serializer.serialize(type, object, node);
|
||||
return true;
|
||||
} catch (SerializationException ex) {
|
||||
ex.initPath(node::path);
|
||||
LOGGER.error("Could not serialize {} {} from {} at {}: {}", mapPart, object, type, path, ex.rawMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<?, ?> emptyValue(AnnotatedType specificType, ConfigurationOptions options) {
|
||||
if (specificType.isAnnotationPresent(ThrowExceptions.class)) {
|
||||
return this.fallback.emptyValue(specificType, options);
|
||||
}
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public interface WriteBack { // marker interface
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.papermc.paper.configuration.serializer.collections;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.Table;
|
||||
import io.leangen.geantyref.TypeFactory;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.spongepowered.configurate.BasicConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationOptions;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
public class TableSerializer implements TypeSerializer<Table<?, ?, ?>> {
|
||||
private static final int ROW_TYPE_ARGUMENT_INDEX = 0;
|
||||
private static final int COLUMN_TYPE_ARGUMENT_INDEX = 1;
|
||||
private static final int VALUE_TYPE_ARGUMENT_INDEX = 2;
|
||||
|
||||
@Override
|
||||
public Table<?, ?, ?> deserialize(final Type type, final ConfigurationNode node) throws SerializationException {
|
||||
final Table<?, ?, ?> table = HashBasedTable.create();
|
||||
if (!node.empty() && node.isMap()) {
|
||||
this.deserialize0(table, (ParameterizedType) type, node);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R, C, V> void deserialize0(final Table<R, C, V> table, final ParameterizedType type, final ConfigurationNode node) throws SerializationException {
|
||||
final Type rowType = type.getActualTypeArguments()[ROW_TYPE_ARGUMENT_INDEX];
|
||||
final Type columnType = type.getActualTypeArguments()[COLUMN_TYPE_ARGUMENT_INDEX];
|
||||
final Type valueType = type.getActualTypeArguments()[VALUE_TYPE_ARGUMENT_INDEX];
|
||||
|
||||
final TypeSerializer<R> rowKeySerializer = (TypeSerializer<R>) node.options().serializers().get(rowType);
|
||||
if (rowKeySerializer == null) {
|
||||
throw new SerializationException("Could not find serializer for table row type " + rowType);
|
||||
}
|
||||
|
||||
final Type mapType = TypeFactory.parameterizedClass(Map.class, columnType, valueType);
|
||||
final TypeSerializer<Map<C, V>> columnValueSerializer = (TypeSerializer<Map<C, V>>) node.options().serializers().get(mapType);
|
||||
if (columnValueSerializer == null) {
|
||||
throw new SerializationException("Could not find serializer for table column-value map " + type);
|
||||
}
|
||||
|
||||
final BasicConfigurationNode rowKeyNode = BasicConfigurationNode.root(node.options());
|
||||
|
||||
for (final Object key : node.childrenMap().keySet()) {
|
||||
final R rowKey = rowKeySerializer.deserialize(rowType, rowKeyNode.set(key));
|
||||
final Map<C, V> map = columnValueSerializer.deserialize(mapType, node.node(rowKeyNode.raw()));
|
||||
map.forEach((column, value) -> table.put(rowKey, column, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Type type, final @Nullable Table<?, ?, ?> table, final ConfigurationNode node) throws SerializationException {
|
||||
if (table != null) {
|
||||
this.serialize0(table, (ParameterizedType) type, node);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private <R, C, V> void serialize0(final Table<R, C, V> table, final ParameterizedType type, final ConfigurationNode node) throws SerializationException {
|
||||
final Type rowType = type.getActualTypeArguments()[ROW_TYPE_ARGUMENT_INDEX];
|
||||
final Type columnType = type.getActualTypeArguments()[COLUMN_TYPE_ARGUMENT_INDEX];
|
||||
final Type valueType = type.getActualTypeArguments()[VALUE_TYPE_ARGUMENT_INDEX];
|
||||
|
||||
final TypeSerializer rowKeySerializer = node.options().serializers().get(rowType);
|
||||
if (rowKeySerializer == null) {
|
||||
throw new SerializationException("Could not find a serializer for table row type " + rowType);
|
||||
}
|
||||
|
||||
final BasicConfigurationNode rowKeyNode = BasicConfigurationNode.root(node.options());
|
||||
for (final R key : table.rowKeySet()) {
|
||||
rowKeySerializer.serialize(rowType, key, rowKeyNode.set(key));
|
||||
final Object keyObj = Objects.requireNonNull(rowKeyNode.raw());
|
||||
node.node(keyObj).set(TypeFactory.parameterizedClass(Map.class, columnType, valueType), table.row(key));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Table<?, ?, ?> emptyValue(Type specificType, ConfigurationOptions options) {
|
||||
return ImmutableTable.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.configuration.serializer.collections;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.configuration.serializer;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.papermc.paper.configuration.serializer.registry;
|
||||
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.configurate.serialize.ScalarSerializer;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
abstract class RegistryEntrySerializer<T, R> extends ScalarSerializer<T> {
|
||||
|
||||
private final RegistryAccess registryAccess;
|
||||
private final ResourceKey<? extends Registry<R>> registryKey;
|
||||
private final boolean omitMinecraftNamespace;
|
||||
|
||||
protected RegistryEntrySerializer(TypeToken<T> type, final RegistryAccess registryAccess, ResourceKey<? extends Registry<R>> registryKey, boolean omitMinecraftNamespace) {
|
||||
super(type);
|
||||
this.registryAccess = registryAccess;
|
||||
this.registryKey = registryKey;
|
||||
this.omitMinecraftNamespace = omitMinecraftNamespace;
|
||||
}
|
||||
|
||||
protected RegistryEntrySerializer(Class<T> type, final RegistryAccess registryAccess, ResourceKey<? extends Registry<R>> registryKey, boolean omitMinecraftNamespace) {
|
||||
super(type);
|
||||
this.registryAccess = registryAccess;
|
||||
this.registryKey = registryKey;
|
||||
this.omitMinecraftNamespace = omitMinecraftNamespace;
|
||||
}
|
||||
|
||||
protected final Registry<R> registry() {
|
||||
return this.registryAccess.lookupOrThrow(this.registryKey);
|
||||
}
|
||||
|
||||
protected abstract T convertFromResourceKey(ResourceKey<R> key) throws SerializationException;
|
||||
|
||||
@Override
|
||||
public final T deserialize(Type type, Object obj) throws SerializationException {
|
||||
return this.convertFromResourceKey(this.deserializeKey(obj));
|
||||
}
|
||||
|
||||
protected abstract ResourceKey<R> convertToResourceKey(T value);
|
||||
|
||||
@Override
|
||||
protected final Object serialize(T item, Predicate<Class<?>> typeSupported) {
|
||||
final ResourceKey<R> key = this.convertToResourceKey(item);
|
||||
if (this.omitMinecraftNamespace && key.location().getNamespace().equals(ResourceLocation.DEFAULT_NAMESPACE)) {
|
||||
return key.location().getPath();
|
||||
} else {
|
||||
return key.location().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceKey<R> deserializeKey(final Object input) throws SerializationException {
|
||||
final ResourceLocation key = ResourceLocation.tryParse(input.toString());
|
||||
if (key == null) {
|
||||
throw new SerializationException("Could not create a key from " + input);
|
||||
}
|
||||
return ResourceKey.create(this.registryKey, key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.papermc.paper.configuration.serializer.registry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.leangen.geantyref.TypeFactory;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
public final class RegistryHolderSerializer<T> extends RegistryEntrySerializer<Holder<T>, T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public RegistryHolderSerializer(TypeToken<T> typeToken, final RegistryAccess registryAccess, ResourceKey<? extends Registry<T>> registryKey, boolean omitMinecraftNamespace) {
|
||||
super((TypeToken<Holder<T>>) TypeToken.get(TypeFactory.parameterizedClass(Holder.class, typeToken.getType())), registryAccess, registryKey, omitMinecraftNamespace);
|
||||
}
|
||||
|
||||
public RegistryHolderSerializer(Class<T> type, final RegistryAccess registryAccess, ResourceKey<? extends Registry<T>> registryKey, boolean omitMinecraftNamespace) {
|
||||
this(TypeToken.get(type), registryAccess, registryKey, omitMinecraftNamespace);
|
||||
Preconditions.checkArgument(type.getTypeParameters().length == 0, "%s must have 0 type parameters", type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Holder<T> convertFromResourceKey(ResourceKey<T> key) throws SerializationException {
|
||||
return this.registry().get(key).orElseThrow(() -> new SerializationException("Missing holder in " + this.registry().key() + " with key " + key));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceKey<T> convertToResourceKey(Holder<T> value) {
|
||||
return value.unwrap().map(Function.identity(), r -> this.registry().getResourceKey(r).orElseThrow());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.papermc.paper.configuration.serializer.registry;
|
||||
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
/**
|
||||
* Use {@link RegistryHolderSerializer} for datapack-configurable things.
|
||||
*/
|
||||
public final class RegistryValueSerializer<T> extends RegistryEntrySerializer<T, T> {
|
||||
|
||||
public RegistryValueSerializer(TypeToken<T> type, final RegistryAccess registryAccess, ResourceKey<? extends Registry<T>> registryKey, boolean omitMinecraftNamespace) {
|
||||
super(type, registryAccess, registryKey, omitMinecraftNamespace);
|
||||
}
|
||||
|
||||
public RegistryValueSerializer(Class<T> type, final RegistryAccess registryAccess, ResourceKey<? extends Registry<T>> registryKey, boolean omitMinecraftNamespace) {
|
||||
super(type, registryAccess, registryKey, omitMinecraftNamespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T convertFromResourceKey(ResourceKey<T> key) throws SerializationException {
|
||||
final T value = this.registry().getValue(key);
|
||||
if (value == null) {
|
||||
throw new SerializationException("Missing value in " + this.registry() + " with key " + key.location());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceKey<T> convertToResourceKey(T value) {
|
||||
return this.registry().getResourceKey(value).orElseThrow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package io.papermc.paper.configuration.serializer.registry;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
Reference in New Issue
Block a user