WIP: Almost done

This commit is contained in:
Adrian Bergqvist 2022-09-27 00:14:33 +02:00
parent 2ab257ffe7
commit 007ff8511a
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
9 changed files with 129 additions and 98 deletions

View File

@ -4,7 +4,7 @@ plugins {
} }
group 'org.adde0109' group 'org.adde0109'
version '1.0.0-alpha' version '1.0.2-alpha'
repositories { repositories {
maven { maven {

View File

@ -20,6 +20,7 @@ import org.adde0109.ambassador.forge.ForgeConnection;
import org.adde0109.ambassador.forge.ForgeHandshakeHandler; import org.adde0109.ambassador.forge.ForgeHandshakeHandler;
import org.adde0109.ambassador.forge.ForgeHandshakeUtils; import org.adde0109.ambassador.forge.ForgeHandshakeUtils;
import org.adde0109.ambassador.forge.ForgeServerSwitchHandler; import org.adde0109.ambassador.forge.ForgeServerSwitchHandler;
import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer;
import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer; import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer;
import org.adde0109.ambassador.velocity.VelocityEventHandler; import org.adde0109.ambassador.velocity.VelocityEventHandler;
import org.bstats.charts.SingleLineChart; import org.bstats.charts.SingleLineChart;
@ -28,7 +29,7 @@ import org.slf4j.Logger;
import java.nio.file.Path; import java.nio.file.Path;
@Plugin(id = "ambassador", name = "Ambassador", version = "1.0.0-alpha", authors = {"adde0109"}) @Plugin(id = "ambassador", name = "Ambassador", version = "1.0.2-alpha", authors = {"adde0109"})
public class Ambassador { public class Ambassador {
public ProxyServer server; public ProxyServer server;
@ -72,11 +73,12 @@ public class Ambassador {
private void inject() throws ReflectiveOperationException { private void inject() throws ReflectiveOperationException {
Field cmField = VelocityServer.class.getDeclaredField("cm"); Field cmField = VelocityServer.class.getDeclaredField("cm");
cmField.setAccessible(true); cmField.setAccessible(true);
Field endpointMap = ConnectionManager.class.getDeclaredField("endpoints");
endpointMap.setAccessible(true);
ChannelInitializer<?> original = ((ConnectionManager) cmField.get(server)).serverChannelInitializer.get(); ChannelInitializer<?> original = ((ConnectionManager) cmField.get(server)).serverChannelInitializer.get();
((ConnectionManager) cmField.get(server)).serverChannelInitializer.set(new VelocityServerChannelInitializer(original)); ((ConnectionManager) cmField.get(server)).serverChannelInitializer.set(new VelocityServerChannelInitializer(original));
ChannelInitializer<?> originalBackend = ((ConnectionManager) cmField.get(server)).backendChannelInitializer.get();
((ConnectionManager) cmField.get(server)).backendChannelInitializer.set(new VelocityBackendChannelInitializer(originalBackend,(VelocityServer) server));
} }
@Subscribe @Subscribe

View File

@ -2,4 +2,5 @@ package org.adde0109.ambassador.forge;
public class ForgeConstants { public class ForgeConstants {
public static final String HANDLER = "Modern Forge handler"; public static final String HANDLER = "Modern Forge handler";
public static final ForgeFML2ConnectionType ForgeFML2 = new ForgeFML2ConnectionType();
} }

View File

@ -25,13 +25,12 @@ import org.checkerframework.checker.units.qual.A;
import org.checkerframework.checker.units.qual.C; import org.checkerframework.checker.units.qual.C;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnectionPhase { public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnectionPhase {
private boolean isResettable; private boolean isResettable;
@ -40,24 +39,30 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
public byte[] modListData; public byte[] modListData;
private final ArrayList<Integer> listenerList = new ArrayList(); private final ArrayList<Integer> listenerList = new ArrayList();
public ArrayList<MinecraftPacket> packagesToSendAfterReset = new ArrayList<>();
private Runnable whenComplete; private Runnable whenComplete;
public boolean isReady = false; public ClientPhase clientPhase = ClientPhase.HANDSHAKE;
@Override @Override
public void handleLogin(ConnectedPlayer player,ForgeHandshakeUtils.CachedServerHandshake handshake, Continuation continuation) { public void handleLogin(ConnectedPlayer player,ForgeHandshakeUtils.CachedServerHandshake handshake, Continuation continuation) {
this.whenComplete = continuation::resume;
final MinecraftConnection connection = player.getConnection(); final MinecraftConnection connection = player.getConnection();
VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),player); VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(), player);
if(handshake == null) { if (handshake == null) {
connection.delayedWrite(new LoginPluginMessage(0,"fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist))); connection.delayedWrite(new LoginPluginMessage(98, "fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
listenerList.add(0); listenerList.add(98);
this.whenComplete = () -> {
this.clientPhase = ClientPhase.VANILLA;
continuation.resume();
};
} else { } else {
connection.delayedWrite(new LoginPluginMessage(0,"fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket))); connection.delayedWrite(new LoginPluginMessage(0, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket)));
listenerList.add(0); listenerList.add(0);
for (int i = 0;i<handshake.otherPackets.size();i++) { for (int i = 0; i < handshake.otherPackets.size(); i++) {
connection.delayedWrite(new LoginPluginMessage(i+1,"fml:loginwrapper", Unpooled.wrappedBuffer(handshake.otherPackets.get(i)))); connection.delayedWrite(new LoginPluginMessage(i + 1, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.otherPackets.get(i))));
listenerList.add(i+1); listenerList.add(i + 1);
} }
this.whenComplete = () -> {
this.clientPhase = ClientPhase.MODDED;
continuation.resume();
};
} }
connection.setSessionHandler(sessionHandler); connection.setSessionHandler(sessionHandler);
connection.flush(); connection.flush();
@ -65,40 +70,35 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
@Override @Override
public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) { public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) {
if (packet.getId() == 98) {
this.clientPhase = ClientPhase.HANDSHAKE;
this.isResettable = packet.isSuccess();
} else if (packet.getId() == 0) {
this.clientPhase = ClientPhase.MODLIST;
}
if (!listenerList.removeIf(id -> id.equals(packet.getId()))) { if (!listenerList.removeIf(id -> id.equals(packet.getId()))) {
player.getConnectionInFlight().getConnection().write(packet.retain()); player.getConnectionInFlight().getConnection().write(packet.retain());
return true; } else if (listenerList.isEmpty() && whenComplete != null) {
}
if (packet.getId() == 98) {
isReady = true;
for (MinecraftPacket packet1 : packagesToSendAfterReset) {
player.getConnection().delayedWrite(packet1);
}
packagesToSendAfterReset = new ArrayList<>();
player.getConnection().flush();
} else if (packet.getId() == 0) {
if (!packet.isSuccess()) {
//TODO: Write disconnect message to end user
player.getConnection().close();
return true;
}
modListData = ByteBufUtil.getBytes(packet.content());
}
if (listenerList.isEmpty() && whenComplete != null) {
whenComplete.run(); whenComplete.run();
whenComplete = null; whenComplete = null;
} }
return true; return true;
} }
public void reset(ConnectedPlayer player,MinecraftConnection connection) { public void reset(ConnectedPlayer player, Runnable whenComplete) {
isReady = false; this.whenComplete = whenComplete;
if (player.getConnectedServer() != null) { if (player.getConnectedServer() != null) {
player.getConnectedServer().disconnect(); player.getConnectedServer().disconnect();
} }
MinecraftConnection connection = player.getConnection();
connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),player)); connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),player));
connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); if (connection.getState() == StateRegistry.LOGIN) {
connection.write(new LoginPluginMessage(98,"fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
} else {
connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
connection.setState(StateRegistry.LOGIN);
}
listenerList.add(98); listenerList.add(98);
connection.setState(StateRegistry.LOGIN); this.clientPhase = null;
} }
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) { public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
VelocityConfiguration configuration = (VelocityConfiguration) server.getConfiguration(); VelocityConfiguration configuration = (VelocityConfiguration) server.getConfiguration();
@ -109,16 +109,21 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
ServerLoginSuccess success = new ServerLoginSuccess(); ServerLoginSuccess success = new ServerLoginSuccess();
success.setUsername(player.getUsername()); success.setUsername(player.getUsername());
success.setUuid(playerUniqueId); success.setUuid(playerUniqueId);
send(player,success); connection.write(success);
this.clientPhase = this.clientPhase == ClientPhase.MODLIST ? ClientPhase.MODDED : ClientPhase.VANILLA;
connection.setState(StateRegistry.PLAY); connection.setState(StateRegistry.PLAY);
connection.setSessionHandler(((VelocityForgeHandshakeSessionHandler) connection.getSessionHandler()).getOriginal()); connection.setSessionHandler(((VelocityForgeHandshakeSessionHandler) connection.getSessionHandler()).getOriginal());
} }
public void send(ConnectedPlayer player, MinecraftPacket message) { public void send(ConnectedPlayer player, MinecraftPacket message) {
if (isReady) {
player.getConnection().write(message); player.getConnection().write(message);
} else { }
packagesToSendAfterReset.add(message);
} public enum ClientPhase {
VANILLA,
HANDSHAKE,
MODLIST,
MODDED
} }
} }

View File

@ -20,7 +20,7 @@ public class ForgeFML2ConnectionType implements ConnectionType {
@Override @Override
public BackendConnectionPhase getInitialBackendPhase() { public BackendConnectionPhase getInitialBackendPhase() {
return BackendConnectionPhases.UNKNOWN; return new VelocityForgeBackendConnectionPhase();
} }
@Override @Override

View File

@ -1,16 +1,11 @@
package org.adde0109.ambassador.velocity; package org.adde0109.ambassador.velocity;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import org.adde0109.ambassador.forge.ForgeConstants; import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.forge.ForgeFML2ClientConnectionPhase;
import org.adde0109.ambassador.forge.ForgeFML2ConnectionType;
import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendHandshakeHandler; import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendHandshakeHandler;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -36,20 +31,8 @@ public class VelocityBackendChannelInitializer extends ChannelInitializer<Channe
} }
@Override @Override
protected void initChannel(@NotNull Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
INIT_CHANNEL.invoke(original); INIT_CHANNEL.invoke(original,ch);
} ch.pipeline().addLast(ForgeConstants.HANDLER, new VelocityForgeBackendHandshakeHandler(server));
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.handler() instanceof MinecraftConnection connection) {
if (connection.getAssociation() instanceof VelocityServerConnection serverConnection) {
if (serverConnection.getPlayer().getPhase() instanceof ForgeFML2ClientConnectionPhase) {
connection.setType(new ForgeFML2ConnectionType());
ctx.pipeline().addBefore(ctx.name(), ForgeConstants.HANDLER, new VelocityForgeBackendHandshakeHandler(connection, serverConnection, server));
}
}
}
super.handlerAdded(ctx);
} }
} }

View File

@ -8,7 +8,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.Handshake;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.adde0109.ambassador.forge.ForgeFML2ConnectionType; import org.adde0109.ambassador.forge.ForgeConstants;
public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler { public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler {
private final HandshakeSessionHandler original; private final HandshakeSessionHandler original;
@ -24,7 +24,7 @@ public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler
handshake.handle(original); handshake.handle(original);
if (connection.getType() == ConnectionTypes.VANILLA && connection.getState() == StateRegistry.LOGIN) { if (connection.getType() == ConnectionTypes.VANILLA && connection.getState() == StateRegistry.LOGIN) {
if (handshake.getServerAddress().split("\0")[1].equals("FML2")) { if (handshake.getServerAddress().split("\0")[1].equals("FML2")) {
connection.setType(new ForgeFML2ConnectionType()); connection.setType(ForgeConstants.ForgeFML2);
} }
} }
return true; return true;

View File

@ -1,45 +1,46 @@
package org.adde0109.ambassador.velocity.backend; package org.adde0109.ambassador.velocity.backend;
import com.velocitypowered.proxy.Velocity;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.adde0109.ambassador.Ambassador;
import org.adde0109.ambassador.forge.ForgeFML2ClientConnectionPhase; import org.adde0109.ambassador.forge.ForgeFML2ClientConnectionPhase;
import org.adde0109.ambassador.forge.ForgeHandshakeUtils;
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase { public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
private final Ambassador ambassador; private final List<LoginPluginMessage> queuedHandshakePackets = new ArrayList<>();
private boolean vanilla = true;
public VelocityForgeBackendConnectionPhase(Ambassador ambassador) { public VelocityForgeBackendConnectionPhase() {
this.ambassador = ambassador;
} }
public void handleSuccess(VelocityServerConnection serverCon, VelocityServer server) { public void handleSuccess(VelocityServerConnection serverCon, VelocityServer server) {
ForgeFML2ClientConnectionPhase clientPhase = ((ForgeFML2ClientConnectionPhase) serverCon.getPlayer().getPhase()); ForgeFML2ClientConnectionPhase clientPhase = ((ForgeFML2ClientConnectionPhase) serverCon.getPlayer().getPhase());
if (vanilla) { if (clientPhase.clientPhase == ForgeFML2ClientConnectionPhase.ClientPhase.HANDSHAKE || clientPhase.clientPhase == ForgeFML2ClientConnectionPhase.ClientPhase.MODLIST) {
clientPhase.reset(serverCon.getPlayer(),serverCon.getPlayer().getConnection()); clientPhase.complete((VelocityServer) server,serverCon.getPlayer(),serverCon.getPlayer().getConnection());
} }
clientPhase.complete((VelocityServer) ambassador.server,serverCon.getPlayer(),serverCon.getPlayer().getConnection());
} }
public boolean handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) { public boolean handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) throws Exception {
vanilla = false; ForgeFML2ClientConnectionPhase clientPhase = ((ForgeFML2ClientConnectionPhase) player.getPhase());
message.retain(); message.retain();
((ForgeFML2ClientConnectionPhase) player.getPhase()).send(player,message); if (clientPhase.clientPhase == ForgeFML2ClientConnectionPhase.ClientPhase.VANILLA) {
clientPhase.reset(player, () -> {
for (LoginPluginMessage msg: queuedHandshakePackets) {
player.getConnection().delayedWrite(msg);
}
player.getConnection().flush();
});
queuedHandshakePackets.add(message);
} else if (clientPhase.clientPhase != null) {
player.getConnection().write(message);
} else {
queuedHandshakePackets.add(message);
}
return true; return true;
} }

View File

@ -3,34 +3,73 @@ package org.adde0109.ambassador.velocity.backend;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.*;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.forge.ForgeFML2ClientConnectionPhase;
public class VelocityForgeBackendHandshakeHandler extends ChannelInboundHandlerAdapter { import java.util.concurrent.CountDownLatch;
private final MinecraftConnection connection; public class VelocityForgeBackendHandshakeHandler extends ChannelDuplexHandler {
private final VelocityServerConnection serverConnection;
private VelocityServerConnection serverConnection;
private final VelocityServer server; private final VelocityServer server;
public VelocityForgeBackendHandshakeHandler(MinecraftConnection connection, VelocityServerConnection serverConnection, VelocityServer server) { public VelocityForgeBackendHandshakeHandler(VelocityServer server) {
this.connection = connection;
this.serverConnection = serverConnection;
this.server = server; this.server = server;
} }
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if ((msg instanceof ServerLogin)) {
ChannelHandler handler = ctx.pipeline().get(Connections.HANDLER);
if (handler instanceof MinecraftConnection connection) {
if (connection.getAssociation() instanceof VelocityServerConnection serverConnection) {
if (serverConnection.getPlayer().getPhase() instanceof ForgeFML2ClientConnectionPhase phase) {
init(connection,serverConnection);
if (phase.clientPhase == ForgeFML2ClientConnectionPhase.ClientPhase.MODDED) {
phase.reset(serverConnection.getPlayer(), () -> {
ctx.write(msg, promise);
ctx.flush();
});
} else {
ctx.write(msg,promise);
}
} else {
ctx.pipeline().remove(this);
ctx.write(msg,promise);
}
} else {
throw new Exception("Connection not associated with a server connection");
}
} else {
throw new Exception("Default minecraft packet handler not found");
}
} else {
ctx.write(msg,promise);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof LoginPluginMessage message && message.getChannel().equals("fml:loginwrapper")) { if (msg instanceof LoginPluginMessage message && message.getChannel().equals("fml:loginwrapper")) {
((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handle(serverConnection, serverConnection.getPlayer(), message); ((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handle(serverConnection, serverConnection.getPlayer(), message);
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
} else if (msg instanceof ServerLoginSuccess) { } else if (msg instanceof ServerLoginSuccess) {
((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handleSuccess(serverConnection,server); ((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handleSuccess(serverConnection,server);
ReferenceCountUtil.release(msg); ctx.fireChannelRead(msg);
} else { } else {
ctx.fireChannelRead(msg); ctx.fireChannelRead(msg);
} }
} }
private void init(MinecraftConnection connection, VelocityServerConnection serverConnection) {
this.serverConnection = serverConnection;
connection.setType(ForgeConstants.ForgeFML2);
serverConnection.setConnectionPhase(connection.getType().getInitialBackendPhase());
}
} }