mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-27 13:15:52 +01:00
Add 1.17 support to TinyProtocol (#194)
This commit is contained in:
parent
46f6e76f91
commit
e77ed96957
@ -3,6 +3,8 @@ package com.comphenix.tinyprotocol;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -177,6 +179,31 @@ public final class Reflection {
|
|||||||
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a field with a given type and parameters. This is most useful
|
||||||
|
* when dealing with Collections.
|
||||||
|
*
|
||||||
|
* @param target the target class.
|
||||||
|
* @param fieldType Type of the field
|
||||||
|
* @param params Variable length array of type parameters
|
||||||
|
* @return The field
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException If the field cannot be found
|
||||||
|
*/
|
||||||
|
public static Field getParameterizedField(Class<?> target, Class<?> fieldType, Class<?>... params) {
|
||||||
|
for (Field field : target.getDeclaredFields()) {
|
||||||
|
if (field.getType().equals(fieldType)) {
|
||||||
|
Type type = field.getGenericType();
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
* Search for the first publicly and privately defined method of the given name and parameter count.
|
||||||
*
|
*
|
||||||
@ -301,6 +328,23 @@ public final class Reflection {
|
|||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a class from its full name with alternatives, without knowing its type on compile time.
|
||||||
|
* <p>
|
||||||
|
* This is useful when looking up fields by a NMS or OBC type.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @see {@link #getClass()} for more information.
|
||||||
|
* @param lookupName - the class name with variables.
|
||||||
|
* @param aliases - alternative names for this class.
|
||||||
|
* @return The class.
|
||||||
|
*/
|
||||||
|
public static Class<Object> getUntypedClass(String lookupName, String... aliases) {
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
Class<Object> clazz = (Class) getClass(lookupName, aliases);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a class from its full name.
|
* Retrieve a class from its full name.
|
||||||
* <p>
|
* <p>
|
||||||
@ -333,6 +377,61 @@ public final class Reflection {
|
|||||||
return getCanonicalClass(expandVariables(lookupName));
|
return getCanonicalClass(expandVariables(lookupName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the first class that matches the full class name.
|
||||||
|
* <p>
|
||||||
|
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
|
||||||
|
* <p>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <th>Variable</th>
|
||||||
|
* <th>Content</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{nms}</td>
|
||||||
|
* <td>Actual package name of net.minecraft.server.VERSION</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{obc}</td>
|
||||||
|
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>{version}</td>
|
||||||
|
* <td>The current Minecraft package VERSION, if any.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* @param lookupName - the class name with variables.
|
||||||
|
* @param aliases - alternative names for this class.
|
||||||
|
* @return Class object.
|
||||||
|
* @throws RuntimeException If we are unable to find any of the given classes.
|
||||||
|
*/
|
||||||
|
public static Class<?> getClass(String lookupName, String... aliases) {
|
||||||
|
try {
|
||||||
|
// Try the main class first
|
||||||
|
return getClass(lookupName);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Class<?> success = null;
|
||||||
|
|
||||||
|
// Try every alias too
|
||||||
|
for (String alias : aliases) {
|
||||||
|
try {
|
||||||
|
success = getClass(alias);
|
||||||
|
break;
|
||||||
|
} catch (RuntimeException e1) {
|
||||||
|
// e1.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success != null) {
|
||||||
|
return success;
|
||||||
|
} else {
|
||||||
|
// Hack failed
|
||||||
|
throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a class in the net.minecraft.server.VERSION.* package.
|
* Retrieve a class in the net.minecraft.server.VERSION.* package.
|
||||||
*
|
*
|
||||||
|
@ -9,6 +9,7 @@ import io.netty.channel.ChannelInitializer;
|
|||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -44,21 +45,25 @@ import com.mojang.authlib.GameProfile;
|
|||||||
public abstract class TinyProtocol {
|
public abstract class TinyProtocol {
|
||||||
private static final AtomicInteger ID = new AtomicInteger(0);
|
private static final AtomicInteger ID = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// Required Minecraft classes
|
||||||
|
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
|
||||||
|
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
|
||||||
|
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
|
||||||
|
|
||||||
// Used in order to lookup a channel
|
// Used in order to lookup a channel
|
||||||
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
|
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
|
||||||
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
|
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
|
||||||
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
|
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
|
||||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
|
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
|
||||||
|
|
||||||
// Looking up ServerConnection
|
// Looking up ServerConnection
|
||||||
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
|
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
|
||||||
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
|
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
|
||||||
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
|
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
|
||||||
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
|
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
|
||||||
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
|
||||||
|
|
||||||
// Packets we have to intercept
|
// Packets we have to intercept
|
||||||
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
|
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
|
||||||
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
|
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
|
||||||
|
|
||||||
// Speedup channel lookup
|
// Speedup channel lookup
|
||||||
@ -199,8 +204,22 @@ public abstract class TinyProtocol {
|
|||||||
Object serverConnection = getServerConnection.get(mcServer);
|
Object serverConnection = getServerConnection.get(mcServer);
|
||||||
boolean looking = true;
|
boolean looking = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
networkManagers = (List<Object>) field.get(serverConnection);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
|
||||||
|
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
|
||||||
|
|
||||||
|
networkManagers = (List<Object>) method.invoke(null, serverConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkManagers == null) {
|
||||||
|
throw new IllegalArgumentException("Failed to obtain list of network managers");
|
||||||
|
}
|
||||||
// We need to synchronize against this list
|
// We need to synchronize against this list
|
||||||
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
|
|
||||||
createServerChannelHandler();
|
createServerChannelHandler();
|
||||||
|
|
||||||
// Find the correct list, or implicitly throw an exception
|
// Find the correct list, or implicitly throw an exception
|
||||||
|
Loading…
Reference in New Issue
Block a user