Skip to content

Commit 3c8a699

Browse files
committed
fix packet type collection in new versions
1 parent d7b399f commit 3c8a699

5 files changed

Lines changed: 127 additions & 107 deletions

File tree

src/main/java/dev/derklaro/protocolgenerator/protocol/McClassNames.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ final class McClassNames {
2929
public static final String BOOTSTRAP = "net.minecraft.server.Bootstrap";
3030
public static final String SHARED_CONSTANTS = "net.minecraft.SharedConstants";
3131

32-
public static final String REGISTRY_ACCESS = "net.minecraft.core.RegistryAccess";
33-
public static final String REGISTRY_FRIENDLY_BB = "net.minecraft.network.RegistryFriendlyByteBuf";
32+
public static final String PACKET_VISITOR = "net.minecraft.network.ProtocolInfo$Details$PacketVisitor";
3433

3534
private McClassNames() {
3635
throw new UnsupportedOperationException();

src/main/java/dev/derklaro/protocolgenerator/protocol/McProtocolsDecoder.java

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import dev.derklaro.reflexion.Reflexion;
2828
import dev.derklaro.reflexion.matcher.FieldMatcher;
29+
import dev.derklaro.reflexion.matcher.MethodMatcher;
2930
import java.io.IOException;
3031
import java.lang.reflect.Modifier;
3132
import java.util.EnumMap;
@@ -34,10 +35,12 @@
3435
import java.util.Locale;
3536
import java.util.Map;
3637
import java.util.Objects;
38+
import java.util.function.Predicate;
3739
import javax.annotation.Nullable;
3840
import lombok.NonNull;
3941
import org.objectweb.asm.ClassReader;
4042
import org.objectweb.asm.Handle;
43+
import org.objectweb.asm.Type;
4144
import org.objectweb.asm.tree.AbstractInsnNode;
4245
import org.objectweb.asm.tree.ClassNode;
4346
import org.objectweb.asm.tree.FieldInsnNode;
@@ -47,13 +50,30 @@
4750

4851
final class McProtocolsDecoder {
4952

53+
// matcher for the UnboundProtocol constants in XXXProtocols classes
5054
private static final FieldMatcher PROTOCOL_INFO_MATCHER = FieldMatcher.newMatcher()
5155
.hasModifier(Modifier.STATIC)
5256
.and(field -> {
53-
var name = field.getType().getCanonicalName();
54-
return name.endsWith(".ProtocolInfo") || name.endsWith(".ProtocolInfo.Unbound");
57+
var typeName = field.getType().getCanonicalName();
58+
return typeName.endsWith(".UnboundProtocol") || typeName.endsWith(".SimpleUnboundProtocol");
5559
});
5660

61+
// matcher to find the listPackets(PacketVisitor) method in ProtocolInfo$Details
62+
private static final MethodMatcher LIST_PACKETS_MATCHER = MethodMatcher.newMatcher()
63+
.parameterCount(1)
64+
.hasName("listPackets");
65+
66+
// matcher for InvokeDynamicInsnNode that constructs the info for a protocol
67+
private static final Predicate<InvokeDynamicInsnNode> PROTOCOL_LAMBDA_BUILDER_FILTER = node -> {
68+
var bsmArgs = node.bsmArgs;
69+
return bsmArgs != null
70+
&& bsmArgs.length == 3
71+
&& bsmArgs[2] instanceof Type type
72+
&& type.getArgumentCount() == 1
73+
&& type.getReturnType() == Type.VOID_TYPE
74+
&& type.getArgumentTypes()[0].getClassName().endsWith(".ProtocolInfoBuilder");
75+
};
76+
5777
private static final String STREAM_CODEC_DESC = "Lnet/minecraft/network/codec/StreamCodec;";
5878
private static final String PACKET_TYPE_DESC = "Lnet/minecraft/network/protocol/PacketType;";
5979

@@ -75,8 +95,16 @@ public McProtocolsDecoder(@NonNull Class<?> targetClass, @NonNull ClassLoader mc
7595
}
7696

7797
// there can be two calls: one for serverbound, one for clientbound
78-
var firstInvokeDynamic = this.findNodeOfType(InvokeDynamicInsnNode.class, clinit, null);
79-
var secondInvokeDynamic = this.findNodeOfType(InvokeDynamicInsnNode.class, clinit, firstInvokeDynamic);
98+
var firstInvokeDynamic = this.findNodeOfType(
99+
InvokeDynamicInsnNode.class,
100+
clinit,
101+
null,
102+
PROTOCOL_LAMBDA_BUILDER_FILTER);
103+
var secondInvokeDynamic = this.findNodeOfType(
104+
InvokeDynamicInsnNode.class,
105+
clinit,
106+
firstInvokeDynamic,
107+
PROTOCOL_LAMBDA_BUILDER_FILTER);
80108

81109
// resolve the protocol info (ProtocolInfoBuilder type -> Invocation Target for configuration)
82110
var firstProtocolInfo = this.resolveProtocolInvocationInfo(clinit, firstInvokeDynamic);
@@ -90,36 +118,34 @@ public McProtocolsDecoder(@NonNull Class<?> targetClass, @NonNull ClassLoader mc
90118
return result;
91119
}
92120

93-
public @NonNull Map<McPacketFlow, Map<Object, Integer>> resolvePacketIds() {
121+
public @NonNull Map<McPacketFlow, Map<Object, Integer>> resolvePacketIds(@NonNull Class<?> packetVisitorClass) {
94122
Map<McPacketFlow, Map<Object, Integer>> result = new EnumMap<>(McPacketFlow.class);
95123

96124
// resolve the packet ids from the constants in the given class
97125
var protocolInfoFields = Reflexion.on(this.targetClass).findFields(PROTOCOL_INFO_MATCHER);
98126
for (var protocolInfoField : protocolInfoFields) {
127+
// get the details from the protocol info
99128
var protocolInfo = protocolInfoField.getValue().get();
100-
if (McUnboundProtocolBinder.isUnbound(protocolInfo.getClass())) {
101-
// need to bind protocol info first
102-
var binder = new McUnboundProtocolBinder(protocolInfo, this.mcClassLoader);
103-
protocolInfo = binder.bind();
104-
}
105-
106-
// get codec and mc packet flow
107-
var codec = Reflexion.onBound(protocolInfo)
108-
.findMethod("codec")
129+
var details = Reflexion.onBound(protocolInfo)
130+
.findMethod("details")
109131
.flatMap(accessor -> accessor.invoke().asOptional())
110132
.orElseThrow();
111-
var flow = Reflexion.onBound(protocolInfo)
133+
134+
// resolve the packet list debug method & flow method
135+
var detailsReflexion = Reflexion.onBound(details);
136+
var listPacketsMethod = detailsReflexion.findMethod(LIST_PACKETS_MATCHER).orElseThrow();
137+
var flow = detailsReflexion
112138
.findMethod("flow")
113139
.flatMap(accessor -> accessor.invoke().asOptional())
114140
.orElseThrow();
115141

116-
// try to convert the packet flow
142+
// convert the packet flow, register the packet type to id mapping for the flow
117143
var convertedFlow = this.resolveFlowFromBuilderType(flow.toString());
118144
if (convertedFlow != null) {
119-
var typeToId = Reflexion.onBound(codec)
120-
.findField("toId")
121-
.flatMap(accessor -> accessor.<Map<Object, Integer>>getValue().asOptional())
122-
.orElseThrow();
145+
var typeToId = new HashMap<Object, Integer>();
146+
var packetVisitorImplGenerator = new PacketVisitorImplGenerator(packetVisitorClass, typeToId);
147+
var packetVisitorImpl = packetVisitorImplGenerator.generatePacketVisitor();
148+
var ignored = listPacketsMethod.invokeWithArgs(packetVisitorImpl).getOrThrow();
123149
result.put(convertedFlow, typeToId);
124150
}
125151
}
@@ -300,6 +326,15 @@ private void doMapTypes(
300326
@NonNull Class<T> type,
301327
@NonNull MethodNode methodNode,
302328
@Nullable AbstractInsnNode after
329+
) {
330+
return this.findNodeOfType(type, methodNode, after, ignored -> true);
331+
}
332+
333+
private @Nullable <T extends AbstractInsnNode> T findNodeOfType(
334+
@NonNull Class<T> type,
335+
@NonNull MethodNode methodNode,
336+
@Nullable AbstractInsnNode after,
337+
@NonNull Predicate<T> filter
303338
) {
304339
// either start from the top or from the next node of the insn we want to start after
305340
AbstractInsnNode start;
@@ -317,7 +352,10 @@ private void doMapTypes(
317352
}
318353

319354
if (current.getClass() == type) {
320-
return type.cast(current);
355+
var node = type.cast(current);
356+
if (filter.test(node)) {
357+
return node;
358+
}
321359
}
322360

323361
current = current.getNext();

src/main/java/dev/derklaro/protocolgenerator/protocol/McUnboundProtocolBinder.java

Lines changed: 0 additions & 83 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* This file is part of mc-protocol-generator, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) 2023 - 2024 Pasqual K. and contributors
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package dev.derklaro.protocolgenerator.protocol;
26+
27+
import java.lang.reflect.InvocationHandler;
28+
import java.lang.reflect.Proxy;
29+
import java.util.Map;
30+
import lombok.NonNull;
31+
32+
final class PacketVisitorImplGenerator {
33+
34+
private final Class<?> packetVisitorClass;
35+
private final Map<Object, Integer> targetPacketTypeToIdMap;
36+
37+
public PacketVisitorImplGenerator(
38+
@NonNull Class<?> packetVisitorClass,
39+
@NonNull Map<Object, Integer> targetPacketTypeToIdMap
40+
) {
41+
this.packetVisitorClass = packetVisitorClass;
42+
this.targetPacketTypeToIdMap = targetPacketTypeToIdMap;
43+
}
44+
45+
public @NonNull Object generatePacketVisitor() {
46+
InvocationHandler packetVisitorHandler = (ignored, method, args) -> {
47+
// accept(PacketType, int)
48+
if (method.getName().equals("accept")
49+
&& method.getParameterCount() == 2
50+
&& method.getReturnType() == void.class) {
51+
var packetType = args[0];
52+
var packetId = (Integer) args[1];
53+
this.targetPacketTypeToIdMap.put(packetType, packetId);
54+
return null;
55+
}
56+
57+
// unknown method
58+
throw new UnsupportedOperationException("Called unsupported method: " + method.getName() + " on proxy");
59+
};
60+
return Proxy.newProxyInstance(
61+
this.packetVisitorClass.getClassLoader(),
62+
new Class<?>[]{this.packetVisitorClass},
63+
packetVisitorHandler);
64+
}
65+
}

src/main/java/dev/derklaro/protocolgenerator/protocol/ProtocolInfoCollector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public ProtocolInfoCollector(@NonNull Path remappedJarPath, @NonNull ClassLoader
5252
// build a class loader which is aware of the remapped jar
5353
var remappedJarUrl = this.remappedJarPath.toUri().toURL();
5454
var loader = new URLClassLoader(new URL[]{remappedJarUrl}, this.libraryClassLoader);
55+
var packetVisitorClass = Class.forName(McClassNames.PACKET_VISITOR, true, loader);
5556

5657
// important first step: initialize minecraft registries
5758
var mcInitializer = new McInit(loader);
@@ -66,7 +67,7 @@ public ProtocolInfoCollector(@NonNull Path remappedJarPath, @NonNull ClassLoader
6667
var protocolsClass = Class.forName(state.protocolsClass(), true, loader);
6768
var infoDecoder = new McProtocolsDecoder(protocolsClass, loader);
6869
var typeToPacketClassPerFlow = infoDecoder.decodeAssociatedPackets();
69-
var typeToPacketIdPerFlow = infoDecoder.resolvePacketIds();
70+
var typeToPacketIdPerFlow = infoDecoder.resolvePacketIds(packetVisitorClass);
7071

7172
// register for each flow (if present)
7273
for (var flow : McPacketFlow.values()) {

0 commit comments

Comments
 (0)