mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-13 13:10:13 +02:00
BungeeCord and Velocity info forwarding support
This commit is contained in:
parent
d4e553e4be
commit
7953244f80
9
src/main/java/ru/nanit/limbo/LimboConstants.java
Normal file
9
src/main/java/ru/nanit/limbo/LimboConstants.java
Normal file
@ -0,0 +1,9 @@
|
||||
package ru.nanit.limbo;
|
||||
|
||||
public final class LimboConstants {
|
||||
|
||||
public static final String VELOCITY_INFO_CHANNEL = "velocity:player_info";
|
||||
|
||||
private LimboConstants(){}
|
||||
|
||||
}
|
@ -35,7 +35,7 @@ public final class LimboConfig {
|
||||
|
||||
public void load() throws Exception {
|
||||
Configuration conf = YamlConfiguration.builder()
|
||||
.source(ConfigSources.resource("/limbo.yml", this).copyTo(root))
|
||||
.source(ConfigSources.resource("/settings.yml", this).copyTo(root))
|
||||
.build();
|
||||
|
||||
conf.reload();
|
||||
|
@ -1,9 +1,11 @@
|
||||
package ru.nanit.limbo.connection;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import ru.nanit.limbo.LimboConstants;
|
||||
import ru.nanit.limbo.protocol.packets.login.*;
|
||||
import ru.nanit.limbo.protocol.packets.play.*;
|
||||
import ru.nanit.limbo.protocol.pipeline.PacketDecoder;
|
||||
@ -17,7 +19,10 @@ import ru.nanit.limbo.protocol.registry.Version;
|
||||
import ru.nanit.limbo.server.LimboServer;
|
||||
import ru.nanit.limbo.util.Logger;
|
||||
import ru.nanit.limbo.util.UuidUtil;
|
||||
import ru.nanit.limbo.util.VelocityUtil;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@ -25,31 +30,38 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final LimboServer server;
|
||||
private final Channel channel;
|
||||
private final GameProfile profile;
|
||||
|
||||
private State state;
|
||||
private Version clientVersion;
|
||||
private SocketAddress address;
|
||||
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
private int velocityLoginMessageId = -1;
|
||||
|
||||
public ClientConnection(Channel channel, LimboServer server){
|
||||
this.server = server;
|
||||
this.channel = channel;
|
||||
this.address = channel.remoteAddress();
|
||||
this.profile = new GameProfile();
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return profile.getUuid();
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return profile.getUsername();
|
||||
}
|
||||
|
||||
private void setAddress(String host){
|
||||
this.address = new InetSocketAddress(host, ((InetSocketAddress)this.address).getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
if (state.equals(State.PLAY)){
|
||||
server.getConnections().removeConnection(this);
|
||||
Logger.info("Player %s disconnected", this.username);
|
||||
Logger.info("Player %s disconnected", getUsername());
|
||||
}
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
@ -69,9 +81,20 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||
public void handlePacket(Object packet){
|
||||
if (packet instanceof PacketHandshake){
|
||||
PacketHandshake handshake = (PacketHandshake) packet;
|
||||
updateState(State.getById(handshake.getNextState()));
|
||||
clientVersion = handshake.getVersion();
|
||||
Logger.debug("Pinged from " + handshake.getHost() + ":" + handshake.getPort());
|
||||
updateState(State.getById(handshake.getNextState()));
|
||||
Logger.debug("Pinged from " + address);
|
||||
|
||||
if (server.getConfig().getInfoForwarding().isLegacy()){
|
||||
String[] split = handshake.getHost().split("\00");
|
||||
|
||||
if (split.length == 3 || split.length == 4){
|
||||
setAddress(split[1]);
|
||||
profile.setUuid(UuidUtil.fromString(split[2]));
|
||||
} else {
|
||||
disconnect("You've enabled player info forwarding. To join, enable it in your proxy too");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof PacketStatusRequest){
|
||||
@ -93,23 +116,67 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
this.username = ((PacketLoginStart) packet).getUsername();
|
||||
this.uuid = UuidUtil.getOfflineModeUuid(this.username);
|
||||
if (server.getConfig().getInfoForwarding().isModern()){
|
||||
velocityLoginMessageId = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
|
||||
PacketLoginPluginRequest request = new PacketLoginPluginRequest();
|
||||
request.setMessageId(velocityLoginMessageId);
|
||||
request.setChannel(LimboConstants.VELOCITY_INFO_CHANNEL);
|
||||
request.setData(Unpooled.EMPTY_BUFFER);
|
||||
sendPacket(request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (server.getConfig().getInfoForwarding().isNone()){
|
||||
profile.setUsername(((PacketLoginStart) packet).getUsername());
|
||||
profile.setUuid(UuidUtil.getOfflineModeUuid(getUsername()));
|
||||
}
|
||||
|
||||
fireLoginSuccess();
|
||||
}
|
||||
|
||||
if (packet instanceof PacketLoginPluginResponse){
|
||||
PacketLoginPluginResponse response = (PacketLoginPluginResponse) packet;
|
||||
|
||||
if (server.getConfig().getInfoForwarding().isModern()
|
||||
&& response.getMessageId() == velocityLoginMessageId){
|
||||
|
||||
if (!response.isSuccessful() || response.getData() == null){
|
||||
disconnect("You need to connect with Velocity");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VelocityUtil.checkIntegrity(response.getData())) {
|
||||
disconnect("Can't verify forwarded player info");
|
||||
return;
|
||||
}
|
||||
|
||||
setAddress(response.getData().readString());
|
||||
profile.setUuid(response.getData().readUuid());
|
||||
profile.setUsername(response.getData().readString());
|
||||
|
||||
fireLoginSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fireLoginSuccess(){
|
||||
if (server.getConfig().getInfoForwarding().isModern() && velocityLoginMessageId == -1){
|
||||
disconnect("You need to connect with Velocity");
|
||||
return;
|
||||
}
|
||||
|
||||
PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
|
||||
|
||||
loginSuccess.setUuid(UuidUtil.getOfflineModeUuid(this.username));
|
||||
loginSuccess.setUsername(this.username);
|
||||
|
||||
loginSuccess.setUuid(UuidUtil.getOfflineModeUuid(getUsername()));
|
||||
loginSuccess.setUsername(getUsername());
|
||||
sendPacket(loginSuccess);
|
||||
updateState(State.PLAY);
|
||||
|
||||
updateState(State.PLAY);
|
||||
server.getConnections().addConnection(this);
|
||||
Logger.info("Player %s connected (%s)", this.username, channel.remoteAddress());
|
||||
|
||||
Logger.info("Player %s connected (%s)", getUsername(), address);
|
||||
|
||||
sendJoinPackets();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendJoinPackets(){
|
||||
PacketJoinGame joinGame = new PacketJoinGame();
|
||||
|
25
src/main/java/ru/nanit/limbo/connection/GameProfile.java
Normal file
25
src/main/java/ru/nanit/limbo/connection/GameProfile.java
Normal file
@ -0,0 +1,25 @@
|
||||
package ru.nanit.limbo.connection;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class GameProfile {
|
||||
|
||||
private String username;
|
||||
private UUID uuid;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ public class ByteMessage extends ByteBuf {
|
||||
return readString(readVarInt());
|
||||
}
|
||||
|
||||
private String readString(int length) {
|
||||
public String readString(int length) {
|
||||
String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
||||
buf.skipBytes(length);
|
||||
return str;
|
||||
|
@ -0,0 +1,32 @@
|
||||
package ru.nanit.limbo.protocol.packets.login;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import ru.nanit.limbo.protocol.ByteMessage;
|
||||
import ru.nanit.limbo.protocol.PacketOut;
|
||||
|
||||
public class PacketLoginPluginRequest implements PacketOut {
|
||||
|
||||
private int messageId;
|
||||
private String channel;
|
||||
private ByteBuf data;
|
||||
|
||||
public void setMessageId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void setData(ByteBuf data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteMessage msg) {
|
||||
msg.writeVarInt(messageId);
|
||||
msg.writeString(channel);
|
||||
msg.writeBytes(data);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package ru.nanit.limbo.protocol.packets.login;
|
||||
|
||||
import ru.nanit.limbo.protocol.ByteMessage;
|
||||
import ru.nanit.limbo.protocol.PacketIn;
|
||||
|
||||
public class PacketLoginPluginResponse implements PacketIn {
|
||||
|
||||
private int messageId;
|
||||
private boolean successful;
|
||||
private ByteMessage data;
|
||||
|
||||
public int getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public boolean isSuccessful() {
|
||||
return successful;
|
||||
}
|
||||
|
||||
public ByteMessage getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteMessage msg) {
|
||||
messageId = msg.readVarInt();
|
||||
successful = msg.readBoolean();
|
||||
|
||||
if (msg.readableBytes() > 0){
|
||||
int i = msg.readableBytes();
|
||||
data = new ByteMessage(msg.readBytes(i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -41,5 +41,4 @@ public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
public void updateState(State state){
|
||||
this.mappings = state.serverBound;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,8 +30,10 @@ public enum State {
|
||||
LOGIN(2){
|
||||
{
|
||||
serverBound.register(0x00, PacketLoginStart::new);
|
||||
serverBound.register(0x02, PacketLoginPluginResponse::new);
|
||||
clientBound.register(0x00, PacketDisconnect::new);
|
||||
clientBound.register(0x02, PacketLoginSuccess::new);
|
||||
clientBound.register(0x04, PacketLoginPluginRequest::new);
|
||||
}
|
||||
},
|
||||
PLAY(3){
|
||||
|
@ -12,6 +12,7 @@ import ru.nanit.limbo.protocol.packets.play.PacketBossBar;
|
||||
import ru.nanit.limbo.protocol.packets.play.PacketChatMessage;
|
||||
import ru.nanit.limbo.server.data.*;
|
||||
import ru.nanit.limbo.util.Logger;
|
||||
import ru.nanit.limbo.util.VelocityUtil;
|
||||
import ru.nanit.limbo.world.DimensionRegistry;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
@ -64,6 +65,10 @@ public final class LimboServer {
|
||||
|
||||
Logger.setLevel(config.getDebugLevel());
|
||||
|
||||
if (config.getInfoForwarding().isModern()){
|
||||
VelocityUtil.init(config);
|
||||
}
|
||||
|
||||
dimensionRegistry = new DimensionRegistry();
|
||||
dimensionRegistry.load(config.getDimensionType());
|
||||
|
||||
|
@ -4,19 +4,31 @@ import napi.configurate.data.ConfigNode;
|
||||
import napi.configurate.serializing.NodeSerializer;
|
||||
import napi.configurate.serializing.NodeSerializingException;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class InfoForwarding {
|
||||
|
||||
private Type type;
|
||||
private String secret;
|
||||
private byte[] secretKey;
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Optional<String> getSecret() {
|
||||
return Optional.ofNullable(secret);
|
||||
public byte[] getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public boolean isNone(){
|
||||
return type == Type.NONE;
|
||||
}
|
||||
|
||||
public boolean isLegacy(){
|
||||
return type == Type.LEGACY;
|
||||
}
|
||||
|
||||
public boolean isModern(){
|
||||
return type == Type.MODERN;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
@ -38,14 +50,14 @@ public class InfoForwarding {
|
||||
}
|
||||
|
||||
if (forwarding.type == Type.MODERN){
|
||||
forwarding.secret = node.getNode("secret").getString();
|
||||
forwarding.secretKey = node.getNode("secret").getString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
return forwarding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(InfoForwarding infoForwarding, ConfigNode configNode) throws NodeSerializingException {
|
||||
public void serialize(InfoForwarding infoForwarding, ConfigNode configNode) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,9 @@ public final class UuidUtil {
|
||||
.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static UUID fromString(String str){
|
||||
if(str.contains("-")) return UUID.fromString(str);
|
||||
return UUID.fromString(str.replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5"));
|
||||
}
|
||||
|
||||
}
|
||||
|
42
src/main/java/ru/nanit/limbo/util/VelocityUtil.java
Normal file
42
src/main/java/ru/nanit/limbo/util/VelocityUtil.java
Normal file
@ -0,0 +1,42 @@
|
||||
package ru.nanit.limbo.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import ru.nanit.limbo.configuration.LimboConfig;
|
||||
import ru.nanit.limbo.protocol.ByteMessage;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public final class VelocityUtil {
|
||||
|
||||
private static byte[] secretKey;
|
||||
|
||||
private VelocityUtil(){}
|
||||
|
||||
public static void init(LimboConfig config){
|
||||
secretKey = config.getInfoForwarding().getSecretKey();
|
||||
}
|
||||
|
||||
public static boolean checkIntegrity(ByteMessage buf) {
|
||||
byte[] signature = new byte[32];
|
||||
buf.readBytes(signature);
|
||||
byte[] data = new byte[buf.readableBytes()];
|
||||
buf.getBytes(buf.readerIndex(), data);
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secretKey, "HmacSHA256"));
|
||||
byte[] mySignature = mac.doFinal(data);
|
||||
if (!MessageDigest.isEqual(signature, mySignature))
|
||||
return false;
|
||||
} catch (InvalidKeyException |java.security.NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
int version = buf.readVarInt();
|
||||
if (version != 1)
|
||||
throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + '\001');
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -57,12 +57,12 @@ public final class DimensionRegistry {
|
||||
overWorld = CompoundBinaryTag.builder()
|
||||
.putString("name", "minecraft:overworld")
|
||||
.putByte("piglin_safe", (byte) 0)
|
||||
.putByte("natural", (byte) 0)
|
||||
.putByte("natural", (byte) 1)
|
||||
.putFloat("ambient_light", 0.0F)
|
||||
.putString("infiniburn", "minecraft:infiniburn_overworld")
|
||||
.putByte("respawn_anchor_works", (byte) 0)
|
||||
.putByte("has_skylight", (byte) 0)
|
||||
.putByte("bed_works", (byte) 0)
|
||||
.putByte("has_skylight", (byte) 1)
|
||||
.putByte("bed_works", (byte) 1)
|
||||
.putString("effects", "minecraft:overworld")
|
||||
.putLong("fixed_time", 6000L)
|
||||
.putByte("has_raids", (byte) 1)
|
||||
@ -74,20 +74,20 @@ public final class DimensionRegistry {
|
||||
|
||||
nether = CompoundBinaryTag.builder()
|
||||
.putString("name", "minecraft:the_nether")
|
||||
.putByte("piglin_safe", (byte) 0)
|
||||
.putByte("piglin_safe", (byte) 1)
|
||||
.putByte("natural", (byte) 0)
|
||||
.putFloat("ambient_light", 0.0F)
|
||||
.putFloat("ambient_light", 0.1F)
|
||||
.putString("infiniburn", "minecraft:infiniburn_nether")
|
||||
.putByte("respawn_anchor_works", (byte) 0)
|
||||
.putByte("respawn_anchor_works", (byte) 1)
|
||||
.putByte("has_skylight", (byte) 0)
|
||||
.putByte("bed_works", (byte) 0)
|
||||
.putString("effects", "minecraft:the_nether")
|
||||
.putLong("fixed_time", 18000L)
|
||||
.putByte("has_raids", (byte) 1)
|
||||
.putByte("has_raids", (byte) 0)
|
||||
.putInt("logical_height", 128)
|
||||
.putDouble("coordinate_scale", 1.0)
|
||||
.putByte("ultrawarm", (byte) 0)
|
||||
.putByte("has_ceiling", (byte) 0)
|
||||
.putByte("ultrawarm", (byte) 1)
|
||||
.putByte("has_ceiling", (byte) 1)
|
||||
.build();
|
||||
|
||||
theEnd = CompoundBinaryTag.builder()
|
||||
@ -110,13 +110,13 @@ public final class DimensionRegistry {
|
||||
|
||||
CompoundBinaryTag overWorldData = CompoundBinaryTag.builder()
|
||||
.putString("name", "minecraft:overworld")
|
||||
.putInt("id", 2)
|
||||
.putInt("id", 0)
|
||||
.put("element", overWorld)
|
||||
.build();
|
||||
|
||||
CompoundBinaryTag netherData = CompoundBinaryTag.builder()
|
||||
.putString("name", "minecraft:the_nether")
|
||||
.putInt("id", 2)
|
||||
.putInt("id", 1)
|
||||
.put("element", nether)
|
||||
.build();
|
||||
|
||||
|
@ -21,7 +21,7 @@ dimension: THE_END
|
||||
# Spawn position in the world
|
||||
spawnPosition:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
y: 32.0
|
||||
z: 0.0
|
||||
yaw: 0.0
|
||||
pitch: 0.0
|
Loading…
x
Reference in New Issue
Block a user