1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Use less reflection on Velocity and fixed ChildFirstClassLoader

This commit is contained in:
Tim203
2024-05-13 12:01:04 +02:00
parent 794f9c249b
commit 1a07026c58
13 changed files with 86 additions and 215 deletions

View File

@@ -26,7 +26,6 @@
import net.kyori.indra.git.IndraGitExtension import net.kyori.indra.git.IndraGitExtension
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.MinimalExternalModuleDependency import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.the import org.gradle.kotlin.dsl.the
@@ -59,23 +58,20 @@ fun buildNumberAsString(): String =
val providedDependencies = mutableMapOf<String, MutableSet<Pair<String, Any>>>() val providedDependencies = mutableMapOf<String, MutableSet<Pair<String, Any>>>()
val relocatedPackages = mutableMapOf<String, MutableSet<String>>() val relocatedPackages = mutableMapOf<String, MutableSet<String>>()
fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) { fun Project.providedDependency(dependency: MinimalExternalModuleDependency) {
val format = "${calcExclusion(pattern, 0b100, excludedOn)}:" + val format = "${dependency.group}:${dependency.name}:"
"${calcExclusion(name, 0b10, excludedOn)}:" +
calcExclusion(version, 0b1, excludedOn)
providedDependencies.getOrPut(project.name) { mutableSetOf() }.add(Pair(format, format)) providedDependencies.getOrPut(project.name) { mutableSetOf() }.add(Pair(format, format))
dependencies.add("compileOnlyApi", "$pattern:$name:$version")
} }
fun Project.provided(dependency: ProjectDependency) { fun Project.providedDependency(provider: Provider<MinimalExternalModuleDependency>) =
providedDependencies.getOrPut(project.name) { mutableSetOf() } providedDependency(provider.get())
.add(Pair(dependency.group + ":" + dependency.name, dependency))
dependencies.add("compileOnlyApi", dependency)
}
fun Project.provided(dependency: MinimalExternalModuleDependency) = fun Project.provided(dependency: MinimalExternalModuleDependency) {
provided(dependency.module.group, dependency.module.name, dependency.versionConstraint.requiredVersion) providedDependency(dependency)
dependencies.add("compileOnlyApi",
"${dependency.group}:${dependency.name}:${dependency.versionConstraint.requiredVersion}"
)
}
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) = fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
provided(provider.get()) provided(provider.get())
@@ -83,6 +79,3 @@ fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
fun Project.relocate(pattern: String) = fun Project.relocate(pattern: String) =
relocatedPackages.getOrPut(project.name) { mutableSetOf() } relocatedPackages.getOrPut(project.name) { mutableSetOf() }
.add(pattern) .add(pattern)
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
if (excludedOn and bit > 0) section else ""

View File

@@ -21,8 +21,8 @@ tasks {
doFirst { doFirst {
mergeServiceFiles() mergeServiceFiles()
providedDependencies[project.name]?.forEach { (name, notation) -> sJar.dependencies {
sJar.dependencies { providedDependencies[project.name]?.forEach { (name, notation) ->
println("Excluding $name from ${project.name}") println("Excluding $name from ${project.name}")
exclude(dependency(notation)) exclude(dependency(notation))
} }

View File

@@ -41,7 +41,9 @@ dependencies {
// present on all platforms // present on all platforms
provided(libs.netty.transport) provided(libs.netty.transport)
provided(libs.netty.codec) provided(libs.netty.codec)
providedDependency(libs.slf4j)
// we're isolated but bstats doesn't know that
relocate("org.bstats") relocate("org.bstats")
tasks { tasks {

View File

@@ -2,5 +2,5 @@ org.gradle.configureondemand=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.parallel=true org.gradle.parallel=true
version=2.2.2-SNAPSHOT version=3.0.0-SNAPSHOT
micronautVersion=4.3.1 micronautVersion=4.3.1

View File

@@ -28,7 +28,7 @@ paper = "1.20.2-R0.1-SNAPSHOT"
authlib = "5.0.47" authlib = "5.0.47"
# velocity # velocity
velocity = "3.2.0-SNAPSHOT" velocity = "3.3.0-SNAPSHOT"
# buildSrc # buildSrc
indra = "3.1.3" indra = "3.1.3"
@@ -93,6 +93,7 @@ authlib = { module = "com.mojang:authlib", version.ref = "authlib" }
# velocity # velocity
cloud-velocity = { module = "org.incendo:cloud-velocity", version.ref = "cloud" } cloud-velocity = { module = "org.incendo:cloud-velocity", version.ref = "cloud" }
velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" } velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
velocity-proxy = { module = "com.velocitypowered:velocity-proxy", version.ref = "velocity" }
# buildSrc # buildSrc
checker-qual = { module = "org.checkerframework:checker-qual", version.ref = "checkerframework" } checker-qual = { module = "org.checkerframework:checker-qual", version.ref = "checkerframework" }

View File

@@ -61,4 +61,8 @@ public class LibraryClassLoader extends URLClassLoader {
public boolean isLoaded(Path path) { public boolean isLoaded(Path path) {
return loadedPaths.contains(path); return loadedPaths.contains(path);
} }
static {
ClassLoader.registerAsParallelCapable();
}
} }

View File

@@ -28,98 +28,65 @@ package org.geysermc.floodgate.isolation.util;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.geysermc.floodgate.isolation.library.classloader.LibraryClassLoader; import org.geysermc.floodgate.isolation.library.classloader.LibraryClassLoader;
// Based of a Medium article https://medium.com/@isuru89/java-a-child-first-class-loader-cbd9c3d0305
public class ChildFirstClassLoader extends LibraryClassLoader { public class ChildFirstClassLoader extends LibraryClassLoader {
private final ClassLoader systemClassLoader;
public ChildFirstClassLoader(ClassLoader parent) { public ChildFirstClassLoader(ClassLoader parent) {
super(Objects.requireNonNull(parent)); super(Objects.requireNonNull(parent));
systemClassLoader = getSystemClassLoader();
} }
@Override @Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> loadedClass = findLoadedClass(name); synchronized (getClassLoadingLock(name)) {
if (loadedClass == null) { Class<?> loadedClass = findLoadedClass(name);
try { if (loadedClass == null) {
if (systemClassLoader != null) { // actual Java system classes (like java.lang.Integer) should be loaded by the parent classloader,
loadedClass = systemClassLoader.loadClass(name); // which does use the system class loader (unlike us). findClass only returns its own classes.
} // We don't call the system class loader ourselves since e.g. on Velocity, Velocity is the system.
} catch (ClassNotFoundException ignored) {} // This resulted in Velocity overriding our own Configurate version for example
try {
try {
if (loadedClass == null) {
loadedClass = findClass(name); loadedClass = findClass(name);
} catch (ClassNotFoundException ignored) {
loadedClass = super.loadClass(name, resolve);
} }
} catch (ClassNotFoundException e) {
loadedClass = super.loadClass(name, resolve);
} }
}
if (resolve) { if (resolve) {
resolveClass(loadedClass); resolveClass(loadedClass);
}
return loadedClass;
} }
return loadedClass;
} }
@Override @Override
public Enumeration<URL> getResources(String name) throws IOException { public Enumeration<URL> getResources(String name) throws IOException {
List<URL> allResources = new LinkedList<>();
Enumeration<URL> systemResources = systemClassLoader.getResources(name);
if (systemResources != null) {
while (systemResources.hasMoreElements()) {
allResources.add(systemResources.nextElement());
}
}
Enumeration<URL> thisResources = findResources(name);
if (thisResources != null) {
while (thisResources.hasMoreElements()) {
allResources.add(thisResources.nextElement());
}
}
Enumeration<URL> parentResources = getParent().getResources(name);
if (parentResources != null) {
while (parentResources.hasMoreElements()) {
allResources.add(parentResources.nextElement());
}
}
return new Enumeration<>() { return new Enumeration<>() {
final Iterator<URL> it = allResources.iterator(); final Enumeration<URL> thisResources = findResources(name);
final Enumeration<URL> parentResources = getParent().getResources(name);
@Override @Override
public boolean hasMoreElements() { public boolean hasMoreElements() {
return it.hasNext(); return thisResources.hasMoreElements() || parentResources.hasMoreElements();
} }
@Override @Override
public URL nextElement() { public URL nextElement() {
return it.next(); return thisResources.hasMoreElements() ? thisResources.nextElement() : parentResources.nextElement();
} }
}; };
} }
@Override @Override
public URL getResource(String name) { public URL getResource(String name) {
URL resource = null; URL resource = findResource(name);
if (systemClassLoader != null) {
resource = systemClassLoader.getResource(name);
}
if (resource == null) {
resource = findResource(name);
}
if (resource == null) { if (resource == null) {
resource = getParent().getResource(name); resource = getParent().getResource(name);
} }
return resource; return resource;
} }
static {
ClassLoader.registerAsParallelCapable();
}
} }

View File

@@ -17,3 +17,5 @@ relocate("org.yaml.snakeyaml")
// these dependencies are already present on the platform // these dependencies are already present on the platform
provided(libs.gson) provided(libs.gson)
provided(libs.velocity.api) provided(libs.velocity.api)
provided(libs.velocity.proxy)
providedDependency(libs.slf4j)

View File

@@ -26,18 +26,18 @@
package org.geysermc.floodgate.velocity.addon.data; package org.geysermc.floodgate.velocity.addon.data;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getClassOrFallbackPrefixed;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField; import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethodByName;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass; import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass;
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
import static org.geysermc.floodgate.core.util.ReflectionUtils.setValue; import static org.geysermc.floodgate.core.util.ReflectionUtils.setValue;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.InitialLoginSessionHandler;
import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import org.geysermc.api.connection.Connection; import org.geysermc.api.connection.Connection;
import org.geysermc.floodgate.core.addon.data.CommonNettyDataHandler; import org.geysermc.floodgate.core.addon.data.CommonNettyDataHandler;
@@ -49,51 +49,16 @@ import org.geysermc.floodgate.core.connection.FloodgateDataHandler.HandleResult;
import org.geysermc.floodgate.core.logger.FloodgateLogger; import org.geysermc.floodgate.core.logger.FloodgateLogger;
public final class VelocityProxyDataHandler extends CommonNettyDataHandler { public final class VelocityProxyDataHandler extends CommonNettyDataHandler {
private static final Field HANDSHAKE;
private static final Class<?> HANDSHAKE_PACKET;
private static final Field HANDSHAKE_SERVER_ADDRESS;
private static final Field REMOTE_ADDRESS; private static final Field REMOTE_ADDRESS;
private static final Class<?> SERVER_LOGIN_PACKET;
private static final Method GET_SESSION_HANDLER;
private static final Class<?> INITIAL_LOGIN_SESSION_HANDLER; private static final Class<?> INITIAL_LOGIN_SESSION_HANDLER;
private static final Field FORCE_KEY_AUTHENTICATION; private static final Field FORCE_KEY_AUTHENTICATION;
static { static {
Class<?> iic = getPrefixedClass("connection.client.InitialInboundConnection");
requireNonNull(iic, "InitialInboundConnection class cannot be null");
HANDSHAKE = getField(iic, "handshake");
requireNonNull(HANDSHAKE, "Handshake field cannot be null");
HANDSHAKE_PACKET = getClassOrFallbackPrefixed(
"protocol.packet.HandshakePacket",
"protocol.packet.Handshake"
);
requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
HANDSHAKE_SERVER_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");
requireNonNull(HANDSHAKE_SERVER_ADDRESS, "Address in the Handshake packet cannot be null");
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection"); Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress"); REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress");
requireNonNull(REMOTE_ADDRESS, "remoteAddress cannot be null"); requireNonNull(REMOTE_ADDRESS, "remoteAddress cannot be null");
SERVER_LOGIN_PACKET = getClassOrFallbackPrefixed(
"protocol.packet.ServerLoginPacket",
"protocol.packet.ServerLogin"
);
requireNonNull(SERVER_LOGIN_PACKET, "ServerLogin packet class cannot be null");
Method sessionHandler = getMethodByName(minecraftConnection, "getActiveSessionHandler", true);
if (sessionHandler == null) {
// We are pre-1.20.2
sessionHandler = getMethodByName(minecraftConnection, "getSessionHandler", true);
}
GET_SESSION_HANDLER = sessionHandler;
requireNonNull(GET_SESSION_HANDLER, "getSessionHandler method cannot be null");
INITIAL_LOGIN_SESSION_HANDLER = getPrefixedClass("connection.client.InitialLoginSessionHandler"); INITIAL_LOGIN_SESSION_HANDLER = getPrefixedClass("connection.client.InitialLoginSessionHandler");
requireNonNull(INITIAL_LOGIN_SESSION_HANDLER, "InitialLoginSessionHandler cannot be null"); requireNonNull(INITIAL_LOGIN_SESSION_HANDLER, "InitialLoginSessionHandler cannot be null");
@@ -122,7 +87,7 @@ public final class VelocityProxyDataHandler extends CommonNettyDataHandler {
@Override @Override
protected Object setHostname(Object handshakePacket, String hostname) { protected Object setHostname(Object handshakePacket, String hostname) {
setValue(handshakePacket, HANDSHAKE_SERVER_ADDRESS, hostname); ((HandshakePacket) handshakePacket).setServerAddress(hostname);
return handshakePacket; return handshakePacket;
} }
@@ -138,23 +103,21 @@ public final class VelocityProxyDataHandler extends CommonNettyDataHandler {
@Override @Override
public boolean channelRead(Object packet) { public boolean channelRead(Object packet) {
if (HANDSHAKE_PACKET.isInstance(packet)) { if (packet instanceof HandshakePacket handshake) {
handle(packet, getCastedValue(packet, HANDSHAKE_SERVER_ADDRESS)); handle(packet, handshake.getServerAddress());
// otherwise, it'll get read twice. once by the packet queue and once by this method // otherwise, it'll get read twice. once by the packet queue and once by this method
return false; return false;
} }
// at this point we know that forceKeyAuthentication is enabled // at this point we know that forceKeyAuthentication is enabled
if (SERVER_LOGIN_PACKET.isInstance(packet)) { if (packet instanceof ServerLoginPacket) {
Object minecraftConnection = ctx.pipeline().get("handler"); MinecraftConnection minecraftConnection = (MinecraftConnection) ctx.pipeline().get("handler");
Object sessionHandler = invoke(minecraftConnection, GET_SESSION_HANDLER); MinecraftSessionHandler sessionHandler = minecraftConnection.getActiveSessionHandler();
if (!INITIAL_LOGIN_SESSION_HANDLER.isInstance(sessionHandler)) { if (!(sessionHandler instanceof InitialLoginSessionHandler)) {
logger.error("Expected player's session handler to be InitialLoginSessionHandler"); logger.error("Expected player's session handler to be InitialLoginSessionHandler");
return true; return true;
} }
if (FORCE_KEY_AUTHENTICATION != null) { setValue(sessionHandler, FORCE_KEY_AUTHENTICATION, false);
setValue(sessionHandler, FORCE_KEY_AUTHENTICATION, false);
}
} }
return true; return true;
} }

View File

@@ -25,17 +25,9 @@
package org.geysermc.floodgate.velocity.addon.data; package org.geysermc.floodgate.velocity.addon.data;
import static java.util.Objects.requireNonNull;
import static org.geysermc.floodgate.core.util.ReflectionUtils.castedInvoke;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getClassOrFallbackPrefixed;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass;
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
import static org.geysermc.floodgate.core.util.ReflectionUtils.setValue;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
@@ -44,8 +36,6 @@ import io.netty.util.AttributeKey;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.geysermc.api.connection.Connection; import org.geysermc.api.connection.Connection;
import org.geysermc.floodgate.core.api.SimpleFloodgateApi; import org.geysermc.floodgate.core.api.SimpleFloodgateApi;
import org.geysermc.floodgate.core.connection.FloodgateConnection; import org.geysermc.floodgate.core.connection.FloodgateConnection;
@@ -55,32 +45,6 @@ import org.geysermc.floodgate.core.crypto.FloodgateDataCodec;
@ChannelHandler.Sharable @ChannelHandler.Sharable
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapter { public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapter {
private static final Class<?> HANDSHAKE_PACKET;
private static final Field HANDSHAKE_ADDRESS;
private static final Method GET_ASSOCIATION;
private static final Method GET_PLAYER;
static {
HANDSHAKE_PACKET = getClassOrFallbackPrefixed(
"protocol.packet.HandshakePacket",
"protocol.packet.Handshake"
);
requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
HANDSHAKE_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");
requireNonNull(HANDSHAKE_ADDRESS, "Address field of the Handshake packet cannot be null");
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
GET_ASSOCIATION = getMethod(minecraftConnection, "getAssociation");
requireNonNull(GET_ASSOCIATION, "getAssociation in MinecraftConnection cannot be null");
Class<?> serverConnection = getPrefixedClass("connection.backend.VelocityServerConnection");
GET_PLAYER = getMethod(serverConnection, "getPlayer");
requireNonNull(GET_PLAYER, "getPlayer in VelocityServerConnection cannot be null");
}
@Inject SimpleFloodgateApi api; @Inject SimpleFloodgateApi api;
@Inject FloodgateDataCodec dataCodec; @Inject FloodgateDataCodec dataCodec;
@@ -90,16 +54,15 @@ public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapt
@Override @Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (HANDSHAKE_PACKET.isInstance(packet)) { if (packet instanceof HandshakePacket handshake) {
String address = getCastedValue(packet, HANDSHAKE_ADDRESS); String address = handshake.getServerAddress();
// The connection to the backend server is not the same connection as the client to the proxy. // The connection to the backend server is not the same connection as the client to the proxy.
// This gets the client to proxy Connection from the backend server connection. // This gets the client to proxy Connection from the backend server connection.
// get the FloodgatePlayer from the ConnectedPlayer // get the FloodgatePlayer from the ConnectedPlayer
Object minecraftConnection = ctx.pipeline().get("handler"); MinecraftConnection minecraftConnection = (MinecraftConnection) ctx.pipeline().get("handler");
Object association = invoke(minecraftConnection, GET_ASSOCIATION); Player velocityPlayer = (Player) minecraftConnection.getAssociation();
Player velocityPlayer = castedInvoke(association, GET_PLAYER);
Connection connection = api.connectionByPlatformIdentifier(velocityPlayer); Connection connection = api.connectionByPlatformIdentifier(velocityPlayer);
if (connection != null) { if (connection != null) {
@@ -120,7 +83,7 @@ public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapt
remaining = address.substring(addressFinished); remaining = address.substring(addressFinished);
} }
setValue(packet, HANDSHAKE_ADDRESS, originalAddress + '\0' + encodedData + remaining); handshake.setServerAddress(originalAddress + '\0' + encodedData + remaining);
} }
ctx.pipeline().remove(this); ctx.pipeline().remove(this);

View File

@@ -25,12 +25,12 @@
package org.geysermc.floodgate.velocity.inject; package org.geysermc.floodgate.velocity.inject;
import static org.geysermc.floodgate.core.util.ReflectionUtils.castedInvoke; import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod; import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue;
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke; import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.proxy.network.ConnectionManager;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -46,31 +46,23 @@ public final class VelocityInjector extends CommonPlatformInjector {
@Getter private boolean injected; @Getter private boolean injected;
@Override @Override
@SuppressWarnings("rawtypes") @SuppressWarnings({"DataFlowIssue", "deprecation"})
public void inject() { public void inject() {
if (isInjected()) { if (isInjected()) {
return; return;
} }
Object connectionManager = getValue(server, "cm"); ConnectionManager connectionManager = getCastedValue(server, "cm");
// Client <-> Proxy // Client <-> Proxy
Object serverInitializerHolder = getValue(connectionManager, "serverChannelInitializer"); var serverChannelInitializer = connectionManager.serverChannelInitializer;
ChannelInitializer serverInitializer = castedInvoke(serverInitializerHolder, "get"); serverChannelInitializer.set(new VelocityChannelInitializer(this, serverChannelInitializer.get(), false));
Method serverSetter = getMethod(serverInitializerHolder, "set", ChannelInitializer.class);
invoke(serverInitializerHolder, serverSetter,
new VelocityChannelInitializer(this, serverInitializer, false));
// Proxy <-> Server // Proxy <-> Server
Object backendInitializerHolder = getValue(connectionManager, "backendChannelInitializer"); var backendChannelInitializer = connectionManager.backendChannelInitializer;
ChannelInitializer backendInitializer = castedInvoke(backendInitializerHolder, "get"); backendChannelInitializer.set(new VelocityChannelInitializer(this, backendChannelInitializer.get(), true));
Method backendSetter = getMethod(backendInitializerHolder, "set", ChannelInitializer.class);
invoke(backendInitializerHolder, backendSetter,
new VelocityChannelInitializer(this, backendInitializer, true));
injected = true; injected = true;
} }

View File

@@ -26,20 +26,18 @@
package org.geysermc.floodgate.velocity.player; package org.geysermc.floodgate.velocity.player;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField; import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getFieldOfType;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass; import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass;
import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue; import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue;
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.client.LoginInboundConnection;
import com.velocitypowered.proxy.connection.util.VelocityInboundConnection;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.inject.Singleton; import jakarta.inject.Singleton;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.api.connection.Connection; import org.geysermc.api.connection.Connection;
import org.geysermc.floodgate.core.connection.ConnectionManager; import org.geysermc.floodgate.core.connection.ConnectionManager;
@@ -48,10 +46,6 @@ import org.geysermc.floodgate.core.connection.ConnectionManager;
public class VelocityConnectionManager extends ConnectionManager { public class VelocityConnectionManager extends ConnectionManager {
private static final Class<?> LOGIN_INBOUND_CONNECTION; private static final Class<?> LOGIN_INBOUND_CONNECTION;
private static final Field INITIAL_CONNECTION_DELEGATE; private static final Field INITIAL_CONNECTION_DELEGATE;
private static final Class<?> INITIAL_INBOUND_CONNECTION;
private static final Class<?> MINECRAFT_CONNECTION;
private static final Method GET_CONNECTION;
private static final Field CHANNEL;
@Inject @Inject
@Named("connectionAttribute") @Named("connectionAttribute")
@@ -59,25 +53,21 @@ public class VelocityConnectionManager extends ConnectionManager {
@Override @Override
protected @Nullable Object platformIdentifierOrConnectionFor(Object input) { protected @Nullable Object platformIdentifierOrConnectionFor(Object input) {
if (input instanceof Player) { // ConnectedPlayer (Player) implements VelocityInboundConnection,
// ConnectedPlayer implements VelocityInboundConnection, // just like InitialInboundConnection
// just like InitialInboundConnection if (input instanceof VelocityInboundConnection connection) {
return invoke(input, GET_CONNECTION); return connection.getConnection();
} }
// LoginInboundConnection doesn't have a direct Channel reference, // LoginInboundConnection doesn't have a direct Channel reference,
// but it does have an InitialInboundConnection reference // but it does have an InitialInboundConnection reference
if (LOGIN_INBOUND_CONNECTION.isInstance(input)) { if (input instanceof LoginInboundConnection) {
return getValue(input, INITIAL_CONNECTION_DELEGATE); return getValue(input, INITIAL_CONNECTION_DELEGATE);
} }
// InitialInboundConnection -> MinecraftConnection -> Channel -> FloodgateConnection attribute // InitialInboundConnection -> MinecraftConnection -> Channel -> FloodgateConnection attribute
if (input instanceof MinecraftConnection connection) {
if (INITIAL_INBOUND_CONNECTION.isInstance(input)) { return connection.getChannel();
return invoke(input, GET_CONNECTION);
}
if (MINECRAFT_CONNECTION.isInstance(input)) {
return getValue(input, CHANNEL);
} }
if (input instanceof Channel channel) { if (input instanceof Channel channel) {
return channel.attr(connectionAttribute).get(); return channel.attr(connectionAttribute).get();
@@ -96,13 +86,5 @@ public class VelocityConnectionManager extends ConnectionManager {
static { static {
LOGIN_INBOUND_CONNECTION = getPrefixedClass("connection.client.LoginInboundConnection"); LOGIN_INBOUND_CONNECTION = getPrefixedClass("connection.client.LoginInboundConnection");
INITIAL_CONNECTION_DELEGATE = getField(LOGIN_INBOUND_CONNECTION, "delegate"); INITIAL_CONNECTION_DELEGATE = getField(LOGIN_INBOUND_CONNECTION, "delegate");
INITIAL_INBOUND_CONNECTION = getPrefixedClass("connection.client.InitialInboundConnection");
MINECRAFT_CONNECTION = getPrefixedClass("connection.MinecraftConnection");
Class<?> velocityInboundConnection = getPrefixedClass("connection.util.VelocityInboundConnection");
GET_CONNECTION = getMethod(velocityInboundConnection, "getConnection");
CHANNEL = getFieldOfType(MINECRAFT_CONNECTION, Channel.class);
} }
} }

View File

@@ -7,6 +7,8 @@ dependencies {
compileOnlyApi(libs.velocity.api) compileOnlyApi(libs.velocity.api)
} }
providedDependency(libs.slf4j)
tasks { tasks {
jar { jar {
dependsOn(":velocity-base:build", configurations.runtimeClasspath) dependsOn(":velocity-base:build", configurations.runtimeClasspath)