Merge pull request #22 from GrizzlT/feature/modern-forwarding

Feature: Added Velocity Modern Forwarding and BungeeGuard support!
This commit is contained in:
LOOHP 2021-08-19 18:55:37 +08:00 committed by GitHub
commit a0f592d8fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 310 additions and 115 deletions

View File

@ -1,26 +1,21 @@
package com.loohp.limbo.file;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import javax.imageio.ImageIO;
import com.google.common.collect.Lists;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.NamespacedKey;
import com.loohp.limbo.world.World;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
public class ServerProperties {
public static final String COMMENT = "For explaination of what each of the options does, please visit:\nhttps://github.com/LOOHP/Limbo/blob/master/src/main/resources/server.properties";
@ -40,6 +35,9 @@ public class ServerProperties {
private String versionString;
private int protocol;
private boolean bungeecord;
private boolean velocityModern;
private boolean bungeeGuard;
private List<String> forwardingSecrets;
private int viewDistance;
private double ticksPerSecond;
private boolean handshakeVerbose;
@ -91,6 +89,27 @@ public class ServerProperties {
motdJson = prop.getProperty("motd");
versionString = prop.getProperty("version");
bungeecord = Boolean.parseBoolean(prop.getProperty("bungeecord"));
velocityModern = Boolean.parseBoolean(prop.getProperty("velocity-modern"));
bungeeGuard = Boolean.parseBoolean(prop.getProperty("bungee-guard"));
if (velocityModern || bungeeGuard) {
String forwardingSecretsStr = prop.getProperty("forwarding-secrets");
if (forwardingSecretsStr == null || forwardingSecretsStr.equals("")) {
Limbo.getInstance().getConsole().sendMessage("Velocity Modern Forwarding or BungeeGuard is enabled but no forwarding-secret was found!");
Limbo.getInstance().getConsole().sendMessage("Server will exit!");
System.exit(1);
return;
}
this.forwardingSecrets = Lists.newArrayList(forwardingSecretsStr.split(";"));
if (bungeecord) {
Limbo.getInstance().getConsole().sendMessage("BungeeCord is enabled but so is Velocity Modern Forwarding or BungeeGuard, We will automatically disable BungeeCord forwarding because of this");
bungeecord = false;
}
if (velocityModern && bungeeGuard) {
Limbo.getInstance().getConsole().sendMessage("Both Velocity Modern Forwarding and BungeeGuard are enabled! Because of this we always prefer Modern Forwarding, disabling BungeeGuard");
bungeeGuard = false;
}
}
viewDistance = Integer.parseInt(prop.getProperty("view-distance"));
ticksPerSecond = Double.parseDouble(prop.getProperty("ticks-per-second"));
handshakeVerbose = Boolean.parseBoolean(prop.getProperty("handshake-verbose"));
@ -123,6 +142,18 @@ public class ServerProperties {
return bungeecord;
}
public boolean isVelocityModern() {
return velocityModern;
}
public boolean isBungeeGuard() {
return bungeeGuard;
}
public List<String> getForwardingSecrets() {
return forwardingSecrets;
}
public Optional<BufferedImage> getFavicon() {
return favicon;
}

View File

@ -1,81 +1,41 @@
package com.loohp.limbo.server;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.events.player.*;
import com.loohp.limbo.events.status.StatusPingEvent;
import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInteractManager;
import com.loohp.limbo.server.packets.*;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoAction;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty;
import com.loohp.limbo.server.packets.PacketPlayOutTabComplete.TabCompleteMatches;
import com.loohp.limbo.utils.*;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.world.BlockPosition;
import com.loohp.limbo.world.World;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.events.player.PlayerJoinEvent;
import com.loohp.limbo.events.player.PlayerLoginEvent;
import com.loohp.limbo.events.player.PlayerMoveEvent;
import com.loohp.limbo.events.player.PlayerQuitEvent;
import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent;
import com.loohp.limbo.events.status.StatusPingEvent;
import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInteractManager;
import com.loohp.limbo.server.packets.Packet;
import com.loohp.limbo.server.packets.PacketHandshakingIn;
import com.loohp.limbo.server.packets.PacketLoginInLoginStart;
import com.loohp.limbo.server.packets.PacketLoginOutDisconnect;
import com.loohp.limbo.server.packets.PacketLoginOutLoginSuccess;
import com.loohp.limbo.server.packets.PacketOut;
import com.loohp.limbo.server.packets.PacketPlayInChat;
import com.loohp.limbo.server.packets.PacketPlayInHeldItemChange;
import com.loohp.limbo.server.packets.PacketPlayInKeepAlive;
import com.loohp.limbo.server.packets.PacketPlayInPosition;
import com.loohp.limbo.server.packets.PacketPlayInPositionAndLook;
import com.loohp.limbo.server.packets.PacketPlayInRotation;
import com.loohp.limbo.server.packets.PacketPlayInTabComplete;
import com.loohp.limbo.server.packets.PacketPlayOutDeclareCommands;
import com.loohp.limbo.server.packets.PacketPlayOutDisconnect;
import com.loohp.limbo.server.packets.PacketPlayOutEntityMetadata;
import com.loohp.limbo.server.packets.PacketPlayOutGameState;
import com.loohp.limbo.server.packets.PacketPlayOutHeldItemChange;
import com.loohp.limbo.server.packets.PacketPlayOutLogin;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoAction;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty;
import com.loohp.limbo.server.packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.server.packets.PacketPlayOutSpawnPosition;
import com.loohp.limbo.server.packets.PacketPlayOutTabComplete;
import com.loohp.limbo.server.packets.PacketPlayOutTabComplete.TabCompleteMatches;
import com.loohp.limbo.server.packets.PacketPlayOutUpdateViewPosition;
import com.loohp.limbo.server.packets.PacketStatusInPing;
import com.loohp.limbo.server.packets.PacketStatusInRequest;
import com.loohp.limbo.server.packets.PacketStatusOutPong;
import com.loohp.limbo.server.packets.PacketStatusOutResponse;
import com.loohp.limbo.utils.CustomStringUtils;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.DeclareCommands;
import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.MojangAPIUtils;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.utils.NamespacedKey;
import com.loohp.limbo.world.BlockPosition;
import com.loohp.limbo.world.World;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
public class ClientConnection extends Thread {
public static enum ClientState {
@ -87,6 +47,9 @@ public class ClientConnection extends Thread {
DISCONNECTED;
}
private final Random random = new Random();
private final JsonParser jsonParser = new JsonParser();
private final Socket client_socket;
private boolean running;
private ClientState state;
@ -201,10 +164,12 @@ public class ClientConnection extends Thread {
PacketHandshakingIn handshake = new PacketHandshakingIn(input);
boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord();
boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord();
boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard();
boolean isVelocityModern = Limbo.getInstance().getServerProperties().isVelocityModern();
String bungeeForwarding = handshake.getServerAddress();
UUID bungeeUUID = null;
SkinResponse bungeeSkin = null;
SkinResponse forwardedSkin = null;
try {
switch (handshake.getHandshakeType()) {
@ -236,7 +201,7 @@ public class ClientConnection extends Thread {
case LOGIN:
state = ClientState.LOGIN;
if (isBungeecord) {
if (isBungeecord || isBungeeGuard) {
try {
String[] data = bungeeForwarding.split("\\x00");
//String host = data[0];
@ -244,20 +209,34 @@ public class ClientConnection extends Thread {
bungeeUUID = UUID.fromString(data[2].replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5"));
inetAddress = InetAddress.getByName(ip);
boolean bungeeGuardFound = false;
if (data.length > 3) {
String skinJson = data[3];
String skin = skinJson.split("\"value\":\"")[1].split("\"")[0];
String signature = skinJson.split("\"signature\":\"")[1].split("\"")[0];
bungeeSkin = new SkinResponse(skin, signature);
JsonArray skinJson = this.jsonParser.parse(data[3]).getAsJsonArray();
for (JsonElement property : skinJson) {
if (property.getAsJsonObject().get("name").getAsString().equals("textures")) {
String skin = property.getAsJsonObject().get("value").getAsString();
String signature = property.getAsJsonObject().get("signature").getAsString();
forwardedSkin = new SkinResponse(skin, signature);
} else if (isBungeeGuard && property.getAsJsonObject().get("name").getAsString().equals("bungeeguard-token")) {
String token = property.getAsJsonObject().get("value").getAsString();
bungeeGuardFound = Limbo.getInstance().getServerProperties().getForwardingSecrets().contains(token);
}
}
}
if (isBungeeGuard && !bungeeGuardFound) {
disconnectDuringLogin(TextComponent.fromLegacyText("Invalid information forwarding"));
break;
}
} catch (Exception e) {
Limbo.getInstance().getConsole().sendMessage("If you wish to use bungeecord's IP forwarding, please enable that in your bungeecord config.yml as well!");
disconnectDuringLogin(new BaseComponent[] {new TextComponent(ChatColor.RED + "Please connect from the proxy!")});
}
}
int messageId = this.random.nextInt();
while (client_socket.isConnected()) {
int size = DataTypeIO.readVarInt(input);
int packetId = DataTypeIO.readVarInt(input);
@ -268,7 +247,14 @@ public class ClientConnection extends Thread {
} else if (packetType.equals(PacketLoginInLoginStart.class)) {
PacketLoginInLoginStart start = new PacketLoginInLoginStart(input);
String username = start.getUsername();
UUID uuid = isBungeecord ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
if (Limbo.getInstance().getServerProperties().isVelocityModern()) {
PacketLoginOutPluginMessaging loginPluginRequest = new PacketLoginOutPluginMessaging(messageId, ForwardingUtils.VELOCITY_FORWARDING_CHANNEL);
sendPacket(loginPluginRequest);
continue;
}
UUID uuid = isBungeecord || isBungeeGuard ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, username);
sendPacket(success);
@ -279,6 +265,34 @@ public class ClientConnection extends Thread {
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player);
break;
} else if (packetType.equals(PacketLoginInPluginMessaging.class)) {
PacketLoginInPluginMessaging response = new PacketLoginInPluginMessaging(input, size, packetId);
if (response.getMessageId() != messageId) {
disconnectDuringLogin(TextComponent.fromLegacyText("Internal error, messageId did not match"));
break;
}
if (response.getData() == null) {
disconnectDuringLogin(TextComponent.fromLegacyText("Unknown login plugin response packet!"));
break;
}
if (!ForwardingUtils.validateVelocityModernResponse(response.getData())) {
disconnectDuringLogin(TextComponent.fromLegacyText("Invalid playerinfo forwarding!"));
break;
}
ForwardingUtils.VelocityModernForwardingData data = ForwardingUtils.getVelocityDataFrom(response.getData());
inetAddress = InetAddress.getByName(data.getIpAddress());
forwardedSkin = data.getSkinResponse();
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(data.getUuid(), data.getUsername());
sendPacket(success);
state = ClientState.PLAY;
player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player);
break;
} else {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
@ -293,6 +307,7 @@ public class ClientConnection extends Thread {
break;
}
} catch (Exception e) {
e.printStackTrace();
client_socket.close();
state = ClientState.DISCONNECTED;
}
@ -312,7 +327,7 @@ public class ClientConnection extends Thread {
sendPacket(join);
Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, properties.getDefaultGamemode());
SkinResponse skinresponce = isBungeecord && bungeeSkin != null ? bungeeSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName());
SkinResponse skinresponce = (isVelocityModern || isBungeeGuard || isBungeecord) && forwardedSkin != null ? forwardedSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName());
PlayerSkinProperty skin = skinresponce != null ? new PlayerSkinProperty(skinresponce.getSkin(), skinresponce.getSignature()) : null;
PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(PlayerInfoAction.ADD_PLAYER, player.getUniqueId(), new PlayerInfoData.PlayerInfoDataAddPlayer(player.getName(), Optional.ofNullable(skin), properties.getDefaultGamemode(), 0, false, Optional.empty()));
sendPacket(info);
@ -327,7 +342,7 @@ public class ClientConnection extends Thread {
PacketPlayOutPlayerAbilities abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, flags.toArray(new PlayerAbilityFlags[flags.size()]));
sendPacket(abilities);
String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName();
String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName() + "(" + player.getUniqueId() + ")";
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!");
PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player);

View File

@ -1,39 +1,39 @@
package com.loohp.limbo.server.packets;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.NamespacedKey;
public class PacketLoginInPluginMessaging extends PacketIn {
private int messageId;
private NamespacedKey channel;
private byte[] data;
private boolean successful;
private byte[] data = null;
public PacketLoginInPluginMessaging(int messageId, NamespacedKey channel, byte[] data) {
public PacketLoginInPluginMessaging(int messageId, boolean successful, byte[] data) {
this.messageId = messageId;
this.channel = channel;
this.data = data;
}
public PacketLoginInPluginMessaging(DataInputStream in, int packetLength, int packetId) throws IOException {
messageId = DataTypeIO.readVarInt(in);
String rawChannel = DataTypeIO.readString(in, StandardCharsets.UTF_8);
channel = new NamespacedKey(rawChannel);
int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getVarIntLength(messageId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8);
data = new byte[dataLength];
in.read(data);
successful = in.readBoolean();
if (successful) {
int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getVarIntLength(messageId) - 1;
if (dataLength != 0) {
data = new byte[dataLength];
in.readFully(data);
}
}
}
public int getMessageId() {
return messageId;
}
public NamespacedKey getChannel() {
return channel;
public boolean isSuccessful() {
return successful;
}
public byte[] getData() {

View File

@ -1,19 +1,23 @@
package com.loohp.limbo.server.packets;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.NamespacedKey;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.NamespacedKey;
public class PacketLoginOutPluginMessaging extends PacketOut {
private int messageId;
private NamespacedKey channel;
private byte[] data;
public PacketLoginOutPluginMessaging(int messageId, NamespacedKey channel) {
this(messageId, channel, null);
}
public PacketLoginOutPluginMessaging(int messageId, NamespacedKey channel, byte[] data) {
this.messageId = messageId;
this.channel = channel;
@ -40,7 +44,9 @@ public class PacketLoginOutPluginMessaging extends PacketOut {
output.writeByte(Packet.getLoginOut().get(getClass()));
DataTypeIO.writeVarInt(output, messageId);
DataTypeIO.writeString(output, channel.toString(), StandardCharsets.UTF_8);
output.write(data);
if (data != null) {
output.write(data);
}
return buffer.toByteArray();
}

View File

@ -23,6 +23,10 @@ public class DataTypeIO {
out.writeLong(uuid.getMostSignificantBits());
out.writeLong(uuid.getLeastSignificantBits());
}
public static UUID readUUID(DataInputStream in) throws IOException {
return new UUID(in.readLong(), in.readLong());
}
public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

View File

@ -0,0 +1,127 @@
package com.loohp.limbo.utils;
import com.loohp.limbo.Limbo;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;
public class ForwardingUtils {
public static final NamespacedKey VELOCITY_FORWARDING_CHANNEL = new NamespacedKey("velocity", "player_info");
public static boolean validateVelocityModernResponse(byte[] data) throws IOException {
ByteArrayInputStream byteIn = new ByteArrayInputStream(data);
DataInputStream input = new DataInputStream(byteIn);
byte[] signature = new byte[32];
input.readFully(signature);
boolean foundValid = false;
try {
Mac mac = Mac.getInstance("HmacSHA256");
for (String secret : Limbo.getInstance().getServerProperties().getForwardingSecrets()) {
SecretKey key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(key);
mac.update(data, 32, data.length - 32);
byte[] sig = mac.doFinal();
if (Arrays.equals(signature, sig)) {
foundValid = true;
break;
}
}
} catch (InvalidKeyException e) {
throw new RuntimeException("Unable to authenticate data", e);
} catch (NoSuchAlgorithmException e) {
// Should never happen
throw new AssertionError(e);
}
return foundValid;
}
public static VelocityModernForwardingData getVelocityDataFrom(byte[] data) throws IOException {
ByteArrayInputStream byteIn = new ByteArrayInputStream(data);
DataInputStream input = new DataInputStream(byteIn);
input.skipBytes(32);
int velocityVersion = DataTypeIO.readVarInt(input);
if (velocityVersion != 1) {
System.out.println("Unsupported Velocity version! Stopping Execution");
throw new AssertionError("Unknown Velocity Packet");
}
String address = DataTypeIO.readString(input, StandardCharsets.UTF_8);
UUID uuid = DataTypeIO.readUUID(input);
String username = DataTypeIO.readString(input, StandardCharsets.UTF_8);
//cycle properties
MojangAPIUtils.SkinResponse response = null;
int count = DataTypeIO.readVarInt(input);
for (int i = 0; i < count; ++i) {
String propertyName = DataTypeIO.readString(input, StandardCharsets.UTF_8);
String propertyValue = DataTypeIO.readString(input, StandardCharsets.UTF_8);
String propertySignature = "";
boolean signatureIncluded = input.readBoolean();
if (signatureIncluded) {
propertySignature = DataTypeIO.readString(input, StandardCharsets.UTF_8);
}
if (propertyName.equals("textures")) {
response = new MojangAPIUtils.SkinResponse(propertyValue, propertySignature);
break; // we don't use others properties for now
}
}
return new VelocityModernForwardingData(velocityVersion, address, uuid, username, response);
}
public static class VelocityModernForwardingData {
private final int version;
private final String ipAddress;
private final UUID uuid;
private final String username;
private final MojangAPIUtils.SkinResponse skinResponse;
public VelocityModernForwardingData(int version, String ipAddress, UUID uuid, String username, MojangAPIUtils.SkinResponse skinResponse) {
this.version = version;
this.ipAddress = ipAddress;
this.uuid = uuid;
this.username = username;
this.skinResponse = skinResponse;
}
public int getVersion()
{
return version;
}
public String getIpAddress()
{
return ipAddress;
}
public UUID getUuid()
{
return uuid;
}
public String getUsername()
{
return username;
}
public MojangAPIUtils.SkinResponse getSkinResponse()
{
return skinResponse;
}
}
}

View File

@ -9,7 +9,7 @@
"LoginOut": {
"PacketLoginOutLoginSuccess": "0x02",
"PacketLoginOutDisconnect": "0x00",
"PacketLoginOutPluginMessaging": "0x04",
"PacketLoginOutPluginMessaging": "0x04"
},
"PlayIn": {
"0x0F": "PacketPlayInKeepAlive",

View File

@ -7,9 +7,21 @@ server-port=30000
#Server ip, localhost for local access only
server-ip=0.0.0.0
#Whether this is server is behind a bungeecord proxy
#Whether this server is behind a bungeecord proxy
#Mutually exclusive with velocity-modern and bungee-guard
bungeecord=false
#Whether this server is behind a velocity proxy with modern player forwarding
#Mutually exclusive with bungeecord and bungee-guard
velocity-modern=false
#Whether this server is behind a bungeecord proxy with BungeeGuard installed (velocity can do this too for <1.13)
#Mutually exclusive with bungeecord and velocity-modern
bungee-guard=false
#For Velocity Modern Forwarding or BungeeGuard a list (separated by `;`) of valid secrets
forwarding-secrets=
#World Name and the Schematic file containing map
level-name=world;spawn.schem