Minify TinyProtocol and ReflectionUtil
Dieser Commit ist enthalten in:
Ursprung
5b2d86a0e8
Commit
9fbb8b4633
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
import de.lixfel.tinyprotocol.TinyProtocol;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
@ -27,7 +26,7 @@ public class AltAuthBukkit extends JavaPlugin {
|
|||||||
saveDefaultConfig();
|
saveDefaultConfig();
|
||||||
String altAuthServer = getConfig().getString("altauth-proxy");
|
String altAuthServer = getConfig().getString("altauth-proxy");
|
||||||
|
|
||||||
TinyProtocol.init();
|
ProtocolInjector.init();
|
||||||
serviceInjector = new SessionServiceInjector(altAuthServer);
|
serviceInjector = new SessionServiceInjector(altAuthServer);
|
||||||
requestInjector = new EncryptionRequestInjector(altAuthServer);
|
requestInjector = new EncryptionRequestInjector(altAuthServer);
|
||||||
}
|
}
|
||||||
@ -36,6 +35,6 @@ public class AltAuthBukkit extends JavaPlugin {
|
|||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
requestInjector.remove();
|
requestInjector.remove();
|
||||||
serviceInjector.revert();
|
serviceInjector.revert();
|
||||||
TinyProtocol.instance.close();
|
ProtocolInjector.instance.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,29 @@
|
|||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
import de.lixfel.tinyprotocol.Reflection;
|
import de.lixfel.ReflectionUtil;
|
||||||
import de.lixfel.tinyprotocol.TinyProtocol;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class EncryptionRequestInjector {
|
public class EncryptionRequestInjector {
|
||||||
|
|
||||||
private static final Class<?> EncryptionRequest = Reflection.getClass("{nms.network.protocol.login}.PacketLoginOutEncryptionBegin");
|
private static final Class<?> EncryptionRequest = ReflectionUtil.getClass("net.minecraft.network.protocol.login.PacketLoginOutEncryptionBegin");
|
||||||
private static final Reflection.FieldAccessor<String> ServerID = Reflection.getField(EncryptionRequest, String.class, 0);
|
private static final ReflectionUtil.FieldWrapper<String> ServerID = ReflectionUtil.getField(EncryptionRequest, String.class, 0);
|
||||||
|
|
||||||
private final String altAuthServer;
|
private final String altAuthServer;
|
||||||
|
|
||||||
public EncryptionRequestInjector(String altAuthServer) {
|
public EncryptionRequestInjector(String altAuthServer) {
|
||||||
this.altAuthServer = altAuthServer;
|
this.altAuthServer = altAuthServer;
|
||||||
|
|
||||||
TinyProtocol.instance.addFilter(EncryptionRequest, this::handleEncryptionRequest);
|
ProtocolInjector.instance.addFilter(EncryptionRequest, this::handleEncryptionRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object handleEncryptionRequest(Player player, Object packet) {
|
public Object handleEncryptionRequest(Player player, Object packet) {
|
||||||
ServerID.set(packet, altAuthServer);
|
ServerID.set(packet, altAuthServer);
|
||||||
TinyProtocol.instance.getInterceptor(player).ifPresent(TinyProtocol.PacketInterceptor::close);
|
ProtocolInjector.instance.getInterceptor(player).ifPresent(ProtocolInjector.PacketInterceptor::close);
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove() {
|
public void remove() {
|
||||||
TinyProtocol.instance.removeFilter(EncryptionRequest, this::handleEncryptionRequest);
|
ProtocolInjector.instance.removeFilter(EncryptionRequest, this::handleEncryptionRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package de.lixfel.tinyprotocol;
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
import de.lixfel.tinyprotocol.Reflection.FieldAccessor;
|
import de.lixfel.ReflectionUtil;
|
||||||
import de.lixfel.altauth.bukkit.AltAuthBukkit;
|
import de.lixfel.ReflectionUtil.FieldWrapper;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelDuplexHandler;
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
@ -22,21 +22,21 @@ import java.util.*;
|
|||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class TinyProtocol implements Listener {
|
// ReflectionUtil heavily inspired by TinyProtocol
|
||||||
|
public class ProtocolInjector implements Listener {
|
||||||
|
|
||||||
private static final Class<?> craftServer = Reflection.getClass("{obc}.CraftServer");
|
private static final Class<?> craftServer = ReflectionUtil.getClass("org.bukkit.craftbukkit.CraftServer");
|
||||||
private static final Class<?> dedicatedPlayerList = Reflection.getClass("{nms.server.dedicated}.DedicatedPlayerList");
|
private static final Class<?> dedicatedPlayerList = ReflectionUtil.getClass("net.minecraft.server.dedicated.DedicatedPlayerList");
|
||||||
private static final FieldAccessor<?> getPlayerList = Reflection.getField(craftServer, dedicatedPlayerList, 0);
|
private static final FieldWrapper<?> getPlayerList = ReflectionUtil.getField(craftServer, dedicatedPlayerList, 0);
|
||||||
private static final Class<?> playerList = Reflection.getClass("{nms.server.players}.PlayerList");
|
private static final Class<?> playerList = ReflectionUtil.getClass("net.minecraft.server.players.PlayerList");
|
||||||
private static final Class<?> minecraftServer = Reflection.getClass("{nms.server}.MinecraftServer");
|
private static final Class<?> minecraftServer = ReflectionUtil.getClass("net.minecraft.server.MinecraftServer");
|
||||||
private static final FieldAccessor<?> getMinecraftServer = Reflection.getField(playerList, minecraftServer, 0);
|
private static final FieldWrapper<?> getMinecraftServer = ReflectionUtil.getField(playerList, minecraftServer, 0);
|
||||||
private static final Class<?> serverConnection = Reflection.getClass("{nms.server.network}.ServerConnection");
|
private static final Class<?> serverConnection = ReflectionUtil.getClass("net.minecraft.server.network.ServerConnection");
|
||||||
private static final FieldAccessor<?> getServerConnection = Reflection.getField(minecraftServer, serverConnection, 0);
|
private static final FieldWrapper<?> getServerConnection = ReflectionUtil.getField(minecraftServer, serverConnection, 0);
|
||||||
private static final Class<?> networkManager = Reflection.getClass("{nms.network}.NetworkManager");
|
private static final Class<?> networkManager = ReflectionUtil.getClass("net.minecraft.network.NetworkManager");
|
||||||
private static final FieldAccessor<List> getConnections = Reflection.getField(serverConnection, List.class, 0, networkManager);
|
private static final FieldWrapper<List> getConnections = ReflectionUtil.getField(serverConnection, List.class, 0, networkManager);
|
||||||
|
|
||||||
public static final TinyProtocol instance = new TinyProtocol(AltAuthBukkit.getInstance());
|
public static final ProtocolInjector instance = new ProtocolInjector(AltAuthBukkit.getInstance());
|
||||||
private static int id = 0;
|
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
//enforce init
|
//enforce init
|
||||||
@ -50,9 +50,9 @@ public class TinyProtocol implements Listener {
|
|||||||
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
|
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
|
||||||
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
|
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
|
||||||
|
|
||||||
private TinyProtocol(final Plugin plugin) {
|
private ProtocolInjector(final Plugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.handlerName = "tiny-" + plugin.getName() + "-" + ++id;
|
this.handlerName = "altauth";
|
||||||
this.connections = getConnections.get(getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer()))));
|
this.connections = getConnections.get(getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer()))));
|
||||||
|
|
||||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
@ -89,14 +89,6 @@ public class TinyProtocol implements Listener {
|
|||||||
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
|
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(Player player, Object packet) {
|
|
||||||
getInterceptor(player).ifPresent(i -> i.sendPacket(packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void receivePacket(Player player, Object packet) {
|
|
||||||
getInterceptor(player).ifPresent(i -> i.receivePacket(packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void close() {
|
public final void close() {
|
||||||
if(closed)
|
if(closed)
|
||||||
return;
|
return;
|
||||||
@ -115,8 +107,8 @@ public class TinyProtocol implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManager, Channel.class, 0);
|
private static final FieldWrapper<Channel> getChannel = ReflectionUtil.getField(networkManager, Channel.class, 0);
|
||||||
private static final FieldAccessor<UUID> getUUID = Reflection.getField(networkManager, UUID.class, 0);
|
private static final FieldWrapper<UUID> getUUID = ReflectionUtil.getField(networkManager, UUID.class, 0);
|
||||||
|
|
||||||
public final class PacketInterceptor extends ChannelDuplexHandler {
|
public final class PacketInterceptor extends ChannelDuplexHandler {
|
||||||
private final Player player;
|
private final Player player;
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package de.lixfel.altauth.bukkit;
|
package de.lixfel.altauth.bukkit;
|
||||||
|
|
||||||
import de.lixfel.tinyprotocol.Reflection;
|
import de.lixfel.ReflectionUtil;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@ -10,10 +10,10 @@ import java.util.logging.Level;
|
|||||||
|
|
||||||
public class SessionServiceInjector {
|
public class SessionServiceInjector {
|
||||||
|
|
||||||
private static final Class<?> SESSION_SERVICE = Reflection.getClass("com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService");
|
private static final Class<?> SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService");
|
||||||
|
|
||||||
private static final Reflection.FieldAccessor<URL> JOIN_URL = Reflection.getField(SESSION_SERVICE, "JOIN_URL", URL.class);
|
private static final ReflectionUtil.FieldWrapper<URL> JOIN_URL = ReflectionUtil.getField(SESSION_SERVICE, URL.class, "JOIN_URL");
|
||||||
private static final Reflection.FieldAccessor<URL> CHECK_URL = Reflection.getField(SESSION_SERVICE, "CHECK_URL", URL.class);
|
private static final ReflectionUtil.FieldWrapper<URL> CHECK_URL = ReflectionUtil.getField(SESSION_SERVICE, URL.class, "CHECK_URL");
|
||||||
|
|
||||||
private final URL joinUrlBackup;
|
private final URL joinUrlBackup;
|
||||||
private final URL checkUrlBackup;
|
private final URL checkUrlBackup;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package de.lixfel.altauth.bungee;
|
package de.lixfel.altauth.bungee;
|
||||||
|
|
||||||
import de.lixfel.tinyprotocol.Reflection;
|
import de.lixfel.ReflectionUtil;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageCodec;
|
import io.netty.handler.codec.MessageToMessageCodec;
|
||||||
import net.md_5.bungee.BungeeCord;
|
import net.md_5.bungee.BungeeCord;
|
||||||
@ -36,12 +36,12 @@ import java.util.logging.Level;
|
|||||||
|
|
||||||
public class AltAuthHandler extends MessageToMessageCodec<PacketWrapper, PacketWrapper> {
|
public class AltAuthHandler extends MessageToMessageCodec<PacketWrapper, PacketWrapper> {
|
||||||
|
|
||||||
private static final Class<?> InitialHandler = Reflection.getClass("net.md_5.bungee.connection.InitialHandler");
|
private static final Class<?> InitialHandler = ReflectionUtil.getClass("net.md_5.bungee.connection.InitialHandler");
|
||||||
private static final Reflection.FieldAccessor<ChannelWrapper> CH = Reflection.getField(InitialHandler, ChannelWrapper.class, 0);
|
private static final ReflectionUtil.FieldWrapper<ChannelWrapper> CH = ReflectionUtil.getField(InitialHandler, ChannelWrapper.class, 0);
|
||||||
private static final Reflection.FieldAccessor<LoginResult> LoginProfile = Reflection.getField(InitialHandler, "loginProfile", LoginResult.class);
|
private static final ReflectionUtil.FieldWrapper<LoginResult> LoginProfile = ReflectionUtil.getField(InitialHandler, LoginResult.class, "loginProfile");
|
||||||
private static final Reflection.FieldAccessor<String> Name = Reflection.getField(InitialHandler, "name", String.class);
|
private static final ReflectionUtil.FieldWrapper<String> Name = ReflectionUtil.getField(InitialHandler, String.class, "name");
|
||||||
private static final Reflection.FieldAccessor<UUID> UniqueId = Reflection.getField(InitialHandler, "uniqueId", UUID.class);
|
private static final ReflectionUtil.FieldWrapper<UUID> UniqueId = ReflectionUtil.getField(InitialHandler, UUID.class, "uniqueId");
|
||||||
private static final Reflection.MethodInvoker Finish = Reflection.getMethod(InitialHandler, "finish");
|
private static final ReflectionUtil.MethodWrapper Finish = ReflectionUtil.getMethod(InitialHandler, "finish");
|
||||||
|
|
||||||
private final ProxyServer bungee;
|
private final ProxyServer bungee;
|
||||||
private final String altAuthUrl;
|
private final String altAuthUrl;
|
||||||
|
142
common/src/de/lixfel/ReflectionUtil.java
Normale Datei
142
common/src/de/lixfel/ReflectionUtil.java
Normale Datei
@ -0,0 +1,142 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package de.lixfel;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// ReflectionUtil heavily inspired by TinyProtocol
|
||||||
|
public final class ReflectionUtil {
|
||||||
|
private ReflectionUtil() {}
|
||||||
|
|
||||||
|
private static final String ORG_BUKKIT_CRAFTBUKKIT;
|
||||||
|
private static final boolean REPLACE_NET_MINECRAFT;
|
||||||
|
static {
|
||||||
|
String craftbukkitPackage;
|
||||||
|
boolean legacyNms;
|
||||||
|
try {
|
||||||
|
craftbukkitPackage = Bukkit.getServer().getClass().getPackage().getName();
|
||||||
|
legacyNms = Integer.parseInt(craftbukkitPackage.split("[.](?=[^.]*$)")[1].split("_")[1]) < 17;
|
||||||
|
} catch (NoClassDefFoundError e) {
|
||||||
|
craftbukkitPackage = "";
|
||||||
|
legacyNms = false;
|
||||||
|
}
|
||||||
|
ORG_BUKKIT_CRAFTBUKKIT = craftbukkitPackage;
|
||||||
|
REPLACE_NET_MINECRAFT = legacyNms;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String LEGACY_NET_MINECRAFT_SERVER = ORG_BUKKIT_CRAFTBUKKIT.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||||
|
|
||||||
|
public static <T> FieldWrapper<T> getField(Class<?> target, Class<T> fieldType, String name) {
|
||||||
|
return getField(target, fieldType, name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> FieldWrapper<T> getField(Class<?> target, Class<T> fieldType, int index, Class<?>... parameters) {
|
||||||
|
return getField(target, fieldType, null, index, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> FieldWrapper<T> getField(Class<?> target, Class<T> fieldType, String name, int index, Class<?>... parameters) {
|
||||||
|
for (final Field field : target.getDeclaredFields()) {
|
||||||
|
if(matching(field, name, fieldType, parameters) && index-- <= 0) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
return new FieldWrapper<T>() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get(Object target) {
|
||||||
|
try {
|
||||||
|
return (T) field.get(target);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException("Access denied", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object target, Object value) {
|
||||||
|
try {
|
||||||
|
field.set(target, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException("Access denied", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.getSuperclass() != null)
|
||||||
|
return getField(target.getSuperclass(), fieldType, name, index);
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Field not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean matching(Field field, String name, Class<T> fieldType, Class<?>... parameters) {
|
||||||
|
if(name != null && !field.getName().equals(name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!fieldType.isAssignableFrom(field.getType()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(parameters.length > 0) {
|
||||||
|
Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
|
||||||
|
|
||||||
|
for(int i = 0; i < parameters.length; i++) {
|
||||||
|
if(arguments[i] != parameters[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodWrapper getMethod(Class<?> clazz, String methodName, Class<?>... args) {
|
||||||
|
return getMethod(clazz, null, methodName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodWrapper getMethod(Class<?> clazz, Class<?> returnType, String methodName, Class<?>... args) {
|
||||||
|
for (final Method method : clazz.getDeclaredMethods()) {
|
||||||
|
if ((methodName == null || method.getName().equals(methodName))
|
||||||
|
&& (returnType == null || method.getReturnType().equals(returnType))
|
||||||
|
&& Arrays.equals(method.getParameterTypes(), args)) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
|
||||||
|
return (target, arguments) -> {
|
||||||
|
try {
|
||||||
|
return method.invoke(target, arguments);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Exception on call", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clazz.getSuperclass() != null)
|
||||||
|
return getMethod(clazz.getSuperclass(), methodName, args);
|
||||||
|
|
||||||
|
throw new IllegalStateException("Method not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getClass(String name) {
|
||||||
|
try {
|
||||||
|
if(name.startsWith("org.bukkit.craftbukkit")) {
|
||||||
|
return Class.forName(ORG_BUKKIT_CRAFTBUKKIT + name.substring(22));
|
||||||
|
} else if(REPLACE_NET_MINECRAFT && name.startsWith("net.minecraft")) {
|
||||||
|
return Class.forName(LEGACY_NET_MINECRAFT_SERVER + "." + name.split("[.](?=[^.]*$)")[1]);
|
||||||
|
} else {
|
||||||
|
return Class.forName(name);
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new NoClassDefFoundError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MethodWrapper {
|
||||||
|
Object invoke(Object target, Object... arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FieldWrapper<T> {
|
||||||
|
T get(Object target);
|
||||||
|
|
||||||
|
void set(Object target, Object value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,412 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package de.lixfel.tinyprotocol;
|
|
||||||
|
|
||||||
import jdk.internal.misc.Unsafe;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An utility class that simplifies reflection in Bukkit plugins.
|
|
||||||
*
|
|
||||||
* @author Kristian
|
|
||||||
*/
|
|
||||||
public final class Reflection {
|
|
||||||
|
|
||||||
private static final int MINECRAFT_VERSION;
|
|
||||||
|
|
||||||
static {
|
|
||||||
int mcVersion;
|
|
||||||
try {
|
|
||||||
mcVersion = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().split("[.](?=[^.]*$)")[1].split("_")[1]);
|
|
||||||
} catch (NoClassDefFoundError e) {
|
|
||||||
mcVersion = 0;
|
|
||||||
}
|
|
||||||
MINECRAFT_VERSION = mcVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for invoking a specific constructor.
|
|
||||||
*/
|
|
||||||
public interface ConstructorInvoker {
|
|
||||||
/**
|
|
||||||
* Invoke a constructor for a specific class.
|
|
||||||
*
|
|
||||||
* @param arguments - the arguments to pass to the constructor.
|
|
||||||
* @return The constructed object.
|
|
||||||
*/
|
|
||||||
Object invoke(Object... arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for invoking a specific method.
|
|
||||||
*/
|
|
||||||
public interface MethodInvoker {
|
|
||||||
/**
|
|
||||||
* Invoke a method on a specific target object.
|
|
||||||
*
|
|
||||||
* @param target - the target object, or NULL for a static method.
|
|
||||||
* @param arguments - the arguments to pass to the method.
|
|
||||||
* @return The return value, or NULL if is void.
|
|
||||||
*/
|
|
||||||
Object invoke(Object target, Object... arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for retrieving the field content.
|
|
||||||
*
|
|
||||||
* @param <T> - field type.
|
|
||||||
*/
|
|
||||||
public interface FieldAccessor<T> {
|
|
||||||
/**
|
|
||||||
* Retrieve the content of a field.
|
|
||||||
*
|
|
||||||
* @param target - the target object, or NULL for a static field.
|
|
||||||
* @return The value of the field.
|
|
||||||
*/
|
|
||||||
T get(Object target);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the content of a field.
|
|
||||||
*
|
|
||||||
* @param target - the target object, or NULL for a static field.
|
|
||||||
* @param value - the new value of the field.
|
|
||||||
*/
|
|
||||||
void set(Object target, Object value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the given object has this field.
|
|
||||||
*
|
|
||||||
* @param target - the object to test.
|
|
||||||
* @return TRUE if it does, FALSE otherwise.
|
|
||||||
*/
|
|
||||||
boolean hasField(Object target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deduce the net.minecraft.server.v* package
|
|
||||||
private static final String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName();
|
|
||||||
private static final String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
|
||||||
private static final String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "");
|
|
||||||
|
|
||||||
// Variable replacement
|
|
||||||
private static final Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}");
|
|
||||||
|
|
||||||
private Reflection() {
|
|
||||||
// Seal class
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a field accessor for a specific field type and name.
|
|
||||||
*
|
|
||||||
* @param target - the target type.
|
|
||||||
* @param name - the name of the field, or NULL to ignore.
|
|
||||||
* @param fieldType - a compatible field type.
|
|
||||||
* @return The field accessor.
|
|
||||||
*/
|
|
||||||
public static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType) {
|
|
||||||
return getField(target, name, fieldType, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a field accessor for a specific field type and name.
|
|
||||||
*
|
|
||||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
|
||||||
* @param name - the name of the field, or NULL to ignore.
|
|
||||||
* @param fieldType - a compatible field type.
|
|
||||||
* @return The field accessor.
|
|
||||||
*/
|
|
||||||
public static <T> FieldAccessor<T> getField(String className, String name, Class<T> fieldType) {
|
|
||||||
return getField(getClass(className), name, fieldType, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a field accessor for a specific field type and name.
|
|
||||||
*
|
|
||||||
* @param target - the target type.
|
|
||||||
* @param fieldType - a compatible field type.
|
|
||||||
* @param index - the number of compatible fields to skip.
|
|
||||||
* @return The field accessor.
|
|
||||||
*/
|
|
||||||
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index) {
|
|
||||||
return getField(target, null, fieldType, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a field accessor for a specific field type and name.
|
|
||||||
*
|
|
||||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
|
||||||
* @param fieldType - a compatible field type.
|
|
||||||
* @param index - the number of compatible fields to skip.
|
|
||||||
* @return The field accessor.
|
|
||||||
*/
|
|
||||||
public static <T> FieldAccessor<T> getField(String className, Class<T> fieldType, int index) {
|
|
||||||
return getField(getClass(className), fieldType, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> FieldAccessor<T> getField(Class<?> target, Class<T> fieldType, int index, Class<?>... parameters) {
|
|
||||||
return getField(target, null, fieldType, index, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> FieldAccessor<T> getField(Class<?> target, String name, Class<T> fieldType, int index, Class<?>... parameters) {
|
|
||||||
for (final Field field : target.getDeclaredFields()) {
|
|
||||||
if(matching(field, name, fieldType, parameters) && index-- <= 0) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
return new FieldAccessor<T>() {
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public T get(Object target) {
|
|
||||||
try {
|
|
||||||
return (T) field.get(target);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new IllegalArgumentException("Cannot access reflection.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(Object target, Object value) {
|
|
||||||
try {
|
|
||||||
field.set(target, value);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new IllegalArgumentException("Cannot access reflection.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasField(Object target) {
|
|
||||||
// target instanceof DeclaringClass
|
|
||||||
return field.getDeclaringClass().isAssignableFrom(target.getClass());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search in parent classes
|
|
||||||
if (target.getSuperclass() != null)
|
|
||||||
return getField(target.getSuperclass(), name, fieldType, index);
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("Cannot find field with type " + fieldType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> boolean matching(Field field, String name, Class<T> fieldType, Class<?>... parameters) {
|
|
||||||
if(name != null && !field.getName().equals(name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(!fieldType.isAssignableFrom(field.getType()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(parameters.length > 0) {
|
|
||||||
Type[] arguments = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
|
|
||||||
|
|
||||||
for(int i = 0; i < parameters.length; i++) {
|
|
||||||
if(arguments[i] != parameters[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
|
||||||
*
|
|
||||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
|
||||||
* @param methodName - the method name, or NULL to skip.
|
|
||||||
* @param params - the expected parameters.
|
|
||||||
* @return An object that invokes this specific method.
|
|
||||||
* @throws IllegalStateException If we cannot find this method.
|
|
||||||
*/
|
|
||||||
public static MethodInvoker getMethod(String className, String methodName, Class<?>... params) {
|
|
||||||
return getTypedMethod(getClass(className), methodName, null, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
|
||||||
*
|
|
||||||
* @param clazz - a class to start with.
|
|
||||||
* @param methodName - the method name, or NULL to skip.
|
|
||||||
* @param params - the expected parameters.
|
|
||||||
* @return An object that invokes this specific method.
|
|
||||||
* @throws IllegalStateException If we cannot find this method.
|
|
||||||
*/
|
|
||||||
public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
|
|
||||||
return getTypedMethod(clazz, methodName, null, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for the first publicly and privately defined method of the given name and parameter count.
|
|
||||||
*
|
|
||||||
* @param clazz - a class to start with.
|
|
||||||
* @param methodName - the method name, or NULL to skip.
|
|
||||||
* @param returnType - the expected return type, or NULL to ignore.
|
|
||||||
* @param params - the expected parameters.
|
|
||||||
* @return An object that invokes this specific method.
|
|
||||||
* @throws IllegalStateException If we cannot find this method.
|
|
||||||
*/
|
|
||||||
public static MethodInvoker getTypedMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... params) {
|
|
||||||
for (final Method method : clazz.getDeclaredMethods()) {
|
|
||||||
if ((methodName == null || method.getName().equals(methodName))
|
|
||||||
&& (returnType == null || method.getReturnType().equals(returnType))
|
|
||||||
&& Arrays.equals(method.getParameterTypes(), params)) {
|
|
||||||
method.setAccessible(true);
|
|
||||||
|
|
||||||
return (target, arguments) -> {
|
|
||||||
try {
|
|
||||||
return method.invoke(target, arguments);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("Cannot invoke method " + method, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search in every superclass
|
|
||||||
if (clazz.getSuperclass() != null)
|
|
||||||
return getMethod(clazz.getSuperclass(), methodName, params);
|
|
||||||
|
|
||||||
throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
|
||||||
*
|
|
||||||
* @param className - lookup name of the class, see {@link #getClass(String)}.
|
|
||||||
* @param params - the expected parameters.
|
|
||||||
* @return An object that invokes this constructor.
|
|
||||||
* @throws IllegalStateException If we cannot find this method.
|
|
||||||
*/
|
|
||||||
public static ConstructorInvoker getConstructor(String className, Class<?>... params) {
|
|
||||||
return getConstructor(getClass(className), params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for the first publically and privately defined constructor of the given name and parameter count.
|
|
||||||
*
|
|
||||||
* @param clazz - a class to start with.
|
|
||||||
* @param params - the expected parameters.
|
|
||||||
* @return An object that invokes this constructor.
|
|
||||||
* @throws IllegalStateException If we cannot find this method.
|
|
||||||
*/
|
|
||||||
public static ConstructorInvoker getConstructor(Class<?> clazz, Class<?>... params) {
|
|
||||||
for (final Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
|
||||||
if (Arrays.equals(constructor.getParameterTypes(), params)) {
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
|
|
||||||
return arguments -> {
|
|
||||||
try {
|
|
||||||
return constructor.newInstance(arguments);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("Cannot invoke constructor " + constructor, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a class from its full name, without knowing its type on compile time.
|
|
||||||
* <p>
|
|
||||||
* This is useful when looking up fields by a NMS or OBC type.
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* @param lookupName - the class name with variables.
|
|
||||||
* @return The class.
|
|
||||||
*/
|
|
||||||
public static Class<Object> getUntypedClass(String lookupName) {
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
Class<Object> clazz = (Class) getClass(lookupName);
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a class from its full 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.
|
|
||||||
* @return The looked up class.
|
|
||||||
* @throws IllegalArgumentException If a variable or class could not be found.
|
|
||||||
*/
|
|
||||||
public static Class<?> getClass(String lookupName) {
|
|
||||||
try {
|
|
||||||
return Class.forName(expandVariables(lookupName));
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new IllegalArgumentException("Cannot find " + expandVariables(lookupName), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expand variables such as "{nms}" and "{obc}" to their corresponding packages.
|
|
||||||
*
|
|
||||||
* @param name - the full name of the class.
|
|
||||||
* @return The expanded string.
|
|
||||||
*/
|
|
||||||
private static String expandVariables(String name) {
|
|
||||||
StringBuffer output = new StringBuffer();
|
|
||||||
Matcher matcher = MATCH_VARIABLE.matcher(name);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
String variable = matcher.group(1);
|
|
||||||
String replacement;
|
|
||||||
|
|
||||||
// Expand all detected variables
|
|
||||||
if (variable.startsWith("nms")) {
|
|
||||||
if(MINECRAFT_VERSION >= 17)
|
|
||||||
replacement = "net.minecraft" + variable.substring(3);
|
|
||||||
else
|
|
||||||
replacement = NMS_PREFIX;
|
|
||||||
} else if ("obc".equals(variable))
|
|
||||||
replacement = OBC_PREFIX;
|
|
||||||
else if ("version".equals(variable))
|
|
||||||
replacement = VERSION;
|
|
||||||
else
|
|
||||||
throw new IllegalArgumentException("Unknown variable: " + variable);
|
|
||||||
|
|
||||||
// Assume the expanded variables are all packages, and append a dot
|
|
||||||
if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.')
|
|
||||||
replacement += ".";
|
|
||||||
matcher.appendReplacement(output, Matcher.quoteReplacement(replacement));
|
|
||||||
}
|
|
||||||
|
|
||||||
matcher.appendTail(output);
|
|
||||||
return output.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static Object newInstance(Class<?> clazz) {
|
|
||||||
try {
|
|
||||||
if (MINECRAFT_VERSION > 15) {
|
|
||||||
return Unsafe.getUnsafe().allocateInstance(clazz);
|
|
||||||
} else {
|
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
|
||||||
} catch (InstantiationException | IllegalAccessException e) {
|
|
||||||
throw new IllegalArgumentException("Could not create object", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren