Compare commits
6 Commits
non-api
...
bungeecord
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac242b631e | ||
|
|
4be2b3760a | ||
|
|
a48f2ae309 | ||
|
|
c72f5b72f2 | ||
|
|
2a004712e3 | ||
|
|
2063b1364e |
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,7 @@
|
|||
.gradle
|
||||
/build/
|
||||
/ambassador-bungeecord/build/
|
||||
/ambassador-velocity/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
|
|
|||
37
README.md
37
README.md
|
|
@ -2,28 +2,21 @@
|
|||
|
||||
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".
|
||||
3. If you want to use player-information forwarding you can use any of these mods on the 1.13+ forge server:
|
||||
- https://github.com/adde0109/Proxy-Compatible-Forge (Modern forwarding) (Magma 1.18.2 and higher includes this)
|
||||
Unlike other solutions, this plugin does not require any special modifications to the client. (The player doesn't need to do anything)
|
||||
|
||||
- https://github.com/caunt/BungeeForge (Legacy forwarding)
|
||||
This plugin is right now in its alpha stage and should not be used in production, use it at your own risk, you have been warned.
|
||||
## How to get started:
|
||||
### On the Velocity proxy side:
|
||||
1. Download and install this plugin to your proxy.
|
||||
|
||||
### On the Forge server side:
|
||||
1. Download and install "Ambassador-Forge" as a mod to your Forge server. (Found at https://github.com/adde0109/Ambassador-Forge)
|
||||
2. Start the server.
|
||||
3. If you wish to use modern forwarding, close the server and open "ambassador-common.toml" in the config folder and put your forwarding secret in the "forwardingSecret" field.
|
||||
4. In "server.properties" make sure online-mode is set to false.
|
||||
5. You are now ready to start the server and connect to it with Velocity!
|
||||
|
||||
## 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
|
||||
|
||||
## 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.)
|
||||
|
||||
## Discord
|
||||
https://discord.gg/Vusz9pBNyJ
|
||||
* Server switching.
|
||||
* Server switching using kick to reset the client.
|
||||
* Server switching using client mod: https://github.com/Just-Chaldea/Forge-Client-Reset-Packet
|
||||
|
|
|
|||
2
Velocity
2
Velocity
|
|
@ -1 +1 @@
|
|||
Subproject commit c3583e182ca6585e40d1eef0da8c18547c0b1bc1
|
||||
Subproject commit f744b37ad5c7b6f00ca01681f26b75cad2584b7e
|
||||
17
ambassador-bungeecord/build.gradle.kts
Normal file
17
ambassador-bungeecord/build.gradle.kts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
plugins {
|
||||
java
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
}
|
||||
|
||||
group = "org.adde0109"
|
||||
version = "1.1.7-alpha-bungeecord"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // This lets gradle find the BungeeCord files online
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("net.md-5:bungeecord-api:1.19-R0.1-SNAPSHOT")
|
||||
implementation("org.javassist:javassist:3.29.2-GA")
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.adde0109.ambassador;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.adde0109.ambassador.forgeCommandArgument.Injector;
|
||||
|
||||
public class AmbassadorPlugin extends Plugin {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// You should not put an enable message in your plugin.
|
||||
// BungeeCord already does so
|
||||
getLogger().info("Yay! It loads!");
|
||||
Injector.inject();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.adde0109.ambassador.forgeCommandArgument;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
public class EnumArgumentProperty {
|
||||
|
||||
private final String className;
|
||||
|
||||
public EnumArgumentProperty(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.adde0109.ambassador.forgeCommandArgument;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
|
||||
/**
|
||||
* An argument property serializer that will serialize and deserialize nothing.
|
||||
*/
|
||||
public class EnumArgumentPropertySerializer {
|
||||
|
||||
public static final EnumArgumentPropertySerializer ENUM;
|
||||
|
||||
static {
|
||||
try {
|
||||
ENUM = new EnumArgumentPropertySerializer();
|
||||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | ClassNotFoundException |
|
||||
NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
EnumArgumentPropertySerializer() throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
|
||||
Constructor constructor = EnumArgumentPropertySerializer.class.getSuperclass().getDeclaredConstructor(Class.forName("net.md_5.bungee.protocol.packet.Commands$1"));
|
||||
constructor.setAccessible(true);
|
||||
constructor.newInstance(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected Object read(ByteBuf buf) {
|
||||
return new EnumArgumentProperty(DefinedPacket.readString(buf));
|
||||
}
|
||||
|
||||
protected void write(ByteBuf buf, Object t) {
|
||||
DefinedPacket.writeString(((EnumArgumentProperty) t).getClassName(),buf);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.adde0109.ambassador.forgeCommandArgument;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.bytecode.ClassFile;
|
||||
import javassist.expr.MethodCall;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
import net.md_5.bungee.protocol.packet.Commands;
|
||||
import org.adde0109.ambassador.forgeCommandArgument.EnumArgumentPropertySerializer;
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Injector{
|
||||
|
||||
|
||||
|
||||
static public void inject() {
|
||||
try {
|
||||
Class<?> argumentRegistryClass = Class.forName("net.md_5.bungee.protocol.packet.Commands$ArgumentRegistry");
|
||||
Class<?> networkNodeClass = Class.forName("net.md_5.bungee.protocol.packet.Commands$NetworkNode");
|
||||
Class<?> argumentSerializerClass = Class.forName("net.md_5.bungee.protocol.packet.Commands$ArgumentRegistry$ArgumentSerializer");
|
||||
|
||||
ClassPool.getDefault().insertClassPath(new ClassClassPath(EnumArgumentPropertySerializer.class));
|
||||
ClassPool.getDefault().insertClassPath(new ClassClassPath(EnumArgumentProperty.class));
|
||||
CtClass cc = ClassPool.getDefault().get("org.adde0109.ambassador.forgeCommandArgument.EnumArgumentPropertySerializer");
|
||||
CtClass scc = ClassPool.getDefault().get("net.md_5.bungee.protocol.packet.Commands$ArgumentRegistry$ArgumentSerializer");
|
||||
CtClass newcc = scc.getDeclaringClass().makeNestedClass("EnumArgumentPropertySerializer",true);
|
||||
newcc.addMethod(new CtMethod(cc.getDeclaredMethod("read"), newcc, null));
|
||||
newcc.addMethod(new CtMethod(cc.getDeclaredMethod("write"), newcc, null));
|
||||
newcc.setSuperclass(scc);
|
||||
|
||||
//CtConstructor ctconstructor = new CtConstructor(cc.getDeclaredConstructor(null),newcc,null);
|
||||
//newcc.addConstructor(ctconstructor);
|
||||
Class<?> loaded= newcc.toClass(argumentSerializerClass);
|
||||
|
||||
Method method = argumentRegistryClass.getDeclaredMethod("register",String.class,argumentSerializerClass);
|
||||
//Constructor<?> superconstructor = argumentSerializerClass.getDeclaredConstructors()[0];
|
||||
//method.setAccessible(true);
|
||||
//Constructor<?> constructor = loaded.getDeclaredConstructor();
|
||||
//constructor.setAccessible(true);
|
||||
//method.invoke(null,"forge:enum", constructor.newInstance());
|
||||
|
||||
CtClass dcc = scc.getDeclaringClass();
|
||||
dcc.getClassInitializer().insertAfter("$class.getDeclaredMethod(\"register\",String.class,Class.forName(\"net.md_5.bungee.protocol.packet.Commands$ArgumentRegistry$ArgumentSerializer\"));");
|
||||
dcc.toClass(networkNodeClass);
|
||||
MethodCall
|
||||
} catch (ReflectiveOperationException | NotFoundException | CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
4
ambassador-bungeecord/src/main/resources/plugin.yml
Normal file
4
ambassador-bungeecord/src/main/resources/plugin.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name: Ambassador
|
||||
main: org.adde0109.ambassador.AmbassadorPlugin
|
||||
version: 0.0.0-Bungeecord
|
||||
author: adde0109
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
plugins {
|
||||
java
|
||||
idea
|
||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
}
|
||||
|
||||
group = "org.adde0109"
|
||||
version = "1.5.3-beta"
|
||||
version = "1.1.7-alpha"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
|
@ -19,12 +18,11 @@ dependencies {
|
|||
compileOnly("com.velocitypowered:velocity-api")
|
||||
compileOnly("com.velocitypowered:velocity-proxy")
|
||||
annotationProcessor("com.velocitypowered:velocity-api")
|
||||
compileOnly("com.electronwill.night-config:toml:3.6.6")
|
||||
implementation("org.bstats:bstats-velocity:3.0.1")
|
||||
compileOnly("io.netty:netty-buffer:4.1.90.Final")
|
||||
compileOnly("io.netty:netty-transport:4.1.90.Final")
|
||||
compileOnly("io.netty:netty-codec:4.1.90.Final")
|
||||
compileOnly("io.netty:netty-handler:4.1.90.Final")
|
||||
implementation("com.electronwill.night-config:toml:3.6.6")
|
||||
implementation("org.bstats:bstats-velocity:3.0.0")
|
||||
implementation("org.apache.commons:commons-collections4:4.4")
|
||||
compileOnly("io.netty:netty-buffer:4.1.86.Final")
|
||||
compileOnly("io.netty:netty-transport:4.1.86.Final")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package org.adde0109.ambassador;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.network.ConnectionManager;
|
||||
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.ChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.VelocityBackendChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.VelocityServerChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.VelocityEventHandler;
|
||||
import org.adde0109.ambassador.velocity.commands.EnumArgumentProperty;
|
||||
import org.adde0109.ambassador.velocity.commands.EnumArgumentPropertySerializer;
|
||||
import org.adde0109.ambassador.velocity.commands.ModIdArgumentProperty;
|
||||
import org.bstats.velocity.Metrics;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Plugin(id = "ambassador", name = "Ambassador", version = "1.1.7-alpha", authors = {"adde0109"})
|
||||
public class Ambassador {
|
||||
|
||||
public ProxyServer server;
|
||||
public final Logger logger;
|
||||
private final Metrics.Factory metricsFactory;
|
||||
private final Path dataDirectory;
|
||||
|
||||
private static Ambassador instance;
|
||||
public static Ambassador getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@Inject
|
||||
public Ambassador(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory, Metrics.Factory metricsFactory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataDirectory = dataDirectory;
|
||||
this.metricsFactory = metricsFactory;
|
||||
Ambassador.instance = this;
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) throws ReflectiveOperationException {
|
||||
initMetrics();
|
||||
|
||||
server.getEventManager().register(this, new VelocityEventHandler(this));
|
||||
|
||||
inject();
|
||||
}
|
||||
|
||||
private void inject() throws ReflectiveOperationException {
|
||||
Field cmField = VelocityServer.class.getDeclaredField("cm");
|
||||
cmField.setAccessible(true);
|
||||
|
||||
ChannelInitializer<?> original = ((ConnectionManager) cmField.get(server)).serverChannelInitializer.get();
|
||||
((ConnectionManager) cmField.get(server)).serverChannelInitializer.set(new VelocityServerChannelInitializer(original,(VelocityServer) server));
|
||||
|
||||
ChannelInitializer<?> originalBackend = ((ConnectionManager) cmField.get(server)).backendChannelInitializer.get();
|
||||
((ConnectionManager) cmField.get(server)).backendChannelInitializer.set(new VelocityBackendChannelInitializer(originalBackend,(VelocityServer) server));
|
||||
|
||||
Method argumentRegistry = ArgumentPropertyRegistry.class.getDeclaredMethod("register", ArgumentIdentifier.class, Class.class, ArgumentPropertySerializer.class);
|
||||
argumentRegistry.setAccessible(true);
|
||||
argumentRegistry.invoke(null,ArgumentIdentifier.id("forge:enum"), EnumArgumentProperty.class, EnumArgumentPropertySerializer.ENUM);
|
||||
argumentRegistry.invoke(null,ArgumentIdentifier.id("forge:modid"), ModIdArgumentProperty.class,
|
||||
new ArgumentPropertySerializer<>() {
|
||||
@Override
|
||||
public ModIdArgumentProperty deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
return new ModIdArgumentProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void initMetrics() {
|
||||
Metrics metrics = metricsFactory.make(this, 15655);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.api.event.player.KickedFromServerEvent;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.UuidUtils;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
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.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler;
|
||||
import org.adde0109.ambassador.velocity.VelocityLoginPayloadManager;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnectionPhase {
|
||||
|
||||
//TODO: Use modData inside ConnectedPlayer instead
|
||||
public byte[] modListData;
|
||||
private RegisteredServer backupServer;
|
||||
|
||||
public FML2CRPMClientConnectionPhase(VelocityForgeClientConnectionPhase.ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) {
|
||||
super(clientPhase,payloadManager);
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> reset(RegisteredServer server, ConnectedPlayer player) {
|
||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||
if (player.getConnectedServer() != null) {
|
||||
backupServer = player.getConnectedServer().getServer();
|
||||
player.getConnectedServer().disconnect();
|
||||
player.setConnectedServer(null);
|
||||
}
|
||||
|
||||
MinecraftConnection connection = player.getConnection();
|
||||
connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),this));
|
||||
|
||||
((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player);
|
||||
this.clientPhase = null;
|
||||
|
||||
ScheduledFuture<?> scheduledFuture = connection.eventLoop().schedule(()-> {
|
||||
connection.getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
|
||||
future.complete(false);
|
||||
},5, TimeUnit.SECONDS);
|
||||
connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, ForgeConstants.RESET_LISTENER,new FML2CRPMResetCompleteDecoder());
|
||||
getPayloadManager().listenFor(98).thenAccept(ignore -> {
|
||||
if (scheduledFuture.cancel(false)) {
|
||||
connection.getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
this.clientPhase = ClientPhase.HANDSHAKE;
|
||||
future.complete(true);
|
||||
}
|
||||
});
|
||||
connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
|
||||
return future;
|
||||
}
|
||||
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
|
||||
VelocityConfiguration configuration = (VelocityConfiguration) 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);
|
||||
|
||||
this.clientPhase = this.clientPhase == ClientPhase.MODLIST ? ClientPhase.MODDED : ClientPhase.VANILLA;
|
||||
|
||||
connection.setState(StateRegistry.PLAY);
|
||||
connection.setSessionHandler(((VelocityForgeHandshakeSessionHandler) connection.getSessionHandler()).getOriginal());
|
||||
((VelocityServer) Ambassador.getInstance().server).registerConnection(player);
|
||||
|
||||
backupServer = null;
|
||||
}
|
||||
|
||||
public void handleKick(KickedFromServerEvent event) {
|
||||
if (backupServer != null && !(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) {
|
||||
net.kyori.adventure.text.Component reason = event.getServerKickReason().orElse(null);
|
||||
event.setResult(KickedFromServerEvent.RedirectPlayer.create(backupServer,reason));
|
||||
backupServer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
package org.adde0109.ambassador.velocity.client;
|
||||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import org.adde0109.ambassador.forge.packet.Context;
|
||||
import org.adde0109.ambassador.forge.packet.GenericForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
|
||||
|
||||
public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
|
|
@ -27,8 +25,8 @@ public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter {
|
|||
boolean success = buf.readBoolean();
|
||||
if (id == 98) {
|
||||
try {
|
||||
ctx.fireChannelRead(GenericForgeLoginWrapperPacket.read(
|
||||
Unpooled.EMPTY_BUFFER, Context.createClientContext(id, success, "fml:handshake")));
|
||||
MinecraftPacket packet = new LoginPluginResponse(id,success,buf.readRetainedSlice(buf.readableBytes()));
|
||||
ctx.fireChannelRead(packet);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
import org.apache.commons.collections4.map.PassiveExpiringMap;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhase {
|
||||
|
||||
private static String OUTBOUND_CATCHER_NAME = "ambassador-catcher";
|
||||
|
||||
private static final PassiveExpiringMap<String,RegisteredServer> TEMPORARY_FORCED = new PassiveExpiringMap<>(120, TimeUnit.SECONDS);
|
||||
|
||||
private Throwable throwable;
|
||||
private RegisteredServer triedServer;
|
||||
private Continuation continuation;
|
||||
private CompletableFuture<Void> onJoinGame;
|
||||
|
||||
private static final Method CONNECT_TO_INITIAL_SERVER;
|
||||
|
||||
static {
|
||||
Class clazz;
|
||||
try {
|
||||
clazz = Class.forName("com.velocitypowered.proxy.connection.client.LoginSessionHandler");
|
||||
} catch (ClassNotFoundException ignored){
|
||||
try {
|
||||
clazz = Class.forName("com.velocitypowered.proxy.connection.client.AuthSessionHandler");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
CONNECT_TO_INITIAL_SERVER = clazz.getDeclaredMethod("connectToInitialServer", ConnectedPlayer.class);
|
||||
CONNECT_TO_INITIAL_SERVER.setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
this.continuation = continuation;
|
||||
final MinecraftConnection connection = player.getConnection();
|
||||
|
||||
forced = TEMPORARY_FORCED.remove(player.getUsername());
|
||||
if (forced != null) {
|
||||
player.createConnectionRequest(forced).fireAndForget();
|
||||
} else {
|
||||
try {
|
||||
CONNECT_TO_INITIAL_SERVER.invoke(player.getConnection().getSessionHandler(),player);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
continuation.resumeWithException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> reset(RegisteredServer server, ConnectedPlayer player) {
|
||||
FML2CRPMClientConnectionPhase newPhase = new FML2CRPMClientConnectionPhase(clientPhase,getPayloadManager());
|
||||
player.setPhase(newPhase);
|
||||
CompletableFuture<Boolean> future = newPhase.reset(server,player);
|
||||
future.thenAccept(success -> {
|
||||
if (!success) {
|
||||
TEMPORARY_FORCED.put(player.getUsername(),server);
|
||||
player.disconnect(Component.text("Please reconnect"));
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
|
||||
if (triedServer != null)
|
||||
player.sendMessage(Component.translatable("velocity.error.connecting-server-error",
|
||||
Component.text(triedServer.getServerInfo().getName())));
|
||||
clientPhase = clientPhase == ClientPhase.MODLIST ? ClientPhase.MODDED : ClientPhase.VANILLA;
|
||||
internalServerConnection = player.getConnectionInFlight();
|
||||
player.resetInFlightConnection();
|
||||
this.onJoinGame = new CompletableFuture<>();
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
public void handleJoinGame() {
|
||||
this.onJoinGame.complete(null);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> awaitJoinGame() {
|
||||
return this.onJoinGame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleForward(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
|
||||
final ByteBuf buf = payload.content().duplicate();
|
||||
ProtocolUtils.readString(buf); //Channel
|
||||
ProtocolUtils.readVarInt(buf); //Length
|
||||
if (ProtocolUtils.readVarInt(buf) == 1) {
|
||||
getPayloadManager().listenFor(payload.getId()).thenAccept(rawResponse -> {
|
||||
final ByteBuf response = rawResponse.duplicate();
|
||||
ProtocolUtils.readString(response); //Channel
|
||||
ProtocolUtils.readVarInt(response); //Length
|
||||
if (ProtocolUtils.readVarInt(response) == 2) {
|
||||
String[] mods = ProtocolUtils.readStringArray(response);
|
||||
if (Arrays.stream(mods).anyMatch(s -> s.equals("clientresetpacket"))) {
|
||||
serverConnection.getPlayer().setPhase(new FML2CRPMClientConnectionPhase(clientPhase,getPayloadManager()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.proxy.connection.ConnectionType;
|
||||
|
||||
public class ForgeConstants {
|
||||
public static final String HANDLER = "Modern Forge handler";
|
||||
public static final String OUTBOUND_CATCHER_NAME = "ambassador-catcher";
|
||||
public static final String RESET_LISTENER = "ambassador-reset-listener";
|
||||
|
||||
public static final String FML2Marker = "\0FML2\0";
|
||||
public static final String FML3Marker = "\0FML3\0";
|
||||
public static final ConnectionType ForgeFML2 = new ForgeFMLConnectionType(2);
|
||||
public static final ConnectionType ForgeFML3 = new ForgeFMLConnectionType(3);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
|||
import com.velocitypowered.proxy.connection.ConnectionType;
|
||||
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase;
|
||||
import com.velocitypowered.proxy.connection.client.ClientConnectionPhase;
|
||||
import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendConnectionPhase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
|
|
@ -18,18 +19,17 @@ public class ForgeFMLConnectionType implements ConnectionType {
|
|||
|
||||
@Override
|
||||
public ClientConnectionPhase getInitialClientPhase() {
|
||||
return VelocityForgeClientConnectionPhase.NOT_STARTED;
|
||||
return new FML2ClientConnectionPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendConnectionPhase getInitialBackendPhase() {
|
||||
return VelocityForgeBackendConnectionPhase.NOT_STARTED;
|
||||
return new VelocityForgeBackendConnectionPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfile addGameProfileTokensIfRequired(GameProfile original, PlayerInfoForwarding forwardingType) {
|
||||
//This is meant for Arclight to parse
|
||||
if (forwardingType == PlayerInfoForwarding.LEGACY || forwardingType == PlayerInfoForwarding.BUNGEEGUARD) {
|
||||
if (forwardingType == PlayerInfoForwarding.LEGACY) {
|
||||
return original.addProperties(Collections.singleton(new GameProfile.Property("extraData", "\1FML" + netVersion + "\1", "")));
|
||||
} else {
|
||||
return original;
|
||||
|
|
@ -3,12 +3,6 @@ package org.adde0109.ambassador.forge;
|
|||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
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;
|
||||
|
||||
|
|
@ -77,23 +71,13 @@ public class ForgeHandshakeUtils {
|
|||
return dataAndPacketIdStream.toByteArray();
|
||||
}
|
||||
|
||||
public static final byte[] emptyModlistFML2 = generateEmptyModlist(2);
|
||||
public static final byte[] emptyModlistFML3 = generateEmptyModlist(3);
|
||||
private static byte[] generateEmptyModlist(int fmlVersion) {
|
||||
public static final byte[] emptyModlist = generateEmptyModlist();
|
||||
private static byte[] generateEmptyModlist() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,1);
|
||||
writeVarInt(dataAndPacketIdStream,0); //Mods
|
||||
if (fmlVersion == 3) {
|
||||
writeVarInt(dataAndPacketIdStream,1); //Channels
|
||||
writeUtf(dataAndPacketIdStream, "forge:tier_sorting");
|
||||
writeUtf(dataAndPacketIdStream,"1.0");
|
||||
writeVarInt(dataAndPacketIdStream,0); //Registries
|
||||
writeVarInt(dataAndPacketIdStream,0); //Data-packs
|
||||
} else {
|
||||
writeVarInt(dataAndPacketIdStream,0); //Channels
|
||||
writeVarInt(dataAndPacketIdStream,0); //Registries
|
||||
}
|
||||
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
|
||||
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
|
||||
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
|
||||
|
|
@ -116,61 +100,4 @@ public class ForgeHandshakeUtils {
|
|||
stream.write(dataAndPacketId);
|
||||
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) {
|
||||
try {
|
||||
Enum.valueOf(ThirdPartyChannel.class,
|
||||
packet.getContext().getChannelName().replace(':', '_').toUpperCase());
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ThirdPartyChannel getThirdPartyChannel(GenericForgeLoginWrapperPacket<Context> packet) {
|
||||
return Enum.valueOf(ThirdPartyChannel.class,
|
||||
packet.getContext().getChannelName().replace(':', '_').toUpperCase());
|
||||
}
|
||||
|
||||
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 ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, packetID);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context.ClientContext getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,40 +5,40 @@ import com.velocitypowered.proxy.network.BackendChannelInitializer;
|
|||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.velocity.backend.FMLMarkerAdder;
|
||||
import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendHandshakeHandler;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class VelocityBackendChannelInitializer extends BackendChannelInitializer {
|
||||
|
||||
private final Method INIT_CHANNEL;
|
||||
private static final Method INIT_CHANNEL;
|
||||
|
||||
private final ChannelInitializer<Channel> delegate;
|
||||
private final ChannelInitializer<?> delegate;
|
||||
private final VelocityServer server;
|
||||
|
||||
public VelocityBackendChannelInitializer(ChannelInitializer<Channel> delegate, VelocityServer server) {
|
||||
super(server);
|
||||
this.delegate = delegate;
|
||||
this.server = server;
|
||||
static {
|
||||
try {
|
||||
INIT_CHANNEL = delegate.getClass().getDeclaredMethod("initChannel", Channel.class);
|
||||
INIT_CHANNEL = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||
INIT_CHANNEL.setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public VelocityBackendChannelInitializer(ChannelInitializer<?> delegate, VelocityServer server) {
|
||||
super(server);
|
||||
this.delegate = delegate;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel ch) {
|
||||
try {
|
||||
INIT_CHANNEL.invoke(delegate, ch);
|
||||
INIT_CHANNEL.invoke(delegate,ch);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ch.pipeline().addLast(ForgeConstants.MARKER_ADDER, new FMLMarkerAdder(server));
|
||||
ch.pipeline().addLast(ForgeConstants.HANDLER, new VelocityForgeBackendHandshakeHandler(server));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
|
||||
import com.velocitypowered.api.event.player.*;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase;
|
||||
import org.adde0109.ambassador.forge.FML2ClientConnectionPhase;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendConnectionPhase;
|
||||
|
||||
public class VelocityEventHandler {
|
||||
|
||||
private final Ambassador ambassador;
|
||||
|
||||
public VelocityEventHandler(Ambassador ambassador) {
|
||||
this.ambassador = ambassador;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPermissionsSetupEvent(PermissionsSetupEvent event, Continuation continuation) {
|
||||
if(!(event.getSubject() instanceof ConnectedPlayer player)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
player.getConnection().eventLoop().submit(() -> phase.fireLoginEvent(player, (VelocityServer) ambassador.server,continuation));
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onKickedFromServerEvent(KickedFromServerEvent event, Continuation continuation) {
|
||||
if (((ConnectedPlayer) event.getPlayer()).getPhase() instanceof FML2CRPMClientConnectionPhase phase) {
|
||||
phase.handleKick(event);
|
||||
}
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onServerPreConnectEvent(ServerPreConnectEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
if (phase.internalServerConnection != null) {
|
||||
event.setResult(ServerPreConnectEvent.ServerResult.denied());
|
||||
player.setConnectedServer((VelocityServerConnection) phase.internalServerConnection);
|
||||
phase.internalServerConnection = null;
|
||||
} else if (phase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.MODDED) {
|
||||
player.getConnection().eventLoop().submit(() -> phase.reset(event.getOriginalServer(), (ConnectedPlayer) event.getPlayer())
|
||||
.thenAccept((ignored) -> continuation.resume()));
|
||||
} else {
|
||||
continuation.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onPlayerChooseInitialServerEvent(PlayerChooseInitialServerEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
if (event.getInitialServer().isEmpty())
|
||||
event.setInitialServer(phase.internalServerConnection.getServer());
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onServerPostConnectEvent(ServerPostConnectEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
if (phase instanceof FML2ClientConnectionPhase specialPhase) {
|
||||
specialPhase.handleJoinGame();
|
||||
}
|
||||
//if (((ConnectedPlayer) event.getPlayer()).getConnectedServer() != null && ((ConnectedPlayer) event.getPlayer()).getConnectedServer().getConnection() != null) {
|
||||
// ((ConnectedPlayer) event.getPlayer()).getConnectedServer().getConnection().write(new ClientSettings("en_GB", (byte) 10, 0, true, (short) 0xFF,1,false,true));
|
||||
//}
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onLoginEvent(PostLoginEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
|
||||
if (phase instanceof FML2ClientConnectionPhase specialPhase) {
|
||||
specialPhase.awaitJoinGame().thenAcceptAsync((ignored) -> {
|
||||
player.setConnectedServer(null);
|
||||
continuation.resume();
|
||||
},player.getConnection().eventLoop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ClientConnectionPhase;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class VelocityForgeClientConnectionPhase implements ClientConnectionPhase {
|
||||
//TODO:Make class when PCF is done
|
||||
|
||||
|
||||
VelocityLoginPayloadManager payloadManager;
|
||||
public VelocityForgeClientConnectionPhase.ClientPhase clientPhase = ClientPhase.HANDSHAKE;
|
||||
|
||||
public ServerConnection internalServerConnection;
|
||||
public RegisteredServer forced;
|
||||
|
||||
protected VelocityForgeClientConnectionPhase(ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) {
|
||||
this.clientPhase = clientPhase;
|
||||
this.payloadManager = payloadManager;
|
||||
}
|
||||
protected VelocityForgeClientConnectionPhase() {
|
||||
}
|
||||
|
||||
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> reset(RegisteredServer server, ConnectedPlayer player) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
|
||||
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
|
||||
|
||||
}
|
||||
|
||||
final void fireLoginEvent(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
|
||||
payloadManager = new VelocityLoginPayloadManager(player.getConnection());
|
||||
handleLogin(player,server,continuation);
|
||||
|
||||
VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(), this);
|
||||
player.getConnection().setSessionHandler(sessionHandler);
|
||||
}
|
||||
|
||||
public void handleForward(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
|
||||
}
|
||||
|
||||
final public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
|
||||
handleForward(serverConnection,payload);
|
||||
if (payloadManager == null) {
|
||||
payload.release();
|
||||
return;
|
||||
}
|
||||
payloadManager.sendPayload("fml:loginwrapper",payload.content()).thenAccept((responseData) -> {
|
||||
//Move this to the backend. Backend should have its own forwarder.
|
||||
serverConnection.getConnection().write(new LoginPluginResponse(payload.getId(),responseData.isReadable(),responseData.retain()));
|
||||
});
|
||||
clientPhase = ClientPhase.MODLIST;
|
||||
}
|
||||
|
||||
public final VelocityLoginPayloadManager getPayloadManager() {
|
||||
return payloadManager;
|
||||
}
|
||||
|
||||
public enum ClientPhase {
|
||||
VANILLA,
|
||||
HANDSHAKE,
|
||||
MODLIST,
|
||||
MODDED
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class VelocityForgeHandshakeSessionHandler implements MinecraftSessionHandler {
|
||||
private final MinecraftSessionHandler original;
|
||||
private final VelocityForgeClientConnectionPhase phase;
|
||||
|
||||
public VelocityForgeHandshakeSessionHandler(MinecraftSessionHandler original, VelocityForgeClientConnectionPhase phase) {
|
||||
this.original = original;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(LoginPluginResponse packet) {
|
||||
if (phase.getPayloadManager().handlePayload(packet)) {
|
||||
return true;
|
||||
} else {
|
||||
return original.handle(packet);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void handleUnknown(ByteBuf buf) {
|
||||
original.handleUnknown(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
original.disconnected();
|
||||
}
|
||||
|
||||
public MinecraftSessionHandler getOriginal() {
|
||||
return this.original;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,34 @@
|
|||
package org.adde0109.ambassador.velocity.client;
|
||||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
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;
|
||||
|
||||
public class VelocityHandshakeSessionHandler extends HandshakeSessionHandler {
|
||||
public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler {
|
||||
private final HandshakeSessionHandler original;
|
||||
private final MinecraftConnection connection;
|
||||
|
||||
public VelocityHandshakeSessionHandler(HandshakeSessionHandler original, MinecraftConnection connection, VelocityServer server) {
|
||||
super(connection, server);
|
||||
public VelocityHandshakeSessionHandler(HandshakeSessionHandler original, MinecraftConnection connection) {
|
||||
this.original = original;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@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");
|
||||
if (connection.getState() == StateRegistry.LOGIN && markerSplit.length > 1 && markerSplit[1].startsWith("FML")) {
|
||||
if (connection.getState() == StateRegistry.LOGIN && markerSplit.length > 1) {
|
||||
switch (markerSplit[1]) {
|
||||
case "FML2":
|
||||
connection.setType(ForgeConstants.ForgeFML2);
|
||||
break;
|
||||
case "FML3":
|
||||
connection.setType(ForgeConstants.ForgeFML3);
|
||||
break;
|
||||
case "FML2" -> connection.setType(ForgeConstants.ForgeFML2);
|
||||
case "FML3" -> connection.setType(ForgeConstants.ForgeFML3);
|
||||
}
|
||||
connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.SERVER_SUCCESS_LISTENER, new OutboundSuccessHolder());
|
||||
connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class VelocityLoginPayloadManager {
|
||||
private final HashMap<Integer, CompletableFuture<ByteBuf>> listenerList = new HashMap<>();
|
||||
private int counter = 0;
|
||||
private final MinecraftConnection connection;
|
||||
|
||||
public VelocityLoginPayloadManager(MinecraftConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> sendPayload(String channel, ByteBuf data) {
|
||||
connection.write(new LoginPluginMessage(counter,channel,data));
|
||||
CompletableFuture<ByteBuf> future = listenFor(counter);
|
||||
counter++;
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> sendPayloads(String channel, List<ByteBuf> dataList) {
|
||||
final CompletableFuture<ByteBuf> callback = new CompletableFuture<>();
|
||||
for (ByteBuf data : dataList) {
|
||||
connection.delayedWrite(new LoginPluginMessage(counter, channel, data));
|
||||
listenerList.put(counter,callback);
|
||||
counter++;
|
||||
}
|
||||
connection.flush();
|
||||
return callback;
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> listenFor(int id) {
|
||||
CompletableFuture<ByteBuf> value = listenerList.get(id);
|
||||
if (value == null) {
|
||||
CompletableFuture<ByteBuf> callback = new CompletableFuture<>();
|
||||
listenerList.put(id,callback);
|
||||
return callback;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
boolean handlePayload(LoginPluginResponse response) {
|
||||
final CompletableFuture<ByteBuf> callback = listenerList.get(response.getId());
|
||||
if (callback != null) {
|
||||
listenerList.remove(response.getId());
|
||||
if (!listenerList.containsValue(callback)) {
|
||||
callback.complete(response.content());
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,33 +5,32 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
|
|||
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.network.ServerChannelInitializer;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import org.adde0109.ambassador.velocity.client.VelocityHandshakeSessionHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class VelocityServerChannelInitializer extends ServerChannelInitializer {
|
||||
private Method INIT_CHANNEL;
|
||||
private static final Method INIT_CHANNEL;
|
||||
|
||||
private final ChannelInitializer<?> delegate;
|
||||
private final VelocityServer server;
|
||||
|
||||
public VelocityServerChannelInitializer(ChannelInitializer<?> delegate,VelocityServer server) {
|
||||
super(server);
|
||||
this.delegate = delegate;
|
||||
this.server = server;
|
||||
static {
|
||||
try {
|
||||
INIT_CHANNEL = delegate.getClass().getDeclaredMethod("initChannel", Channel.class);
|
||||
INIT_CHANNEL = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||
INIT_CHANNEL.setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public VelocityServerChannelInitializer(ChannelInitializer<?> delegate,VelocityServer server) {
|
||||
super(server);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(@NotNull Channel ch){
|
||||
try {
|
||||
|
|
@ -39,12 +38,9 @@ public class VelocityServerChannelInitializer extends ServerChannelInitializer {
|
|||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally {
|
||||
if (ch.pipeline().get(MinecraftConnection.class) == null)
|
||||
super.initChannel(ch);
|
||||
MinecraftConnection handler = ch.pipeline().get(MinecraftConnection.class);
|
||||
HandshakeSessionHandler originalSessionHandler = (HandshakeSessionHandler) handler.getActiveSessionHandler();
|
||||
handler.setActiveSessionHandler(StateRegistry.HANDSHAKE, new VelocityHandshakeSessionHandler(originalSessionHandler, handler, server));
|
||||
}
|
||||
ChannelHandler handler = ch.pipeline().get(Connections.HANDLER);
|
||||
if (!(handler instanceof final MinecraftConnection connection)) throw new RuntimeException("plugin conflict");
|
||||
HandshakeSessionHandler originalSessionHandler = (HandshakeSessionHandler) connection.getSessionHandler();
|
||||
connection.setSessionHandler(new VelocityHandshakeSessionHandler(originalSessionHandler, connection));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.LoginSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
|
||||
public class ForgeHandshakeSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
private final LoginSessionHandler original;
|
||||
private final VelocityServerConnection serverConnection;
|
||||
private final VelocityServer server;
|
||||
|
||||
public ForgeHandshakeSessionHandler(LoginSessionHandler original, VelocityServerConnection serverConnection, VelocityServer server) {
|
||||
this.original = original;
|
||||
this.serverConnection = serverConnection;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(LoginPluginMessage packet) {
|
||||
if (packet.getChannel().equals("fml:loginwrapper")) {
|
||||
if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
if (!(serverConnection.getPlayer().getConnection().getType() instanceof ForgeFMLConnectionType clientType)) {
|
||||
final String reason = "This server has mods that require Forge to be installed on the client. Contact your server admin for more details.";
|
||||
original.handle(Disconnect.create(Component.text(reason, NamedTextColor.RED),serverConnection.getPlayer().getProtocolVersion()));
|
||||
return true;
|
||||
}
|
||||
serverConnection.getConnection().setType(clientType);
|
||||
serverConnection.setConnectionPhase(clientType.getInitialBackendPhase());
|
||||
}
|
||||
((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handle(serverConnection,serverConnection.getPlayer(),packet);
|
||||
return true;
|
||||
}
|
||||
return original.handle(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ServerLoginSuccess packet) {
|
||||
if ((serverConnection.getPlayer().getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
phase.complete(server,serverConnection.getPlayer(),serverConnection.getPlayer().getConnection());
|
||||
}
|
||||
return original.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
|
||||
|
||||
|
||||
public VelocityForgeBackendConnectionPhase() {
|
||||
}
|
||||
|
||||
public void handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) {
|
||||
VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) player.getPhase());
|
||||
if (clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.VANILLA) {
|
||||
final LoginPluginMessage msg = message;
|
||||
|
||||
msg.content().retain().discardSomeReadBytes();
|
||||
|
||||
server.getConnection().getChannel().config().setAutoRead(false);
|
||||
clientPhase.reset(server.getServer(),player).thenAccept((success) -> {
|
||||
if (success) {
|
||||
clientPhase.forwardPayload(server,msg);
|
||||
server.getConnection().getChannel().config().setAutoRead(true);
|
||||
} else {
|
||||
msg.release();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
clientPhase.forwardPayload(server, (LoginPluginMessage) message.retain());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.LoginSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VelocityForgeBackendHandshakeHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final VelocityServer server;
|
||||
|
||||
public VelocityForgeBackendHandshakeHandler(VelocityServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(@NotNull ChannelHandlerContext ctx) throws Exception {
|
||||
MinecraftConnection connection = (MinecraftConnection) ctx.pipeline().get(Connections.HANDLER);
|
||||
VelocityServerConnection serverConnection = (VelocityServerConnection) connection.getAssociation();
|
||||
|
||||
if (serverConnection.getPlayer().getConnection().getType() instanceof ForgeFMLConnectionType) {
|
||||
connection.setSessionHandler(new ForgeHandshakeSessionHandler((LoginSessionHandler) connection.getSessionHandler(),serverConnection,server));
|
||||
}
|
||||
|
||||
ctx.pipeline().remove(this);
|
||||
ctx.pipeline().fireChannelActive();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.adde0109.ambassador.velocity.commands;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
public class EnumArgumentProperty implements ArgumentType<String> {
|
||||
|
||||
private final String className;
|
||||
|
||||
public EnumArgumentProperty(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,21 @@
|
|||
package org.adde0109.ambassador.velocity.protocol;
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.adde0109.ambassador.velocity.commands;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
|
|
@ -25,4 +42,4 @@ public class EnumArgumentPropertySerializer implements ArgumentPropertySerialize
|
|||
public void serialize(EnumArgumentProperty object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, object.getClassName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,21 @@
|
|||
package org.adde0109.ambassador.velocity.protocol;
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.adde0109.ambassador.velocity.commands;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
|
|
@ -44,4 +61,4 @@ public class ModIdArgumentProperty implements ArgumentType<String> {
|
|||
public Collection<String> getExamples() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
|||
11
gradlew
vendored
11
gradlew
vendored
|
|
@ -85,6 +85,9 @@ done
|
|||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
|
|
@ -141,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
|
|
@ -149,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
|
@ -194,10 +197,6 @@ if "$cygwin" || "$msys" ; then
|
|||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
rootProject.name = "Ambassador"
|
||||
includeBuild("Velocity") {
|
||||
dependencySubstitution {
|
||||
substitute(module("com.velocitypowered:velocity-api")).using(project(":velocity-api"))
|
||||
substitute(module("com.velocitypowered:velocity-proxy")).using(project(":velocity-proxy"))
|
||||
|
||||
val velocityPath = rootProject.projectDir.toPath().resolve("Velocity/");
|
||||
includeBuild("Velocity") {
|
||||
dependencySubstitution {
|
||||
if(java.nio.file.Files.isDirectory(velocityPath.resolve("proxy/"))) {
|
||||
substitute(module("com.velocitypowered:velocity-proxy")).using(project(":velocity-proxy"))
|
||||
substitute(module("com.velocitypowered:velocity-api")).using(project(":velocity-api"))
|
||||
} else {
|
||||
logger.warn("Git Submodule 'Velocity' not initialized!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
include("ambassador-velocity")
|
||||
include("ambassador-bungeecord")
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
package org.adde0109.ambassador;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
import org.adde0109.ambassador.velocity.protocol.EnumArgumentProperty;
|
||||
import org.adde0109.ambassador.velocity.protocol.EnumArgumentPropertySerializer;
|
||||
import org.adde0109.ambassador.velocity.protocol.ModIdArgumentProperty;
|
||||
import org.bstats.velocity.Metrics;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
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"})
|
||||
public class Ambassador {
|
||||
|
||||
//Don't forget to update checkCompatibleVersion() when changing this value
|
||||
private static final String minVelocityVersion = "velocity-3.3.0-SNAPSHOT-330";
|
||||
|
||||
public ProxyServer server;
|
||||
public final Logger logger;
|
||||
private final Metrics.Factory metricsFactory;
|
||||
private final Path dataDirectory;
|
||||
|
||||
public AmbassadorConfig config;
|
||||
|
||||
private static final MapWithExpiration<String, RegisteredServer> TEMPORARY_FORCED = new MapWithExpiration<>();
|
||||
|
||||
private static Ambassador instance;
|
||||
public static Ambassador getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@Inject
|
||||
public Ambassador(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory, Metrics.Factory metricsFactory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataDirectory = dataDirectory;
|
||||
this.metricsFactory = metricsFactory;
|
||||
Ambassador.instance = this;
|
||||
}
|
||||
|
||||
boolean checkCompatibleVersion() {
|
||||
//Update this when changing minVelocityVersion
|
||||
try {
|
||||
Class.forName("com.velocitypowered.proxy.protocol.packet.DisconnectPacket");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
initMetrics();
|
||||
|
||||
try {
|
||||
if (!checkCompatibleVersion()) {
|
||||
logger.error("Incompatible velocity version! Please use '" + minVelocityVersion + "' or higher.");
|
||||
return;
|
||||
}
|
||||
|
||||
Files.createDirectories(dataDirectory);
|
||||
|
||||
Path configPath = dataDirectory.resolve("Ambassador.toml");
|
||||
config = AmbassadorConfig.read(configPath);
|
||||
|
||||
inject();
|
||||
|
||||
server.getEventManager().register(this, new VelocityEventHandler(this));
|
||||
} catch (Exception e) {
|
||||
logger.error("An error prevented Ambassador to load correctly: "+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyReload(ProxyReloadEvent event) {
|
||||
try {
|
||||
Path configPath = dataDirectory.resolve("Ambassador.toml");
|
||||
final AmbassadorConfig newconfig = AmbassadorConfig.read(configPath);
|
||||
|
||||
config = newconfig;
|
||||
} catch (Exception e) {
|
||||
logger.error(e.toString());
|
||||
logger.warn("Reload unsuccessful, old config will be used.");
|
||||
}
|
||||
}
|
||||
|
||||
private void inject() throws ReflectiveOperationException {
|
||||
Field cmField = VelocityServer.class.getDeclaredField("cm");
|
||||
cmField.setAccessible(true);
|
||||
|
||||
ConnectionManager connectionManager = (ConnectionManager) cmField.get(server);
|
||||
|
||||
ChannelInitializer<Channel> original = connectionManager.getServerChannelInitializer().get();
|
||||
connectionManager.getServerChannelInitializer().set(new VelocityServerChannelInitializer(original, (VelocityServer) server));
|
||||
|
||||
ChannelInitializer<Channel> originalBackend = connectionManager.getBackendChannelInitializer().get();
|
||||
connectionManager.getBackendChannelInitializer().set(new VelocityBackendChannelInitializer(originalBackend, (VelocityServer) server));
|
||||
|
||||
Method argumentRegistry = ArgumentPropertyRegistry.class.getDeclaredMethod("register", ArgumentIdentifier.class, Class.class, ArgumentPropertySerializer.class);
|
||||
argumentRegistry.setAccessible(true);
|
||||
argumentRegistry.invoke(null,ArgumentIdentifier.id("forge:enum", mapSet(MINECRAFT_1_19, 50)), EnumArgumentProperty.class, EnumArgumentPropertySerializer.ENUM);
|
||||
argumentRegistry.invoke(null,ArgumentIdentifier.id("forge:modid", mapSet(MINECRAFT_1_19, 51)), ModIdArgumentProperty.class,
|
||||
new ArgumentPropertySerializer<>() {
|
||||
@Override
|
||||
public ModIdArgumentProperty deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
return new ModIdArgumentProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static MapWithExpiration<String, RegisteredServer> getTemporaryForced() {
|
||||
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);
|
||||
}
|
||||
|
||||
public static class MapWithExpiration<K, V> {
|
||||
|
||||
private final Map<K, ExpiringValue<V, Long>> expirationMap = new HashMap<>();
|
||||
|
||||
|
||||
public V remove(K key) {
|
||||
ExpiringValue<V, Long> expiringValue = expirationMap.remove(key);
|
||||
if (expiringValue != null && expiringValue.value > System.currentTimeMillis()) {
|
||||
return expiringValue.key;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void put(K key, V value, int expirationTime, TimeUnit unit) {
|
||||
expirationMap.values().removeIf((v) -> v.value <= System.currentTimeMillis());
|
||||
expirationMap.put(key, new ExpiringValue<>(value,System.currentTimeMillis() + unit.toMillis(expirationTime)));
|
||||
}
|
||||
|
||||
private record ExpiringValue<K, V>(K key, V value) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package org.adde0109.ambassador;
|
||||
|
||||
import com.electronwill.nightconfig.core.conversion.InvalidValueException;
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.google.gson.annotations.Expose;
|
||||
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 boolean debugMode = false;
|
||||
|
||||
@Expose
|
||||
private boolean enableKickReset = false;
|
||||
|
||||
@Expose
|
||||
private String kickReconnectMessageString = "<red>Please reconnect.</red>";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public int getServerSwitchCancellationTime() {
|
||||
return serverSwitchCancellationTime;
|
||||
}
|
||||
|
||||
public boolean isSilenceWarnings() {
|
||||
return silenceWarnings;
|
||||
}
|
||||
|
||||
public boolean isBypassRegistryCheck() {
|
||||
return bypassRegistryCheck;
|
||||
}
|
||||
|
||||
public boolean isBypassModCheck() {
|
||||
return bypassModCheck;
|
||||
}
|
||||
|
||||
public boolean isDebugMode() {
|
||||
return debugMode;
|
||||
}
|
||||
|
||||
public boolean isEnableKickReset() {
|
||||
return enableKickReset;
|
||||
}
|
||||
|
||||
public String getKickReconnectMessageString() {
|
||||
return kickReconnectMessageString;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.proxy.connection.ConnectionType;
|
||||
|
||||
public class ForgeConstants {
|
||||
public static final String HANDLER = "Modern Forge handler";
|
||||
public static final String MARKER_ADDER = "FML2/3 Marker Adder";
|
||||
public static final String RESET_LISTENER = "ambassador-reset-listener";
|
||||
public static final String SERVER_SUCCESS_LISTENER = "ambassador-server-success-listener";
|
||||
public static final String PLUGIN_PACKET_QUEUE = "ambassador-plugin-generated-packet-queue";
|
||||
public static final String LOGIN_PACKET_QUEUE = "ambassador-login-packet-queue";
|
||||
public static final String FORGE_HANDSHAKE_DECODER = "ambassador-forge-decoder";
|
||||
public static final String FORGE_HANDSHAKE_HANDLER = "ambassador-forge-handler";
|
||||
public static final String COMMAND_ERROR_CATCHER = "ambassador-command-catcher";
|
||||
|
||||
public static final String FML2Marker = "\0FML2\0";
|
||||
public static final String FML3Marker = "\0FML3\0";
|
||||
public static final ConnectionType ForgeFML2 = new ForgeFMLConnectionType(2);
|
||||
public static final ConnectionType ForgeFML3 = new ForgeFMLConnectionType(3);
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Adler32;
|
||||
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() {
|
||||
}
|
||||
|
||||
public ModListReplyPacket getModListReplyPacket() {
|
||||
return modListReplyPacket;
|
||||
}
|
||||
|
||||
public void setModListReplyPacket(ModListReplyPacket modListReplyPacket) {
|
||||
this.modListReplyPacket = modListReplyPacket;
|
||||
}
|
||||
|
||||
public void addRegistry(RegistryPacket packet) {
|
||||
Checksum registryChecksum = new Adler32();
|
||||
registryChecksum.update(packet.getSnapshot());
|
||||
registries.put(packet.getRegistryName(), registryChecksum.getValue());
|
||||
}
|
||||
|
||||
public Map<String, Long> getRegistries() {
|
||||
return registries;
|
||||
}
|
||||
|
||||
public boolean isCompatible(ForgeHandshake handshake) {
|
||||
return this.registries.equals(handshake.registries);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase;
|
||||
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 org.adde0109.ambassador.forge.packet.*;
|
||||
import org.adde0109.ambassador.forge.pipeline.CommandDecoderErrorCatcher;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
|
||||
NOT_STARTED {
|
||||
@Override
|
||||
VelocityForgeBackendConnectionPhase nextPhase() {
|
||||
return IN_PROGRESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
//Safe if the server hasn't initiated the handshake yet.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
IN_PROGRESS {
|
||||
@Override
|
||||
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {
|
||||
serverCon.setConnectionPhase(VelocityForgeBackendConnectionPhase.COMPLETE);
|
||||
|
||||
serverCon.getConnection().getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER,
|
||||
ForgeConstants.COMMAND_ERROR_CATCHER,
|
||||
new CommandDecoderErrorCatcher(serverCon.getConnection().getProtocolVersion(),player));
|
||||
}
|
||||
|
||||
@Override
|
||||
void onTransitionToNewPhase(VelocityServerConnection connection) {
|
||||
MinecraftConnection mc = connection.getConnection();
|
||||
if (mc != null) {
|
||||
//This looks ugly. But unless the player didn't have a FML marker, we're fine.
|
||||
mc.setType(connection.getPlayer().getConnection().getType());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
COMPLETE {
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public ForgeHandshake handshake = new ForgeHandshake();
|
||||
CountDownLatch remainingRegistries;
|
||||
|
||||
VelocityForgeBackendConnectionPhase() {
|
||||
}
|
||||
|
||||
public void handle(VelocityServerConnection server, ConnectedPlayer player, IForgeLoginWrapperPacket<Context> message) {
|
||||
VelocityForgeBackendConnectionPhase newPhase = getNewPhase(server,message);
|
||||
|
||||
server.setConnectionPhase(newPhase);
|
||||
|
||||
//Forge -> Forge
|
||||
|
||||
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
|
||||
|
||||
|
||||
if (!player.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clientPhase.consideredComplete()) {
|
||||
//Initial Forge
|
||||
if (message instanceof ModListPacket modListPacket) {
|
||||
clientPhase.forgeHandshake = new ForgeHandshake();
|
||||
}
|
||||
if (message instanceof RegistryPacket registryPacket) {
|
||||
clientPhase.forgeHandshake.addRegistry(registryPacket);
|
||||
}
|
||||
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();
|
||||
} catch (InterruptedException e) {
|
||||
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)) {
|
||||
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)));
|
||||
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));
|
||||
}
|
||||
}
|
||||
//Forge server
|
||||
//To avoid unnecessary resets, we wait until we get the handshake even if we know that we should
|
||||
//reset because that the previous server was Forge.
|
||||
}
|
||||
|
||||
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {
|
||||
}
|
||||
|
||||
void onTransitionToNewPhase(VelocityServerConnection connection) {
|
||||
}
|
||||
|
||||
VelocityForgeBackendConnectionPhase nextPhase() {
|
||||
return this;
|
||||
}
|
||||
|
||||
private VelocityForgeBackendConnectionPhase getNewPhase(VelocityServerConnection serverConnection,
|
||||
IForgeLoginWrapperPacket<Context> packet) {
|
||||
VelocityForgeBackendConnectionPhase phaseToTransitionTo = nextPhase();
|
||||
if (phaseToTransitionTo != this) {
|
||||
phaseToTransitionTo.onTransitionToNewPhase(serverConnection);
|
||||
}
|
||||
return phaseToTransitionTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(VelocityServerConnection server, ConnectedPlayer player, PluginMessagePacket message) {
|
||||
if (message.getChannel().equals("ambassador:commands")) {
|
||||
AvailableCommandsPacket packet = new AvailableCommandsPacket();
|
||||
packet.decode(message.content(), ProtocolUtils.Direction.CLIENTBOUND,server.getConnection().getProtocolVersion());
|
||||
server.getConnection().getActiveSessionHandler().handle(packet);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean consideredComplete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.util.ModInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ClientConnectionPhase;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.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 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 java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase {
|
||||
|
||||
NOT_STARTED {
|
||||
@Override
|
||||
VelocityForgeClientConnectionPhase nextPhase() {
|
||||
return IN_PROGRESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(ConnectedPlayer player) {
|
||||
//When no handshake has taken place.
|
||||
//Test if the client supports CRP.
|
||||
ClientResetType.CRP.doReset(player);
|
||||
}
|
||||
},
|
||||
IN_PROGRESS {
|
||||
},
|
||||
WAITING_RESET() {
|
||||
@Override
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
//We unregister so no plugin sees this client while the client is being reset.
|
||||
((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player);
|
||||
player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,
|
||||
ForgeConstants.LOGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.PLAY));
|
||||
if (player.getConnection().getChannel().pipeline().get(ForgeConstants.PLUGIN_PACKET_QUEUE) == null)
|
||||
player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,
|
||||
ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN));
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
player.getConnection().getChannel().pipeline().remove(ForgeConstants.LOGIN_PACKET_QUEUE);
|
||||
|
||||
if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
// -> vanilla
|
||||
complete(player, ((Context.ClientContext) msg.getContext()).success() ? ClientResetType.CRP : null);
|
||||
}
|
||||
|
||||
if (player.getConnectionInFlight() != null) {
|
||||
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConnectionPhase(ConnectedPlayer player) {
|
||||
getResetType().doReset(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(ConnectedPlayer player) {
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Not resetting"));
|
||||
}
|
||||
}
|
||||
|
||||
@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());
|
||||
player.setModInfo(modInfo);
|
||||
forgeHandshake.setModListReplyPacket(replyPacket);
|
||||
if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
complete(player);
|
||||
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true);
|
||||
return true;
|
||||
}
|
||||
replyPacket.getChannels().put(MinecraftChannelIdentifier.from("ambassador:commands"),"1");
|
||||
}
|
||||
|
||||
player.getConnectionInFlight().getConnection().write(msg);
|
||||
|
||||
player.setPhase(nextPhase());
|
||||
nextPhase().forgeHandshake = this.forgeHandshake;
|
||||
|
||||
return true;
|
||||
}
|
||||
public void complete(ConnectedPlayer player) {
|
||||
complete(player, null);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (Ambassador.getInstance().config.isDebugMode()) {
|
||||
player.sendMessage(Component.text("Forge handshake complete"));
|
||||
}
|
||||
}
|
||||
|
||||
void onTransitionToNewPhase(ConnectedPlayer player) {
|
||||
|
||||
}
|
||||
|
||||
VelocityForgeClientConnectionPhase nextPhase() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean consideredComplete() {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class ACKPacket implements IForgeLoginWrapperPacket<Context.ClientContext> {
|
||||
|
||||
private final Context.ClientContext context;
|
||||
|
||||
public ACKPacket(Context.ClientContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, 99);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context.ClientContext getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class ConfigDataPacket implements IForgeLoginWrapperPacket<Context> {
|
||||
|
||||
private final String fileName;
|
||||
private final byte[] fileData;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public ConfigDataPacket(String fileName, byte[] fileData, Context context) {
|
||||
this.fileName = fileName;
|
||||
this.fileData = fileData;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static ConfigDataPacket read(ByteBuf input, Context context, boolean FML3) {
|
||||
String registryName = ProtocolUtils.readString(input);
|
||||
byte[] snapshot = ProtocolUtils.readByteArray(input);
|
||||
|
||||
return new ConfigDataPacket(registryName, snapshot, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, 4);
|
||||
|
||||
ProtocolUtils.writeString(buf, fileName);
|
||||
ProtocolUtils.writeByteArray(buf, fileData);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
public class Context {
|
||||
|
||||
private final int responseID;
|
||||
|
||||
private final String channelName;
|
||||
|
||||
private Context(int responseID, String channelName) {
|
||||
this.responseID = responseID;
|
||||
this.channelName = channelName;
|
||||
}
|
||||
|
||||
public static Context createContext(int responseID, String channelName) {
|
||||
return new Context(responseID, channelName);
|
||||
}
|
||||
|
||||
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 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);
|
||||
this.clientSuccess = clientSuccess;
|
||||
}
|
||||
|
||||
public boolean success() {
|
||||
return clientSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class GenericForgeLoginWrapperPacket<T extends Context> implements IForgeLoginWrapperPacket<T> {
|
||||
|
||||
private final byte[] content;
|
||||
private final T context;
|
||||
|
||||
public GenericForgeLoginWrapperPacket(byte[] content, T context) {
|
||||
this.content = content;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
static public GenericForgeLoginWrapperPacket<?> read(ByteBuf input, Context context) {
|
||||
byte[] content = new byte[input.readableBytes()];
|
||||
input.readBytes(content);
|
||||
return new GenericForgeLoginWrapperPacket<>(content, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
buf.writeBytes(content);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public interface IForgeLoginWrapperPacket<T extends Context> {
|
||||
ByteBuf encode();
|
||||
T getContext();
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
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 io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ModListPacket implements IForgeLoginWrapperPacket<Context> {
|
||||
private List<String> mods;
|
||||
private Map<ChannelIdentifier, String> channels;
|
||||
private List<String> registries;
|
||||
private final List<String> dataPackRegistries;
|
||||
|
||||
private final Context context;
|
||||
private ModListPacket(List<String> mods, Map<ChannelIdentifier,
|
||||
String> channels, List<String> registries, Context context, List<String> dataPackRegistries) {
|
||||
this.mods = mods;
|
||||
this.channels = channels;
|
||||
this.registries = registries;
|
||||
this.context = context;
|
||||
this.dataPackRegistries = dataPackRegistries;
|
||||
}
|
||||
|
||||
public static ModListPacket read(ByteBuf input, Context context, boolean FML3) {
|
||||
|
||||
List<String> mods = new ArrayList<>();
|
||||
int len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
mods.add(ProtocolUtils.readString(input, 0x100));
|
||||
|
||||
Map<ChannelIdentifier, String> channels = new HashMap<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
channels.put(MinecraftChannelIdentifier.from(ProtocolUtils.readString(input, 32767)),
|
||||
ProtocolUtils.readString(input, 0x100));
|
||||
|
||||
List<String> registries = new ArrayList<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
registries.add(ProtocolUtils.readString(input, 32767));
|
||||
|
||||
List<String> dataPackRegistries = null;
|
||||
if (FML3 && input.isReadable()) {
|
||||
dataPackRegistries = new ArrayList<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
dataPackRegistries.add(ProtocolUtils.readString(input, 0x100));
|
||||
}
|
||||
|
||||
return new ModListPacket(mods, channels, registries, context, dataPackRegistries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, 1);
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, mods.size());
|
||||
mods.forEach(m -> ProtocolUtils.writeString(buf, m));
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, channels.size());
|
||||
channels.forEach((k, v) -> {
|
||||
ProtocolUtils.writeString(buf,k.getId());
|
||||
ProtocolUtils.writeString(buf,v);
|
||||
});
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, registries.size());
|
||||
registries.forEach(k -> ProtocolUtils.writeString(buf, k));
|
||||
|
||||
if (dataPackRegistries != null) {
|
||||
ProtocolUtils.writeVarInt(buf, dataPackRegistries.size());
|
||||
dataPackRegistries.forEach(k -> ProtocolUtils.writeString(buf, k));
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public List<String> getMods() {
|
||||
return mods;
|
||||
}
|
||||
|
||||
public Map<ChannelIdentifier, String> getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public List<String> getRegistries() {
|
||||
return registries;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
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 io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ModListReplyPacket implements IForgeLoginWrapperPacket<Context.ClientContext> {
|
||||
|
||||
private List<String> mods;
|
||||
private Map<ChannelIdentifier, String> channels;
|
||||
private Map<String, String> registries;
|
||||
|
||||
private final Context.ClientContext context;
|
||||
private ModListReplyPacket(List<String> mods, Map<ChannelIdentifier,
|
||||
String> channels, Map<String, String> registries, Context.ClientContext context) {
|
||||
this.mods = mods;
|
||||
this.channels = channels;
|
||||
this.registries = registries;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static ModListReplyPacket read(ByteBuf input, Context.ClientContext context) {
|
||||
|
||||
List<String> mods = new ArrayList<>();
|
||||
int len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
mods.add(ProtocolUtils.readString(input, 0x100));
|
||||
|
||||
Map<ChannelIdentifier, String> channels = new HashMap<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
channels.put(MinecraftChannelIdentifier.from(ProtocolUtils.readString(input, 32767)),
|
||||
ProtocolUtils.readString(input, 0x100));
|
||||
|
||||
Map<String, String> registries = new HashMap<>();
|
||||
len = ProtocolUtils.readVarInt(input);
|
||||
for (int x = 0; x < len; x++)
|
||||
registries.put(ProtocolUtils.readString(input, 32767), ProtocolUtils.readString(input, 0x100));
|
||||
|
||||
return new ModListReplyPacket(mods, channels, registries, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, 2);
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, mods.size());
|
||||
mods.forEach(m -> ProtocolUtils.writeString(buf, m));
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, channels.size());
|
||||
channels.forEach((k, v) -> {
|
||||
ProtocolUtils.writeString(buf,k.getId());
|
||||
ProtocolUtils.writeString(buf,v);
|
||||
});
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, registries.size());
|
||||
registries.forEach((k, v) -> {
|
||||
ProtocolUtils.writeString(buf, k);
|
||||
ProtocolUtils.writeString(buf, v);
|
||||
});
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context.ClientContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public List<String> getMods() {
|
||||
return mods;
|
||||
}
|
||||
|
||||
public Map<ChannelIdentifier, String> getChannels() {
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.packet;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
public class RegistryPacket implements IForgeLoginWrapperPacket<Context> {
|
||||
|
||||
private final String registryName;
|
||||
|
||||
private final byte[] snapshot;
|
||||
|
||||
private final Context context;
|
||||
public RegistryPacket(String registryName, byte[] snapshot, Context context) {
|
||||
this.registryName = registryName;
|
||||
this.snapshot = snapshot;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static RegistryPacket read(ByteBuf input, Context context, boolean FML3) {
|
||||
String registryName = ProtocolUtils.readString(input);
|
||||
byte[] snapshot = null;
|
||||
if (input.readBoolean()) {
|
||||
snapshot = new byte[input.readableBytes()];
|
||||
input.readBytes(snapshot);
|
||||
}
|
||||
|
||||
return new RegistryPacket(registryName, snapshot, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf encode() {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
|
||||
ProtocolUtils.writeVarInt(buf, 3);
|
||||
|
||||
ProtocolUtils.writeString(buf, registryName);
|
||||
if (snapshot != null) {
|
||||
buf.writeBoolean(true);
|
||||
buf.writeBytes(snapshot);
|
||||
} else {
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
public byte[] getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.pipeline;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
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.util.except.QuietRuntimeException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CommandDecoderErrorCatcher extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
||||
|
||||
private final ConnectedPlayer player;
|
||||
private boolean sentWarning = false;
|
||||
|
||||
public CommandDecoderErrorCatcher(ProtocolVersion protocolVersion, ConnectedPlayer player) {
|
||||
this.registry = StateRegistry.PLAY.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND, protocolVersion);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception {
|
||||
if (msg instanceof ByteBuf buf) {
|
||||
if (!ctx.channel().isActive() || !buf.isReadable()) {
|
||||
buf.release();
|
||||
return;
|
||||
}
|
||||
|
||||
int originalReaderIndex = buf.readerIndex();
|
||||
int packetId = ProtocolUtils.readVarInt(buf);
|
||||
MinecraftPacket packet = registry.createPacket(packetId);
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
if (packet instanceof AvailableCommandsPacket) {
|
||||
try {
|
||||
((MinecraftDecoder) ctx.pipeline().get(Connections.MINECRAFT_DECODER)).channelRead(ctx, msg);
|
||||
} catch (QuietRuntimeException | CorruptedFrameException e) {
|
||||
RegisteredServer server = player.getConnectedServer().getServer();
|
||||
if (!Ambassador.getInstance().config.isSilenceWarnings() && !sentWarning) {
|
||||
player.sendMessage(Component.text("[Ambassador Warning]: Unsupported command argument type detected! " +
|
||||
"Please install Proxy-Compatible-Forge mod on this backend server to have access to commands. " +
|
||||
"This message can be silenced in the ambassador.toml config file.", NamedTextColor.YELLOW));
|
||||
sentWarning = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
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.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.packet.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
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<>();
|
||||
|
||||
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();
|
||||
}
|
||||
} else {
|
||||
//New context.
|
||||
LoginPluginMessagePacket msg = (LoginPluginMessagePacket) in;
|
||||
String channel = ProtocolUtils.readString(buf);
|
||||
|
||||
context = Context.createContext(msg.getId(), channel);
|
||||
}
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
} 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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
ByteBuf encoded = msg.encode();
|
||||
ProtocolUtils.writeString(wrapped, channel);
|
||||
if (includeLength)
|
||||
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));
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package org.adde0109.ambassador.forge.pipeline;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.forge.VelocityForgeBackendConnectionPhase;
|
||||
import org.adde0109.ambassador.forge.packet.Context;
|
||||
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
|
||||
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
|
||||
|
||||
public class ForgeLoginWrapperHandler extends SimpleChannelInboundHandler<IForgeLoginWrapperPacket<?>> {
|
||||
|
||||
private final MinecraftConnectionAssociation connection;
|
||||
|
||||
|
||||
public ForgeLoginWrapperHandler(MinecraftConnectionAssociation connection) {
|
||||
super(false);
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, IForgeLoginWrapperPacket msg) throws Exception {
|
||||
if (connection instanceof ConnectedPlayer player) {
|
||||
VelocityForgeClientConnectionPhase phase = (VelocityForgeClientConnectionPhase) player.getPhase();
|
||||
phase.handle(player,msg,player.getConnectionInFlight());
|
||||
} else if (connection instanceof VelocityServerConnection serverConnection){
|
||||
if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
serverConnection.getConnection().setType(serverConnection.getPlayer().getConnection().getType());
|
||||
serverConnection.setConnectionPhase(serverConnection.getConnection().getType().getInitialBackendPhase());
|
||||
}
|
||||
|
||||
VelocityForgeBackendConnectionPhase phase =
|
||||
(VelocityForgeBackendConnectionPhase) serverConnection.getPhase();
|
||||
phase.handle(serverConnection, serverConnection.getPlayer(), msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.event.player.*;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.ModInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.network.Connections;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import org.adde0109.ambassador.Ambassador;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
|
||||
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperCodec;
|
||||
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperHandler;
|
||||
|
||||
public class VelocityEventHandler {
|
||||
|
||||
private final Ambassador ambassador;
|
||||
|
||||
public VelocityEventHandler(Ambassador ambassador) {
|
||||
this.ambassador = ambassador;
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onPostLoginEvent(PostLoginEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (player.getPhase() instanceof VelocityForgeClientConnectionPhase) {
|
||||
((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player);
|
||||
|
||||
player.getConnection().eventLoop().submit(() -> {
|
||||
player.getConnection().setState(StateRegistry.LOGIN);
|
||||
|
||||
player.getConnection().getChannel().pipeline().addBefore(
|
||||
Connections.HANDLER,
|
||||
ForgeConstants.FORGE_HANDSHAKE_DECODER, new ForgeLoginWrapperCodec(
|
||||
player.getConnection().getType() == ForgeConstants.ForgeFML3));
|
||||
player.getConnection().getChannel().pipeline().addAfter(
|
||||
ForgeConstants.FORGE_HANDSHAKE_DECODER,
|
||||
ForgeConstants.FORGE_HANDSHAKE_HANDLER, new ForgeLoginWrapperHandler(player));
|
||||
});
|
||||
}
|
||||
//event.getPlayer().sendMessage(Component.text("post login event"));
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onPlayerChooseInitialServerEvent(PlayerChooseInitialServerEvent event, Continuation continuation) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
|
||||
continuation.resume();
|
||||
return;
|
||||
}
|
||||
RegisteredServer chosenServer = Ambassador.getTemporaryForced().remove(player.getUsername());
|
||||
if (chosenServer != null)
|
||||
event.setInitialServer(chosenServer);
|
||||
//event.getPlayer().sendMessage(Component.text("choose server event"));
|
||||
continuation.resume();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerChannelRegisterEvent(PlayerChannelRegisterEvent event) {
|
||||
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
|
||||
if (!(player.getConnection().getType() instanceof ForgeFMLConnectionType)) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
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 io.netty.channel.*;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
|
||||
|
||||
public class FMLMarkerAdder extends MessageToMessageEncoder<HandshakePacket> {
|
||||
|
||||
final VelocityServer server;
|
||||
|
||||
public FMLMarkerAdder(VelocityServer server) {
|
||||
super(HandshakePacket.class);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, HandshakePacket msg, List<Object> out) {
|
||||
MinecraftConnection connection = (MinecraftConnection) ctx.pipeline().get(Connections.HANDLER);
|
||||
VelocityServerConnection serverConnection = (VelocityServerConnection) connection.getAssociation();
|
||||
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
|
||||
|
||||
if (serverConnection.getPlayer().getConnection().getType() instanceof ForgeFMLConnectionType FMLType
|
||||
&& forwardingMode != PlayerInfoForwarding.LEGACY
|
||||
&& forwardingMode != PlayerInfoForwarding.BUNGEEGUARD) {
|
||||
msg.setServerAddress(msg.getServerAddress() + (FMLType == ForgeConstants.ForgeFML3 ? ForgeConstants.FML3Marker : ForgeConstants.FML2Marker));
|
||||
}
|
||||
out.add(msg);
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
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.util.except.QuietRuntimeException;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.adde0109.ambassador.forge.*;
|
||||
|
||||
public class ForgeLoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
private final LoginSessionHandler original;
|
||||
private final VelocityServerConnection serverConnection;
|
||||
private final VelocityServer server;
|
||||
|
||||
public ForgeLoginSessionHandler(LoginSessionHandler original, VelocityServerConnection serverConnection, VelocityServer server) {
|
||||
this.original = original;
|
||||
this.serverConnection = serverConnection;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ServerLoginSuccessPacket 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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean handle(DisconnectPacket packet) {
|
||||
return original.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.backend;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.backend.LoginSessionHandler;
|
||||
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.StateRegistry;
|
||||
import io.netty.channel.*;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
|
||||
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperCodec;
|
||||
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VelocityForgeBackendHandshakeHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final VelocityServer server;
|
||||
|
||||
public VelocityForgeBackendHandshakeHandler(VelocityServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(@NotNull ChannelHandlerContext ctx) throws Exception {
|
||||
MinecraftConnection connection = (MinecraftConnection) ctx.pipeline().get(Connections.HANDLER);
|
||||
VelocityServerConnection serverConnection = (VelocityServerConnection) connection.getAssociation();
|
||||
|
||||
ctx.pipeline().remove(this);
|
||||
|
||||
ConnectedPlayer player = serverConnection.getPlayer();
|
||||
if (player.getConnection().getType() instanceof ForgeFMLConnectionType) {
|
||||
ForgeLoginSessionHandler forgeLoginSessionHandler = new ForgeLoginSessionHandler((LoginSessionHandler) connection.getActiveSessionHandler(), serverConnection,server);
|
||||
connection.setActiveSessionHandler(StateRegistry.LOGIN, forgeLoginSessionHandler);
|
||||
|
||||
serverConnection.getConnection().getChannel().pipeline().addBefore(
|
||||
Connections.HANDLER,
|
||||
ForgeConstants.FORGE_HANDSHAKE_DECODER, new ForgeLoginWrapperCodec(
|
||||
player.getConnection().getType() == ForgeConstants.ForgeFML3));
|
||||
serverConnection.getConnection().getChannel().pipeline().addAfter(
|
||||
ForgeConstants.FORGE_HANDSHAKE_DECODER,
|
||||
ForgeConstants.FORGE_HANDSHAKE_HANDLER, new ForgeLoginWrapperHandler(serverConnection));
|
||||
}
|
||||
|
||||
ctx.pipeline().fireChannelActive();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.client;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import io.netty.channel.*;
|
||||
|
||||
|
||||
public class ClientPacketQueue extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private PendingWriteQueue queue;
|
||||
private final StateRegistry allow;
|
||||
|
||||
public ClientPacketQueue(StateRegistry registry) {
|
||||
this.allow = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
queue = new PendingWriteQueue(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
MinecraftConnection connection = ctx.pipeline().get(MinecraftConnection.class);
|
||||
if (msg instanceof MinecraftPacket packet) {
|
||||
try {
|
||||
allow.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND ,
|
||||
connection.getProtocolVersion()).getPacketId(packet);
|
||||
ctx.write(msg,promise);
|
||||
} catch (IllegalArgumentException e) {
|
||||
queue.add(msg, promise);
|
||||
}
|
||||
} else {
|
||||
ctx.write(msg,promise);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
||||
if (ctx.channel().isActive()) {
|
||||
queue.removeAndWriteAll();
|
||||
ctx.flush();
|
||||
} else {
|
||||
queue.removeAndFailAll(new ChannelException());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.client;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
|
||||
public class OutboundSuccessHolder extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
private ServerLoginSuccessPacket packet;
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if ((msg instanceof ServerLoginSuccessPacket packet)) {
|
||||
this.packet = packet;
|
||||
} else {
|
||||
ctx.write(msg, promise);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket() {
|
||||
ctx.write(packet, ctx.voidPromise());
|
||||
ctx.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package org.adde0109.ambassador.velocity.protocol;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
public class EnumArgumentProperty implements ArgumentType<String> {
|
||||
|
||||
private final String className;
|
||||
|
||||
public EnumArgumentProperty(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# Do not change this
|
||||
config-version = "2.1"
|
||||
|
||||
# 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.
|
||||
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