Reconnect-to-sync handler
This commit is contained in:
parent
81d22900fa
commit
b021bed467
|
|
@ -30,21 +30,17 @@ import java.util.NoSuchElementException;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class FML2CRPMClientConnectionPhase implements VelocityForgeClientConnectionPhase {
|
||||
public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnectionPhase {
|
||||
private static String OUTBOUND_CATCHER_NAME = "ambassador-catcher";
|
||||
|
||||
//TODO: Use modData inside ConnectedPlayer instead
|
||||
public byte[] modListData;
|
||||
private RegisteredServer backupServer;
|
||||
|
||||
private VelocityLoginPayloadManager payloadManager;
|
||||
public ClientPhase clientPhase = ClientPhase.HANDSHAKE;
|
||||
@Override
|
||||
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
final MinecraftConnection connection = player.getConnection();
|
||||
payloadManager = new VelocityLoginPayloadManager(connection);
|
||||
VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(), player);
|
||||
payloadManager.sendPayload("fml:loginwrapper",Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist)).thenAccept((data) -> {
|
||||
getPayloadManager().sendPayload("fml:loginwrapper",Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist)).thenAccept((data) -> {
|
||||
if (modListData == null)
|
||||
modListData = ByteBufUtil.getBytes(data);
|
||||
this.clientPhase = ClientPhase.MODDED;
|
||||
|
|
@ -54,11 +50,7 @@ public class FML2CRPMClientConnectionPhase implements VelocityForgeClientConnect
|
|||
connection.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) {
|
||||
return true;
|
||||
}
|
||||
public void reset(ConnectedPlayer player, Runnable whenComplete) {
|
||||
public void reset(VelocityServerConnection serverConnection, ConnectedPlayer player, Runnable whenComplete) {
|
||||
if (player.getConnectedServer() != null) {
|
||||
backupServer = player.getConnectedServer().getServer();
|
||||
player.getConnectedServer().disconnect();
|
||||
|
|
@ -70,12 +62,12 @@ public class FML2CRPMClientConnectionPhase implements VelocityForgeClientConnect
|
|||
|
||||
|
||||
if (connection.getState() == StateRegistry.LOGIN) {
|
||||
payloadManager.sendPayload("fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket()));
|
||||
getPayloadManager().sendPayload("fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket()));
|
||||
} else {
|
||||
connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
}
|
||||
payloadManager.listenFor(98).thenAccept((ignored) -> {
|
||||
getPayloadManager().listenFor(98).thenAccept((ignored) -> {
|
||||
this.clientPhase = ClientPhase.HANDSHAKE;
|
||||
whenComplete.run();
|
||||
});
|
||||
|
|
@ -112,22 +104,4 @@ public class FML2CRPMClientConnectionPhase implements VelocityForgeClientConnect
|
|||
}
|
||||
}
|
||||
|
||||
public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
|
||||
payloadManager.sendPayload("fml:loginwrapper",payload.content()).thenAccept((responseData) -> {
|
||||
//Move this to the backend. Backend should have its own forwarder.
|
||||
serverConnection.getConnection().write(new LoginPluginResponse(payload.getId(),responseData.isReadable(),responseData.retain()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public VelocityLoginPayloadManager getPayloadManager() {
|
||||
return payloadManager;
|
||||
}
|
||||
|
||||
public enum ClientPhase {
|
||||
VANILLA,
|
||||
HANDSHAKE,
|
||||
MODLIST,
|
||||
MODDED
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,109 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerPing;
|
||||
import com.velocitypowered.api.util.ModInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.connection.client.LoginSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
import org.adde0109.ambassador.velocity.VelocityLoginPayloadManager;
|
||||
import org.apache.commons.collections4.map.PassiveExpiringMap;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FML2ClientConnectionPhase implements VelocityForgeClientConnectionPhase {
|
||||
public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhase {
|
||||
|
||||
private static final PassiveExpiringMap<String,RegisteredServer> TEMPORARY_FORCED = new PassiveExpiringMap<>(120, TimeUnit.SECONDS);
|
||||
|
||||
private Throwable throwable;
|
||||
private RegisteredServer triedServer;
|
||||
private Continuation continuation;
|
||||
|
||||
|
||||
@Override
|
||||
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
MinecraftSessionHandler sessionHandler = player.getConnection().getSessionHandler();
|
||||
if (player.getConnection().getSessionHandler() == null) {
|
||||
continuation.resumeWithException(new Exception("No current player session handler"));
|
||||
return;
|
||||
}
|
||||
if (!(player.getConnection().getSessionHandler() instanceof LoginSessionHandler)) {
|
||||
continuation.resumeWithException(new Exception("Invalid current player session handler:" + player.getConnection().getSessionHandler().getClass().getName()));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Method connectToInitalServer = LoginSessionHandler.class.getDeclaredMethod("connectToInitialServer");
|
||||
connectToInitalServer.setAccessible(true);
|
||||
connectToInitalServer.invoke(sessionHandler);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
continuation.resumeWithException(e);
|
||||
this.continuation = continuation;
|
||||
final MinecraftConnection connection = player.getConnection();
|
||||
|
||||
final Runnable defaultTask = () -> {
|
||||
Optional<RegisteredServer> initialFromConfig = player.getNextServerToTry();
|
||||
PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
|
||||
initialFromConfig.orElse(null));
|
||||
server.getEventManager().fire(event)
|
||||
.thenRun(() -> {
|
||||
Optional<RegisteredServer> toTry = event.getInitialServer();
|
||||
tryServer(player, toTry.orElse(null));
|
||||
});
|
||||
};
|
||||
|
||||
final RegisteredServer forced = TEMPORARY_FORCED.remove(player.getUsername());
|
||||
if (forced != null) {
|
||||
forced.ping().whenCompleteAsync((msg,ex) -> {
|
||||
if (ex == null) {
|
||||
if (throwable == null)
|
||||
throwable = ex;
|
||||
handlePingResponse(msg);
|
||||
} else {
|
||||
defaultTask.run();
|
||||
}
|
||||
},connection.eventLoop());
|
||||
} else {
|
||||
connection.eventLoop().submit(defaultTask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) {
|
||||
return VelocityForgeClientConnectionPhase.super.handle(player, packet);
|
||||
public void reset(VelocityServerConnection serverConnection, ConnectedPlayer player, Runnable whenComplete) {
|
||||
TEMPORARY_FORCED.put(player.getUsername(),serverConnection.getServer());
|
||||
player.disconnect(Component.text("Please reconnect"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
|
||||
if (triedServer != null)
|
||||
player.sendMessage(Component.translatable("velocity.error.connecting-server-error",
|
||||
Component.text(triedServer.getServerInfo().getName())));
|
||||
}
|
||||
|
||||
private void tryServer(ConnectedPlayer player, RegisteredServer server) {
|
||||
if (server == null) {
|
||||
player.disconnect0(Component.translatable("velocity.error.no-available-servers",
|
||||
NamedTextColor.RED), true);
|
||||
return;
|
||||
}
|
||||
server.ping().whenCompleteAsync((msg,ex) -> {
|
||||
if (ex != null) {
|
||||
if (throwable == null)
|
||||
throwable = ex;
|
||||
tryServer(player,player.getNextServerToTry().orElse(null));
|
||||
} else {
|
||||
handlePingResponse(msg);
|
||||
}
|
||||
}, player.getConnection().eventLoop());
|
||||
}
|
||||
|
||||
private void handlePingResponse(ServerPing ping) {
|
||||
if (ping.getModinfo().isEmpty() || !ping.getModinfo().get().getType().equals("Ambassador")) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
ModInfo.Mod mod = ping.getModinfo().get().getMods().get(0);
|
||||
String data = mod.getId();
|
||||
String markers = mod.getVersion();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public class ForgeFML2ConnectionType implements ConnectionType {
|
|||
|
||||
@Override
|
||||
public ClientConnectionPhase getInitialClientPhase() {
|
||||
return new FML2CRPMClientConnectionPhase();
|
||||
return new FML2ClientConnectionPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class VelocityEventHandler {
|
|||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
player.getConnection().eventLoop().submit(() -> phase.handleLogin(player, (VelocityServer) ambassador.server,continuation));
|
||||
player.getConnection().eventLoop().submit(() -> phase.fireLoginEvent(player, (VelocityServer) ambassador.server,continuation));
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
|
|
|
|||
|
|
@ -3,26 +3,57 @@ package org.adde0109.ambassador.velocity;
|
|||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ClientConnectionPhase;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase;
|
||||
import org.adde0109.ambassador.forge.ForgeHandshakeUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public interface VelocityForgeClientConnectionPhase extends ClientConnectionPhase {
|
||||
public abstract class VelocityForgeClientConnectionPhase implements ClientConnectionPhase {
|
||||
//TODO:Make class when PCF is done
|
||||
|
||||
|
||||
default void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
VelocityLoginPayloadManager payloadManager;
|
||||
public FML2CRPMClientConnectionPhase.ClientPhase clientPhase = ClientPhase.HANDSHAKE;
|
||||
|
||||
}
|
||||
default boolean handle(ConnectedPlayer player,LoginPluginResponse packet) {
|
||||
return false;
|
||||
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
}
|
||||
|
||||
default VelocityLoginPayloadManager getPayloadManager() {
|
||||
return null;
|
||||
public void reset(VelocityServerConnection serverConnection,ConnectedPlayer player, Runnable whenComplete) {
|
||||
}
|
||||
|
||||
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
|
||||
|
||||
}
|
||||
|
||||
final void fireLoginEvent(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
payloadManager = new VelocityLoginPayloadManager(player.getConnection());
|
||||
handleLogin(player,server,continuation);
|
||||
}
|
||||
|
||||
public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
|
||||
if (payloadManager == null) {
|
||||
return;
|
||||
}
|
||||
payloadManager.sendPayload("fml:loginwrapper",payload.content()).thenAccept((responseData) -> {
|
||||
//Move this to the backend. Backend should have its own forwarder.
|
||||
serverConnection.getConnection().write(new LoginPluginResponse(payload.getId(),responseData.isReadable(),responseData.retain()));
|
||||
});
|
||||
}
|
||||
|
||||
public final VelocityLoginPayloadManager getPayloadManager() {
|
||||
return payloadManager;
|
||||
}
|
||||
|
||||
public enum ClientPhase {
|
||||
VANILLA,
|
||||
HANDSHAKE,
|
||||
MODLIST,
|
||||
MODDED
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
|||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
@ -18,17 +19,17 @@ public class VelocityForgeBackendConnectionPhase implements BackendConnectionPha
|
|||
}
|
||||
|
||||
public void handleSuccess(VelocityServerConnection serverCon, VelocityServer server) {
|
||||
FML2CRPMClientConnectionPhase clientPhase = ((FML2CRPMClientConnectionPhase) serverCon.getPlayer().getPhase());
|
||||
if (clientPhase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.HANDSHAKE || clientPhase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.MODLIST) {
|
||||
VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) serverCon.getPlayer().getPhase());
|
||||
if (clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.HANDSHAKE || clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.MODLIST) {
|
||||
clientPhase.complete((VelocityServer) server,serverCon.getPlayer(),serverCon.getPlayer().getConnection());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) throws Exception {
|
||||
FML2CRPMClientConnectionPhase clientPhase = ((FML2CRPMClientConnectionPhase) player.getPhase());
|
||||
VelocityForgeClientConnectionPhase clientPhase = ((FML2CRPMClientConnectionPhase) player.getPhase());
|
||||
message.retain();
|
||||
if (clientPhase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.VANILLA) {
|
||||
clientPhase.reset(player, () -> {
|
||||
clientPhase.reset(server,player, () -> {
|
||||
for (LoginPluginMessage msg: queuedHandshakePackets) {
|
||||
clientPhase.forwardPayload(server,msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class VelocityForgeBackendHandshakeHandler extends ChannelDuplexHandler {
|
|||
if (serverConnection.getPlayer().getPhase() instanceof FML2CRPMClientConnectionPhase phase) {
|
||||
init(connection,serverConnection);
|
||||
if (phase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.MODDED) {
|
||||
phase.reset(serverConnection.getPlayer(), () -> {
|
||||
phase.reset(serverConnection ,serverConnection.getPlayer(), () -> {
|
||||
ctx.flush();
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user