Skip to content

Commit 6c3f172

Browse files
[fix] 1.21.9 NetworkError when viewing tags (#1979)
* fix: network protocol error when viewing tags feat: re-implement s2c tag packet using codecs * remove unused methods from `TagData` * implement `C2STagDataPacket` * feat: replace manual de/serialization with `C2STagDataPacket` * style: reformat file * chore: remove unused import directives * rename `Uuids` -> `UUIDUtils` * chore: remove unused `writeResourceLocation` * chore: remove unused `import` directive * style: try to restore whitespace to previous state * chore: `./gradlew licenseFormat` * style: apply 'keep whitespace on empty lines' * Prepare for publication --------- Co-authored-by: shedaniel <daniel@shedaniel.me>
1 parent bcef0b6 commit 6c3f172

6 files changed

Lines changed: 131 additions & 80 deletions

File tree

.github/workflows/curseforge.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ on:
2424
- 18.x-1.21.4
2525
- 19.x-1.21.5
2626
- 20.x-1.21.6
27+
- 1.21.9
2728

2829
jobs:
2930
build:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* This file is licensed under the MIT License, part of Roughly Enough Items.
3+
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all
13+
* copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
package me.shedaniel.rei.api.common.util;
25+
26+
import io.netty.buffer.ByteBuf;
27+
import net.minecraft.network.codec.StreamCodec;
28+
import org.jetbrains.annotations.NotNull;
29+
30+
import java.util.UUID;
31+
32+
public class UUIDUtils {
33+
public static final StreamCodec<ByteBuf, UUID> STREAM_CODEC = new StreamCodec<ByteBuf, UUID>() {
34+
@Override
35+
public void encode(ByteBuf buf, UUID uuid) {
36+
buf.writeLong(uuid.getMostSignificantBits());
37+
buf.writeLong(uuid.getLeastSignificantBits());
38+
}
39+
40+
@Override
41+
public @NotNull UUID decode(ByteBuf buf) {
42+
var mostSignificantBits = buf.readLong();
43+
var leastSignificantBits = buf.readLong();
44+
return new UUID(mostSignificantBits, leastSignificantBits);
45+
}
46+
};
47+
}

default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public void registerCategories(CategoryRegistry registry) {
249249
registry.configure(TAG, config -> config.setQuickCraftingEnabledByDefault(false));
250250

251251
registry.registerVisibilityPredicate(category -> {
252-
if (category instanceof DefaultTagCategory && Minecraft.getInstance().getSingleplayerServer() == null && !NetworkManager.canServerReceive(TagNodes.REQUEST_TAGS_PACKET_C2S)) {
252+
if (category instanceof DefaultTagCategory && Minecraft.getInstance().getSingleplayerServer() == null && !NetworkManager.canServerReceive(TagNodes.REQUEST_TAGS_C2S_PACKET_ID)) {
253253
return EventResult.interruptFalse();
254254
}
255255

default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/tag/TagNodes.java

Lines changed: 79 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,32 @@
2323

2424
package me.shedaniel.rei.plugin.common.displays.tag;
2525

26+
import com.google.common.collect.Maps;
2627
import com.mojang.serialization.DataResult;
2728
import dev.architectury.event.events.client.ClientLifecycleEvent;
28-
import dev.architectury.impl.NetworkAggregator;
2929
import dev.architectury.networking.NetworkManager;
30-
import dev.architectury.networking.transformers.SplitPacketTransformer;
31-
import dev.architectury.platform.Platform;
3230
import dev.architectury.utils.Env;
3331
import dev.architectury.utils.EnvExecutor;
34-
import io.netty.buffer.Unpooled;
3532
import it.unimi.dsi.fastutil.ints.IntArrayList;
3633
import it.unimi.dsi.fastutil.ints.IntList;
37-
import me.shedaniel.rei.api.common.display.basic.BasicDisplay;
34+
import me.shedaniel.rei.api.common.util.UUIDUtils;
3835
import net.fabricmc.api.EnvType;
3936
import net.fabricmc.api.Environment;
4037
import net.minecraft.client.Minecraft;
4138
import net.minecraft.core.Holder;
4239
import net.minecraft.core.HolderSet;
4340
import net.minecraft.core.Registry;
4441
import net.minecraft.core.registries.BuiltInRegistries;
45-
import net.minecraft.network.FriendlyByteBuf;
4642
import net.minecraft.network.RegistryFriendlyByteBuf;
43+
import net.minecraft.network.codec.ByteBufCodecs;
44+
import net.minecraft.network.codec.StreamCodec;
45+
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
4746
import net.minecraft.resources.ResourceKey;
4847
import net.minecraft.resources.ResourceLocation;
4948
import net.minecraft.server.level.ServerPlayer;
5049
import net.minecraft.tags.TagKey;
5150
import org.jetbrains.annotations.ApiStatus;
51+
import org.jetbrains.annotations.NotNull;
5252

5353
import java.util.*;
5454
import java.util.concurrent.ConcurrentHashMap;
@@ -57,8 +57,11 @@
5757

5858
@ApiStatus.Internal
5959
public class TagNodes {
60-
public static final ResourceLocation REQUEST_TAGS_PACKET_C2S = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "request_tags_c2s");
61-
public static final ResourceLocation REQUEST_TAGS_PACKET_S2C = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "request_tags_s2c");
60+
public static final ResourceLocation REQUEST_TAGS_C2S_PACKET_ID = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "request_tags_c2s");
61+
public static final ResourceLocation REQUEST_TAGS_S2C_PACKET_ID = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "request_tags_s2c");
62+
63+
public static final CustomPacketPayload.Type<C2STagDataPacket> REQUEST_TAGS_C2S_PACKET_TYPE = new CustomPacketPayload.Type<>(REQUEST_TAGS_C2S_PACKET_ID);
64+
public static final CustomPacketPayload.Type<S2CTagDataPacket> REQUEST_TAGS_S2C_PACKET_TYPE = new CustomPacketPayload.Type<>(REQUEST_TAGS_S2C_PACKET_ID);
6265

6366
public static final Map<String, ResourceKey<? extends Registry<?>>> TAG_DIR_MAP = new HashMap<>();
6467
public static final ThreadLocal<String> CURRENT_TAG_DIR = new ThreadLocal<>();
@@ -88,78 +91,72 @@ public record RawTagData(List<ResourceLocation> otherElements, List<ResourceLoca
8891
}
8992

9093
public record TagData(IntList otherElements, List<ResourceLocation> otherTags) {
91-
private static TagData fromNetwork(FriendlyByteBuf buf) {
92-
int count = buf.readVarInt();
93-
IntList otherElements = new IntArrayList(count + 1);
94-
for (int i = 0; i < count; i++) {
95-
otherElements.add(buf.readVarInt());
96-
}
97-
count = buf.readVarInt();
98-
List<ResourceLocation> otherTags = new ArrayList<>(count + 1);
99-
for (int i = 0; i < count; i++) {
100-
otherTags.add(buf.readResourceLocation());
101-
}
102-
return new TagData(otherElements, otherTags);
103-
}
94+
public static final StreamCodec<RegistryFriendlyByteBuf, TagData> STREAM_CODEC = StreamCodec.composite(
95+
ByteBufCodecs.collection(IntArrayList::new, ByteBufCodecs.VAR_INT), TagData::otherElements,
96+
ByteBufCodecs.collection(ArrayList::new, ResourceLocation.STREAM_CODEC), TagData::otherTags,
97+
TagData::new
98+
);
99+
}
100+
101+
public record C2STagDataPacket(UUID uuid, ResourceLocation registryName) implements CustomPacketPayload {
102+
public static final StreamCodec<RegistryFriendlyByteBuf, C2STagDataPacket> STREAM_CODEC = StreamCodec.composite(
103+
UUIDUtils.STREAM_CODEC, C2STagDataPacket::uuid,
104+
ResourceLocation.STREAM_CODEC, C2STagDataPacket::registryName,
105+
C2STagDataPacket::new
106+
);
104107

105-
private void toNetwork(FriendlyByteBuf buf) {
106-
buf.writeVarInt(otherElements.size());
107-
for (int integer : otherElements) {
108-
buf.writeVarInt(integer);
109-
}
110-
buf.writeVarInt(otherTags.size());
111-
for (ResourceLocation tag : otherTags) {
112-
writeResourceLocation(buf, tag);
113-
}
108+
@Override
109+
public @NotNull Type<? extends CustomPacketPayload> type() {
110+
return REQUEST_TAGS_C2S_PACKET_TYPE;
114111
}
115112
}
116113

117-
private static void writeResourceLocation(FriendlyByteBuf buf, ResourceLocation location) {
118-
if (location.getNamespace().equals("minecraft")) {
119-
buf.writeUtf(location.getPath());
120-
} else {
121-
buf.writeUtf(location.toString());
114+
public record S2CTagDataPacket(UUID uuid, Map<ResourceLocation, TagData> map) implements CustomPacketPayload {
115+
public static final StreamCodec<RegistryFriendlyByteBuf, S2CTagDataPacket> STREAM_CODEC = StreamCodec.composite(
116+
UUIDUtils.STREAM_CODEC, S2CTagDataPacket::uuid,
117+
ByteBufCodecs.map(
118+
Maps::newHashMapWithExpectedSize,
119+
ResourceLocation.STREAM_CODEC,
120+
TagData.STREAM_CODEC
121+
), S2CTagDataPacket::map,
122+
S2CTagDataPacket::new
123+
);
124+
125+
@Override
126+
public @NotNull Type<? extends CustomPacketPayload> type() {
127+
return REQUEST_TAGS_S2C_PACKET_TYPE;
122128
}
123129
}
124130

125131
public static void init() {
126132
EnvExecutor.runInEnv(Env.CLIENT, () -> Client::init);
127-
128-
// Fix for TagNodes not being loaded on the server
129-
// A bit hacky as it uses Architectury's internal API, but this class needs rewriting to use codecs due to the deprecation of the old serialization system anyway.
130-
if(Platform.getEnvironment() != Env.CLIENT) {
131-
NetworkAggregator.registerS2CType(REQUEST_TAGS_PACKET_S2C, Collections.singletonList(new SplitPacketTransformer()));
132-
}
133+
EnvExecutor.runInEnv(Env.SERVER, () -> Server::init);
133134

134-
NetworkManager.registerReceiver(NetworkManager.c2s(), REQUEST_TAGS_PACKET_C2S, Collections.singletonList(new SplitPacketTransformer()), (buf, context) -> {
135-
UUID uuid = buf.readUUID();
136-
ResourceKey<? extends Registry<?>> resourceKey = ResourceKey.createRegistryKey(buf.readResourceLocation());
137-
RegistryFriendlyByteBuf newBuf = new RegistryFriendlyByteBuf(Unpooled.buffer(), context.registryAccess());
138-
newBuf.writeUUID(uuid);
139-
Map<ResourceLocation, TagData> dataMap = TAG_DATA_MAP.getOrDefault(resourceKey, Collections.emptyMap());
140-
newBuf.writeInt(dataMap.size());
141-
for (Map.Entry<ResourceLocation, TagData> entry : dataMap.entrySet()) {
142-
writeResourceLocation(newBuf, entry.getKey());
143-
entry.getValue().toNetwork(newBuf);
144-
}
145-
NetworkManager.sendToPlayer((ServerPlayer) context.getPlayer(), REQUEST_TAGS_PACKET_S2C, newBuf);
146-
});
135+
NetworkManager.registerReceiver(
136+
NetworkManager.c2s(),
137+
REQUEST_TAGS_C2S_PACKET_TYPE,
138+
C2STagDataPacket.STREAM_CODEC,
139+
(C2STagDataPacket payload, NetworkManager.PacketContext context) -> {
140+
ResourceKey<? extends Registry<?>> registryKey = ResourceKey.createRegistryKey(payload.registryName);
141+
Map<ResourceLocation, TagData> dataMap = TAG_DATA_MAP.getOrDefault(registryKey, Collections.emptyMap());
142+
var packet = new S2CTagDataPacket(payload.uuid, dataMap);
143+
NetworkManager.sendToPlayer((ServerPlayer) context.getPlayer(), packet);
144+
}
145+
);
147146
}
148147

149148
@Environment(EnvType.CLIENT)
150149
public static void requestTagData(ResourceKey<? extends Registry<?>> resourceKey, Consumer<DataResult<Map<ResourceLocation, TagData>>> callback) {
151150
if (Minecraft.getInstance().getSingleplayerServer() != null) {
152151
callback.accept(DataResult.success(TAG_DATA_MAP.get(resourceKey)));
153-
} else if (!NetworkManager.canServerReceive(REQUEST_TAGS_PACKET_C2S)) {
152+
} else if (!NetworkManager.canServerReceive(REQUEST_TAGS_C2S_PACKET_ID)) {
154153
callback.accept(DataResult.error(() -> "Cannot request tags from server"));
155154
} else if (requestedTags.containsKey(resourceKey)) {
156155
requestedTags.get(resourceKey).accept(callback);
157156
callback.accept(DataResult.success(TAG_DATA_MAP.getOrDefault(resourceKey, Collections.emptyMap())));
158157
} else {
159-
RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), BasicDisplay.registryAccess());
160158
UUID uuid = UUID.randomUUID();
161-
buf.writeUUID(uuid);
162-
buf.writeResourceLocation(resourceKey.location());
159+
var packet = new C2STagDataPacket(uuid, resourceKey.location());
163160
Client.nextUUID = uuid;
164161
Client.nextResourceKey = resourceKey;
165162
List<Consumer<DataResult<Map<ResourceLocation, TagData>>>> callbacks = new CopyOnWriteArrayList<>();
@@ -171,7 +168,13 @@ public static void requestTagData(ResourceKey<? extends Registry<?>> resourceKey
171168
}
172169
};
173170
requestedTags.put(resourceKey, callbacks::add);
174-
NetworkManager.sendToServer(REQUEST_TAGS_PACKET_C2S, buf);
171+
NetworkManager.sendToServer(packet);
172+
}
173+
}
174+
175+
private static class Server {
176+
private static void init() {
177+
NetworkManager.registerS2CPayloadType(REQUEST_TAGS_S2C_PACKET_TYPE, S2CTagDataPacket.STREAM_CODEC);
175178
}
176179
}
177180

@@ -184,23 +187,22 @@ private static void init() {
184187
ClientLifecycleEvent.CLIENT_LEVEL_LOAD.register(world -> {
185188
requestedTags.clear();
186189
});
187-
NetworkManager.registerReceiver(NetworkManager.s2c(), REQUEST_TAGS_PACKET_S2C, (buf, context) -> {
188-
UUID uuid = buf.readUUID();
189-
if (nextUUID.equals(uuid)) {
190-
Map<ResourceLocation, TagData> map = new HashMap<>();
191-
int count = buf.readInt();
192-
for (int i = 0; i < count; i++) {
193-
map.put(buf.readResourceLocation(), TagData.fromNetwork(buf));
190+
191+
NetworkManager.registerReceiver(
192+
NetworkManager.s2c(),
193+
REQUEST_TAGS_S2C_PACKET_TYPE,
194+
S2CTagDataPacket.STREAM_CODEC,
195+
(S2CTagDataPacket payload, NetworkManager.PacketContext context) -> {
196+
if (!nextUUID.equals(payload.uuid)) return;
197+
198+
TAG_DATA_MAP.put(nextResourceKey, payload.map);
199+
nextCallback.accept(DataResult.success(payload.map));
200+
201+
nextUUID = null;
202+
nextResourceKey = null;
203+
nextCallback = null;
194204
}
195-
196-
TAG_DATA_MAP.put(nextResourceKey, map);
197-
nextCallback.accept(DataResult.success(map));
198-
199-
nextUUID = null;
200-
nextResourceKey = null;
201-
nextCallback = null;
202-
}
203-
});
205+
);
204206
}
205207
}
206208

@@ -232,7 +234,8 @@ private static <T> Optional<DataResult<TagNode<T>>> resolveTag(TagKey<T> tagKey,
232234
Optional<DataResult<TagNode<T>>> resultOptional = resolveTag(childTagKey, registry, tagDataMap);
233235
if (resultOptional.isPresent()) {
234236
DataResult<TagNode<T>> result = resultOptional.get();
235-
if (result.error().isPresent()) return Optional.of(DataResult.error(() -> result.error().get().message()));
237+
if (result.error().isPresent())
238+
return Optional.of(DataResult.error(() -> result.error().get().message()));
236239
self.addChild(result.result().get());
237240
}
238241
}

fabric/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ unifiedPublishing {
121121
project {
122122
displayName = "[Fabric $rootProject.supported_version] v$project.version"
123123
releaseType = rootProject.unstable == "false" ? "release" : "alpha"
124-
gameVersions = ["1.21.9"]
124+
gameVersions = ["1.21.9", "1.21.10"]
125125
gameLoaders = ["fabric"]
126126
changelog = rootProject.releaseChangelog
127127

neoforge/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ unifiedPublishing {
168168
project {
169169
displayName = "[NeoForge $rootProject.supported_version] v$project.version"
170170
releaseType = "beta"
171-
gameVersions = ["1.21.9"]
171+
gameVersions = ["1.21.9", "1.21.10"]
172172
gameLoaders = ["neoforge"]
173173
changelog = rootProject.releaseChangelog
174174

@@ -208,7 +208,7 @@ unifiedPublishing {
208208
project {
209209
displayName = "[NeoForge $rootProject.supported_version] v$project.version"
210210
releaseType = "release"
211-
gameVersions = ["1.21.9"]
211+
gameVersions = ["1.21.9", "1.21.10"]
212212
gameLoaders = ["neoforge"]
213213
changelog = rootProject.releaseChangelog
214214

0 commit comments

Comments
 (0)