2626
2727import dev .derklaro .reflexion .Reflexion ;
2828import dev .derklaro .reflexion .matcher .FieldMatcher ;
29+ import dev .derklaro .reflexion .matcher .MethodMatcher ;
2930import java .io .IOException ;
3031import java .lang .reflect .Modifier ;
3132import java .util .EnumMap ;
3435import java .util .Locale ;
3536import java .util .Map ;
3637import java .util .Objects ;
38+ import java .util .function .Predicate ;
3739import javax .annotation .Nullable ;
3840import lombok .NonNull ;
3941import org .objectweb .asm .ClassReader ;
4042import org .objectweb .asm .Handle ;
43+ import org .objectweb .asm .Type ;
4144import org .objectweb .asm .tree .AbstractInsnNode ;
4245import org .objectweb .asm .tree .ClassNode ;
4346import org .objectweb .asm .tree .FieldInsnNode ;
4750
4851final 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 ();
0 commit comments