Restructured project
This commit is contained in:
parent
6a541ff8ce
commit
2063b1364e
7
ambassador-bungeecord/build.gradle.kts
Normal file
7
ambassador-bungeecord/build.gradle.kts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
java
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
}
|
||||
|
||||
group = "org.adde0109"
|
||||
version = "1.1.7-alpha-bungeecord"
|
||||
Binary file not shown.
2
ambassador-bungeecord/build/tmp/jar/MANIFEST.MF
Normal file
2
ambassador-bungeecord/build/tmp/jar/MANIFEST.MF
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Manifest-Version: 1.0
|
||||
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
plugins {
|
||||
java
|
||||
idea
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
{"id":"ambassador","name":"Ambassador","version":"1.1.7-alpha","authors":["adde0109"],"dependencies":[],"main":"org.adde0109.ambassador.Ambassador"}
|
||||
Binary file not shown.
Binary file not shown.
2
ambassador-velocity/build/tmp/shadowJar/MANIFEST.MF
Normal file
2
ambassador-velocity/build/tmp/shadowJar/MANIFEST.MF
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Manifest-Version: 1.0
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
if (msg instanceof ByteBuf buf) {
|
||||
if (!ctx.channel().isActive() || !buf.isReadable()) {
|
||||
buf.release();
|
||||
return;
|
||||
}
|
||||
|
||||
int originalReaderIndex = buf.readerIndex();
|
||||
int packetId = ProtocolUtils.readVarInt(buf);
|
||||
if (packetId == 0x02 && buf.readableBytes() > 1) {
|
||||
try {
|
||||
int id = ProtocolUtils.readVarInt(buf);
|
||||
boolean success = buf.readBoolean();
|
||||
if (id == 98) {
|
||||
try {
|
||||
MinecraftPacket packet = new LoginPluginResponse(id,success,buf.readRetainedSlice(buf.readableBytes()));
|
||||
ctx.fireChannelRead(packet);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
buf.readerIndex(originalReaderIndex);
|
||||
}
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
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;
|
||||
|
||||
public class ForgeFMLConnectionType implements ConnectionType {
|
||||
|
||||
final int netVersion;
|
||||
|
||||
public ForgeFMLConnectionType(int netVersion) {
|
||||
this.netVersion = netVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientConnectionPhase getInitialClientPhase() {
|
||||
return new FML2ClientConnectionPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendConnectionPhase getInitialBackendPhase() {
|
||||
return new VelocityForgeBackendConnectionPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfile addGameProfileTokensIfRequired(GameProfile original, PlayerInfoForwarding forwardingType) {
|
||||
if (forwardingType == PlayerInfoForwarding.LEGACY) {
|
||||
return original.addProperties(Collections.singleton(new GameProfile.Property("extraData", "\1FML" + netVersion + "\1", "")));
|
||||
} else {
|
||||
return original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package org.adde0109.ambassador.forge;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ForgeHandshakeUtils {
|
||||
|
||||
public static int readVarInt(ByteArrayDataInput stream) {
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
byte b0;
|
||||
do {
|
||||
b0 = stream.readByte();
|
||||
i |= (b0 & 127) << j++ * 7;
|
||||
if (j > 5) {
|
||||
throw new RuntimeException("VarInt too big");
|
||||
}
|
||||
} while((b0 & 128) == 128);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void writeVarInt(ByteArrayDataOutput stream,int i) {
|
||||
while((i & -128) != 0) {
|
||||
stream.writeByte(i & 127 | 128);
|
||||
i >>>= 7;
|
||||
}
|
||||
|
||||
stream.writeByte(i);
|
||||
}
|
||||
|
||||
public static void writeUtf(ByteArrayDataOutput stream,String p_211400_1_) {
|
||||
byte[] abyte = p_211400_1_.getBytes(StandardCharsets.UTF_8);
|
||||
writeVarInt(stream,abyte.length);
|
||||
stream.write(abyte);
|
||||
}
|
||||
|
||||
public static byte[] generateTestPacket() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,4);
|
||||
writeUtf(dataAndPacketIdStream,"ambassadortestpacket");
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
|
||||
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
|
||||
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
|
||||
writeUtf(stream,"fml:handshake");
|
||||
writeVarInt(stream,dataAndPacketId.length);
|
||||
stream.write(dataAndPacketId);
|
||||
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] generateResetPacket() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,98);
|
||||
|
||||
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
|
||||
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
|
||||
writeUtf(stream,"fml:handshake");
|
||||
writeVarInt(stream,dataAndPacketId.length);
|
||||
stream.write(dataAndPacketId);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
public static byte[] generatePluginResetPacket() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,98);
|
||||
return dataAndPacketIdStream.toByteArray();
|
||||
}
|
||||
|
||||
public static final byte[] emptyModlist = generateEmptyModlist();
|
||||
private static byte[] generateEmptyModlist() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,1);
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
writeVarInt(dataAndPacketIdStream,0);
|
||||
|
||||
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
|
||||
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
|
||||
writeUtf(stream,"fml:handshake");
|
||||
writeVarInt(stream,dataAndPacketId.length);
|
||||
stream.write(dataAndPacketId);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static final byte[] ACKPacket = generateACKPacket();
|
||||
private static byte[] generateACKPacket() {
|
||||
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
|
||||
writeVarInt(dataAndPacketIdStream,99);
|
||||
|
||||
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
|
||||
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
|
||||
writeUtf(stream,"fml:handshake");
|
||||
writeVarInt(stream,dataAndPacketId.length);
|
||||
stream.write(dataAndPacketId);
|
||||
return stream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
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.VelocityForgeBackendHandshakeHandler;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class VelocityBackendChannelInitializer extends BackendChannelInitializer {
|
||||
|
||||
private static final Method INIT_CHANNEL;
|
||||
|
||||
private final ChannelInitializer<?> delegate;
|
||||
private final VelocityServer server;
|
||||
|
||||
static {
|
||||
try {
|
||||
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);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
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.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.adde0109.ambassador.forge.ForgeConstants;
|
||||
|
||||
public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler {
|
||||
private final HandshakeSessionHandler original;
|
||||
private final MinecraftConnection connection;
|
||||
|
||||
public VelocityHandshakeSessionHandler(HandshakeSessionHandler original, MinecraftConnection connection) {
|
||||
this.original = original;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
switch (markerSplit[1]) {
|
||||
case "FML2" -> connection.setType(ForgeConstants.ForgeFML2);
|
||||
case "FML3" -> connection.setType(ForgeConstants.ForgeFML3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleGeneric(MinecraftPacket packet) {
|
||||
original.handleGeneric(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUnknown(ByteBuf buf) {
|
||||
original.handleUnknown(buf);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package org.adde0109.ambassador.velocity;
|
||||
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
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 io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class VelocityServerChannelInitializer extends ServerChannelInitializer {
|
||||
private static final Method INIT_CHANNEL;
|
||||
|
||||
private final ChannelInitializer<?> delegate;
|
||||
|
||||
static {
|
||||
try {
|
||||
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 {
|
||||
INIT_CHANNEL.invoke(delegate,ch);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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;
|
||||
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertySerializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* An argument property serializer that will serialize and deserialize nothing.
|
||||
*/
|
||||
public class EnumArgumentPropertySerializer implements ArgumentPropertySerializer<EnumArgumentProperty> {
|
||||
|
||||
public static final EnumArgumentPropertySerializer ENUM = new EnumArgumentPropertySerializer();
|
||||
|
||||
private EnumArgumentPropertySerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EnumArgumentProperty deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
return new EnumArgumentProperty(ProtocolUtils.readString(buf));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(EnumArgumentProperty object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, object.getClassName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.util.ModInfo;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ModIdArgumentProperty implements ArgumentType<String> {
|
||||
|
||||
public ModIdArgumentProperty() {}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context,
|
||||
SuggestionsBuilder builder) {
|
||||
S source = context.getSource();
|
||||
|
||||
if (source instanceof Player) {
|
||||
ModInfo modInfo = ((Player) source).getModInfo().orElse(null);
|
||||
|
||||
if (modInfo != null) {
|
||||
for (ModInfo.Mod mod : modInfo.getMods()) {
|
||||
builder.suggest(mod.getId());
|
||||
}
|
||||
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
|
||||
return Suggestions.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
Loading…
Reference in New Issue
Block a user