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:
@@ -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 ""
|
|
||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user