Works
This commit is contained in:
parent
007ff8511a
commit
2a4aba72e9
|
|
@ -1,11 +1,8 @@
|
||||||
package org.adde0109.ambassador;
|
package org.adde0109.ambassador;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.velocitypowered.api.event.Continuation;
|
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
|
|
||||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
|
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
|
@ -16,10 +13,6 @@ import java.util.concurrent.Callable;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.network.ConnectionManager;
|
import com.velocitypowered.proxy.network.ConnectionManager;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import org.adde0109.ambassador.forge.ForgeConnection;
|
|
||||||
import org.adde0109.ambassador.forge.ForgeHandshakeHandler;
|
|
||||||
import org.adde0109.ambassador.forge.ForgeHandshakeUtils;
|
|
||||||
import org.adde0109.ambassador.forge.ForgeServerSwitchHandler;
|
|
||||||
import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer;
|
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;
|
||||||
|
|
@ -34,12 +27,9 @@ public class Ambassador {
|
||||||
|
|
||||||
public ProxyServer server;
|
public ProxyServer server;
|
||||||
public final Logger logger;
|
public final Logger logger;
|
||||||
public AmbassadorConfig config;
|
|
||||||
private final Metrics.Factory metricsFactory;
|
private final Metrics.Factory metricsFactory;
|
||||||
private final Path dataDirectory;
|
private final Path dataDirectory;
|
||||||
|
|
||||||
public ForgeHandshakeHandler forgeHandshakeHandler;
|
|
||||||
public ForgeServerSwitchHandler forgeServerSwitchHandler;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,18 +45,8 @@ public class Ambassador {
|
||||||
public void onProxyInitialization(ProxyInitializeEvent event) throws ReflectiveOperationException {
|
public void onProxyInitialization(ProxyInitializeEvent event) throws ReflectiveOperationException {
|
||||||
initMetrics();
|
initMetrics();
|
||||||
|
|
||||||
config = AmbassadorConfig.readOrCreateConfig(dataDirectory,server,logger);
|
server.getEventManager().register(this, new VelocityEventHandler(this));
|
||||||
if(config != null) {
|
|
||||||
forgeHandshakeHandler = new ForgeHandshakeHandler(this);
|
|
||||||
forgeServerSwitchHandler = new ForgeServerSwitchHandler(this);
|
|
||||||
server.getEventManager().register(this, new VelocityEventHandler(this));
|
|
||||||
server.getEventManager().register(this,forgeServerSwitchHandler);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.warn("Ambassador will be disabled because of errors");
|
|
||||||
}
|
|
||||||
|
|
||||||
ForgeHandshakeUtils.HandshakeReceiver.logger = logger;
|
|
||||||
inject();
|
inject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,39 +61,7 @@ public class Ambassador {
|
||||||
((ConnectionManager) cmField.get(server)).backendChannelInitializer.set(new VelocityBackendChannelInitializer(originalBackend,(VelocityServer) server));
|
((ConnectionManager) cmField.get(server)).backendChannelInitializer.set(new VelocityBackendChannelInitializer(originalBackend,(VelocityServer) server));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onProxyReloadEvent(ProxyReloadEvent event) {
|
|
||||||
AmbassadorConfig c = AmbassadorConfig.readOrCreateConfig(dataDirectory,server,logger);
|
|
||||||
if (config != null) {
|
|
||||||
config = c;
|
|
||||||
logger.info("Successfully reloaded the config");
|
|
||||||
} else {
|
|
||||||
logger.warn("Using the old config");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onPlayerChooseInitialServerEvent(PlayerChooseInitialServerEvent event, Continuation continuation) {
|
|
||||||
//Only handle Forge connections
|
|
||||||
if((event.getInitialServer().isPresent()) && (forgeHandshakeHandler.getForgeConnection(event.getPlayer()).isPresent())) {
|
|
||||||
//Forge client
|
|
||||||
ForgeConnection forgeConnection = forgeHandshakeHandler.getForgeConnection(event.getPlayer()).get();
|
|
||||||
if (forgeConnection.isForced()) {
|
|
||||||
event.setInitialServer(forgeConnection.getSyncedServer().get());
|
|
||||||
}
|
|
||||||
forgeConnection.setForced(config.getForced(forgeConnection.getConnection().getProtocolVersion().getProtocol()));
|
|
||||||
}
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMetrics() {
|
private void initMetrics() {
|
||||||
Metrics metrics = metricsFactory.make(this, 15655);
|
Metrics metrics = metricsFactory.make(this, 15655);
|
||||||
metrics.addCustomChart(new SingleLineChart("modern_forge_players", new Callable<Integer>() {
|
|
||||||
@Override
|
|
||||||
public Integer call() throws Exception {
|
|
||||||
return (forgeHandshakeHandler != null) ? forgeHandshakeHandler.getAmountOfForgeConnections() : 0;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
package org.adde0109.ambassador;
|
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.CommentedConfig;
|
|
||||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
|
||||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class AmbassadorConfig {
|
|
||||||
|
|
||||||
private static final int CONFIG_VERSION = 1;
|
|
||||||
private final ProxyServer server;
|
|
||||||
private final Logger logger;
|
|
||||||
private Differentiators differentiatorsSettings;
|
|
||||||
private ReSync reSyncSettings;
|
|
||||||
|
|
||||||
|
|
||||||
private AmbassadorConfig(ProxyServer server,Logger logger) {
|
|
||||||
this.server = server;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public RegisteredServer getServer(int protocolVersion) {
|
|
||||||
return differentiatorsSettings.differentiators.get(protocolVersion).handshakeServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getForced (int protocolVersion) {
|
|
||||||
return differentiatorsSettings.differentiators.get(protocolVersion).forced;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldHandle(int protocolVersion) {
|
|
||||||
return differentiatorsSettings.differentiators.containsKey(protocolVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getReSyncTimeout() {
|
|
||||||
return reSyncSettings.reSyncTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public reSyncOption reSyncOptionForge() {
|
|
||||||
return reSyncSettings.reSyncForgeForge;
|
|
||||||
}
|
|
||||||
|
|
||||||
public reSyncOption reSyncOptionVanilla() {
|
|
||||||
return reSyncSettings.reSyncForgeVanilla;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static AmbassadorConfig readOrCreateConfig(Path dataDirectory,ProxyServer server, Logger logger) {
|
|
||||||
AmbassadorConfig ambassadorConfig = new AmbassadorConfig(server,logger);
|
|
||||||
try {
|
|
||||||
Files.createDirectories(dataDirectory);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (FileAlreadyExistsException ignored) {
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.error("Config related error: " + e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
CommentedFileConfig config = CommentedFileConfig.builder(dataDirectory.resolve("ambassador.toml"))
|
|
||||||
.defaultData(Ambassador.class.getClassLoader().getResource("default-ambassador.toml"))
|
|
||||||
.autosave()
|
|
||||||
.preserveInsertionOrder()
|
|
||||||
.sync()
|
|
||||||
.build();
|
|
||||||
config.load();
|
|
||||||
|
|
||||||
if (config.getOrElse("config-version",0) != CONFIG_VERSION) {
|
|
||||||
throw new Exception("Incompatible config-version detected! Please delete 'ambassador.toml' and reload.");
|
|
||||||
}
|
|
||||||
CommentedConfig differentiatorsSettingsConfig = config.get("Differentiators");
|
|
||||||
CommentedConfig reSyncSettingsConfig = config.get("ReSync");
|
|
||||||
|
|
||||||
|
|
||||||
ambassadorConfig.differentiatorsSettings = ambassadorConfig.new Differentiators(differentiatorsSettingsConfig);
|
|
||||||
ambassadorConfig.reSyncSettings = ambassadorConfig.new ReSync(reSyncSettingsConfig);
|
|
||||||
|
|
||||||
|
|
||||||
config.save();
|
|
||||||
return ambassadorConfig;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Config related error: " + e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReSync {
|
|
||||||
private int reSyncTimeout = 30;
|
|
||||||
private reSyncOption reSyncForgeForge = reSyncOption.ALWAYS;
|
|
||||||
private reSyncOption reSyncForgeVanilla = reSyncOption.NEVER;
|
|
||||||
|
|
||||||
private ReSync(CommentedConfig config) {
|
|
||||||
if (config != null) {
|
|
||||||
reSyncTimeout = config.getOrElse("resync-timeout",reSyncTimeout);
|
|
||||||
reSyncForgeForge = reSyncOption.valueOf(
|
|
||||||
config.getOrElse("resync-forge-to-forge",reSyncForgeForge.name()).toUpperCase());
|
|
||||||
}
|
|
||||||
reSyncForgeVanilla = reSyncOption.valueOf(
|
|
||||||
config.getOrElse("unsync-forge-to-vanilla",reSyncForgeVanilla.name()).toUpperCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum reSyncOption {
|
|
||||||
NEVER,
|
|
||||||
ASK,
|
|
||||||
ALWAYS
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Differentiators {
|
|
||||||
private Map<Integer,DifferentiatorSettings> differentiators = ImmutableMap.of(
|
|
||||||
758, new DifferentiatorSettings(),
|
|
||||||
754, new DifferentiatorSettings()
|
|
||||||
);
|
|
||||||
private Differentiators(){
|
|
||||||
}
|
|
||||||
|
|
||||||
private Differentiators(CommentedConfig config) throws Exception {
|
|
||||||
if (config != null) {
|
|
||||||
Map<Integer,DifferentiatorSettings> differentiators = new HashMap<>();
|
|
||||||
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
|
|
||||||
if (entry.getValue() instanceof CommentedConfig) {
|
|
||||||
differentiators.put(Integer.decode(entry.getKey()),new DifferentiatorSettings(entry.getValue()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.differentiators = ImmutableMap.copyOf(differentiators);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DifferentiatorSettings {
|
|
||||||
private RegisteredServer handshakeServer = null;
|
|
||||||
private boolean forced = false;
|
|
||||||
|
|
||||||
private DifferentiatorSettings(){
|
|
||||||
}
|
|
||||||
|
|
||||||
private DifferentiatorSettings(CommentedConfig config) throws Exception {
|
|
||||||
if (config != null) {
|
|
||||||
String serverName = config.getOrElse("default-forge-server", "");
|
|
||||||
if (!Objects.equals(serverName, ""))
|
|
||||||
handshakeServer = server.getServer(serverName)
|
|
||||||
.orElseThrow(() -> new Exception(serverName + "is not a registered server!"));
|
|
||||||
this.forced = config.getOrElse("forced",forced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
package org.adde0109.ambassador.forge;
|
|
||||||
|
|
||||||
import com.google.common.io.ByteArrayDataInput;
|
|
||||||
import com.velocitypowered.api.event.Continuation;
|
|
||||||
import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent;
|
|
||||||
import com.velocitypowered.api.proxy.LoginPhaseConnection;
|
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class ForgeConnection {
|
|
||||||
|
|
||||||
private final Logger logger;
|
|
||||||
private final LoginPhaseConnection connection;
|
|
||||||
|
|
||||||
private Optional<byte[]> recivedClientModlist = Optional.empty();
|
|
||||||
private static byte[] recivedClientACK;
|
|
||||||
private boolean ignoreSyncExepction = false;
|
|
||||||
|
|
||||||
private Optional<ForgeHandshakeUtils.CachedServerHandshake> transmittedHandshake = Optional.empty();
|
|
||||||
private boolean forced = false;
|
|
||||||
private Optional<RegisteredServer> syncedTo = Optional.empty();
|
|
||||||
|
|
||||||
private boolean resettable;
|
|
||||||
|
|
||||||
public ForgeConnection(LoginPhaseConnection connection, Logger logger) {
|
|
||||||
this.connection = connection;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<Boolean> testIfForge(LoginPhaseConnection connection) {
|
|
||||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
|
||||||
|
|
||||||
byte[] testPacket = ForgeHandshakeUtils.generateTestPacket();
|
|
||||||
//This gets also sent to vanilla
|
|
||||||
connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), testPacket,
|
|
||||||
responseBody -> {
|
|
||||||
future.complete(responseBody != null);
|
|
||||||
ignoreSyncExepction = responseBody == null;
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public CompletableFuture<Boolean> sync(ForgeServerConnection forgeServerConnection) {
|
|
||||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
|
||||||
forgeServerConnection.getHandshake().whenComplete((msg,ex) -> {
|
|
||||||
if (ex != null) {
|
|
||||||
future.complete(false);
|
|
||||||
logger.warn("Sync Exception: " + ex);
|
|
||||||
} else {
|
|
||||||
//This gets also sent to vanilla
|
|
||||||
sendModlist(msg.modListPacket).thenAccept((response) -> {
|
|
||||||
if (!ignoreSyncExepction && response == null) {
|
|
||||||
logger.warn("Sync Exception: Client responded with an empty body.");
|
|
||||||
}
|
|
||||||
if (response != null) {
|
|
||||||
recivedClientModlist = Optional.of(response);
|
|
||||||
//If the client is resettable.
|
|
||||||
ByteBuf clientModListPacket = Unpooled.wrappedBuffer(response);
|
|
||||||
clientModListPacket.readBytes(14); //Channel Identifier
|
|
||||||
ProtocolUtils.readVarInt(clientModListPacket); //Length
|
|
||||||
int packetID = ProtocolUtils.readVarInt(clientModListPacket);
|
|
||||||
String[] mods = ProtocolUtils.readStringArray(clientModListPacket);
|
|
||||||
resettable = Arrays.stream(mods).anyMatch((s) -> s.equals("clientresetpacket"));
|
|
||||||
clientModListPacket.release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//This gets also sent to vanilla
|
|
||||||
sendOther(msg.otherPackets).thenAccept((response) -> {
|
|
||||||
if (!ignoreSyncExepction && response == null) {
|
|
||||||
logger.warn("Sync Exception: Client responded with an empty body.");
|
|
||||||
}
|
|
||||||
//TODO: Generate the ACK packet ourself.
|
|
||||||
ForgeConnection.recivedClientACK = (response == null) ? ForgeConnection.recivedClientACK : response;
|
|
||||||
transmittedHandshake = Optional.of(msg);
|
|
||||||
syncedTo = Optional.of(forgeServerConnection.getServer());
|
|
||||||
});
|
|
||||||
future.complete(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<byte[]> sendModlist(byte[] modListPacket) {
|
|
||||||
CompletableFuture<byte[]> future = new CompletableFuture<>();
|
|
||||||
connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), modListPacket,
|
|
||||||
future::complete);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<byte[]> sendOther(List<byte[]> otherPackets) {
|
|
||||||
CompletableFuture<byte[]> future = new CompletableFuture<>();
|
|
||||||
for (int i = 0; i < otherPackets.size(); i++) {
|
|
||||||
connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), otherPackets.get(i),
|
|
||||||
(i < (otherPackets.size() - 1)) ? responseBody -> {
|
|
||||||
} : future::complete);
|
|
||||||
}
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleServerHandshakePacket(ServerLoginPluginMessageEvent event, Continuation continuation) {
|
|
||||||
ByteArrayDataInput data = event.contentsAsDataStream();
|
|
||||||
if (data.skipBytes(14) != 14) { //Channel Identifier
|
|
||||||
continuation.resumeWithException(new EOFException());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ForgeHandshakeUtils.readVarInt(data); //Length
|
|
||||||
int packetID = ForgeHandshakeUtils.readVarInt(data);
|
|
||||||
|
|
||||||
if (packetID == 1) {
|
|
||||||
if (getRecivedClientModlist().isPresent()) {
|
|
||||||
event.setResult(ServerLoginPluginMessageEvent.ResponseResult.reply(getRecivedClientModlist().get()));
|
|
||||||
} else {
|
|
||||||
continuation.resumeWithException(new Exception("Client isn't synced. This should have been caught" +
|
|
||||||
" during serverPreConnect"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (getRecivedClientACK() != null) {
|
|
||||||
event.setResult(ServerLoginPluginMessageEvent.ResponseResult.reply(getRecivedClientACK()));
|
|
||||||
} else {
|
|
||||||
continuation.resumeWithException(new Exception("No response available."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoginPhaseConnection getConnection() {
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isResettable() {
|
|
||||||
return resettable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<ForgeHandshakeUtils.CachedServerHandshake> getTransmittedHandshake() {
|
|
||||||
return transmittedHandshake;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<byte[]> getRecivedClientModlist() {
|
|
||||||
return recivedClientModlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] getRecivedClientACK() {
|
|
||||||
return recivedClientACK;
|
|
||||||
}
|
|
||||||
public Optional<RegisteredServer> getSyncedServer() {
|
|
||||||
return syncedTo;
|
|
||||||
}
|
|
||||||
public void setForced(boolean forced) {
|
|
||||||
this.forced = forced;
|
|
||||||
}
|
|
||||||
public boolean isForced() {
|
|
||||||
return forced;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||||
import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler;
|
import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler;
|
||||||
import org.checkerframework.checker.units.qual.A;
|
import org.checkerframework.checker.units.qual.A;
|
||||||
|
|
@ -45,13 +46,10 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
|
||||||
public void handleLogin(ConnectedPlayer player,ForgeHandshakeUtils.CachedServerHandshake handshake, Continuation continuation) {
|
public void handleLogin(ConnectedPlayer player,ForgeHandshakeUtils.CachedServerHandshake handshake, Continuation continuation) {
|
||||||
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) {
|
//Without initial modlist for now
|
||||||
connection.delayedWrite(new LoginPluginMessage(98, "fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
|
if (true) {
|
||||||
listenerList.add(98);
|
connection.delayedWrite(new LoginPluginMessage(0, "fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist)));
|
||||||
this.whenComplete = () -> {
|
listenerList.add(0);
|
||||||
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);
|
||||||
|
|
@ -59,11 +57,11 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
|
||||||
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();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
this.whenComplete = () -> {
|
||||||
|
this.clientPhase = ClientPhase.MODDED;
|
||||||
|
continuation.resume();
|
||||||
|
};
|
||||||
connection.setSessionHandler(sessionHandler);
|
connection.setSessionHandler(sessionHandler);
|
||||||
connection.flush();
|
connection.flush();
|
||||||
}
|
}
|
||||||
|
|
@ -72,9 +70,11 @@ public class ForgeFML2ClientConnectionPhase implements VelocityForgeClientConnec
|
||||||
public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) {
|
public boolean handle(ConnectedPlayer player, LoginPluginResponse packet) {
|
||||||
if (packet.getId() == 98) {
|
if (packet.getId() == 98) {
|
||||||
this.clientPhase = ClientPhase.HANDSHAKE;
|
this.clientPhase = ClientPhase.HANDSHAKE;
|
||||||
this.isResettable = packet.isSuccess();
|
|
||||||
} else if (packet.getId() == 0) {
|
} else if (packet.getId() == 0) {
|
||||||
this.clientPhase = ClientPhase.MODLIST;
|
this.clientPhase = ClientPhase.MODLIST;
|
||||||
|
if (modListData == null) {
|
||||||
|
modListData = ByteBufUtil.getBytes(packet.content());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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());
|
||||||
|
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
package org.adde0109.ambassador.forge;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.event.Continuation;
|
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
|
||||||
import com.velocitypowered.api.event.player.KickedFromServerEvent;
|
|
||||||
import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent;
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.TranslatableComponent;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import org.adde0109.ambassador.Ambassador;
|
|
||||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
|
||||||
|
|
||||||
public class ForgeHandshakeHandler {
|
|
||||||
|
|
||||||
private final Ambassador ambassador;
|
|
||||||
|
|
||||||
private final Map<RegisteredServer, ForgeServerConnection>
|
|
||||||
forgeServerConnectionMap = new HashMap<>();
|
|
||||||
private final Map<InetSocketAddress,ForgeConnection> incomingForgeConnections = new HashMap<>();
|
|
||||||
|
|
||||||
private static final ChannelIdentifier LOGIN_WRAPPER_ID = MinecraftChannelIdentifier.create("fml","loginwrapper");
|
|
||||||
|
|
||||||
|
|
||||||
public ForgeHandshakeHandler(Ambassador ambassador) {
|
|
||||||
this.ambassador = ambassador;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void handleLogin(ConnectedPlayer player, Continuation continuation) {
|
|
||||||
getInitialHandshake(player).whenCompleteAsync((msg,ex) -> {
|
|
||||||
if (ex != null) {
|
|
||||||
ambassador.logger.warn("Forge player, " + player.getUsername() + ", is entering vanilla-mode because of: " + ex.getMessage());
|
|
||||||
}
|
|
||||||
((VelocityForgeClientConnectionPhase) player.getPhase()).handleLogin(player,msg,continuation);
|
|
||||||
}, player.getConnection().eventLoop());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> getInitialHandshake(ConnectedPlayer player) {
|
|
||||||
CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> future;
|
|
||||||
RegisteredServer initialServer;
|
|
||||||
if((initialServer = ambassador.config.getServer(player.getConnection().getProtocolVersion().getProtocol())) != null) {
|
|
||||||
future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(initialServer);
|
|
||||||
} else {
|
|
||||||
future = CompletableFuture.failedFuture(new Exception("No initial server is specified"));
|
|
||||||
}
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerForgeConnection(ForgeConnection forgeConnection) {
|
|
||||||
if (forgeConnection != null) {
|
|
||||||
incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive());
|
|
||||||
incomingForgeConnections.put(forgeConnection.getConnection().getRemoteAddress(), forgeConnection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<ForgeConnection> getForgeConnection(Player player) {
|
|
||||||
return getForgeConnection(player.getRemoteAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ForgeConnection> getForgeConnection(InetSocketAddress socketAddress) {
|
|
||||||
incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive());
|
|
||||||
return Optional.ofNullable(incomingForgeConnections.get(socketAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAmountOfForgeConnections() {
|
|
||||||
return incomingForgeConnections.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<ForgeServerConnection> getForgeServerConnection(RegisteredServer registeredServer) {
|
|
||||||
return Optional.ofNullable(forgeServerConnectionMap.get(registeredServer));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerForgeServer(RegisteredServer server, ForgeServerConnection forgeServerConnection) {
|
|
||||||
forgeServerConnectionMap.put(server,forgeServerConnection);
|
|
||||||
}
|
|
||||||
public void unRegisterForgeServer(RegisteredServer server) {
|
|
||||||
forgeServerConnectionMap.remove(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
//@Subscribe
|
|
||||||
public void onServerLoginPluginMessageEvent(ServerLoginPluginMessageEvent event, Continuation continuation) {
|
|
||||||
if (!event.getIdentifier().equals(LOGIN_WRAPPER_ID)) {
|
|
||||||
continuation.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Check 2
|
|
||||||
if (getForgeServerConnection(event.getConnection().getServer()).isEmpty()) {
|
|
||||||
registerForgeServer(event.getConnection().getServer(),
|
|
||||||
new ForgeServerConnection(event.getConnection().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (incomingForgeConnections.containsKey(event.getConnection().getPlayer().getRemoteAddress())) {
|
|
||||||
incomingForgeConnections.get(event.getConnection().getPlayer().getRemoteAddress())
|
|
||||||
.handleServerHandshakePacket(event,continuation);
|
|
||||||
} else {
|
|
||||||
//This will lead to "multiplayer.disconnect.unexpected_query_response"
|
|
||||||
//and will be handled during KickFromServerEvent.
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//@Subscribe
|
|
||||||
public void onKickedFromServerEvent(KickedFromServerEvent event, Continuation continuation) {
|
|
||||||
Optional<ForgeConnection> forgeConnectionOptional = getForgeConnection(event.getPlayer());
|
|
||||||
if (forgeConnectionOptional.isPresent()) {
|
|
||||||
if (forgeConnectionOptional.get().isForced() && event.getResult() instanceof KickedFromServerEvent.RedirectPlayer) {
|
|
||||||
event.setResult(KickedFromServerEvent.DisconnectPlayer.create(event.getServerKickReason().get()));
|
|
||||||
}
|
|
||||||
} else if (event.getServerKickReason().isPresent()) {
|
|
||||||
Component reason = event.getServerKickReason().get();
|
|
||||||
if (reason instanceof TranslatableComponent)
|
|
||||||
if (((TranslatableComponent) reason).key().equals("multiplayer.disconnect.unexpected_query_response")) {
|
|
||||||
if (getForgeServerConnection(event.getServer()).isPresent()) {
|
|
||||||
//Turns out the server the vanilla client is connecting to is forge. Let's handle the connection error.
|
|
||||||
ambassador.logger.info("Vanilla player {} tried to connect to forge server {}. The connection error can be ignored.",
|
|
||||||
event.getPlayer(),event.getServer().getServerInfo().getName());
|
|
||||||
KickedFromServerEvent.ServerKickResult result = event.getResult();
|
|
||||||
Component component = Component.text("The server you were trying to connect to requires Forge to be installed.", NamedTextColor.RED);
|
|
||||||
if (result instanceof KickedFromServerEvent.DisconnectPlayer) {
|
|
||||||
event.setResult(KickedFromServerEvent.DisconnectPlayer.create(component));
|
|
||||||
} else if (result instanceof KickedFromServerEvent.RedirectPlayer) {
|
|
||||||
RegisteredServer redirectServer = ((KickedFromServerEvent.RedirectPlayer)event.getResult()).getServer();
|
|
||||||
event.setResult(KickedFromServerEvent.RedirectPlayer.create(redirectServer,component));
|
|
||||||
} else if (result instanceof KickedFromServerEvent.Notify) {
|
|
||||||
event.setResult(KickedFromServerEvent.Notify.create(component));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package org.adde0109.ambassador.forge;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class ForgeServerConnection {
|
|
||||||
|
|
||||||
private static final int PACKET_LENGTH_INDEX = 14; //length of "fml:handshake"+1
|
|
||||||
private final RegisteredServer handshakeServer;
|
|
||||||
private ForgeHandshakeUtils.CachedServerHandshake handshake;
|
|
||||||
|
|
||||||
public RegisteredServer getServer() {
|
|
||||||
return handshakeServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForgeServerConnection(RegisteredServer handshakeServer) {
|
|
||||||
this.handshakeServer = handshakeServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> getHandshake() {
|
|
||||||
CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> future;
|
|
||||||
if (handshake == null) {
|
|
||||||
future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer);
|
|
||||||
} else {
|
|
||||||
future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer,handshake);
|
|
||||||
}
|
|
||||||
future.thenAccept(p -> handshake = p);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerInfo getServerInfo() {
|
|
||||||
return handshakeServer.getServerInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,217 +0,0 @@
|
||||||
package org.adde0109.ambassador.forge;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.event.Continuation;
|
|
||||||
import com.velocitypowered.api.event.PostOrder;
|
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
|
||||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
|
||||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.util.UuidUtils;
|
|
||||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
|
||||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
|
||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.EventLoop;
|
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import org.adde0109.ambassador.Ambassador;
|
|
||||||
|
|
||||||
import org.adde0109.ambassador.AmbassadorConfig;
|
|
||||||
import org.apache.commons.collections4.map.PassiveExpiringMap;
|
|
||||||
|
|
||||||
public class ForgeServerSwitchHandler {
|
|
||||||
private final Ambassador ambassador;
|
|
||||||
public final PassiveExpiringMap<String,ForgeServerConnection> reSyncMap;
|
|
||||||
|
|
||||||
public ForgeServerSwitchHandler(Ambassador ambassador) {
|
|
||||||
this.ambassador = ambassador;
|
|
||||||
this.reSyncMap = new PassiveExpiringMap<>(ambassador.config.getReSyncTimeout(),TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//@Subscribe(order = PostOrder.LAST)
|
|
||||||
public void onServerPreConnectEvent(ServerPreConnectEvent event, Continuation continuation) {
|
|
||||||
if (!event.getResult().isAllowed()) {
|
|
||||||
continuation.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Optional<ForgeServerConnection> forgeServerConnectionOptional = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getOriginalServer());
|
|
||||||
Optional<ForgeConnection> forgeConnectionOptional = ambassador.forgeHandshakeHandler.getForgeConnection(event.getPlayer());
|
|
||||||
if (forgeConnectionOptional.isPresent()) {
|
|
||||||
ForgeConnection forgeConnection = forgeConnectionOptional.get();
|
|
||||||
if (forgeConnection.isResettable()) {
|
|
||||||
continuation.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ForgeServerConnection forgeServerConnection = forgeServerConnectionOptional.orElseGet(() -> new ForgeServerConnection(event.getOriginalServer()));
|
|
||||||
forgeServerConnection.getHandshake().whenComplete((msg, ex) -> {
|
|
||||||
if (ex != null) {
|
|
||||||
//The server was forge but aren't right now. Or it's just offline.
|
|
||||||
if (ex instanceof ForgeHandshakeUtils.HandshakeReceiver.HandshakeNotAvailableException) {
|
|
||||||
//It's not running ambassador, so it should be unregistered.
|
|
||||||
if (forgeServerConnectionOptional.isPresent())
|
|
||||||
ambassador.forgeHandshakeHandler.unRegisterForgeServer(forgeServerConnection.getServer());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//If the server just got discovered, register it.
|
|
||||||
if (forgeServerConnectionOptional.isEmpty())
|
|
||||||
ambassador.forgeHandshakeHandler.registerForgeServer(event.getOriginalServer(),forgeServerConnection);
|
|
||||||
|
|
||||||
//To make legacy forwarding work
|
|
||||||
List<GameProfile.Property> properties = new ArrayList<>(event.getPlayer().getGameProfileProperties());
|
|
||||||
properties.add(new GameProfile.Property("extraData", "\1FML2\1",""));
|
|
||||||
event.getPlayer().setGameProfileProperties(properties);
|
|
||||||
|
|
||||||
if (ambassador.config.reSyncOptionForge() != AmbassadorConfig.reSyncOption.NEVER) {
|
|
||||||
if (forgeConnection.getTransmittedHandshake().isEmpty() || !msg.equals(forgeConnection.getTransmittedHandshake().get())) {
|
|
||||||
event.setResult(ServerPreConnectEvent.ServerResult.denied());
|
|
||||||
kickReSync(event.getPlayer(), forgeServerConnection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continuation.resume();
|
|
||||||
});
|
|
||||||
} else if (forgeServerConnectionOptional.isPresent()) {
|
|
||||||
//If vanilla tries to connect to a server we know is forge
|
|
||||||
event.setResult(ServerPreConnectEvent.ServerResult.denied());
|
|
||||||
event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED));
|
|
||||||
continuation.resume();
|
|
||||||
} else {
|
|
||||||
//The server is not known to us.
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void kickReSync(Player player, ForgeServerConnection forgeServerConnection){
|
|
||||||
ambassador.logger.info("Kicking {} because of re-sync needed", player);
|
|
||||||
player.disconnect(Component.text("Please reconnect"));
|
|
||||||
reSyncMap.put(player.getUsername(),forgeServerConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
//@Subscribe
|
|
||||||
public void onServerConnectedEvent(ServerConnectedEvent event, Continuation continuation) {
|
|
||||||
ConnectedPlayer player = ((ConnectedPlayer) event.getPlayer());
|
|
||||||
Optional<ForgeConnection> forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(player);
|
|
||||||
if (forgeConnection.isEmpty() || !forgeConnection.get().isResettable()) {
|
|
||||||
//Don't bother unless the client can be reset.
|
|
||||||
continuation.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Optional<ForgeServerConnection> forgeServerConnection = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getServer());
|
|
||||||
if (forgeServerConnection.isPresent() && event.getPreviousServer().isPresent()) {
|
|
||||||
Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture = forgeServerConnection.get().getHandshake();
|
|
||||||
player.getConnection().eventLoop().submit(() -> {
|
|
||||||
reSync(player,handshakeFuture,continuation);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reSync(ConnectedPlayer player, Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture, Continuation continuation) {
|
|
||||||
MinecraftConnection connection = player.getConnection();
|
|
||||||
connection.setSessionHandler(new ReSyncHandler(player,handshakeFuture,continuation));
|
|
||||||
connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
|
|
||||||
connection.setState(StateRegistry.LOGIN);
|
|
||||||
}
|
|
||||||
private class ReSyncHandler implements MinecraftSessionHandler {
|
|
||||||
|
|
||||||
|
|
||||||
private final ConnectedPlayer player;
|
|
||||||
|
|
||||||
private final MinecraftConnection connection;
|
|
||||||
private final Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture;
|
|
||||||
private final MinecraftSessionHandler originalHandler;
|
|
||||||
private final Continuation continuation;
|
|
||||||
|
|
||||||
private List<Integer> inTransit = new ArrayList<>();
|
|
||||||
|
|
||||||
ReSyncHandler(ConnectedPlayer player, Future<ForgeHandshakeUtils.CachedServerHandshake> handshakeFuture, Continuation continuation) {
|
|
||||||
this.player = player;
|
|
||||||
this.connection = player.getConnection();
|
|
||||||
this.handshakeFuture = handshakeFuture;
|
|
||||||
this.originalHandler = this.connection.getSessionHandler();
|
|
||||||
this.continuation = continuation;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handle(LoginPluginResponse packet) {
|
|
||||||
if (!inTransit.removeIf((s) -> s == packet.getId())) {
|
|
||||||
if (packet.getId() == 98) {
|
|
||||||
ForgeHandshakeUtils.CachedServerHandshake handshake;
|
|
||||||
try {
|
|
||||||
handshake = handshakeFuture.get();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
sendHandshake(connection, handshake);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else if (inTransit.isEmpty()) {
|
|
||||||
complete();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendHandshake(MinecraftConnection connection, ForgeHandshakeUtils.CachedServerHandshake handshake) {
|
|
||||||
int transactionId = 1;
|
|
||||||
connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(handshake.modListPacket)));
|
|
||||||
inTransit.add(transactionId);
|
|
||||||
for (byte[] data : handshake.otherPackets) {
|
|
||||||
transactionId++;
|
|
||||||
connection.delayedWrite(new LoginPluginMessage(transactionId, "fml:loginwrapper", Unpooled.wrappedBuffer(data)));
|
|
||||||
inTransit.add(transactionId);
|
|
||||||
}
|
|
||||||
connection.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void complete() {
|
|
||||||
VelocityConfiguration configuration = (VelocityConfiguration) ambassador.server.getConfiguration();
|
|
||||||
UUID playerUniqueId = player.getUniqueId();
|
|
||||||
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
|
|
||||||
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
|
|
||||||
}
|
|
||||||
ServerLoginSuccess success = new ServerLoginSuccess();
|
|
||||||
success.setUsername(player.getUsername());
|
|
||||||
success.setUuid(playerUniqueId);
|
|
||||||
connection.write(success);
|
|
||||||
connection.setState(StateRegistry.PLAY);
|
|
||||||
connection.setSessionHandler(originalHandler);
|
|
||||||
continuation.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleUnknown(ByteBuf buf) {
|
|
||||||
originalHandler.handleUnknown(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnected() {
|
|
||||||
originalHandler.disconnected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -36,7 +36,7 @@ public class VelocityEventHandler {
|
||||||
continuation.resume();
|
continuation.resume();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ambassador.forgeHandshakeHandler.handleLogin(player,continuation);
|
phase.handleLogin(player,null,continuation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@Subscribe
|
/*@Subscribe
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
||||||
|
|
||||||
public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
|
public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
|
||||||
|
|
||||||
private final List<LoginPluginMessage> queuedHandshakePackets = new ArrayList<>();
|
private List<LoginPluginMessage> queuedHandshakePackets = new ArrayList<>();
|
||||||
|
|
||||||
public VelocityForgeBackendConnectionPhase() {
|
public VelocityForgeBackendConnectionPhase() {
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +34,9 @@ public class VelocityForgeBackendConnectionPhase implements BackendConnectionPha
|
||||||
player.getConnection().delayedWrite(msg);
|
player.getConnection().delayedWrite(msg);
|
||||||
}
|
}
|
||||||
player.getConnection().flush();
|
player.getConnection().flush();
|
||||||
|
queuedHandshakePackets = null;
|
||||||
});
|
});
|
||||||
|
queuedHandshakePackets = new ArrayList<>();
|
||||||
queuedHandshakePackets.add(message);
|
queuedHandshakePackets.add(message);
|
||||||
} else if (clientPhase.clientPhase != null) {
|
} else if (clientPhase.clientPhase != null) {
|
||||||
player.getConnection().write(message);
|
player.getConnection().write(message);
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
config-version = 1
|
|
||||||
# The player can only be synced to ONE forge server at a time.
|
|
||||||
# Resync will always happen if the player isn't synced to any server and is switching to a forge server.
|
|
||||||
[ReSync]
|
|
||||||
# In seconds. How much time the player has to reconnect before the resync cancels.
|
|
||||||
resync-timeout = 30
|
|
||||||
|
|
||||||
# Possible values: "Always", "Never".
|
|
||||||
# Always: Always kicks the player while switching servers. The safest.
|
|
||||||
# Never: Switches server without kicking/resyncing the player. Can cause client crashes depending on the mods.
|
|
||||||
|
|
||||||
# When the player is connecting to a forge server while being synced to another.
|
|
||||||
# Default: "always"
|
|
||||||
resync-forge-to-forge = "always"
|
|
||||||
# When the player is connecting to a vanilla server and is synced to a forge server.
|
|
||||||
# Default "never". "always" not recommended for servers with vanilla hubs/lobbies.
|
|
||||||
unsync-forge-to-vanilla = "never"
|
|
||||||
|
|
||||||
# Maybe you want to have one 1.16.5 modpack-server and one 1.18.2 modpack-server behind Velocity, in order for Ambassador to tell the
|
|
||||||
# diffrence between modpacks on the connecting client, the plugin looks at the client's protocol version.
|
|
||||||
|
|
||||||
# You may add more diffrentiators, just make sure they have diffrent protocol versions.
|
|
||||||
[Differentiators]
|
|
||||||
|
|
||||||
# Protocol version - 1.18.2
|
|
||||||
[Differentiators.758]
|
|
||||||
# The server that players will initially sync to when connecting to the proxy.
|
|
||||||
# When left empty; Players will be able to join but will need to resync when joining connecting to a forge server.
|
|
||||||
# The name should be the same as specified in the "velocity.toml" config, e.g. "lobby".
|
|
||||||
default-forge-server = ""
|
|
||||||
|
|
||||||
# Some modpacks are not compatible with vanilla servers.
|
|
||||||
# To make sure they don't get connected to one upon login (like a vanilla lobby), change this to true.
|
|
||||||
forced = false
|
|
||||||
|
|
||||||
# Protocol version - 1.16.5
|
|
||||||
[Differentiators.754]
|
|
||||||
default-forge-server = ""
|
|
||||||
forced = false
|
|
||||||
Loading…
Reference in New Issue
Block a user