Skip to content

Commit 31fb016

Browse files
committed
replace per-locale codecs with ops and use them in all stream codecs
1 parent b4682bf commit 31fb016

15 files changed

Lines changed: 275 additions & 92 deletions

build-data/paper.at

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket y
3131
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket yRot
3232
public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket z
3333
public net.minecraft.network.syncher.SynchedEntityData getItem(Lnet/minecraft/network/syncher/EntityDataAccessor;)Lnet/minecraft/network/syncher/SynchedEntityData$DataItem;
34+
public net.minecraft.resources.RegistryOps <init>(Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/resources/RegistryOps$RegistryInfoLookup;)V
3435
public net.minecraft.resources.RegistryOps lookupProvider
3536
public net.minecraft.resources.RegistryOps$HolderLookupAdapter
3637
public net.minecraft.server.Main forceUpgrade(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lcom/mojang/datafixers/DataFixer;ZLjava/util/function/BooleanSupplier;Lnet/minecraft/core/RegistryAccess;Z)V
Lines changed: 10 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,29 @@
11
--- a/net/minecraft/network/chat/ComponentSerialization.java
22
+++ b/net/minecraft/network/chat/ComponentSerialization.java
3-
@@ -35,9 +_,31 @@
4-
5-
public class ComponentSerialization {
6-
public static final Codec<Component> CODEC = Codec.recursive("Component", ComponentSerialization::createCodec);
7-
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC);
8-
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = createTranslationAware(net.minecraft.nbt.NbtAccounter::defaultQuota); // Paper - adventure
9-
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> OPTIONAL_STREAM_CODEC = STREAM_CODEC.apply(ByteBufCodecs::optional);
10-
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(CODEC);
11-
+ // Paper start - adventure; use locale from bytebuf for translation
12-
+ public static final ThreadLocal<Boolean> DONT_RENDER_TRANSLATABLES = ThreadLocal.withInitial(() -> false);
13-
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = createTranslationAware(net.minecraft.nbt.NbtAccounter::unlimitedHeap);
14-
+ private static StreamCodec<RegistryFriendlyByteBuf, Component> createTranslationAware(final java.util.function.Supplier<net.minecraft.nbt.NbtAccounter> sizeTracker) {
15-
+ return new StreamCodec<>() {
16-
+ final StreamCodec<ByteBuf, net.minecraft.nbt.Tag> streamCodec = ByteBufCodecs.tagCodec(sizeTracker);
17-
+ @Override
18-
+ public Component decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) {
19-
+ net.minecraft.nbt.Tag tag = this.streamCodec.decode(registryFriendlyByteBuf);
20-
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
21-
+ return CODEC.parse(registryOps, tag).getOrThrow(error -> new io.netty.handler.codec.DecoderException("Failed to decode: " + error + " " + tag));
22-
+ }
23-
+
24-
+ @Override
25-
+ public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, Component object) {
26-
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
27-
+ net.minecraft.nbt.Tag tag = (DONT_RENDER_TRANSLATABLES.get() ? CODEC : ComponentSerialization.localizedCodec(registryFriendlyByteBuf.adventure$locale))
28-
+ .encodeStart(registryOps, object).getOrThrow(error -> new io.netty.handler.codec.EncoderException("Failed to encode: " + error + " " + object));
29-
+ this.streamCodec.encode(registryFriendlyByteBuf, tag);
30-
+ }
31-
+ };
32-
+ }
33-
+ // Paper end - adventure; use locale from bytebuf for translation
3+
@@ -41,7 +_,7 @@
344
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply(
355
ByteBufCodecs::optional
366
);
37-
@@ -93,7 +_,25 @@
38-
return ExtraCodecs.orCompressed(contentsCodec, discriminatorCodec);
39-
}
7+
- public static final StreamCodec<ByteBuf, Component> TRUSTED_CONTEXT_FREE_STREAM_CODEC = ByteBufCodecs.fromCodecTrusted(CODEC);
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, Component> TRUSTED_CONTEXT_FREE_STREAM_CODEC = ByteBufCodecs.fromCodecTrusted(CODEC); // Paper - server-side translations everywhere
409

41-
+ // Paper start - adventure; create separate codec for each locale
42-
+ private static final java.util.Map<java.util.Locale, Codec<Component>> LOCALIZED_CODECS = new java.util.concurrent.ConcurrentHashMap<>();
43-
+
44-
+ public static Codec<Component> localizedCodec(final java.util.@org.checkerframework.checker.nullness.qual.Nullable Locale locale) {
45-
+ if (locale == null) {
46-
+ return CODEC;
47-
+ }
48-
+ return LOCALIZED_CODECS.computeIfAbsent(locale,
49-
+ loc -> Codec.recursive("Component", selfCodec -> createCodec(selfCodec, loc)));
50-
+ }
51-
+ // Paper end - adventure; create separate codec for each locale
52-
+
53-
private static Codec<Component> createCodec(final Codec<Component> topSerializer) {
54-
+ // Paper start - adventure; create separate codec for each locale
55-
+ return createCodec(topSerializer, null);
56-
+ }
57-
+
58-
+ private static Codec<Component> createCodec(Codec<Component> topSerializer, java.util.@org.jspecify.annotations.Nullable Locale locale) {
59-
+ // Paper end - adventure; create separate codec for each locale
60-
ExtraCodecs.LateBoundIdMapper<String, MapCodec<? extends ComponentContents>> contentTypes = new ExtraCodecs.LateBoundIdMapper<>();
61-
bootstrap(contentTypes);
62-
MapCodec<ComponentContents> compressedContentsCodec = createLegacyComponentMatcher(contentTypes, ComponentContents::codec, "type");
63-
@@ -105,6 +_,34 @@
10+
public static Codec<Component> flatRestrictedCodec(final int maxFlatSize) {
11+
return new Codec<Component>() {
12+
@@ -105,6 +_,35 @@
6413
)
6514
.apply(i, MutableComponent::new)
6615
);
67-
+ // Paper start - adventure; create separate codec for each locale
16+
+ // Paper start - adventure
6817
+ final Codec<Component> origCodec = fullCodec;
6918
+ fullCodec = new Codec<>() {
7019
+ @Override
71-
+ public <T> DataResult<com.mojang.datafixers.util.Pair<Component, T>> decode(final DynamicOps<T> ops, final T input) {
20+
+ public <T> DataResult<Pair<Component, T>> decode(final DynamicOps<T> ops, final T input) {
7221
+ return origCodec.decode(ops, input);
7322
+ }
7423
+
7524
+ @Override
7625
+ public <T> DataResult<T> encode(final Component input, final DynamicOps<T> ops, final T prefix) {
26+
+ final java.util.Locale locale = ops instanceof io.papermc.paper.util.LocaleAwareOps localeAwareOps ? localeAwareOps.locale() : null;
7727
+ final net.kyori.adventure.text.Component adventureComponent;
7828
+ if (input instanceof io.papermc.paper.adventure.AdventureComponent adv) {
7929
+ adventureComponent = adv.adventure$component();
@@ -91,7 +41,7 @@
9141
+ return origCodec.toString() + "[AdventureComponentAware]";
9242
+ }
9343
+ };
94-
+ // Paper end - adventure; create separate codec for each locale
44+
+ // Paper end - adventure
9545
return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(topSerializer.listOf())), fullCodec)
9646
.xmap(
9747
specialOrComponent -> specialOrComponent.map(

paper-server/patches/sources/net/minecraft/network/codec/ByteBufCodecs.java.patch

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,66 @@
11
--- a/net/minecraft/network/codec/ByteBufCodecs.java
22
+++ b/net/minecraft/network/codec/ByteBufCodecs.java
3-
@@ -418,6 +_,48 @@
3+
@@ -363,15 +_,15 @@
4+
}, compoundTag -> (Tag)compoundTag);
5+
}
6+
7+
- static <T> StreamCodec<ByteBuf, T> fromCodecTrusted(final Codec<T> codec) {
8+
+ static <T> StreamCodec<FriendlyByteBuf, T> fromCodecTrusted(final Codec<T> codec) { // Paper - server-side translations everywhere
9+
return fromCodec(codec, NbtAccounter::unlimitedHeap);
10+
}
11+
12+
- static <T> StreamCodec<ByteBuf, T> fromCodec(final Codec<T> codec) {
13+
+ static <T> StreamCodec<FriendlyByteBuf, T> fromCodec(final Codec<T> codec) { // Paper - server-side translations everywhere
14+
return fromCodec(codec, NbtAccounter::defaultQuota);
15+
}
16+
17+
- static <T, B extends ByteBuf, V> StreamCodec.CodecOperation<B, T, V> fromCodec(final DynamicOps<T> ops, final Codec<V> codec) {
18+
+ static <T, B extends FriendlyByteBuf, V> StreamCodec.CodecOperation<B, T, V> fromCodec(final DynamicOps<T> ops, final Codec<V> codec) { // Paper - server-side translations everywhere
19+
return original -> new StreamCodec<B, V>() {
20+
@Override
21+
public V decode(final B input) {
22+
@@ -381,14 +_,27 @@
23+
24+
@Override
25+
public void encode(final B output, final V value) {
26+
- T payload = (T)codec.encodeStart(ops, value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + value));
27+
+ T payload = (T)codec.encodeStart(new io.papermc.paper.util.LocaleAwareOps.Delegating<>(ops, output.adventure$locale), value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + value)); // Paper - server-side translations everywhere
28+
original.encode(output, payload);
29+
}
430
};
531
}
632

33+
- static <T> StreamCodec<ByteBuf, T> fromCodec(final Codec<T> codec, final Supplier<NbtAccounter> accounter) {
34+
- return tagCodec(accounter).apply(fromCodec(NbtOps.INSTANCE, codec));
35+
+ static <T> StreamCodec<FriendlyByteBuf, T> fromCodec(final Codec<T> codec, final Supplier<NbtAccounter> accounter) {
36+
+ final StreamCodec<ByteBuf, Tag> tagCodec = tagCodec(accounter);
37+
+ return new StreamCodec<FriendlyByteBuf, T>() {
38+
+ @Override
39+
+ public T decode(final FriendlyByteBuf input) {
40+
+ Tag tag = tagCodec.decode(input);
41+
+ return codec.parse(NbtOps.INSTANCE, tag).getOrThrow(msg -> new DecoderException("Failed to decode: " + msg + " " + tag));
42+
+ }
43+
+
44+
+ @Override
45+
+ public void encode(final FriendlyByteBuf output, final T value) {
46+
+ Tag tag = codec.encodeStart(new io.papermc.paper.util.LocaleAwareOps.Delegating<>(NbtOps.INSTANCE, output.adventure$locale), value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + value));
47+
+ tagCodec.encode(output, tag);
48+
+ }
49+
+ };
50+
}
51+
52+
static <T> StreamCodec<RegistryFriendlyByteBuf, T> fromCodecWithRegistriesTrusted(final Codec<T> codec) {
53+
@@ -412,11 +_,53 @@
54+
@Override
55+
public void encode(final RegistryFriendlyByteBuf output, final T value) {
56+
RegistryOps<Tag> ops = output.registryAccess().createSerializationContext(NbtOps.INSTANCE);
57+
- Tag tag = codec.encodeStart(ops, value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + value));
58+
+ Tag tag = codec.encodeStart(new io.papermc.paper.util.LocaleAwareOps.Registry<>(NbtOps.INSTANCE, output.registryAccess(), output.adventure$locale), value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + value)); // Paper - server-side translations everywhere
59+
tagCodec.encode(output, tag);
60+
}
61+
};
62+
}
63+
+
764
+ // Paper start - Track codec depth
865
+ static <B extends FriendlyByteBuf, V> StreamCodec<B, V> trackDepth(final StreamCodec<B, V> codec) {
966
+ return new StreamCodec<>() {
@@ -45,7 +102,31 @@
45102
+ };
46103
+ }
47104
+ // Paper end - Track codec depth
48-
+
105+
49106
static <B extends ByteBuf, V> StreamCodec<B, Optional<V>> optional(final StreamCodec<? super B, V> original) {
50107
return new StreamCodec<B, Optional<V>>() {
108+
@@ -717,12 +_,12 @@
109+
};
110+
}
111+
112+
- static StreamCodec<ByteBuf, JsonElement> lenientJson(final int maxStringLength) {
113+
- return new StreamCodec<ByteBuf, JsonElement>() {
114+
+ static StreamCodec<FriendlyByteBuf, JsonElement> lenientJson(final int maxStringLength) {
115+
+ return new StreamCodec<FriendlyByteBuf, JsonElement>() {
116+
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
117+
118+
@Override
119+
- public JsonElement decode(final ByteBuf input) {
120+
+ public JsonElement decode(final FriendlyByteBuf input) {
121+
String payload = Utf8String.read(input, maxStringLength);
122+
123+
try {
124+
@@ -733,7 +_,7 @@
125+
}
126+
51127
@Override
128+
- public void encode(final ByteBuf output, final JsonElement value) {
129+
+ public void encode(final FriendlyByteBuf output, final JsonElement value) {
130+
String payload = GSON.toJson(value);
131+
Utf8String.write(output, payload, maxStringLength);
132+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- a/net/minecraft/network/protocol/common/ClientboundDisconnectPacket.java
2+
+++ b/net/minecraft/network/protocol/common/ClientboundDisconnectPacket.java
3+
@@ -8,7 +_,7 @@
4+
import net.minecraft.network.protocol.PacketType;
5+
6+
public record ClientboundDisconnectPacket(Component reason) implements Packet<ClientCommonPacketListener> {
7+
- public static final StreamCodec<ByteBuf, ClientboundDisconnectPacket> STREAM_CODEC = ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundDisconnectPacket> STREAM_CODEC = ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC // Paper - server-side translation everywhere
9+
.map(ClientboundDisconnectPacket::new, ClientboundDisconnectPacket::reason);
10+
11+
@Override
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- a/net/minecraft/network/protocol/common/ClientboundResourcePackPushPacket.java
2+
+++ b/net/minecraft/network/protocol/common/ClientboundResourcePackPushPacket.java
3+
@@ -14,7 +_,7 @@
4+
public record ClientboundResourcePackPushPacket(UUID id, String url, String hash, boolean required, Optional<Component> prompt)
5+
implements Packet<ClientCommonPacketListener> {
6+
public static final int MAX_HASH_LENGTH = 40;
7+
- public static final StreamCodec<ByteBuf, ClientboundResourcePackPushPacket> STREAM_CODEC = StreamCodec.composite(
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundResourcePackPushPacket> STREAM_CODEC = StreamCodec.composite( // Paper - server-side translation everywhere
9+
UUIDUtil.STREAM_CODEC,
10+
ClientboundResourcePackPushPacket::id,
11+
ByteBufCodecs.STRING_UTF8,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- a/net/minecraft/network/protocol/common/ClientboundServerLinksPacket.java
2+
+++ b/net/minecraft/network/protocol/common/ClientboundServerLinksPacket.java
3+
@@ -8,7 +_,7 @@
4+
import net.minecraft.server.ServerLinks;
5+
6+
public record ClientboundServerLinksPacket(List<ServerLinks.UntrustedEntry> links) implements Packet<ClientCommonPacketListener> {
7+
- public static final StreamCodec<ByteBuf, ClientboundServerLinksPacket> STREAM_CODEC = StreamCodec.composite(
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundServerLinksPacket> STREAM_CODEC = StreamCodec.composite(
9+
ServerLinks.UNTRUSTED_LINKS_STREAM_CODEC, ClientboundServerLinksPacket::links, ClientboundServerLinksPacket::new
10+
);
11+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- a/net/minecraft/network/protocol/common/ClientboundShowDialogPacket.java
2+
+++ b/net/minecraft/network/protocol/common/ClientboundShowDialogPacket.java
3+
@@ -12,7 +_,7 @@
4+
public static final StreamCodec<RegistryFriendlyByteBuf, ClientboundShowDialogPacket> STREAM_CODEC = StreamCodec.composite(
5+
Dialog.STREAM_CODEC, ClientboundShowDialogPacket::dialog, ClientboundShowDialogPacket::new
6+
);
7+
- public static final StreamCodec<ByteBuf, ClientboundShowDialogPacket> CONTEXT_FREE_STREAM_CODEC = StreamCodec.composite(
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundShowDialogPacket> CONTEXT_FREE_STREAM_CODEC = StreamCodec.composite(
9+
Dialog.CONTEXT_FREE_STREAM_CODEC.map(Holder::direct, Holder::value), ClientboundShowDialogPacket::dialog, ClientboundShowDialogPacket::new
10+
);
11+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--- a/net/minecraft/network/protocol/game/ClientboundServerDataPacket.java
2+
+++ b/net/minecraft/network/protocol/game/ClientboundServerDataPacket.java
3+
@@ -10,7 +_,7 @@
4+
import net.minecraft.network.protocol.PacketType;
5+
6+
public record ClientboundServerDataPacket(Component motd, Optional<byte[]> iconBytes) implements Packet<ClientGamePacketListener> {
7+
- public static final StreamCodec<ByteBuf, ClientboundServerDataPacket> STREAM_CODEC = StreamCodec.composite(
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundServerDataPacket> STREAM_CODEC = StreamCodec.composite( // Paper - server-side translation everywhere
9+
ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC,
10+
ClientboundServerDataPacket::motd,
11+
ByteBufCodecs.BYTE_ARRAY.apply(ByteBufCodecs::optional),
Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,11 @@
11
--- a/net/minecraft/network/protocol/login/ClientboundLoginDisconnectPacket.java
22
+++ b/net/minecraft/network/protocol/login/ClientboundLoginDisconnectPacket.java
3-
@@ -14,8 +_,25 @@
3+
@@ -14,7 +_,7 @@
44

55
public record ClientboundLoginDisconnectPacket(Component reason) implements Packet<ClientLoginPacketListener> {
66
private static final RegistryOps<JsonElement> OPS = RegistryAccess.EMPTY.createSerializationContext(JsonOps.INSTANCE);
77
- public static final StreamCodec<ByteBuf, ClientboundLoginDisconnectPacket> STREAM_CODEC = StreamCodec.composite(
8-
- ByteBufCodecs.lenientJson(262144).apply(ByteBufCodecs.fromCodec(OPS, ComponentSerialization.CODEC)),
9-
+ // Paper start - localized codec
10-
+ // In the login phase, buffer.adventure$locale field is most likely null, but plugins may use internals to set it via the channel attribute
11-
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundLoginDisconnectPacket> STREAM_CODEC = StreamCodec.composite(
12-
+ new StreamCodec<>() {
13-
+
14-
+ private static final net.minecraft.network.codec.StreamCodec<ByteBuf, JsonElement> LENIENT_JSON = ByteBufCodecs.lenientJson(net.minecraft.network.FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH);
15-
+ @Override
16-
+ public Component decode(final net.minecraft.network.FriendlyByteBuf buffer) {
17-
+ java.util.Locale bufLocale = buffer.adventure$locale;
18-
+ return LENIENT_JSON.apply(ByteBufCodecs.fromCodec(OPS, ComponentSerialization.localizedCodec(bufLocale == null ? java.util.Locale.US : bufLocale))).decode(buffer);
19-
+ }
20-
+
21-
+ @Override
22-
+ public void encode(final net.minecraft.network.FriendlyByteBuf buffer, final Component value) {
23-
+ java.util.Locale bufLocale = buffer.adventure$locale;
24-
+ LENIENT_JSON.apply(ByteBufCodecs.fromCodec(OPS, ComponentSerialization.localizedCodec(bufLocale == null ? java.util.Locale.US : bufLocale))).encode(buffer, value);
25-
+ }
26-
+ },
27-
+ // Paper end - localized codec
8+
+ public static final StreamCodec<net.minecraft.network.FriendlyByteBuf, ClientboundLoginDisconnectPacket> STREAM_CODEC = StreamCodec.composite( // Paper - server-side translation everywhere
9+
ByteBufCodecs.lenientJson(262144).apply(ByteBufCodecs.fromCodec(OPS, ComponentSerialization.CODEC)),
2810
ClientboundLoginDisconnectPacket::reason,
2911
ClientboundLoginDisconnectPacket::new
30-
);

0 commit comments

Comments
 (0)