format ClientConnection.java

This commit is contained in:
LOOHP 2022-02-10 01:57:52 +00:00
parent 4353859951
commit ea7d33a0e3
1 changed files with 475 additions and 475 deletions

View File

@ -1,29 +1,5 @@
package com.loohp.limbo.network; package com.loohp.limbo.network;
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.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import com.loohp.limbo.network.protocol.packets.PacketIn;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import com.loohp.limbo.Limbo; import com.loohp.limbo.Limbo;
import com.loohp.limbo.events.player.PlayerJoinEvent; import com.loohp.limbo.events.player.PlayerJoinEvent;
import com.loohp.limbo.events.player.PlayerLoginEvent; import com.loohp.limbo.events.player.PlayerLoginEvent;
@ -34,8 +10,8 @@ import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent;
import com.loohp.limbo.events.status.StatusPingEvent; import com.loohp.limbo.events.status.StatusPingEvent;
import com.loohp.limbo.file.ServerProperties; import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.protocol.packets.Packet;
import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn;
import com.loohp.limbo.network.protocol.packets.PacketIn;
import com.loohp.limbo.network.protocol.packets.PacketLoginInLoginStart; import com.loohp.limbo.network.protocol.packets.PacketLoginInLoginStart;
import com.loohp.limbo.network.protocol.packets.PacketLoginInPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketLoginInPluginMessaging;
import com.loohp.limbo.network.protocol.packets.PacketLoginOutDisconnect; import com.loohp.limbo.network.protocol.packets.PacketLoginOutDisconnect;
@ -86,514 +62,538 @@ import com.loohp.limbo.utils.MojangAPIUtils;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.world.BlockPosition; import com.loohp.limbo.world.BlockPosition;
import com.loohp.limbo.world.World; import com.loohp.limbo.world.World;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent; import net.md_5.bungee.api.chat.TranslatableComponent;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
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.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
public class ClientConnection extends Thread { public class ClientConnection extends Thread {
public static enum ClientState { private final Random random = new Random();
LEGACY,
HANDSHAKE,
STATUS,
LOGIN,
PLAY,
DISCONNECTED;
}
private final Random random = new Random();
private final Socket client_socket; private final Socket client_socket;
protected Channel channel;
private boolean running; private boolean running;
private ClientState state; private ClientState state;
private Player player; private Player player;
private TimerTask keepAliveTask; private TimerTask keepAliveTask;
private AtomicLong lastPacketTimestamp; private AtomicLong lastPacketTimestamp;
private AtomicLong lastKeepAlivePayLoad; private AtomicLong lastKeepAlivePayLoad;
private InetAddress inetAddress;
protected Channel channel; private boolean ready;
private InetAddress inetAddress;
private boolean ready;
public ClientConnection(Socket client_socket) { public ClientConnection(Socket client_socket) {
this.client_socket = client_socket; this.client_socket = client_socket;
this.inetAddress = client_socket.getInetAddress(); this.inetAddress = client_socket.getInetAddress();
this.lastPacketTimestamp = new AtomicLong(-1); this.lastPacketTimestamp = new AtomicLong(-1);
this.lastKeepAlivePayLoad = new AtomicLong(-1); this.lastKeepAlivePayLoad = new AtomicLong(-1);
this.channel = new Channel(this, null, null); this.channel = new Channel(this, null, null);
this.running = false; this.running = false;
this.ready = false; this.ready = false;
} }
public InetAddress getInetAddress() { public InetAddress getInetAddress() {
return inetAddress; return inetAddress;
} }
public long getLastKeepAlivePayLoad() { public long getLastKeepAlivePayLoad() {
return lastKeepAlivePayLoad.get(); return lastKeepAlivePayLoad.get();
} }
public void setLastKeepAlivePayLoad(long payLoad) { public void setLastKeepAlivePayLoad(long payLoad) {
this.lastKeepAlivePayLoad.set(payLoad); this.lastKeepAlivePayLoad.set(payLoad);
} }
public long getLastPacketTimestamp() { public long getLastPacketTimestamp() {
return lastPacketTimestamp.get(); return lastPacketTimestamp.get();
} }
public void setLastPacketTimestamp(long payLoad) { public void setLastPacketTimestamp(long payLoad) {
this.lastPacketTimestamp.set(payLoad); this.lastPacketTimestamp.set(payLoad);
} }
public TimerTask getKeepAliveTask() { public TimerTask getKeepAliveTask() {
return this.keepAliveTask; return this.keepAliveTask;
} }
public Player getPlayer() { public Player getPlayer() {
return player; return player;
} }
public ClientState getClientState() { public ClientState getClientState() {
return state; return state;
} }
public Socket getSocket() { public Socket getSocket() {
return client_socket; return client_socket;
} }
public Channel getChannel() { public Channel getChannel() {
return channel; return channel;
} }
public boolean isRunning() { public boolean isRunning() {
return running; return running;
} }
public boolean isReady() { public boolean isReady() {
return ready; return ready;
} }
public synchronized void sendPacket(PacketOut packet) throws IOException { public synchronized void sendPacket(PacketOut packet) throws IOException {
if (channel.writePacket(packet)) { if (channel.writePacket(packet)) {
setLastPacketTimestamp(System.currentTimeMillis()); setLastPacketTimestamp(System.currentTimeMillis());
} }
} }
public void disconnect(BaseComponent[] reason) { public void disconnect(BaseComponent[] reason) {
disconnect(BungeecordAdventureConversionUtils.toComponent(reason)); disconnect(BungeecordAdventureConversionUtils.toComponent(reason));
} }
public void disconnect(Component reason) { public void disconnect(Component reason) {
try { try {
PacketPlayOutDisconnect packet = new PacketPlayOutDisconnect(reason); PacketPlayOutDisconnect packet = new PacketPlayOutDisconnect(reason);
sendPacket(packet); sendPacket(packet);
} catch (IOException ignored) {} } catch (IOException ignored) {
try { }
client_socket.close(); try {
} catch (IOException ignored) {} client_socket.close();
} } catch (IOException ignored) {
}
}
private void disconnectDuringLogin(BaseComponent[] reason) { private void disconnectDuringLogin(BaseComponent[] reason) {
disconnectDuringLogin(BungeecordAdventureConversionUtils.toComponent(reason)); disconnectDuringLogin(BungeecordAdventureConversionUtils.toComponent(reason));
} }
private void disconnectDuringLogin(Component reason) { private void disconnectDuringLogin(Component reason) {
try { try {
PacketLoginOutDisconnect packet = new PacketLoginOutDisconnect(reason); PacketLoginOutDisconnect packet = new PacketLoginOutDisconnect(reason);
sendPacket(packet); sendPacket(packet);
} catch (IOException ignored) {} } catch (IOException ignored) {
try { }
client_socket.close(); try {
} catch (IOException ignored) {} client_socket.close();
} } catch (IOException ignored) {
}
}
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public void run() { public void run() {
running = true; running = true;
state = ClientState.HANDSHAKE; state = ClientState.HANDSHAKE;
try { try {
client_socket.setKeepAlive(true); client_socket.setKeepAlive(true);
channel.input = new DataInputStream(client_socket.getInputStream()); channel.input = new DataInputStream(client_socket.getInputStream());
channel.output = new DataOutputStream(client_socket.getOutputStream()); channel.output = new DataOutputStream(client_socket.getOutputStream());
int handShakeSize = DataTypeIO.readVarInt(channel.input); int handShakeSize = DataTypeIO.readVarInt(channel.input);
//legacy ping //legacy ping
if (handShakeSize == 0xFE) { if (handShakeSize == 0xFE) {
state = ClientState.LEGACY; state = ClientState.LEGACY;
channel.output.writeByte(255); channel.output.writeByte(255);
String str = inetAddress.getHostName() + ":" + client_socket.getPort(); String str = inetAddress.getHostName() + ":" + client_socket.getPort();
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Legacy Status has pinged"); Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Legacy Status has pinged");
ServerProperties p = Limbo.getInstance().getServerProperties(); ServerProperties p = Limbo.getInstance().getServerProperties();
StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), p.getMotd(), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null))); StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), p.getMotd(), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null)));
String response = Limbo.getInstance().buildLegacyPingResponse(event.getVersion(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline()); String response = Limbo.getInstance().buildLegacyPingResponse(event.getVersion(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline());
byte[] bytes = response.getBytes(StandardCharsets.UTF_16BE); byte[] bytes = response.getBytes(StandardCharsets.UTF_16BE);
channel.output.writeShort(response.length()); channel.output.writeShort(response.length());
channel.output.write(bytes); channel.output.write(bytes);
channel.close(); channel.close();
client_socket.close(); client_socket.close();
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
} }
PacketHandshakingIn handshake = (PacketHandshakingIn) channel.readPacket(handShakeSize); PacketHandshakingIn handshake = (PacketHandshakingIn) channel.readPacket(handShakeSize);
boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord(); boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord();
boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard(); boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard();
boolean isVelocityModern = Limbo.getInstance().getServerProperties().isVelocityModern(); boolean isVelocityModern = Limbo.getInstance().getServerProperties().isVelocityModern();
String bungeeForwarding = handshake.getServerAddress(); String bungeeForwarding = handshake.getServerAddress();
UUID bungeeUUID = null; UUID bungeeUUID = null;
SkinResponse forwardedSkin = null; SkinResponse forwardedSkin = null;
try { try {
switch (handshake.getHandshakeType()) { switch (handshake.getHandshakeType()) {
case STATUS: case STATUS:
state = ClientState.STATUS; state = ClientState.STATUS;
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
PacketIn packetIn = channel.readPacket(); PacketIn packetIn = channel.readPacket();
if (packetIn instanceof PacketStatusInRequest) { if (packetIn instanceof PacketStatusInRequest) {
String str = inetAddress.getHostName() + ":" + client_socket.getPort(); String str = inetAddress.getHostName() + ":" + client_socket.getPort();
if (Limbo.getInstance().getServerProperties().handshakeVerboseEnabled()) { if (Limbo.getInstance().getServerProperties().handshakeVerboseEnabled()) {
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Handshake Status has pinged"); Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Handshake Status has pinged");
} }
ServerProperties p = Limbo.getInstance().getServerProperties(); ServerProperties p = Limbo.getInstance().getServerProperties();
StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), p.getMotd(), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null))); StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), p.getMotd(), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null)));
PacketStatusOutResponse response = new PacketStatusOutResponse(Limbo.getInstance().buildServerListResponseJson(event.getVersion(), event.getProtocol(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline(), event.getFavicon())); PacketStatusOutResponse response = new PacketStatusOutResponse(Limbo.getInstance().buildServerListResponseJson(event.getVersion(), event.getProtocol(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline(), event.getFavicon()));
sendPacket(response); sendPacket(response);
} else if (packetIn instanceof PacketStatusInPing) { } else if (packetIn instanceof PacketStatusInPing) {
PacketStatusInPing ping = (PacketStatusInPing) packetIn; PacketStatusInPing ping = (PacketStatusInPing) packetIn;
PacketStatusOutPong pong = new PacketStatusOutPong(ping.getPayload()); PacketStatusOutPong pong = new PacketStatusOutPong(ping.getPayload());
sendPacket(pong); sendPacket(pong);
break; break;
} }
} }
break; break;
case LOGIN: case LOGIN:
state = ClientState.LOGIN; state = ClientState.LOGIN;
if (isBungeecord || isBungeeGuard) { if (isBungeecord || isBungeeGuard) {
try { try {
String[] data = bungeeForwarding.split("\\x00"); String[] data = bungeeForwarding.split("\\x00");
//String host = data[0]; //String host = data[0];
String ip = data[1]; String ip = data[1];
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")); 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); inetAddress = InetAddress.getByName(ip);
boolean bungeeGuardFound = false; boolean bungeeGuardFound = false;
if (data.length > 3) { if (data.length > 3) {
JSONArray skinJson = (JSONArray) new JSONParser().parse(data[3]); JSONArray skinJson = (JSONArray) new JSONParser().parse(data[3]);
for (Object obj : skinJson) { for (Object obj : skinJson) {
JSONObject property = (JSONObject) obj; JSONObject property = (JSONObject) obj;
if (property.get("name").toString().equals("textures")) { if (property.get("name").toString().equals("textures")) {
String skin = property.get("value").toString(); String skin = property.get("value").toString();
String signature = property.get("signature").toString(); String signature = property.get("signature").toString();
forwardedSkin = new SkinResponse(skin, signature); forwardedSkin = new SkinResponse(skin, signature);
} else if (isBungeeGuard && property.get("name").toString().equals("bungeeguard-token")) { } else if (isBungeeGuard && property.get("name").toString().equals("bungeeguard-token")) {
String token = property.get("value").toString(); String token = property.get("value").toString();
bungeeGuardFound = Limbo.getInstance().getServerProperties().getForwardingSecrets().contains(token); bungeeGuardFound = Limbo.getInstance().getServerProperties().getForwardingSecrets().contains(token);
} }
} }
} }
if (isBungeeGuard && !bungeeGuardFound) { if (isBungeeGuard && !bungeeGuardFound) {
disconnectDuringLogin(TextComponent.fromLegacyText("Invalid information forwarding")); disconnectDuringLogin(TextComponent.fromLegacyText("Invalid information forwarding"));
break; break;
} }
} catch (Exception e) { } 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!"); 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!")}); disconnectDuringLogin(new BaseComponent[] {new TextComponent(ChatColor.RED + "Please connect from the proxy!")});
} }
} }
int messageId = this.random.nextInt(); int messageId = this.random.nextInt();
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
PacketIn packetIn = channel.readPacket(); PacketIn packetIn = channel.readPacket();
if (packetIn instanceof PacketLoginInLoginStart) { if (packetIn instanceof PacketLoginInLoginStart) {
PacketLoginInLoginStart start = (PacketLoginInLoginStart) packetIn; PacketLoginInLoginStart start = (PacketLoginInLoginStart) packetIn;
String username = start.getUsername(); String username = start.getUsername();
if (Limbo.getInstance().getServerProperties().isVelocityModern()) { if (Limbo.getInstance().getServerProperties().isVelocityModern()) {
PacketLoginOutPluginMessaging loginPluginRequest = new PacketLoginOutPluginMessaging(messageId, ForwardingUtils.VELOCITY_FORWARDING_CHANNEL); PacketLoginOutPluginMessaging loginPluginRequest = new PacketLoginOutPluginMessaging(messageId, ForwardingUtils.VELOCITY_FORWARDING_CHANNEL);
sendPacket(loginPluginRequest); sendPacket(loginPluginRequest);
continue; continue;
} }
UUID uuid = isBungeecord || isBungeeGuard ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); UUID uuid = isBungeecord || isBungeeGuard ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, username); PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, username);
sendPacket(success); sendPacket(success);
state = ClientState.PLAY; state = ClientState.PLAY;
player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager()); player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player); Limbo.getInstance().addPlayer(player);
break; break;
} else if (packetIn instanceof PacketLoginInPluginMessaging) { } else if (packetIn instanceof PacketLoginInPluginMessaging) {
PacketLoginInPluginMessaging response = (PacketLoginInPluginMessaging) packetIn; PacketLoginInPluginMessaging response = (PacketLoginInPluginMessaging) packetIn;
if (response.getMessageId() != messageId) { if (response.getMessageId() != messageId) {
disconnectDuringLogin(TextComponent.fromLegacyText("Internal error, messageId did not match")); disconnectDuringLogin(TextComponent.fromLegacyText("Internal error, messageId did not match"));
break; break;
} }
if (!response.getData().isPresent()) { if (!response.getData().isPresent()) {
disconnectDuringLogin(TextComponent.fromLegacyText("Unknown login plugin response packet!")); disconnectDuringLogin(TextComponent.fromLegacyText("Unknown login plugin response packet!"));
break; break;
} }
byte[] responseData = response.getData().get(); byte[] responseData = response.getData().get();
if (!ForwardingUtils.validateVelocityModernResponse(responseData)) { if (!ForwardingUtils.validateVelocityModernResponse(responseData)) {
disconnectDuringLogin(TextComponent.fromLegacyText("Invalid playerinfo forwarding!")); disconnectDuringLogin(TextComponent.fromLegacyText("Invalid playerinfo forwarding!"));
break; break;
} }
ForwardingUtils.VelocityModernForwardingData data = ForwardingUtils.getVelocityDataFrom(responseData); ForwardingUtils.VelocityModernForwardingData data = ForwardingUtils.getVelocityDataFrom(responseData);
inetAddress = InetAddress.getByName(data.getIpAddress()); inetAddress = InetAddress.getByName(data.getIpAddress());
forwardedSkin = data.getSkinResponse(); forwardedSkin = data.getSkinResponse();
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(data.getUuid(), data.getUsername()); PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(data.getUuid(), data.getUsername());
sendPacket(success); sendPacket(success);
state = ClientState.PLAY; state = ClientState.PLAY;
player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager()); 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)); player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player); Limbo.getInstance().addPlayer(player);
break; break;
} }
} }
PlayerLoginEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerLoginEvent(this, false)); PlayerLoginEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerLoginEvent(this, false));
if (event.isCancelled()) { if (event.isCancelled()) {
disconnectDuringLogin(event.getCancelReason()); disconnectDuringLogin(event.getCancelReason());
} }
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
channel.close(); channel.close();
client_socket.close(); client_socket.close();
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
} }
if (state == ClientState.PLAY) { if (state == ClientState.PLAY) {
TimeUnit.MILLISECONDS.sleep(500); TimeUnit.MILLISECONDS.sleep(500);
ServerProperties properties = Limbo.getInstance().getServerProperties(); ServerProperties properties = Limbo.getInstance().getServerProperties();
Location worldSpawn = properties.getWorldSpawn(); Location worldSpawn = properties.getWorldSpawn();
PlayerJoinEvent joinEvent = Limbo.getInstance().getEventsManager().callEvent(new PlayerJoinEvent(player, worldSpawn)); PlayerJoinEvent joinEvent = Limbo.getInstance().getEventsManager().callEvent(new PlayerJoinEvent(player, worldSpawn));
worldSpawn = joinEvent.getSpawnLocation(); worldSpawn = joinEvent.getSpawnLocation();
World world = worldSpawn.getWorld(); World world = worldSpawn.getWorld();
PacketPlayOutLogin join = new PacketPlayOutLogin(player.getEntityId(), false, properties.getDefaultGamemode(), Limbo.getInstance().getWorlds(), Limbo.getInstance().getDimensionRegistry().getCodec(), world, 0, (byte) properties.getMaxPlayers(), 8, 8, properties.isReducedDebugInfo(), true, false, true); PacketPlayOutLogin join = new PacketPlayOutLogin(player.getEntityId(), false, properties.getDefaultGamemode(), Limbo.getInstance().getWorlds(), Limbo.getInstance().getDimensionRegistry().getCodec(), world, 0, (byte) properties.getMaxPlayers(), 8, 8, properties.isReducedDebugInfo(), true, false, true);
sendPacket(join); sendPacket(join);
Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, properties.getDefaultGamemode()); Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, properties.getDefaultGamemode());
SkinResponse skinresponce = (isVelocityModern || isBungeeGuard || isBungeecord) && forwardedSkin != null ? forwardedSkin : 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; 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())); 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); sendPacket(info);
Set<PlayerAbilityFlags> flags = new HashSet<>(); Set<PlayerAbilityFlags> flags = new HashSet<>();
if (properties.isAllowFlight()) { if (properties.isAllowFlight()) {
flags.add(PlayerAbilityFlags.FLY); flags.add(PlayerAbilityFlags.FLY);
} }
if (player.getGamemode().equals(GameMode.CREATIVE)) { if (player.getGamemode().equals(GameMode.CREATIVE)) {
flags.add(PlayerAbilityFlags.CREATIVE); flags.add(PlayerAbilityFlags.CREATIVE);
} }
PacketPlayOutPlayerAbilities abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, flags.toArray(new PlayerAbilityFlags[flags.size()])); PacketPlayOutPlayerAbilities abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, flags.toArray(new PlayerAbilityFlags[flags.size()]));
sendPacket(abilities); sendPacket(abilities);
String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName() + "(" + player.getUniqueId() + ")"; String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName() + "(" + player.getUniqueId() + ")";
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!"); Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!");
PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player); PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player);
if (declare != null) { if (declare != null) {
sendPacket(declare); sendPacket(declare);
} }
player.playerInteractManager.update(); player.playerInteractManager.update();
PacketPlayOutSpawnPosition spawnPos = new PacketPlayOutSpawnPosition(BlockPosition.from(worldSpawn), worldSpawn.getPitch()); PacketPlayOutSpawnPosition spawnPos = new PacketPlayOutSpawnPosition(BlockPosition.from(worldSpawn), worldSpawn.getPitch());
sendPacket(spawnPos); sendPacket(spawnPos);
PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch(), 1, false); PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch(), 1, false);
Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, new Location(world, worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch())); Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, new Location(world, worldSpawn.getX(), worldSpawn.getY(), worldSpawn.getZ(), worldSpawn.getYaw(), worldSpawn.getPitch()));
sendPacket(positionLook); sendPacket(positionLook);
player.getDataWatcher().update(); player.getDataWatcher().update();
PacketPlayOutEntityMetadata show = new PacketPlayOutEntityMetadata(player, false, Player.class.getDeclaredField("skinLayers")); PacketPlayOutEntityMetadata show = new PacketPlayOutEntityMetadata(player, false, Player.class.getDeclaredField("skinLayers"));
sendPacket(show); sendPacket(show);
if (properties.isAllowFlight()) { if (properties.isAllowFlight()) {
PacketPlayOutGameState state = new PacketPlayOutGameState(3, player.getGamemode().getId()); PacketPlayOutGameState state = new PacketPlayOutGameState(3, player.getGamemode().getId());
sendPacket(state); sendPacket(state);
} }
// RESOURCEPACK CODE CONRIBUTED BY GAMERDUCK123 // RESOURCEPACK CODE CONRIBUTED BY GAMERDUCK123
if (!properties.getResourcePackLink().equalsIgnoreCase("")) { if (!properties.getResourcePackLink().equalsIgnoreCase("")) {
if (!properties.getResourcePackSHA1().equalsIgnoreCase("")) { if (!properties.getResourcePackSHA1().equalsIgnoreCase("")) {
//SEND RESOURCEPACK //SEND RESOURCEPACK
player.setResourcePack(properties.getResourcePackLink(), properties.getResourcePackSHA1(), properties.getResourcePackRequired(), properties.getResourcePackPrompt()); player.setResourcePack(properties.getResourcePackLink(), properties.getResourcePackSHA1(), properties.getResourcePackRequired(), properties.getResourcePackPrompt());
} else { } else {
//NO SHA //NO SHA
Limbo.getInstance().getConsole().sendMessage("ResourcePacks require SHA1s"); Limbo.getInstance().getConsole().sendMessage("ResourcePacks require SHA1s");
} }
} else { } else {
//RESOURCEPACK NOT ENABLED //RESOURCEPACK NOT ENABLED
} }
// PLAYER LIST HEADER AND FOOTER CODE CONRIBUTED BY GAMERDUCK123 // PLAYER LIST HEADER AND FOOTER CODE CONRIBUTED BY GAMERDUCK123
player.sendPlayerListHeaderAndFooter(properties.getTabHeader(), properties.getTabFooter()); player.sendPlayerListHeaderAndFooter(properties.getTabHeader(), properties.getTabFooter());
ready = true; ready = true;
keepAliveTask = new TimerTask() { keepAliveTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
if (ready) { if (ready) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - getLastPacketTimestamp() > 15000) { if (now - getLastPacketTimestamp() > 15000) {
PacketPlayOutKeepAlive keepAlivePacket = new PacketPlayOutKeepAlive(now); PacketPlayOutKeepAlive keepAlivePacket = new PacketPlayOutKeepAlive(now);
try { try {
sendPacket(keepAlivePacket); sendPacket(keepAlivePacket);
setLastKeepAlivePayLoad(now); setLastKeepAlivePayLoad(now);
} catch (Exception e) {} } catch (Exception e) {
} }
} else { }
this.cancel(); } else {
} this.cancel();
} }
}; }
new Timer().schedule(keepAliveTask, 5000, 10000); };
new Timer().schedule(keepAliveTask, 5000, 10000);
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
try { try {
CheckedBiConsumer<PlayerMoveEvent, Location, IOException> processMoveEvent = (event, originalTo) -> { CheckedBiConsumer<PlayerMoveEvent, Location, IOException> processMoveEvent = (event, originalTo) -> {
if (event.isCancelled()) { if (event.isCancelled()) {
Location returnTo = event.getFrom(); Location returnTo = event.getFrom();
PacketPlayOutPositionAndLook cancel = new PacketPlayOutPositionAndLook(returnTo.getX(), returnTo.getY(), returnTo.getZ(), returnTo.getYaw(), returnTo.getPitch(), 1, false); PacketPlayOutPositionAndLook cancel = new PacketPlayOutPositionAndLook(returnTo.getX(), returnTo.getY(), returnTo.getZ(), returnTo.getYaw(), returnTo.getPitch(), 1, false);
sendPacket(cancel); sendPacket(cancel);
} else { } else {
Location to = event.getTo(); Location to = event.getTo();
Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, to); Limbo.getInstance().getUnsafe().setPlayerLocationSilently(player, to);
// If an event handler used setTo, let's make sure we tell the player about it. // If an event handler used setTo, let's make sure we tell the player about it.
if (!originalTo.equals(to)) { if (!originalTo.equals(to)) {
PacketPlayOutPositionAndLook pos = new PacketPlayOutPositionAndLook(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), 1, false); PacketPlayOutPositionAndLook pos = new PacketPlayOutPositionAndLook(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch(), 1, false);
sendPacket(pos); sendPacket(pos);
} }
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) player.getLocation().getX() >> 4, (int) player.getLocation().getZ() >> 4); PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) player.getLocation().getX() >> 4, (int) player.getLocation().getZ() >> 4);
sendPacket(response); sendPacket(response);
} }
}; };
PacketIn packetIn = channel.readPacket(); PacketIn packetIn = channel.readPacket();
if (packetIn instanceof PacketPlayInPositionAndLook) { if (packetIn instanceof PacketPlayInPositionAndLook) {
PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn; PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn;
Location from = player.getLocation(); Location from = player.getLocation();
Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), pos.getYaw(), pos.getPitch()); Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), pos.getYaw(), pos.getPitch());
if (!from.equals(to)) { if (!from.equals(to)) {
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to)); PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
processMoveEvent.consume(event, to); processMoveEvent.consume(event, to);
} }
} else if (packetIn instanceof PacketPlayInPosition) { } else if (packetIn instanceof PacketPlayInPosition) {
PacketPlayInPosition pos = (PacketPlayInPosition) packetIn; PacketPlayInPosition pos = (PacketPlayInPosition) packetIn;
Location from = player.getLocation(); Location from = player.getLocation();
Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch()); Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch());
if (!from.equals(to)) { if (!from.equals(to)) {
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to)); PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
processMoveEvent.consume(event, to); processMoveEvent.consume(event, to);
} }
} else if (packetIn instanceof PacketPlayInRotation) { } else if (packetIn instanceof PacketPlayInRotation) {
PacketPlayInRotation pos = (PacketPlayInRotation) packetIn; PacketPlayInRotation pos = (PacketPlayInRotation) packetIn;
Location from = player.getLocation(); Location from = player.getLocation();
Location to = new Location(player.getWorld(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), pos.getYaw(), pos.getPitch()); Location to = new Location(player.getWorld(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), pos.getYaw(), pos.getPitch());
if (!from.equals(to)) { if (!from.equals(to)) {
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to)); PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
processMoveEvent.consume(event, to); processMoveEvent.consume(event, to);
} }
} else if (packetIn instanceof PacketPlayInKeepAlive) { } else if (packetIn instanceof PacketPlayInKeepAlive) {
PacketPlayInKeepAlive alive = (PacketPlayInKeepAlive) packetIn; PacketPlayInKeepAlive alive = (PacketPlayInKeepAlive) packetIn;
if (alive.getPayload() != getLastKeepAlivePayLoad()) { if (alive.getPayload() != getLastKeepAlivePayLoad()) {
Limbo.getInstance().getConsole().sendMessage("Incorrect Payload received in KeepAlive packet for player " + player.getName()); Limbo.getInstance().getConsole().sendMessage("Incorrect Payload received in KeepAlive packet for player " + player.getName());
break; break;
} }
} else if (packetIn instanceof PacketPlayInTabComplete) { } else if (packetIn instanceof PacketPlayInTabComplete) {
PacketPlayInTabComplete request = (PacketPlayInTabComplete) packetIn; PacketPlayInTabComplete request = (PacketPlayInTabComplete) packetIn;
String[] command = CustomStringUtils.splitStringToArgs(request.getText().substring(1)); String[] command = CustomStringUtils.splitStringToArgs(request.getText().substring(1));
List<TabCompleteMatches> matches = new ArrayList<TabCompleteMatches>(Limbo.getInstance().getPluginManager().getTabOptions(player, command).stream().map(each -> new TabCompleteMatches(each)).collect(Collectors.toList())); List<TabCompleteMatches> matches = new ArrayList<TabCompleteMatches>(Limbo.getInstance().getPluginManager().getTabOptions(player, command).stream().map(each -> new TabCompleteMatches(each)).collect(Collectors.toList()));
int start = CustomStringUtils.getIndexOfArg(request.getText(), command.length - 1) + 1; int start = CustomStringUtils.getIndexOfArg(request.getText(), command.length - 1) + 1;
int length = command[command.length - 1].length(); int length = command[command.length - 1].length();
PacketPlayOutTabComplete response = new PacketPlayOutTabComplete(request.getId(), start, length, matches.toArray(new TabCompleteMatches[matches.size()])); PacketPlayOutTabComplete response = new PacketPlayOutTabComplete(request.getId(), start, length, matches.toArray(new TabCompleteMatches[matches.size()]));
sendPacket(response); sendPacket(response);
} else if (packetIn instanceof PacketPlayInChat) { } else if (packetIn instanceof PacketPlayInChat) {
PacketPlayInChat chat = (PacketPlayInChat) packetIn; PacketPlayInChat chat = (PacketPlayInChat) packetIn;
if (chat.getMessage().startsWith("/")) { if (chat.getMessage().startsWith("/")) {
Limbo.getInstance().dispatchCommand(player, chat.getMessage()); Limbo.getInstance().dispatchCommand(player, chat.getMessage());
} else { } else {
player.chat(chat.getMessage(), true); player.chat(chat.getMessage(), true);
} }
} else if (packetIn instanceof PacketPlayInHeldItemChange) { } else if (packetIn instanceof PacketPlayInHeldItemChange) {
PacketPlayInHeldItemChange change = (PacketPlayInHeldItemChange) packetIn; PacketPlayInHeldItemChange change = (PacketPlayInHeldItemChange) packetIn;
PlayerSelectedSlotChangeEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerSelectedSlotChangeEvent(player, (byte) change.getSlot())); PlayerSelectedSlotChangeEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerSelectedSlotChangeEvent(player, (byte) change.getSlot()));
if (event.isCancelled()) { if (event.isCancelled()) {
PacketPlayOutHeldItemChange cancelPacket = new PacketPlayOutHeldItemChange(player.getSelectedSlot()); PacketPlayOutHeldItemChange cancelPacket = new PacketPlayOutHeldItemChange(player.getSelectedSlot());
sendPacket(cancelPacket); sendPacket(cancelPacket);
} else if (change.getSlot() != event.getSlot()) { } else if (change.getSlot() != event.getSlot()) {
PacketPlayOutHeldItemChange changePacket = new PacketPlayOutHeldItemChange(event.getSlot()); PacketPlayOutHeldItemChange changePacket = new PacketPlayOutHeldItemChange(event.getSlot());
sendPacket(changePacket); sendPacket(changePacket);
Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot()); Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot());
} else { } else {
Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot()); Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot());
} }
} else if (packetIn instanceof PacketPlayInResourcePackStatus) { } else if (packetIn instanceof PacketPlayInResourcePackStatus) {
PacketPlayInResourcePackStatus rpcheck = (PacketPlayInResourcePackStatus) packetIn; PacketPlayInResourcePackStatus rpcheck = (PacketPlayInResourcePackStatus) packetIn;
// Pass on result to the events // Pass on result to the events
Limbo.getInstance().getEventsManager().callEvent(new PlayerResourcePackStatusEvent(player, rpcheck.getLoadedValue())); Limbo.getInstance().getEventsManager().callEvent(new PlayerResourcePackStatusEvent(player, rpcheck.getLoadedValue()));
if (rpcheck.getLoadedValue().equals(EnumResourcePackStatus.DECLINED) && properties.getResourcePackRequired()) { if (rpcheck.getLoadedValue().equals(EnumResourcePackStatus.DECLINED) && properties.getResourcePackRequired()) {
player.disconnect(new TranslatableComponent("multiplayer.requiredTexturePrompt.disconnect")); player.disconnect(new TranslatableComponent("multiplayer.requiredTexturePrompt.disconnect"));
} }
} }
} catch (Exception e) { } catch (Exception e) {
break; break;
} }
} }
Limbo.getInstance().getEventsManager().callEvent(new PlayerQuitEvent(player)); Limbo.getInstance().getEventsManager().callEvent(new PlayerQuitEvent(player));
str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName(); str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName();
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had disconnected!"); Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had disconnected!");
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
}
try { try {
channel.close(); channel.close();
client_socket.close(); client_socket.close();
} catch (Exception ignored) {} } catch (Exception ignored) {
state = ClientState.DISCONNECTED; }
state = ClientState.DISCONNECTED;
if (player != null) { if (player != null) {
Limbo.getInstance().removePlayer(player); Limbo.getInstance().removePlayer(player);
} }
Limbo.getInstance().getServerConnection().getClients().remove(this); Limbo.getInstance().getServerConnection().getClients().remove(this);
running = false; running = false;
} }
public static enum ClientState {
LEGACY,
HANDSHAKE,
STATUS,
LOGIN,
PLAY,
DISCONNECTED;
}
} }