Compare commits
1 Commits
non-api
...
bypasschec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc3a26f6fe |
16
README.md
16
README.md
|
|
@ -3,8 +3,7 @@
|
|||
This is a Velocity plugin that makes it possible to host a modern Forge server behind a Velocity proxy!
|
||||
|
||||
Unlike other solutions, this plugin does not require any special modifications to the backend server nor the client. (The player doesn't need to do anything)
|
||||
## Only for 1.13-1.20.1
|
||||
Velocity has now added built-in support for newer mc versions and therefore don't need Ambassador. You might still need PCF mod on the server-side, for more info please visit: https://github.com/adde0109/Proxy-Compatible-Forge
|
||||
|
||||
## How to get started:
|
||||
1. Download and install this plugin to your proxy.
|
||||
2. After starting the server, configure the plugin it to your liking using the config file found in the folder "Ambassador".
|
||||
|
|
@ -14,13 +13,12 @@ Velocity has now added built-in support for newer mc versions and therefore don'
|
|||
- https://github.com/caunt/BungeeForge (Legacy forwarding)
|
||||
|
||||
## Features
|
||||
* Server switching without any client side mod when the servers are similar. (Mods must match)
|
||||
* ServerRedirect support for server switching.
|
||||
* Server switching using Client Reset Packet Mod for instant server switching:
|
||||
|
||||
1.16.5: https://github.com/Just-Chaldea/Forge-Client-Reset-Packet
|
||||
|
||||
1.18.2+: https://www.curseforge.com/minecraft/mc-mods/forge-client-reset-packet-forward
|
||||
* Server switching using kick to reset the client with configureble message and switch timeout.
|
||||
* ServerRedirect support for auto-reconnecting during switch.
|
||||
* Server switching using client mod for instant server switching:
|
||||
https://github.com/Just-Chaldea/Forge-Client-Reset-Packet
|
||||
1.18.2 and 1.19 fork:
|
||||
https://github.com/adde0109/Forge-Client-Reset-Packet
|
||||
|
||||
## Stuck on "Negotiating":
|
||||
This is an issue with Client Reset Packet Mod being partly incompatible with certain mods on the client. Please remove incompatible mods on the client if you have this issue. (Yes, this also includes client-side only mods.)
|
||||
|
|
|
|||
2
Velocity
2
Velocity
|
|
@ -1 +1 @@
|
|||
Subproject commit c3583e182ca6585e40d1eef0da8c18547c0b1bc1
|
||||
Subproject commit eb594fc799281ff418dc2c161c2d8a8eb0c89a19
|
||||
|
|
@ -5,7 +5,7 @@ plugins {
|
|||
}
|
||||
|
||||
group = "org.adde0109"
|
||||
version = "1.5.3-beta"
|
||||
version = "1.4.3-beta"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
|
|
|||
|
|
@ -19,18 +19,14 @@ import java.util.Map;
|
|||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.network.ConnectionManager;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier;
|
||||
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertySerializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.VelocityEventHandler;
|
||||
|
|
@ -46,11 +42,11 @@ import java.util.concurrent.TimeUnit;
|
|||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19;
|
||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
|
||||
|
||||
@Plugin(id = "ambassador", name = "Ambassador", version = "1.5.3-beta", authors = {"adde0109"})
|
||||
@Plugin(id = "ambassador", name = "Ambassador", version = "1.4.3-beta", authors = {"adde0109"})
|
||||
public class Ambassador {
|
||||
|
||||
//Don't forget to update checkCompatibleVersion() when changing this value
|
||||
private static final String minVelocityVersion = "velocity-3.3.0-SNAPSHOT-330";
|
||||
private static final String minVelocityVersion = "velocity-3.2.0-SNAPSHOT-266";
|
||||
|
||||
public ProxyServer server;
|
||||
public final Logger logger;
|
||||
|
|
@ -79,11 +75,11 @@ public class Ambassador {
|
|||
boolean checkCompatibleVersion() {
|
||||
//Update this when changing minVelocityVersion
|
||||
try {
|
||||
Class.forName("com.velocitypowered.proxy.protocol.packet.DisconnectPacket");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
MinecraftConnection.class.getDeclaredMethod("setActiveSessionHandler", StateRegistry.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
|
|
@ -100,6 +96,7 @@ public class Ambassador {
|
|||
|
||||
Path configPath = dataDirectory.resolve("Ambassador.toml");
|
||||
config = AmbassadorConfig.read(configPath);
|
||||
config.validate();
|
||||
|
||||
inject();
|
||||
|
||||
|
|
@ -114,6 +111,7 @@ public class Ambassador {
|
|||
try {
|
||||
Path configPath = dataDirectory.resolve("Ambassador.toml");
|
||||
final AmbassadorConfig newconfig = AmbassadorConfig.read(configPath);
|
||||
newconfig.validate();
|
||||
|
||||
config = newconfig;
|
||||
} catch (Exception e) {
|
||||
|
|
@ -156,16 +154,6 @@ public class Ambassador {
|
|||
return TEMPORARY_FORCED;
|
||||
}
|
||||
|
||||
public void reconnectSwitchPlayer(ConnectedPlayer player) {
|
||||
TEMPORARY_FORCED.put(player.getUsername(), player.getConnectionInFlight().getServer(),
|
||||
config.getServerSwitchCancellationTime(), TimeUnit.SECONDS);
|
||||
|
||||
MiniMessage mm = MiniMessage.miniMessage();
|
||||
Component parsed = mm.deserialize(config.getKickReconnectMessageString());
|
||||
|
||||
player.disconnect(parsed);
|
||||
}
|
||||
|
||||
private void initMetrics() {
|
||||
Metrics metrics = metricsFactory.make(this, 15655);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,126 +7,86 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class AmbassadorConfig {
|
||||
|
||||
@Expose
|
||||
private int serverSwitchCancellationTime = 30;
|
||||
|
||||
@Expose
|
||||
private boolean silenceWarnings = false;
|
||||
@Expose
|
||||
private boolean bypassRegistryCheck = false;
|
||||
@Expose
|
||||
private boolean bypassModCheck = false;
|
||||
@Expose
|
||||
private String disconnectResetMessage = "Please reconnect";
|
||||
|
||||
@Expose
|
||||
private boolean debugMode = false;
|
||||
@Expose
|
||||
private int serverSwitchCancellationTime = 120;
|
||||
|
||||
@Expose
|
||||
private boolean enableKickReset = false;
|
||||
@Expose
|
||||
private boolean silenceWarnings = false;
|
||||
|
||||
@Expose
|
||||
private String kickReconnectMessageString = "<red>Please reconnect.</red>";
|
||||
private net.kyori.adventure.text.@MonotonicNonNull Component messageAsAsComponent;
|
||||
|
||||
private AmbassadorConfig(boolean silenceWarnings, boolean bypassRegistryCheck, boolean bypassModCheck,
|
||||
boolean debugMode, boolean enableKickReset, String kickReconnectMessageString) {
|
||||
this.silenceWarnings = silenceWarnings;
|
||||
this.bypassRegistryCheck = bypassRegistryCheck;
|
||||
this.bypassModCheck = bypassModCheck;
|
||||
this.debugMode = debugMode;
|
||||
this.enableKickReset = enableKickReset;
|
||||
this.kickReconnectMessageString = kickReconnectMessageString;
|
||||
private AmbassadorConfig(String kickResetMessage, int serverSwitchCancellationTime, boolean silenceWarnings) {
|
||||
this.disconnectResetMessage = kickResetMessage;
|
||||
this.serverSwitchCancellationTime = serverSwitchCancellationTime;
|
||||
this.silenceWarnings = silenceWarnings;
|
||||
};
|
||||
|
||||
public void validate() {
|
||||
if (serverSwitchCancellationTime <= 0) {
|
||||
throw new InvalidValueException("'server-switch-cancellation-time' can't be less than nor equal to zero: server-switch-cancellation-time=" + serverSwitchCancellationTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static AmbassadorConfig read(Path path) {
|
||||
URL defaultConfigLocation = AmbassadorConfig.class.getClassLoader()
|
||||
.getResource("default-ambassador.toml");
|
||||
if (defaultConfigLocation == null) {
|
||||
throw new RuntimeException("Default configuration file does not exist.");
|
||||
}
|
||||
|
||||
public static AmbassadorConfig read(Path path) throws IOException {
|
||||
URL defaultConfigLocation = AmbassadorConfig.class.getClassLoader()
|
||||
.getResource("default-ambassador.toml");
|
||||
if (defaultConfigLocation == null) {
|
||||
throw new RuntimeException("Default configuration file does not exist.");
|
||||
}
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(path)
|
||||
.defaultData(defaultConfigLocation)
|
||||
.autosave()
|
||||
.preserveInsertionOrder()
|
||||
.sync()
|
||||
.build();
|
||||
config.load();
|
||||
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(path)
|
||||
.defaultData(defaultConfigLocation)
|
||||
.autosave()
|
||||
.preserveInsertionOrder()
|
||||
.sync()
|
||||
.build();
|
||||
config.load();
|
||||
|
||||
double configVersion;
|
||||
try {
|
||||
configVersion = Double.parseDouble(config.getOrElse("config-version", "1.0"));
|
||||
} catch (NumberFormatException e) {
|
||||
configVersion = 1.0;
|
||||
}
|
||||
|
||||
boolean silenceWarnings = config.getOrElse("silence-warnings", false);
|
||||
|
||||
int serverSwitchCancellationTime = config.getOrElse("serverRedirectTimeout", 30);
|
||||
|
||||
boolean bypassRegistryCheck = config.getOrElse("bypass-registry-checks", false);
|
||||
|
||||
boolean bypassModCheck = config.getOrElse("bypass-mod-checks", false);
|
||||
|
||||
boolean debugMode = config.getOrElse("debug-mode", false);
|
||||
|
||||
String kickReconnectMessageString = config.getOrElse("disconnect-reset-message",
|
||||
config.getOrElse("reconnect-message", "<red>Please reconnect.</red>"));
|
||||
|
||||
//Upgrade config
|
||||
if (configVersion <= 2.0) {
|
||||
Files.delete(path);
|
||||
config = CommentedFileConfig.builder(path)
|
||||
.defaultData(defaultConfigLocation)
|
||||
.autosave()
|
||||
.preserveInsertionOrder()
|
||||
.sync()
|
||||
.build();
|
||||
config.load();
|
||||
config.set("silence-warnings", silenceWarnings);
|
||||
config.set("serverRedirectTimeout", serverSwitchCancellationTime);
|
||||
config.set("bypass-registry-checks", bypassRegistryCheck);
|
||||
config.set("bypass-mod-checks", bypassModCheck);
|
||||
config.set("debug-mode", debugMode);
|
||||
config.set("reconnect-message", kickReconnectMessageString);
|
||||
}
|
||||
|
||||
boolean enableKickReset = config.getOrElse("enable-kick-reset", false);
|
||||
|
||||
return new AmbassadorConfig(silenceWarnings, bypassRegistryCheck, bypassModCheck,
|
||||
debugMode, enableKickReset, kickReconnectMessageString);
|
||||
double configVersion;
|
||||
try {
|
||||
configVersion = Double.parseDouble(config.getOrElse("config-version", "1.0"));
|
||||
} catch (NumberFormatException e) {
|
||||
configVersion = 1.0;
|
||||
}
|
||||
|
||||
public int getServerSwitchCancellationTime() {
|
||||
return serverSwitchCancellationTime;
|
||||
if (configVersion < 1.1) {
|
||||
config.set("silence-warnings", false);
|
||||
config.set("config-version", "1.2");
|
||||
}
|
||||
|
||||
public boolean isSilenceWarnings() {
|
||||
return silenceWarnings;
|
||||
}
|
||||
String kickResetMessage = config.getOrElse("disconnect-reset-message", "Please reconnect");
|
||||
int serverSwitchCancellationTime = config.getIntOrElse("server-switch-cancellation-time", 120);
|
||||
|
||||
public boolean isBypassRegistryCheck() {
|
||||
return bypassRegistryCheck;
|
||||
}
|
||||
boolean silenceWarnings = config.getOrElse("silence-warnings", false);
|
||||
|
||||
public boolean isBypassModCheck() {
|
||||
return bypassModCheck;
|
||||
}
|
||||
return new AmbassadorConfig(kickResetMessage, serverSwitchCancellationTime, silenceWarnings);
|
||||
}
|
||||
|
||||
public boolean isDebugMode() {
|
||||
return debugMode;
|
||||
public net.kyori.adventure.text.Component getDisconnectResetMessage() {
|
||||
if (messageAsAsComponent == null) {
|
||||
if (disconnectResetMessage.startsWith("{")) {
|
||||
messageAsAsComponent = GsonComponentSerializer.gson().deserialize(disconnectResetMessage);
|
||||
} else {
|
||||
messageAsAsComponent = LegacyComponentSerializer.legacyAmpersand().deserialize(disconnectResetMessage);
|
||||
}
|
||||
}
|
||||
return messageAsAsComponent;
|
||||
}
|
||||
|
||||
public boolean isEnableKickReset() {
|
||||
return enableKickReset;
|
||||
}
|
||||
public int getServerSwitchCancellationTime() {
|
||||
return serverSwitchCancellationTime;
|
||||
}
|
||||
|
||||
public String getKickReconnectMessageString() {
|
||||
return kickReconnectMessageString;
|
||||
}
|
||||
public boolean isSilenceWarnings() {
|
||||
return silenceWarnings;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import org.adde0109.ambassador.forge.packet.Context;
|
||||
import org.adde0109.ambassador.forge.packet.GenericForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.packet.ModListReplyPacket;
|
||||
import org.adde0109.ambassador.forge.packet.RegistryPacket;
|
||||
|
||||
|
|
@ -13,7 +11,6 @@ import java.util.zip.Checksum;
|
|||
public class ForgeHandshake {
|
||||
private ModListReplyPacket modListReplyPacket;
|
||||
private final Map<String, Long> registries = new HashMap<>();
|
||||
public GenericForgeLoginWrapperPacket<Context.ClientContext> zetaFlagsPacket;
|
||||
|
||||
public ForgeHandshake() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import com.google.common.io.ByteStreams;
|
|||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import org.adde0109.ambassador.forge.packet.Context;
|
||||
import org.adde0109.ambassador.forge.packet.GenericForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
|
@ -117,52 +117,30 @@ public class ForgeHandshakeUtils {
|
|||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public static class ThirdPartyRegistryUtils {
|
||||
|
||||
static enum ThirdPartyChannel {
|
||||
SILENTGEAR_NETWORK {
|
||||
@Override
|
||||
public IForgeLoginWrapperPacket<Context.ClientContext> generateResponsePacket(Context.ClientContext context, ForgeHandshake completed) {
|
||||
return new ACKPacket(context, 3);
|
||||
}
|
||||
},
|
||||
ZETA_MAIN {
|
||||
@Override
|
||||
public IForgeLoginWrapperPacket<Context.ClientContext> generateResponsePacket(Context.ClientContext context, ForgeHandshake completed) {
|
||||
return new GenericForgeLoginWrapperPacket<Context.ClientContext>(completed.zetaFlagsPacket.getContent(), context);
|
||||
}
|
||||
};
|
||||
abstract public IForgeLoginWrapperPacket<Context.ClientContext> generateResponsePacket(Context.ClientContext context, ForgeHandshake completed);
|
||||
}
|
||||
|
||||
static boolean isThirdPartyPacket(GenericForgeLoginWrapperPacket<Context> packet) {
|
||||
public static class SilentGearUtils {
|
||||
public static boolean isSilentGearPacket(byte[] data) {
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(data);
|
||||
String channel = null;
|
||||
try {
|
||||
Enum.valueOf(ThirdPartyChannel.class,
|
||||
packet.getContext().getChannelName().replace(':', '_').toUpperCase());
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
channel = ProtocolUtils.readString(buf);
|
||||
} catch (DecoderException e) {
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
return channel != null && channel.equals("silentgear:network");
|
||||
}
|
||||
|
||||
static ThirdPartyChannel getThirdPartyChannel(GenericForgeLoginWrapperPacket<Context> packet) {
|
||||
return Enum.valueOf(ThirdPartyChannel.class,
|
||||
packet.getContext().getChannelName().replace(':', '_').toUpperCase());
|
||||
}
|
||||
|
||||
static class ACKPacket implements IForgeLoginWrapperPacket<Context.ClientContext> {
|
||||
|
||||
public static class ACKPacket implements IForgeLoginWrapperPacket<Context.ClientContext> {
|
||||
private final Context.ClientContext context;
|
||||
private final int packetID;
|
||||
ACKPacket(Context.ClientContext context, int packetID) {
|
||||
this.context = context;
|
||||
this.packetID = packetID;
|
||||
}
|
||||
|
||||
public ACKPacket(Context.ClientContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, packetID);
|
||||
ProtocolUtils.writeVarInt(buf, 3);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import org.adde0109.ambassador.forge.packet.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
public class ShadowHandshakeReceiver {
|
||||
|
||||
private final ConnectedPlayer player;
|
||||
private final ModListReplyPacket modListReplyPacket;
|
||||
private final Map<String, Long> registries;
|
||||
|
||||
private ShadowHandshakeReceiver(ConnectedPlayer player, ModListReplyPacket modListReplyPacket,
|
||||
Map<String, Long> registries) {
|
||||
this.player = player;
|
||||
this.modListReplyPacket = modListReplyPacket;
|
||||
this.registries = registries;
|
||||
}
|
||||
|
||||
void handle(IForgeLoginWrapperPacket packet) throws IncompatibleHandshake {
|
||||
|
||||
}
|
||||
|
||||
void handle(ModListPacket packet) throws IncompatibleHandshake {
|
||||
|
||||
//player.getConnection().write();
|
||||
}
|
||||
|
||||
void handle(RegistryPacket packet) throws IncompatibleHandshake {
|
||||
|
||||
player.getConnection().write(new ACKPacket(Context.fromContext(packet.getContext(), true)));
|
||||
}
|
||||
void handle(ConfigDataPacket packet) throws IncompatibleHandshake {
|
||||
|
||||
player.getConnection().write(new ACKPacket(Context.fromContext(packet.getContext(), true)));
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
|
||||
ConnectedPlayer player;
|
||||
ModListReplyPacket modListReplyPacket;
|
||||
Map<String, Long> registries = new HashMap<>();
|
||||
|
||||
void addModListPacket(ModListPacket packet) {
|
||||
|
||||
}
|
||||
void setModListReplyPacket(ModListReplyPacket packet) {
|
||||
this.modListReplyPacket = packet;
|
||||
}
|
||||
|
||||
void addRegistryPacket(RegistryPacket packet) {
|
||||
Checksum registryChecksum = new Adler32();
|
||||
registryChecksum.update(packet.getSnapshot());
|
||||
registries.put(packet.getRegistryName(), registryChecksum.getValue());
|
||||
}
|
||||
|
||||
ShadowHandshakeReceiver build() {
|
||||
return new ShadowHandshakeReceiver(player, modListReplyPacket, registries);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class IncompatibleHandshake extends Throwable {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,8 @@ import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
|||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||
import org.adde0109.ambassador.forge.packet.*;
|
||||
import org.adde0109.ambassador.forge.pipeline.CommandDecoderErrorCatcher;
|
||||
|
||||
|
|
@ -17,10 +15,10 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
|
||||
NOT_STARTED {
|
||||
NOT_STARTED() {
|
||||
@Override
|
||||
VelocityForgeBackendConnectionPhase nextPhase() {
|
||||
return IN_PROGRESS;
|
||||
return WAITING_FOR_ACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -29,7 +27,7 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
return true;
|
||||
}
|
||||
},
|
||||
IN_PROGRESS {
|
||||
WAITING_FOR_ACK() {
|
||||
@Override
|
||||
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {
|
||||
serverCon.setConnectionPhase(VelocityForgeBackendConnectionPhase.COMPLETE);
|
||||
|
|
@ -49,7 +47,7 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
}
|
||||
},
|
||||
|
||||
COMPLETE {
|
||||
COMPLETE() {
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
return true;
|
||||
|
|
@ -62,22 +60,20 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
VelocityForgeBackendConnectionPhase() {
|
||||
}
|
||||
|
||||
public void handle(VelocityServerConnection server, ConnectedPlayer player, IForgeLoginWrapperPacket<Context> message) {
|
||||
public void handle(VelocityServerConnection server, ConnectedPlayer player, IForgeLoginWrapperPacket message) {
|
||||
VelocityForgeBackendConnectionPhase newPhase = getNewPhase(server,message);
|
||||
|
||||
server.setConnectionPhase(newPhase);
|
||||
|
||||
//Forge -> Forge
|
||||
|
||||
//Reset client if not ready to receive new handshake
|
||||
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
|
||||
|
||||
|
||||
if (!player.isActive()) {
|
||||
return;
|
||||
if (clientPhase == VelocityForgeClientConnectionPhase.RESETTABLE) {
|
||||
//Initial Forge
|
||||
//Forge -> Forge
|
||||
clientPhase.resetConnectionPhase(player);
|
||||
}
|
||||
|
||||
if (!clientPhase.consideredComplete()) {
|
||||
//Initial Forge
|
||||
if (clientPhase != VelocityForgeClientConnectionPhase.COMPLETE) {
|
||||
if (message instanceof ModListPacket modListPacket) {
|
||||
clientPhase.forgeHandshake = new ForgeHandshake();
|
||||
}
|
||||
|
|
@ -86,37 +82,8 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
}
|
||||
player.getConnection().write(message);
|
||||
} else {
|
||||
//Reset client if not ready to receive new handshake
|
||||
if (clientPhase.getResetType() == VelocityForgeClientConnectionPhase.ClientResetType.CRP ||
|
||||
clientPhase.getResetType() == VelocityForgeClientConnectionPhase.ClientResetType.SR) {
|
||||
clientPhase.resetConnectionPhase(player);
|
||||
player.getConnection().write(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientPhase.forgeHandshake.getModListReplyPacket() == null) {
|
||||
//We have nothing to respond with during this handshake. Unable to proceed.
|
||||
if (Ambassador.getInstance().config.isEnableKickReset()) {
|
||||
//Kick-reset
|
||||
Ambassador.getInstance().reconnectSwitchPlayer(player);
|
||||
} else {
|
||||
Ambassador.getInstance().logger.error("Unable for {} to switch servers. Vanilla({}) -> Forge({}) switch " +
|
||||
"without client side mod or kick-reset enabled is not yet supported!",
|
||||
player.getGameProfile().getName(), player.getConnectedServer().getServerInfo().getName(),
|
||||
server.getServerInfo().getName());
|
||||
server.disconnect();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (message instanceof ModListPacket modListPacket) {
|
||||
remainingRegistries = new CountDownLatch(modListPacket.getRegistries().size());
|
||||
|
||||
if (Ambassador.getInstance().config.isDebugMode())
|
||||
player.sendMessage(Component.text("Expecting " + modListPacket.getRegistries().size() +
|
||||
" packets from server " + server.getServer().getServerInfo().getName()));
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
remainingRegistries.await();
|
||||
|
|
@ -124,39 +91,22 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}).thenAcceptAsync((v) -> {
|
||||
|
||||
if(Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Handshake took: " + (System.currentTimeMillis()-time) + " ms"));
|
||||
player.sendMessage(Component.text("Avg packet time" +
|
||||
(System.currentTimeMillis()-time)/modListPacket.getRegistries().size() + " ms"));
|
||||
}
|
||||
|
||||
if (Ambassador.getInstance().config.isBypassRegistryCheck() ||
|
||||
clientPhase.forgeHandshake.isCompatible(handshake)) {
|
||||
if (true) {
|
||||
server.ensureConnected().write(clientPhase.forgeHandshake.getModListReplyPacket());
|
||||
} else if (Ambassador.getInstance().config.isEnableKickReset()) {
|
||||
//Kick-reset
|
||||
Ambassador.getInstance().reconnectSwitchPlayer(player);
|
||||
} else {
|
||||
Ambassador.getInstance().logger.error("Unable to switch due to the registries of " +
|
||||
server.getServer().getServerInfo().getName() + " being different from the registries of " +
|
||||
player.getConnectedServer().getServer().getServerInfo().getName());
|
||||
server.disconnect();
|
||||
}
|
||||
}, server.ensureConnected().eventLoop());
|
||||
} else if (message instanceof RegistryPacket registryPacket) {
|
||||
server.getConnection().write(new ACKPacket(Context.fromContext(message.getContext(), true)));
|
||||
server.getConnection().write(new ACKPacket(Context.createContext(message.getContext().getResponseID(), true)));
|
||||
handshake.addRegistry(registryPacket);
|
||||
remainingRegistries.countDown();
|
||||
} else if (message instanceof ConfigDataPacket) {
|
||||
server.getConnection().write(new ACKPacket(Context.fromContext(message.getContext(), true)));
|
||||
} else if (message instanceof GenericForgeLoginWrapperPacket<Context> packet
|
||||
&& ForgeHandshakeUtils.ThirdPartyRegistryUtils.isThirdPartyPacket(packet)) {
|
||||
server.getConnection().write(
|
||||
ForgeHandshakeUtils.ThirdPartyRegistryUtils.getThirdPartyChannel(packet).
|
||||
generateResponsePacket(
|
||||
Context.ClientContext.fromContext(packet.getContext(), true),
|
||||
clientPhase.forgeHandshake));
|
||||
server.getConnection().write(new ACKPacket(Context.createContext(message.getContext().getResponseID(), true)));
|
||||
} else if (message instanceof GenericForgeLoginWrapperPacket<?> packet
|
||||
&& ForgeHandshakeUtils.SilentGearUtils.isSilentGearPacket(packet.getContent())) {
|
||||
server.getConnection().write(new ForgeHandshakeUtils.SilentGearUtils.ACKPacket(
|
||||
Context.createContext(message.getContext().getResponseID(), true)));
|
||||
}
|
||||
}
|
||||
//Forge server
|
||||
|
|
@ -184,9 +134,9 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(VelocityServerConnection server, ConnectedPlayer player, PluginMessagePacket message) {
|
||||
public boolean handle(VelocityServerConnection server, ConnectedPlayer player, PluginMessage message) {
|
||||
if (message.getChannel().equals("ambassador:commands")) {
|
||||
AvailableCommandsPacket packet = new AvailableCommandsPacket();
|
||||
AvailableCommands packet = new AvailableCommands();
|
||||
packet.decode(message.content(), ProtocolUtils.Direction.CLIENTBOUND,server.getConnection().getProtocolVersion());
|
||||
server.getConnection().getActiveSessionHandler().handle(packet);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -10,20 +10,19 @@ import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
|||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessagePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.adde0109.ambassador.forge.packet.Context;
|
||||
import org.adde0109.ambassador.forge.packet.GenericForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.packet.ModListReplyPacket;
|
||||
import org.adde0109.ambassador.velocity.client.FML2CRPMResetCompleteDecoder;
|
||||
import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder;
|
||||
import org.adde0109.ambassador.velocity.client.ClientPacketQueue;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -36,15 +35,59 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
}
|
||||
|
||||
@Override
|
||||
public void complete(ConnectedPlayer player) {
|
||||
//When no handshake has taken place.
|
||||
//Test if the client supports CRP.
|
||||
ClientResetType.CRP.doReset(player);
|
||||
public void resetConnectionPhase(ConnectedPlayer player) {
|
||||
RESETTABLE.resetConnectionPhase(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
IN_PROGRESS {
|
||||
},
|
||||
WAITING_RESET() {
|
||||
RESETTABLE {
|
||||
@Override
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
//Plugins may now send packets to client
|
||||
player.getConnection().getChannel().pipeline().remove(ForgeConstants.PLUGIN_PACKET_QUEUE);
|
||||
((VelocityServer) Ambassador.getInstance().server).registerConnection(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConnectionPhase(ConnectedPlayer player) {
|
||||
MinecraftConnection connection = player.getConnection();
|
||||
|
||||
//There is no going back even if the handshake fails. No reason to still be connected.
|
||||
if (player.getConnectedServer() != null) {
|
||||
player.getConnectedServer().disconnect();
|
||||
player.setConnectedServer(null);
|
||||
}
|
||||
//Don't handle anything from the server until the reset has completed.
|
||||
//player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false);
|
||||
|
||||
if (connection.getState() == StateRegistry.PLAY || connection.getState() == StateRegistry.CONFIG) {
|
||||
connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
} else {
|
||||
connection.write(new LoginPluginMessage(98,"fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
|
||||
}
|
||||
|
||||
//Prepare to receive reset ACK
|
||||
connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER,
|
||||
ForgeConstants.RESET_LISTENER, new FML2CRPMResetCompleteDecoder());
|
||||
|
||||
//Transition
|
||||
player.setPhase(WAITING_RESET);
|
||||
WAITING_RESET.onTransitionToNewPhase(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
WAITING_RESET {
|
||||
@Override
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
//We unregister so no plugin sees this client while the client is being reset.
|
||||
|
|
@ -59,7 +102,6 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
@Override
|
||||
public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket msg, VelocityServerConnection server) {
|
||||
if (msg.getContext().getResponseID() == 98) {
|
||||
//Reset complete
|
||||
player.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
|
||||
player.setPhase(NOT_STARTED);
|
||||
|
||||
|
|
@ -67,10 +109,7 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
|
||||
if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
// -> vanilla
|
||||
complete(player, ((Context.ClientContext) msg.getContext()).success() ? ClientResetType.CRP : null);
|
||||
}
|
||||
|
||||
if (player.getConnectionInFlight() != null) {
|
||||
complete(player, ((Context.ClientContext) msg.getContext()).success());
|
||||
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true);
|
||||
}
|
||||
|
||||
|
|
@ -81,15 +120,8 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
}
|
||||
},
|
||||
COMPLETE {
|
||||
|
||||
private ClientResetType resetType = ClientResetType.UNKNOWN;
|
||||
@Override
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
//Send Login Success to client
|
||||
MinecraftConnection connection = player.getConnection();
|
||||
((OutboundSuccessHolder) connection.getChannel().pipeline().get(ForgeConstants.SERVER_SUCCESS_LISTENER))
|
||||
.sendPacket();
|
||||
connection.setState(StateRegistry.PLAY);
|
||||
//Plugins may now send packets to client
|
||||
player.getConnection().getChannel().pipeline().remove(ForgeConstants.PLUGIN_PACKET_QUEUE);
|
||||
((VelocityServer) Ambassador.getInstance().server).registerConnection(player);
|
||||
|
|
@ -97,7 +129,21 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
|
||||
@Override
|
||||
public void resetConnectionPhase(ConnectedPlayer player) {
|
||||
getResetType().doReset(player);
|
||||
Ambassador.getTemporaryForced().put(player.getUsername(), player.getConnectionInFlight().getServer(),
|
||||
Ambassador.getInstance().config.getServerSwitchCancellationTime(), TimeUnit.SECONDS);
|
||||
//Disconnect - Reset
|
||||
if(player.getModInfo().isPresent()
|
||||
&& player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("serverredirect")
|
||||
|| mod.getId().equals("srvredirect:red")))
|
||||
&& player.getVirtualHost().isPresent()) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ProtocolUtils.writeVarInt(buf, 0);
|
||||
buf.writeBytes((player.getVirtualHost().get().getHostName() + ":"
|
||||
+ player.getVirtualHost().get().getPort()).getBytes(StandardCharsets.UTF_8));
|
||||
player.getConnection().write(new PluginMessage("srvredirect:red", buf));
|
||||
} else {
|
||||
player.disconnect(Ambassador.getInstance().config.getDisconnectResetMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -105,36 +151,14 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(ConnectedPlayer player) {
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Not resetting"));
|
||||
}
|
||||
}
|
||||
public void complete(ConnectedPlayer player, boolean resettable) { }
|
||||
|
||||
@Override
|
||||
void setResetType(ConnectedPlayer player, ClientResetType resetType) {
|
||||
this.resetType = resetType;
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Reset type: " + this.resetType.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResetType getResetType() {
|
||||
return resetType;
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: Make a new class that's linked to each player with these fields instead of having them in this phase class
|
||||
public ForgeHandshake forgeHandshake = new ForgeHandshake();
|
||||
|
||||
public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket<Context.ClientContext> msg, VelocityServerConnection server) {
|
||||
|
||||
if (msg.getContext().getChannelName().equals("zeta:main")) {
|
||||
forgeHandshake.zetaFlagsPacket = (GenericForgeLoginWrapperPacket<Context.ClientContext>) msg;
|
||||
}
|
||||
|
||||
if (msg instanceof ModListReplyPacket replyPacket) {
|
||||
ModInfo modInfo = new ModInfo("FML2", replyPacket.getMods().stream().map(
|
||||
(v) -> new ModInfo.Mod(v,"1")).toList());
|
||||
|
|
@ -155,24 +179,35 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void complete(ConnectedPlayer player) {
|
||||
complete(player, null);
|
||||
complete(player, isResettable(player));
|
||||
}
|
||||
|
||||
public void complete(ConnectedPlayer player, ClientResetType resetType) {
|
||||
//Change phase to COMPLETE
|
||||
player.setPhase(COMPLETE);
|
||||
COMPLETE.onTransitionToNewPhase(player);
|
||||
COMPLETE.forgeHandshake = forgeHandshake;
|
||||
if (resetType != null) {
|
||||
COMPLETE.setResetType(player, resetType);
|
||||
}
|
||||
public void complete(ConnectedPlayer player, boolean resettable) {
|
||||
MinecraftConnection connection = player.getConnection();
|
||||
((OutboundSuccessHolder) connection.getChannel().pipeline().get(ForgeConstants.SERVER_SUCCESS_LISTENER))
|
||||
.sendPacket();
|
||||
connection.setState(StateRegistry.PLAY);
|
||||
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Forge handshake complete"));
|
||||
if (resettable) {
|
||||
player.setPhase(RESETTABLE);
|
||||
RESETTABLE.onTransitionToNewPhase(player);
|
||||
RESETTABLE.forgeHandshake = forgeHandshake;
|
||||
} else {
|
||||
player.setPhase(COMPLETE);
|
||||
COMPLETE.onTransitionToNewPhase(player);
|
||||
COMPLETE.forgeHandshake = forgeHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResettable(ConnectedPlayer player) {
|
||||
if (player.getModInfo().isPresent()) {
|
||||
return player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("clientresetpacket")));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
|
||||
}
|
||||
|
|
@ -186,82 +221,5 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
|
|||
return false;
|
||||
}
|
||||
|
||||
public ClientResetType getResetType() {
|
||||
return COMPLETE.getResetType();
|
||||
}
|
||||
|
||||
private ClientResetType getResetType(ConnectedPlayer player) {
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Scanning modlist for client reset mods"));
|
||||
}
|
||||
if (player.getModInfo().isPresent()) {
|
||||
if (player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("clientresetpacket")))) {
|
||||
return ClientResetType.CRP;
|
||||
} else if (Ambassador.getInstance().config.getServerSwitchCancellationTime() >= 0 &&
|
||||
player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("serverredirect")
|
||||
|| mod.getId().equals("srvredirect:red")))
|
||||
&& player.getVirtualHost().isPresent()) {
|
||||
return ClientResetType.SR;
|
||||
}
|
||||
}
|
||||
return ClientResetType.NONE;
|
||||
}
|
||||
|
||||
void setResetType(ConnectedPlayer player, ClientResetType resetType) {
|
||||
COMPLETE.setResetType(player, resetType);
|
||||
}
|
||||
public void updateResetType(ConnectedPlayer player) {
|
||||
COMPLETE.setResetType(player, getResetType(player));
|
||||
}
|
||||
|
||||
public enum ClientResetType {
|
||||
UNKNOWN,
|
||||
NONE,
|
||||
CRP {
|
||||
@Override
|
||||
void doReset(ConnectedPlayer player) {
|
||||
MinecraftConnection connection = player.getConnection();
|
||||
|
||||
//There is no going back even if the handshake fails. No reason to still be connected.
|
||||
if (player.getConnectedServer() != null) {
|
||||
player.getConnectedServer().disconnect();
|
||||
player.setConnectedServer(null);
|
||||
}
|
||||
//Don't handle anything from the server until the reset has completed.
|
||||
if (player.getConnectionInFlight() != null) {
|
||||
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false);
|
||||
}
|
||||
|
||||
if (connection.getState() == StateRegistry.PLAY || connection.getState() == StateRegistry.CONFIG) {
|
||||
connection.write(new PluginMessagePacket("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
} else {
|
||||
connection.write(new LoginPluginMessagePacket(98,"fml:loginwrapper", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket())));
|
||||
}
|
||||
|
||||
//Prepare to receive reset ACK
|
||||
connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER,
|
||||
ForgeConstants.RESET_LISTENER, new FML2CRPMResetCompleteDecoder());
|
||||
|
||||
//Transition
|
||||
player.setPhase(WAITING_RESET);
|
||||
WAITING_RESET.onTransitionToNewPhase(player);
|
||||
}
|
||||
},
|
||||
SR {
|
||||
@Override
|
||||
void doReset(ConnectedPlayer player) {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ProtocolUtils.writeVarInt(buf, 0);
|
||||
buf.writeBytes((player.getVirtualHost().get().getHostName() + ":"
|
||||
+ player.getVirtualHost().get().getPort()).getBytes(StandardCharsets.UTF_8));
|
||||
player.getConnection().write(new PluginMessagePacket("srvredirect:red", buf));
|
||||
|
||||
Ambassador.getInstance().reconnectSwitchPlayer(player);
|
||||
}
|
||||
};
|
||||
|
||||
void doReset(ConnectedPlayer player) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,38 +4,27 @@ public class Context {
|
|||
|
||||
private final int responseID;
|
||||
|
||||
private final String channelName;
|
||||
|
||||
private Context(int responseID, String channelName) {
|
||||
private Context(int responseID) {
|
||||
this.responseID = responseID;
|
||||
this.channelName = channelName;
|
||||
}
|
||||
|
||||
public static Context createContext(int responseID, String channelName) {
|
||||
return new Context(responseID, channelName);
|
||||
public static Context createContext(int responseID) {
|
||||
return new Context(responseID);
|
||||
}
|
||||
|
||||
public static ClientContext createClientContext(int responseID, boolean clientSuccess, String channelName) {
|
||||
return new ClientContext(responseID, clientSuccess, channelName);
|
||||
}
|
||||
|
||||
public static ClientContext fromContext(Context context, boolean clientSuccess) {
|
||||
return new ClientContext(context.responseID, clientSuccess, context.channelName);
|
||||
public static ClientContext createContext(int responseID, boolean clientSuccess) {
|
||||
return new ClientContext(responseID,clientSuccess);
|
||||
}
|
||||
|
||||
public int getResponseID() {
|
||||
return responseID;
|
||||
}
|
||||
|
||||
public String getChannelName() {
|
||||
return channelName;
|
||||
}
|
||||
|
||||
public static class ClientContext extends Context {
|
||||
|
||||
private final boolean clientSuccess;
|
||||
ClientContext(int responseID, boolean clientSuccess, String channelName) {
|
||||
super(responseID, channelName);
|
||||
ClientContext(int responseID, boolean clientSuccess) {
|
||||
super(responseID);
|
||||
this.clientSuccess = clientSuccess;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public class GenericForgeLoginWrapperPacket<T extends Context> implements IForge
|
|||
private final byte[] content;
|
||||
private final T context;
|
||||
|
||||
public GenericForgeLoginWrapperPacket(byte[] content, T context) {
|
||||
GenericForgeLoginWrapperPacket(byte[] content, T context) {
|
||||
this.content = content;
|
||||
this.context = context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,10 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class ModDataPacket implements IForgeLoginWrapperPacket<Context> {
|
||||
private final byte[] content;
|
||||
private final Context context;
|
||||
public class ModDataPacket extends GenericForgeLoginWrapperPacket<Context> {
|
||||
|
||||
ModDataPacket(byte[] content, Context context) {
|
||||
this.content = content;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
static public ModDataPacket read(ByteBuf input, Context context) {
|
||||
byte[] content = new byte[input.readableBytes()];
|
||||
input.readBytes(content);
|
||||
return new ModDataPacket(content, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
ProtocolUtils.writeVarInt(buf, 5); //PacketID
|
||||
buf.writeBytes(content);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return context;
|
||||
super(content, context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class ModListPacket implements IForgeLoginWrapperPacket<Context> {
|
|||
registries.add(ProtocolUtils.readString(input, 32767));
|
||||
|
||||
List<String> dataPackRegistries = null;
|
||||
if (FML3 && input.isReadable()) {
|
||||
if (FML3) {
|
||||
dataPackRegistries = new ArrayList<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.adde0109.ambassador.forge.packet;
|
|||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
|||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
|
||||
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
|
@ -43,7 +43,7 @@ public class CommandDecoderErrorCatcher extends ChannelInboundHandlerAdapter {
|
|||
int packetId = ProtocolUtils.readVarInt(buf);
|
||||
MinecraftPacket packet = registry.createPacket(packetId);
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
if (packet instanceof AvailableCommandsPacket) {
|
||||
if (packet instanceof AvailableCommands) {
|
||||
try {
|
||||
((MinecraftDecoder) ctx.pipeline().get(Connections.MINECRAFT_DECODER)).channelRead(ctx, msg);
|
||||
} catch (QuietRuntimeException | CorruptedFrameException e) {
|
||||
|
|
|
|||
|
|
@ -1,145 +1,114 @@
|
|||
package org.adde0109.ambassador.forge.pipeline;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessagePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponsePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.adde0109.ambassador.forge.ForgeHandshakeUtils;
|
||||
import org.adde0109.ambassador.forge.packet.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ForgeLoginWrapperCodec extends MessageToMessageCodec<DeferredByteBufHolder, IForgeLoginWrapperPacket<?>> {
|
||||
|
||||
private final boolean FML3;
|
||||
private final Map<Integer, Context> loginWrapperContexts = new HashMap<>();
|
||||
private final List<Integer> loginWrapperIDs = new ArrayList<>();
|
||||
|
||||
public ForgeLoginWrapperCodec(boolean fml3) {
|
||||
FML3 = fml3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptInboundMessage(Object msg) throws Exception {
|
||||
return (msg instanceof LoginPluginMessagePacket
|
||||
&& ((LoginPluginMessagePacket) msg).getChannel().equals("fml:loginwrapper"))
|
||||
|| (msg instanceof LoginPluginResponsePacket
|
||||
&& loginWrapperContexts.containsKey(((LoginPluginResponsePacket) msg).getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, DeferredByteBufHolder in, List<Object> out) throws Exception {
|
||||
|
||||
ByteBuf buf = in.content();
|
||||
|
||||
Context context;
|
||||
if (in instanceof LoginPluginResponsePacket msg) {
|
||||
//Continue from stored context
|
||||
context = Context.fromContext(
|
||||
loginWrapperContexts.remove(((LoginPluginResponsePacket) msg).getId()), msg.isSuccess());
|
||||
if (!msg.isSuccess()) {
|
||||
//Nothing to read, just create an empty packet.
|
||||
out.add(GenericForgeLoginWrapperPacket.read(buf, context));
|
||||
return;
|
||||
} else {
|
||||
String channel = ProtocolUtils.readString(buf); //Read the channel even though we know the channel by context.
|
||||
Ambassador.getInstance();
|
||||
}
|
||||
if (in instanceof LoginPluginMessage msg && msg.getChannel().equals("fml:loginwrapper")) {
|
||||
context = Context.createContext(msg.getId());
|
||||
} else if (in instanceof LoginPluginResponse msg && loginWrapperIDs.remove(Integer.valueOf(msg.getId()))) {
|
||||
context = Context.createContext(msg.getId(), msg.isSuccess());
|
||||
} else {
|
||||
//New context.
|
||||
LoginPluginMessagePacket msg = (LoginPluginMessagePacket) in;
|
||||
String channel = ProtocolUtils.readString(buf);
|
||||
|
||||
context = Context.createContext(msg.getId(), channel);
|
||||
ctx.fireChannelRead(in.retain());
|
||||
return;
|
||||
}
|
||||
|
||||
//Decoding of data starts here - channel already read
|
||||
int originalReaderIndex = buf.readerIndex();
|
||||
|
||||
if (!context.getChannelName().equals("fml:handshake")) {
|
||||
out.add(GenericForgeLoginWrapperPacket.read(buf, context));
|
||||
} else {
|
||||
int length = ProtocolUtils.readVarInt(buf);
|
||||
int packetID = ProtocolUtils.readVarInt(buf);
|
||||
if (context instanceof Context.ClientContext clientContext) {
|
||||
switch (packetID) {
|
||||
case 2:
|
||||
out.add(ModListReplyPacket.read(buf, clientContext));
|
||||
break;
|
||||
case 99:
|
||||
out.add(new ACKPacket(clientContext));
|
||||
break;
|
||||
default:
|
||||
//Undo decoding
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
out.add(GenericForgeLoginWrapperPacket.read(buf, context));
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
Ambassador.getInstance().logger.warn(
|
||||
"Unrecognised packet id received from client on fml:handshake: " + packetID);
|
||||
}
|
||||
}
|
||||
try {
|
||||
String channel = ProtocolUtils.readString(buf);
|
||||
if (!channel.equals("fml:handshake")) {
|
||||
throw new DecoderException();
|
||||
} else {
|
||||
switch (packetID) {
|
||||
case 1:
|
||||
out.add(ModListPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 3:
|
||||
out.add(RegistryPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 4:
|
||||
out.add(ConfigDataPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 5:
|
||||
if (FML3) {
|
||||
out.add(ModDataPacket.read(buf, context));
|
||||
int length = ProtocolUtils.readVarInt(buf);
|
||||
int packetID = ProtocolUtils.readVarInt(buf);
|
||||
if (context instanceof Context.ClientContext clientContext) {
|
||||
switch (packetID) {
|
||||
case 2:
|
||||
out.add(ModListReplyPacket.read(buf, clientContext));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//Undo decoding
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
out.add(GenericForgeLoginWrapperPacket.read(buf, context));
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
Ambassador.getInstance().logger.warn(
|
||||
"Unrecognised packet id received from server on fml:handshake: " + packetID);
|
||||
}
|
||||
case 99:
|
||||
out.add(new ACKPacket(clientContext));
|
||||
break;
|
||||
default:
|
||||
throw new DecoderException();
|
||||
}
|
||||
} else {
|
||||
switch (packetID) {
|
||||
case 1:
|
||||
out.add(ModListPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 3:
|
||||
out.add(RegistryPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 4:
|
||||
out.add(ConfigDataPacket.read(buf, context, FML3));
|
||||
break;
|
||||
case 5:
|
||||
if (FML3) {
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
out.add(ModDataPacket.read(buf, context));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new DecoderException();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DecoderException e) {
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
out.add(GenericForgeLoginWrapperPacket.read(buf, context));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, IForgeLoginWrapperPacket<?> msg, List<Object> out) throws Exception {
|
||||
ByteBuf wrapped;
|
||||
|
||||
boolean data = !(msg.getContext() instanceof Context.ClientContext clientContext && !clientContext.success());
|
||||
|
||||
boolean includeLength = !(msg instanceof GenericForgeLoginWrapperPacket);
|
||||
|
||||
String channel = msg.getContext().getChannelName();
|
||||
|
||||
wrapped = Unpooled.buffer();
|
||||
|
||||
if (data) {
|
||||
if (msg instanceof GenericForgeLoginWrapperPacket<?>) {
|
||||
wrapped = msg.encode();
|
||||
} else {
|
||||
String channel = "fml:handshake";
|
||||
if (msg instanceof ForgeHandshakeUtils.SilentGearUtils.ACKPacket) {
|
||||
channel = "silentgear:network";
|
||||
}
|
||||
wrapped = Unpooled.buffer();
|
||||
ByteBuf encoded = msg.encode();
|
||||
ProtocolUtils.writeString(wrapped, channel);
|
||||
if (includeLength)
|
||||
ProtocolUtils.writeVarInt(wrapped, encoded.readableBytes());
|
||||
ProtocolUtils.writeVarInt(wrapped, encoded.readableBytes());
|
||||
wrapped.writeBytes(encoded);
|
||||
encoded.release();
|
||||
}
|
||||
|
||||
if (msg.getContext() instanceof Context.ClientContext clientContext) {
|
||||
out.add(new LoginPluginResponsePacket(clientContext.getResponseID(), clientContext.success(), wrapped));
|
||||
out.add(new LoginPluginResponse(clientContext.getResponseID(), clientContext.success(), wrapped));
|
||||
} else {
|
||||
out.add(new LoginPluginMessagePacket(msg.getContext().getResponseID(), "fml:loginwrapper", wrapped));
|
||||
if (!(msg instanceof ModDataPacket)) { //ModDataPacket doesn't require a response
|
||||
this.loginWrapperContexts.put(msg.getContext().getResponseID(), msg.getContext());
|
||||
out.add(new LoginPluginMessage(msg.getContext().getResponseID(), "fml:loginwrapper", wrapped));
|
||||
if (!(msg instanceof ModDataPacket)) {
|
||||
this.loginWrapperIDs.add(msg.getContext().getResponseID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,12 +71,6 @@ public class VelocityEventHandler {
|
|||
player.setModInfo(new ModInfo("Channels", event.getChannels().stream().map((id) -> {
|
||||
return new ModInfo.Mod(id.getId(), "");
|
||||
}).toList()));
|
||||
|
||||
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
|
||||
//If reset typ is still unknown, set it!
|
||||
if (clientPhase.getResetType() == VelocityForgeClientConnectionPhase.ClientResetType.UNKNOWN) {
|
||||
clientPhase.updateResetType(player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.velocitypowered.proxy.connection.ConnectionTypes;
|
|||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
|
|
@ -16,17 +16,17 @@ import java.util.List;
|
|||
|
||||
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
|
||||
|
||||
public class FMLMarkerAdder extends MessageToMessageEncoder<HandshakePacket> {
|
||||
public class FMLMarkerAdder extends MessageToMessageEncoder<Handshake> {
|
||||
|
||||
final VelocityServer server;
|
||||
|
||||
public FMLMarkerAdder(VelocityServer server) {
|
||||
super(HandshakePacket.class);
|
||||
super(Handshake.class);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, HandshakePacket msg, List<Object> out) {
|
||||
protected void encode(ChannelHandlerContext ctx, Handshake msg, List<Object> out) {
|
||||
MinecraftConnection connection = (MinecraftConnection) ctx.pipeline().get(Connections.HANDLER);
|
||||
VelocityServerConnection serverConnection = (VelocityServerConnection) connection.getAssociation();
|
||||
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
|||
import com.velocitypowered.proxy.connection.backend.*;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessagePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.adde0109.ambassador.forge.*;
|
||||
|
|
@ -26,28 +26,33 @@ public class ForgeLoginSessionHandler implements MinecraftSessionHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ServerLoginSuccessPacket packet) {
|
||||
public boolean handle(ServerLoginSuccess packet) {
|
||||
if ((serverConnection.getPhase() instanceof VelocityForgeBackendConnectionPhase phase)) {
|
||||
phase.onLoginSuccess(serverConnection,serverConnection.getPlayer());
|
||||
}
|
||||
|
||||
original.handle(packet); //Can lead to disconnect.
|
||||
|
||||
//If we are still connected after handling that package.
|
||||
if (serverConnection.getConnection() != null) {
|
||||
ConnectedPlayer player = serverConnection.getPlayer();
|
||||
|
||||
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
|
||||
clientPhase.complete(player);
|
||||
original.handle(packet);
|
||||
if (serverConnection.getConnection() == null) {
|
||||
return true;
|
||||
}
|
||||
ConnectedPlayer player = serverConnection.getPlayer();
|
||||
if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
if (player.getConnectedServer() == null ||
|
||||
player.getConnectedServer().getConnection().getType() instanceof ForgeFMLConnectionType) {
|
||||
//Initial Vanilla - test if the client can be reset
|
||||
//Forge -> vanilla
|
||||
player.getPhase().resetConnectionPhase(player);
|
||||
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false);
|
||||
}
|
||||
} else {
|
||||
((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean handle(DisconnectPacket packet) {
|
||||
public boolean handle(Disconnect packet) {
|
||||
return original.handle(packet);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.TransitionSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.JoinGame;
|
||||
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
|
||||
|
||||
public class ForgePlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
private final TransitionSessionHandler original;
|
||||
private final VelocityServerConnection serverConnection;
|
||||
|
||||
public ForgePlaySessionHandler(TransitionSessionHandler original, VelocityServerConnection serverConnection) {
|
||||
this.original = original;
|
||||
this.serverConnection = serverConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(JoinGame packet) {
|
||||
if (serverConnection.getPlayer().getPhase() instanceof VelocityForgeClientConnectionPhase clientPhase) {
|
||||
serverConnection.getPlayer().setPhase(VelocityForgeClientConnectionPhase.RESETTABLE);
|
||||
}
|
||||
return MinecraftSessionHandler.super.handle(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
original.disconnected();
|
||||
}
|
||||
|
||||
public void handleGeneric(MinecraftPacket packet) {
|
||||
if (!packet.handle(original))
|
||||
original.handleGeneric(packet);
|
||||
}
|
||||
|
||||
public MinecraftSessionHandler getOriginal() {
|
||||
return this.original;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter {
|
|||
if (id == 98) {
|
||||
try {
|
||||
ctx.fireChannelRead(GenericForgeLoginWrapperPacket.read(
|
||||
Unpooled.EMPTY_BUFFER, Context.createClientContext(id, success, "fml:handshake")));
|
||||
Unpooled.EMPTY_BUFFER, Context.createContext(id, success)));
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package org.adde0109.ambassador.velocity.client;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
|
||||
public class OutboundSuccessHolder extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private ServerLoginSuccessPacket packet;
|
||||
private ServerLoginSuccess packet;
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
@Override
|
||||
|
|
@ -17,7 +17,7 @@ public class OutboundSuccessHolder extends ChannelOutboundHandlerAdapter {
|
|||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if ((msg instanceof ServerLoginSuccessPacket packet)) {
|
||||
if ((msg instanceof ServerLoginSuccess packet)) {
|
||||
this.packet = packet;
|
||||
} else {
|
||||
ctx.write(msg, promise);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
|
|||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ public class VelocityHandshakeSessionHandler extends HandshakeSessionHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(HandshakePacket handshake) {
|
||||
public boolean handle(Handshake handshake) {
|
||||
handshake.handle(original);
|
||||
if (connection.getType() == ConnectionTypes.VANILLA) {
|
||||
final String[] markerSplit = handshake.getServerAddress().split("\0");
|
||||
|
|
|
|||
|
|
@ -1,29 +1,9 @@
|
|||
# Do not change this
|
||||
config-version = "2.1"
|
||||
config-version = "1.1"
|
||||
|
||||
# Message displayed to the player when disconnected from proxy during server switch.
|
||||
# Legacy color codes and JSON are accepted.
|
||||
disconnect-reset-message = "&6Please reconnect"
|
||||
# How much time the player has to reconnect before canceling the server switch. (In seconds)
|
||||
# Only for players with ServerRedirect mod installed. Set to -1 to disable ServerRedirect mod support.
|
||||
serverRedirectTimeout = 30
|
||||
|
||||
# Silence PCF absence warnings.
|
||||
server-switch-cancellation-time = 120
|
||||
silence-warnings = false
|
||||
|
||||
# Allow server switches without reset even though the new server's registries don't match the old server’s registry.
|
||||
# Large modpacks often needs this set to true. Warning: This is a safety measure and setting this to true
|
||||
# can lead to unstable behaviour.
|
||||
bypass-registry-checks = false
|
||||
|
||||
# Allow player to switch without reset when the server's mods don't match. Even more unstable than bypassRegistryCheck.
|
||||
# Warning: This is a safety measure for when bypassRegistryCheck is true. Setting this to also true can cause crashes.
|
||||
bypass-mod-checks = false
|
||||
|
||||
# Only for debug/troubleshooting
|
||||
debug-mode = false
|
||||
|
||||
# Make the player reconnect when server switching as a last resort if
|
||||
# the player can't reset or switch servers without resetting.
|
||||
enable-kick-reset = false
|
||||
|
||||
# Message to display when player gets kicked due to server switching according to enable-kick-reset.
|
||||
# This input is deserialzied as MiniMessage and support formating according to the MiniMessage format.
|
||||
reconnect-message = "<red>Please reconnect.</red>"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user