Skip to content

Commit e77ed96

Browse files
authored
Add 1.17 support to TinyProtocol (#194)
1 parent 46f6e76 commit e77ed96

File tree

2 files changed

+126
-8
lines changed

2 files changed

+126
-8
lines changed

TinyProtocol/src/main/java/com/comphenix/tinyprotocol/Reflection.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.lang.reflect.Constructor;
44
import java.lang.reflect.Field;
55
import java.lang.reflect.Method;
6+
import java.lang.reflect.ParameterizedType;
7+
import java.lang.reflect.Type;
68
import java.util.Arrays;
79
import java.util.regex.Matcher;
810
import java.util.regex.Pattern;
@@ -177,6 +179,31 @@ public boolean hasField(Object target) {
177179
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
178180
}
179181

182+
/**
183+
* Retrieves a field with a given type and parameters. This is most useful
184+
* when dealing with Collections.
185+
*
186+
* @param target the target class.
187+
* @param fieldType Type of the field
188+
* @param params Variable length array of type parameters
189+
* @return The field
190+
*
191+
* @throws IllegalArgumentException If the field cannot be found
192+
*/
193+
public static Field getParameterizedField(Class<?> target, Class<?> fieldType, Class<?>... params) {
194+
for (Field field : target.getDeclaredFields()) {
195+
if (field.getType().equals(fieldType)) {
196+
Type type = field.getGenericType();
197+
if (type instanceof ParameterizedType) {
198+
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
199+
return field;
200+
}
201+
}
202+
}
203+
204+
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
205+
}
206+
180207
/**
181208
* Search for the first publicly and privately defined method of the given name and parameter count.
182209
*
@@ -301,6 +328,23 @@ public static Class<Object> getUntypedClass(String lookupName) {
301328
return clazz;
302329
}
303330

331+
/**
332+
* Retrieve a class from its full name with alternatives, without knowing its type on compile time.
333+
* <p>
334+
* This is useful when looking up fields by a NMS or OBC type.
335+
* <p>
336+
*
337+
* @see {@link #getClass()} for more information.
338+
* @param lookupName - the class name with variables.
339+
* @param aliases - alternative names for this class.
340+
* @return The class.
341+
*/
342+
public static Class<Object> getUntypedClass(String lookupName, String... aliases) {
343+
@SuppressWarnings({ "rawtypes", "unchecked" })
344+
Class<Object> clazz = (Class) getClass(lookupName, aliases);
345+
return clazz;
346+
}
347+
304348
/**
305349
* Retrieve a class from its full name.
306350
* <p>
@@ -333,6 +377,61 @@ public static Class<?> getClass(String lookupName) {
333377
return getCanonicalClass(expandVariables(lookupName));
334378
}
335379

380+
/**
381+
* Retrieve the first class that matches the full class name.
382+
* <p>
383+
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
384+
* <p>
385+
* <table border="1">
386+
* <tr>
387+
* <th>Variable</th>
388+
* <th>Content</th>
389+
* </tr>
390+
* <tr>
391+
* <td>{nms}</td>
392+
* <td>Actual package name of net.minecraft.server.VERSION</td>
393+
* </tr>
394+
* <tr>
395+
* <td>{obc}</td>
396+
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
397+
* </tr>
398+
* <tr>
399+
* <td>{version}</td>
400+
* <td>The current Minecraft package VERSION, if any.</td>
401+
* </tr>
402+
* </table>
403+
*
404+
* @param lookupName - the class name with variables.
405+
* @param aliases - alternative names for this class.
406+
* @return Class object.
407+
* @throws RuntimeException If we are unable to find any of the given classes.
408+
*/
409+
public static Class<?> getClass(String lookupName, String... aliases) {
410+
try {
411+
// Try the main class first
412+
return getClass(lookupName);
413+
} catch (RuntimeException e) {
414+
Class<?> success = null;
415+
416+
// Try every alias too
417+
for (String alias : aliases) {
418+
try {
419+
success = getClass(alias);
420+
break;
421+
} catch (RuntimeException e1) {
422+
// e1.printStackTrace();
423+
}
424+
}
425+
426+
if (success != null) {
427+
return success;
428+
} else {
429+
// Hack failed
430+
throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases)));
431+
}
432+
}
433+
}
434+
336435
/**
337436
* Retrieve a class in the net.minecraft.server.VERSION.* package.
338437
*

TinyProtocol/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.netty.channel.ChannelPipeline;
1010
import io.netty.channel.ChannelPromise;
1111

12+
import java.lang.reflect.Field;
1213
import java.util.Collections;
1314
import java.util.List;
1415
import java.util.Map;
@@ -44,21 +45,25 @@
4445
public abstract class TinyProtocol {
4546
private static final AtomicInteger ID = new AtomicInteger(0);
4647

48+
// Required Minecraft classes
49+
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
50+
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
51+
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
52+
4753
// Used in order to lookup a channel
4854
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
49-
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
50-
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
51-
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
55+
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
56+
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
57+
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
5258

5359
// Looking up ServerConnection
54-
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
55-
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
60+
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
61+
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
5662
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
5763
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
58-
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
5964

6065
// Packets we have to intercept
61-
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
66+
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
6267
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
6368

6469
// Speedup channel lookup
@@ -199,8 +204,22 @@ private void registerChannelHandler() {
199204
Object serverConnection = getServerConnection.get(mcServer);
200205
boolean looking = true;
201206

207+
try {
208+
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
209+
field.setAccessible(true);
210+
211+
networkManagers = (List<Object>) field.get(serverConnection);
212+
} catch (Exception ex) {
213+
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
214+
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
215+
216+
networkManagers = (List<Object>) method.invoke(null, serverConnection);
217+
}
218+
219+
if (networkManagers == null) {
220+
throw new IllegalArgumentException("Failed to obtain list of network managers");
221+
}
202222
// We need to synchronize against this list
203-
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
204223
createServerChannelHandler();
205224

206225
// Find the correct list, or implicitly throw an exception

0 commit comments

Comments
 (0)