From 8141349df1caad090401c3960cdf97a90ba7a50d Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Fri, 10 Jun 2022 01:45:41 -0700 Subject: [PATCH] improve packet config serializer (#7920) --- patches/server/0004-Paper-config-files.patch | 125 +++++++++++-------- 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/patches/server/0004-Paper-config-files.patch b/patches/server/0004-Paper-config-files.patch index f0da33fd7..5cffce0ae 100644 --- a/patches/server/0004-Paper-config-files.patch +++ b/patches/server/0004-Paper-config-files.patch @@ -2191,36 +2191,49 @@ index 0000000000000000000000000000000000000000..f2f362883d1825084c277608c791f821 +} diff --git a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java new file mode 100644 -index 0000000000000000000000000000000000000000..a13691523554a85c3ea3efb70d847904a9d91f0c +index 0000000000000000000000000000000000000000..bc065d5cc8975dd189954272116a6bc5bc7f4e28 --- /dev/null +++ b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java -@@ -0,0 +1,61 @@ +@@ -0,0 +1,86 @@ +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.Collection; -+import java.util.Collections; -+import java.util.List; -+import java.util.Map; -+import java.util.Optional; -+import java.util.function.Predicate; -+import java.util.stream.Collectors; +import net.minecraft.network.protocol.Packet; +import org.checkerframework.checker.nullness.qual.Nullable; ++import org.slf4j.Logger; +import org.spongepowered.configurate.serialize.ScalarSerializer; +import org.spongepowered.configurate.serialize.SerializationException; + ++import java.lang.reflect.Type; ++import java.util.List; ++import java.util.Map; ++import java.util.function.Predicate; ++ +@SuppressWarnings("Convert2Diamond") -+public final class PacketClassSerializer extends ScalarSerializer>> { ++public final class PacketClassSerializer extends ScalarSerializer>> implements MapSerializer.WriteBack { ++ ++ private static final Logger LOGGER = LogUtils.getLogger(); + private static final TypeToken>> TYPE = new TypeToken>>() {}; + private static final List SUBPACKAGES = List.of("game", "handshake", "login", "status"); -+ private static final Map MOJANG_TO_OBF = Optional.ofNullable(ObfHelper.INSTANCE.mappingsByMojangName()) -+ .map(Map::entrySet) -+ .map(Collection::stream) -+ .map(stream -> stream.collect(Collectors.toMap(entry -> entry.getValue().mojangName(), entry -> entry.getValue().obfName()))) -+ .orElseGet(Collections::emptyMap); ++ private static final BiMap MOJANG_TO_OBF; ++ ++ static { ++ final ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); ++ final @Nullable Map 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); @@ -2251,9 +2264,21 @@ index 0000000000000000000000000000000000000000..a13691523554a85c3ea3efb70d847904 + } + + @Override -+ protected Object serialize(final Class> item, final Predicate> typeSupported) { -+ //TODO always serialize the mapped class name to not break on switching between mapped/unmapped servers -+ return item.getSimpleName(); ++ protected @Nullable Object serialize(final Class> packetClass, final Predicate> 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; + } +} diff --git a/src/main/java/io/papermc/paper/configuration/serializer/StringRepresentableSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/StringRepresentableSerializer.java @@ -2402,10 +2427,10 @@ index 0000000000000000000000000000000000000000..0b235ebe6e79d7aa420d6b8a52aedb3a +} diff --git a/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java new file mode 100644 -index 0000000000000000000000000000000000000000..f5c0fb018b7f8eff1d6ca1f0425409adac242180 +index 0000000000000000000000000000000000000000..f44d4cb05eab25d79a8ac09b9da981633380c4fc --- /dev/null +++ b/src/main/java/io/papermc/paper/configuration/serializer/collections/MapSerializer.java -@@ -0,0 +1,148 @@ +@@ -0,0 +1,162 @@ +package io.papermc.paper.configuration.serializer.collections; + +import com.mojang.logging.LogUtils; @@ -2466,13 +2491,24 @@ index 0000000000000000000000000000000000000000..f5c0fb018b7f8eff1d6ca1f0425409ad + } + + final BasicConfigurationNode keyNode = BasicConfigurationNode.root(node.options()); ++ final Set keysToClear = new HashSet<>(); + for (Map.Entry ent : node.childrenMap().entrySet()) { -+ final @Nullable Object keyValue = deserialize(key, keySerializer, "key", keyNode.set(ent.getKey()), node.path()); -+ final @Nullable Object valueValue = deserialize(value, valueSerializer, "value", ent.getValue(), ent.getValue().path()); -+ if (keyValue == null || valueValue == null) { ++ 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; + } -+ map.put(keyValue, valueValue); ++ 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; @@ -2553,6 +2589,9 @@ index 0000000000000000000000000000000000000000..f5c0fb018b7f8eff1d6ca1f0425409ad + public @Nullable Map emptyValue(Type specificType, ConfigurationOptions options) { + return new LinkedHashMap<>(); + } ++ ++ public interface WriteBack { // marker interface ++ } +} diff --git a/src/main/java/io/papermc/paper/configuration/serializer/registry/RegistryEntrySerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/registry/RegistryEntrySerializer.java new file mode 100644 @@ -2745,16 +2784,14 @@ index 0000000000000000000000000000000000000000..0300fb1e09d41465e4a50bfdc987b957 +} diff --git a/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java b/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..3be5cb126e41a533e9453bbd6326cd1c10dd879d +index 0000000000000000000000000000000000000000..62e41812b0df4fc548f97273215a92b993ebf1d7 --- /dev/null +++ b/src/main/java/io/papermc/paper/configuration/transformation/global/LegacyPaperConfig.java -@@ -0,0 +1,248 @@ +@@ -0,0 +1,224 @@ +package io.papermc.paper.configuration.transformation.global; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.configuration.Configuration; -+import io.papermc.paper.configuration.serializer.PacketClassSerializer; -+import io.papermc.paper.util.ObfHelper; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; @@ -2768,14 +2805,12 @@ index 0000000000000000000000000000000000000000..3be5cb126e41a533e9453bbd6326cd1c +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + -+import java.util.List; +import java.util.function.Predicate; + +import static org.spongepowered.configurate.NodePath.path; + +public final class LegacyPaperConfig { + private static final Logger LOGGER = LogUtils.getLogger(); -+ private static final PacketClassSerializer PACKET_CLASS_SERIALIZER = new PacketClassSerializer(); + + private LegacyPaperConfig() { + } @@ -2872,31 +2907,11 @@ index 0000000000000000000000000000000000000000..3be5cb126e41a533e9453bbd6326cd1c + .addAction(path("packet-limiter", "limits", "all"), (path, value) -> new Object[]{"packet-limiter", "all-packets"}) + .addAction(path("packet-limiter", "limits"), (path, value) -> new Object[]{"packet-limiter", "overrides"}) + .addAction(path("packet-limiter", "overrides", ConfigurationTransformation.WILDCARD_OBJECT), (path, value) -> { -+ if (ObfHelper.INSTANCE.mappingsByObfName() != null) { // requires mappings to be present -+ final @Nullable Object key = value.key(); -+ if (key != null) { -+ String className = key.toString(); -+ for (final String state : List.of("game", "handshake", "login", "status")) { -+ final String fullClassName = "net.minecraft.network.protocol." + state + "." + className; -+ final ObfHelper.ClassMapping classMapping = ObfHelper.INSTANCE.mappingsByObfName().get(fullClassName); -+ if (classMapping != null) { -+ final String[] split = classMapping.mojangName().split("\\."); -+ className = split[split.length - 1]; -+ break; -+ } -+ } -+ -+ return path.with(path.size() - 1, className).array(); -+ } else { -+ LOGGER.warn("Could not convert spigot-mapped packet class name {}", value); -+ } ++ final @Nullable Object keyValue = value.key(); ++ if (keyValue != null && keyValue.toString().equals("PacketPlayInAutoRecipe")) { // add special cast to handle the default for moj-mapped servers that upgrade the config ++ return path.with(path.size() - 1, ServerboundPlaceRecipePacket.class.getSimpleName()).array(); + } else { -+ final @Nullable Object keyValue = value.key(); -+ if (keyValue != null && keyValue.toString().equals("PacketPlayInAutoRecipe")) { // add special case to catch the default -+ return path.with(path.size() - 1, ServerboundPlaceRecipePacket.class.getSimpleName()).array(); -+ } else { -+ LOGGER.warn("Could not convert spigot-mapped packet class name {} because no mappings were found in the jar", keyValue); -+ } ++ LOGGER.warn("Could not convert spigot-mapped packet class name {} because no mappings were found in the jar", keyValue); + } + return null; + }).addAction(path("loggers"), TransformAction.rename("logging"));