Made packet intercepting possible

This commit is contained in:
LOOHP 2022-02-10 01:54:13 +00:00
parent 00600647ae
commit 4353859951
11 changed files with 346 additions and 109 deletions

View File

@ -5,7 +5,7 @@
<groupId>com.loohp</groupId> <groupId>com.loohp</groupId>
<artifactId>Limbo</artifactId> <artifactId>Limbo</artifactId>
<name>Limbo</name> <name>Limbo</name>
<version>0.6.10-ALPHA</version> <version>0.6.11-ALPHA</version>
<description>Standalone Limbo Minecraft Server.</description> <description>Standalone Limbo Minecraft Server.</description>
<url>https://github.com/LOOHP/Limbo</url> <url>https://github.com/LOOHP/Limbo</url>
@ -230,7 +230,7 @@
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId> <artifactId>bungeecord-chat</artifactId>
<version>1.17-R0.1-SNAPSHOT</version> <version>1.18-R0.1-SNAPSHOT</version>
<type>jar</type> <type>jar</type>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>

View File

@ -1,5 +1,155 @@
package com.loohp.limbo.network; package com.loohp.limbo.network;
public class Channel { import com.loohp.limbo.network.protocol.packets.Packet;
import com.loohp.limbo.network.protocol.packets.PacketIn;
import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.NamespacedKey;
import com.loohp.limbo.utils.Pair;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
public class Channel implements AutoCloseable {
private final ClientConnection clientConnection;
private final List<Pair<NamespacedKey, ChannelPacketHandler>> handlers;
private final AtomicBoolean valid;
protected DataOutputStream output;
protected DataInputStream input;
public Channel(ClientConnection clientConnection, DataOutputStream output, DataInputStream input) {
this.clientConnection = clientConnection;
this.output = output;
this.input = input;
this.handlers = new CopyOnWriteArrayList<>();
this.valid = new AtomicBoolean(true);
}
private void ensureOpen() {
if (!valid.get()) {
throw new IllegalStateException("Channel already closed!");
}
}
public void addBefore(NamespacedKey key, ChannelPacketHandler handler) {
handlers.add(0, new Pair<>(key, handler));
}
public void addAfter(NamespacedKey key, ChannelPacketHandler handler) {
handlers.add(new Pair<>(key, handler));
}
public void remove(NamespacedKey key) {
handlers.removeIf(each -> each.getFirst().equals(key));
}
protected PacketIn readPacket() throws Exception {
return readPacket(-1);
}
protected PacketIn readPacket(int size) throws Exception {
PacketIn packet = null;
do {
ensureOpen();
size = size < 0 ? DataTypeIO.readVarInt(input) : size;
int packetId = DataTypeIO.readVarInt(input);
Class<? extends PacketIn> packetType;
switch (clientConnection.getClientState()) {
case HANDSHAKE:
packetType = Packet.getHandshakeIn().get(packetId);
break;
case STATUS:
packetType = Packet.getStatusIn().get(packetId);
break;
case LOGIN:
packetType = Packet.getLoginIn().get(packetId);
break;
case PLAY:
packetType = Packet.getPlayIn().get(packetId);
break;
default:
throw new IllegalStateException("Illegal ClientState!");
}
if (packetType == null) {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} else {
Constructor<?>[] constructors = packetType.getConstructors();
Constructor<?> constructor = Stream.of(constructors).filter(each -> each.getParameterCount() > 0 && each.getParameterTypes()[0].equals(DataInputStream.class)).findFirst().orElse(null);
if (constructor == null) {
throw new NoSuchMethodException(packetType + " has no valid constructors!");
} else if (constructor.getParameterCount() == 1) {
packet = (PacketIn) constructor.newInstance(input);
} else if (constructor.getParameterCount() == 3) {
packet = (PacketIn) constructor.newInstance(input, size, packetId);
} else {
throw new NoSuchMethodException(packetType + " has no valid constructors!");
}
ChannelPacketRead read = new ChannelPacketRead(packetId, packet);
for (Pair<NamespacedKey, ChannelPacketHandler> pair : handlers) {
read = pair.getSecond().read(read);
if (read == null) {
packet = null;
break;
}
packet = read.getPacket();
if (!packetType.isInstance(packet)) {
throw new IllegalStateException("Packet Handler \"" + pair.getFirst() + "\" changed the packet type illegally!");
}
}
}
size = -1;
} while (packet == null);
return packet;
}
protected boolean writePacket(PacketOut packet) throws IOException {
ensureOpen();
int packetId;
switch (clientConnection.getClientState()) {
case STATUS:
packetId = Packet.getStatusOut().get(packet.getClass());
break;
case LOGIN:
packetId = Packet.getLoginOut().get(packet.getClass());
break;
case PLAY:
packetId = Packet.getPlayOut().get(packet.getClass());
break;
default:
throw new IllegalStateException("Illegal ClientState!");
}
Class<? extends PacketOut> packetType = packet.getClass();
ChannelPacketWrite write = new ChannelPacketWrite(packet);
for (Pair<NamespacedKey, ChannelPacketHandler> pair : handlers) {
write = pair.getSecond().write(write);
if (write == null) {
return false;
}
if (!packetType.isInstance(write.getPacket())) {
throw new IllegalStateException("Packet Handler \"" + pair.getFirst() + "\" changed the packet type illegally!");
}
}
packet = write.getPacket();
byte[] packetByte = packet.serializePacket();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
output.flush();
return true;
}
@Override
public synchronized void close() throws Exception {
if (valid.compareAndSet(false, true)) {
input.close();
output.close();
}
}
} }

View File

@ -0,0 +1,13 @@
package com.loohp.limbo.network;
public abstract class ChannelPacketHandler {
public ChannelPacketRead read(ChannelPacketRead read) {
return read;
}
public ChannelPacketWrite write(ChannelPacketWrite write) {
return write;
}
}

View File

@ -0,0 +1,31 @@
package com.loohp.limbo.network;
import com.loohp.limbo.network.protocol.packets.PacketIn;
public final class ChannelPacketRead {
private int packetId;
private PacketIn packet;
protected ChannelPacketRead(int packetId, PacketIn packet) {
this.packetId = packetId;
this.packet = packet;
}
public int getPacketId() {
return packetId;
}
public void setPacketId(int packetId) {
this.packetId = packetId;
}
public PacketIn getPacket() {
return packet;
}
public void setPacket(PacketIn packet) {
this.packet = packet;
}
}

View File

@ -0,0 +1,21 @@
package com.loohp.limbo.network;
import com.loohp.limbo.network.protocol.packets.PacketOut;
public final class ChannelPacketWrite {
private PacketOut packet;
protected ChannelPacketWrite(PacketOut packet) {
this.packet = packet;
}
public PacketOut getPacket() {
return packet;
}
public void setPacket(PacketOut packet) {
this.packet = packet;
}
}

View File

@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.loohp.limbo.network.protocol.packets.PacketIn;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
@ -114,8 +115,7 @@ public class ClientConnection extends Thread {
private AtomicLong lastPacketTimestamp; private AtomicLong lastPacketTimestamp;
private AtomicLong lastKeepAlivePayLoad; private AtomicLong lastKeepAlivePayLoad;
private DataOutputStream output; protected Channel channel;
private DataInputStream input;
private InetAddress inetAddress; private InetAddress inetAddress;
@ -126,6 +126,7 @@ public class ClientConnection extends Thread {
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.running = false; this.running = false;
this.ready = false; this.ready = false;
} }
@ -166,6 +167,10 @@ public class ClientConnection extends Thread {
return client_socket; return client_socket;
} }
public Channel getChannel() {
return channel;
}
public boolean isRunning() { public boolean isRunning() {
return running; return running;
} }
@ -175,11 +180,9 @@ public class ClientConnection extends Thread {
} }
public synchronized void sendPacket(PacketOut packet) throws IOException { public synchronized void sendPacket(PacketOut packet) throws IOException {
byte[] packetByte = packet.serializePacket(); if (channel.writePacket(packet)) {
DataTypeIO.writeVarInt(output, packetByte.length); setLastPacketTimestamp(System.currentTimeMillis());
output.write(packetByte); }
output.flush();
setLastPacketTimestamp(System.currentTimeMillis());
} }
public void disconnect(BaseComponent[] reason) { public void disconnect(BaseComponent[] reason) {
@ -190,10 +193,10 @@ public class ClientConnection extends Thread {
try { try {
PacketPlayOutDisconnect packet = new PacketPlayOutDisconnect(reason); PacketPlayOutDisconnect packet = new PacketPlayOutDisconnect(reason);
sendPacket(packet); sendPacket(packet);
} catch (IOException e) {} } catch (IOException ignored) {}
try { try {
client_socket.close(); client_socket.close();
} catch (IOException e) {} } catch (IOException ignored) {}
} }
private void disconnectDuringLogin(BaseComponent[] reason) { private void disconnectDuringLogin(BaseComponent[] reason) {
@ -204,10 +207,10 @@ public class ClientConnection extends Thread {
try { try {
PacketLoginOutDisconnect packet = new PacketLoginOutDisconnect(reason); PacketLoginOutDisconnect packet = new PacketLoginOutDisconnect(reason);
sendPacket(packet); sendPacket(packet);
} catch (IOException e) {} } catch (IOException ignored) {}
try { try {
client_socket.close(); client_socket.close();
} catch (IOException e) {} } catch (IOException ignored) {}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@ -217,31 +220,29 @@ public class ClientConnection extends Thread {
state = ClientState.HANDSHAKE; state = ClientState.HANDSHAKE;
try { try {
client_socket.setKeepAlive(true); client_socket.setKeepAlive(true);
input = new DataInputStream(client_socket.getInputStream()); channel.input = new DataInputStream(client_socket.getInputStream());
output = new DataOutputStream(client_socket.getOutputStream()); channel.output = new DataOutputStream(client_socket.getOutputStream());
int handShakeSize = DataTypeIO.readVarInt(input); int handShakeSize = DataTypeIO.readVarInt(channel.input);
//legacy ping //legacy ping
if (handShakeSize == 0xFE) { if (handShakeSize == 0xFE) {
state = ClientState.LEGACY; state = ClientState.LEGACY;
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);
output.writeShort(response.length()); channel.output.writeShort(response.length());
output.write(bytes); channel.output.write(bytes);
channel.close();
client_socket.close(); client_socket.close();
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
} }
@SuppressWarnings("unused") PacketHandshakingIn handshake = (PacketHandshakingIn) channel.readPacket(handShakeSize);
int handShakeId = DataTypeIO.readVarInt(input);
PacketHandshakingIn handshake = new PacketHandshakingIn(input);
boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord(); boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord();
boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard(); boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard();
@ -255,24 +256,20 @@ public class ClientConnection extends Thread {
case STATUS: case STATUS:
state = ClientState.STATUS; state = ClientState.STATUS;
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
DataTypeIO.readVarInt(input); PacketIn packetIn = channel.readPacket();
int packetId = DataTypeIO.readVarInt(input); if (packetIn instanceof PacketStatusInRequest) {
Class<? extends Packet> packetType = Packet.getStatusIn().get(packetId);
if (packetType == null) {
//do nothing
} else if (packetType.equals(PacketStatusInRequest.class)) {
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 packet = 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(packet); sendPacket(response);
} else if (packetType.equals(PacketStatusInPing.class)) { } else if (packetIn instanceof PacketStatusInPing) {
PacketStatusInPing ping = new PacketStatusInPing(input); PacketStatusInPing ping = (PacketStatusInPing) packetIn;
PacketStatusOutPong packet = new PacketStatusOutPong(ping.getPayload()); PacketStatusOutPong pong = new PacketStatusOutPong(ping.getPayload());
sendPacket(packet); sendPacket(pong);
break; break;
} }
} }
@ -318,14 +315,9 @@ public class ClientConnection extends Thread {
int messageId = this.random.nextInt(); int messageId = this.random.nextInt();
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
int size = DataTypeIO.readVarInt(input); PacketIn packetIn = channel.readPacket();
int packetId = DataTypeIO.readVarInt(input); if (packetIn instanceof PacketLoginInLoginStart) {
Class<? extends Packet> packetType = Packet.getLoginIn().get(packetId); PacketLoginInLoginStart start = (PacketLoginInLoginStart) packetIn;
if (packetType == null) {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} else if (packetType.equals(PacketLoginInLoginStart.class)) {
PacketLoginInLoginStart start = new PacketLoginInLoginStart(input);
String username = start.getUsername(); String username = start.getUsername();
if (Limbo.getInstance().getServerProperties().isVelocityModern()) { if (Limbo.getInstance().getServerProperties().isVelocityModern()) {
@ -346,8 +338,8 @@ public class ClientConnection extends Thread {
Limbo.getInstance().addPlayer(player); Limbo.getInstance().addPlayer(player);
break; break;
} else if (packetType.equals(PacketLoginInPluginMessaging.class)) { } else if (packetIn instanceof PacketLoginInPluginMessaging) {
PacketLoginInPluginMessaging response = new PacketLoginInPluginMessaging(input, size, packetId); 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;
@ -375,8 +367,6 @@ public class ClientConnection extends Thread {
Limbo.getInstance().addPlayer(player); Limbo.getInstance().addPlayer(player);
break; break;
} else {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} }
} }
@ -388,6 +378,7 @@ public class ClientConnection extends Thread {
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
channel.close();
client_socket.close(); client_socket.close();
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
} }
@ -487,10 +478,6 @@ public class ClientConnection extends Thread {
while (client_socket.isConnected()) { while (client_socket.isConnected()) {
try { try {
int size = DataTypeIO.readVarInt(input);
int packetId = DataTypeIO.readVarInt(input);
Class<? extends Packet> packetType = Packet.getPlayIn().get(packetId);
//Limbo.getInstance().getConsole().sendMessage(packetId + " -> " + packetType);
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();
@ -508,10 +495,9 @@ public class ClientConnection extends Thread {
sendPacket(response); sendPacket(response);
} }
}; };
if (packetType == null) { PacketIn packetIn = channel.readPacket();
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId)); if (packetIn instanceof PacketPlayInPositionAndLook) {
} else if (packetType.equals(PacketPlayInPositionAndLook.class)) { PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn;
PacketPlayInPositionAndLook pos = new PacketPlayInPositionAndLook(input);
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());
@ -519,8 +505,8 @@ public class ClientConnection extends Thread {
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 (packetType.equals(PacketPlayInPosition.class)) { } else if (packetIn instanceof PacketPlayInPosition) {
PacketPlayInPosition pos = new PacketPlayInPosition(input); 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());
@ -528,8 +514,8 @@ public class ClientConnection extends Thread {
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 (packetType.equals(PacketPlayInRotation.class)) { } else if (packetIn instanceof PacketPlayInRotation) {
PacketPlayInRotation pos = new PacketPlayInRotation(input); 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());
@ -537,34 +523,32 @@ public class ClientConnection extends Thread {
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 (packetType.equals(PacketPlayInKeepAlive.class)) { } else if (packetIn instanceof PacketPlayInKeepAlive) {
PacketPlayInKeepAlive alive = new PacketPlayInKeepAlive(input); PacketPlayInKeepAlive alive = (PacketPlayInKeepAlive) packetIn;
if (alive.getPayload() != getLastKeepAlivePayLoad()) { if (alive.getPayload() != getLastKeepAlivePayLoad()) {
Limbo.getInstance().getConsole().sendMessage("Incorrect Payload recieved 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 (packetType.equals(PacketPlayInTabComplete.class)) { } else if (packetIn instanceof PacketPlayInTabComplete) {
PacketPlayInTabComplete request = new PacketPlayInTabComplete(input); 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>(); List<TabCompleteMatches> matches = new ArrayList<TabCompleteMatches>(Limbo.getInstance().getPluginManager().getTabOptions(player, command).stream().map(each -> new TabCompleteMatches(each)).collect(Collectors.toList()));
matches.addAll(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 (packetType.equals(PacketPlayInChat.class)) { } else if (packetIn instanceof PacketPlayInChat) {
PacketPlayInChat chat = new PacketPlayInChat(input); 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 (packetType.equals(PacketPlayInHeldItemChange.class)) { } else if (packetIn instanceof PacketPlayInHeldItemChange) {
PacketPlayInHeldItemChange change = new PacketPlayInHeldItemChange(input); 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());
@ -577,17 +561,14 @@ public class ClientConnection extends Thread {
Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot()); Limbo.getInstance().getUnsafe().setSelectedSlotSilently(player, event.getSlot());
} }
} else if (packetType.equals(PacketPlayInResourcePackStatus.class)) { } else if (packetIn instanceof PacketPlayInResourcePackStatus) {
PacketPlayInResourcePackStatus rpcheck = new PacketPlayInResourcePackStatus(input); 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"));
} }
} else {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} }
} catch (Exception e) { } catch (Exception e) {
break; break;
} }
@ -600,11 +581,12 @@ public class ClientConnection extends Thread {
} }
} catch (Exception e) {} } catch (Exception ignored) {}
try { try {
channel.close();
client_socket.close(); client_socket.close();
} catch (IOException e) {} } catch (Exception ignored) {}
state = ClientState.DISCONNECTED; state = ClientState.DISCONNECTED;
if (player != null) { if (player != null) {

View File

@ -4,71 +4,71 @@ import java.util.Map;
public class Packet { public class Packet {
private static Map<Integer, Class<? extends PacketIn>> HandshakeIn; private static Map<Integer, Class<? extends PacketIn>> handshakeIn;
private static Map<Integer, Class<? extends PacketIn>> StatusIn; private static Map<Integer, Class<? extends PacketIn>> statusIn;
private static Map<Class<? extends PacketOut>, Integer> StatusOut; private static Map<Class<? extends PacketOut>, Integer> statusOut;
private static Map<Integer, Class<? extends PacketIn>> LoginIn; private static Map<Integer, Class<? extends PacketIn>> loginIn;
private static Map<Class<? extends PacketOut>, Integer> LoginOut; private static Map<Class<? extends PacketOut>, Integer> loginOut;
private static Map<Integer, Class<? extends PacketIn>> PlayIn; private static Map<Integer, Class<? extends PacketIn>> playIn;
private static Map<Class<? extends PacketOut>, Integer> PlayOut; private static Map<Class<? extends PacketOut>, Integer> playOut;
public static Map<Integer, Class<? extends PacketIn>> getHandshakeIn() { public static Map<Integer, Class<? extends PacketIn>> getHandshakeIn() {
return HandshakeIn; return handshakeIn;
} }
public static void setHandshakeIn(Map<Integer, Class<? extends PacketIn>> handshakeIn) { public static void setHandshakeIn(Map<Integer, Class<? extends PacketIn>> handshakeIn) {
HandshakeIn = handshakeIn; Packet.handshakeIn = handshakeIn;
} }
public static Map<Integer, Class<? extends PacketIn>> getStatusIn() { public static Map<Integer, Class<? extends PacketIn>> getStatusIn() {
return StatusIn; return statusIn;
} }
public static void setStatusIn(Map<Integer, Class<? extends PacketIn>> statusIn) { public static void setStatusIn(Map<Integer, Class<? extends PacketIn>> statusIn) {
StatusIn = statusIn; Packet.statusIn = statusIn;
} }
public static Map<Class<? extends PacketOut>, Integer> getStatusOut() { public static Map<Class<? extends PacketOut>, Integer> getStatusOut() {
return StatusOut; return statusOut;
} }
public static void setStatusOut(Map<Class<? extends PacketOut>, Integer> statusOut) { public static void setStatusOut(Map<Class<? extends PacketOut>, Integer> statusOut) {
StatusOut = statusOut; Packet.statusOut = statusOut;
} }
public static Map<Integer, Class<? extends PacketIn>> getLoginIn() { public static Map<Integer, Class<? extends PacketIn>> getLoginIn() {
return LoginIn; return loginIn;
} }
public static void setLoginIn(Map<Integer, Class<? extends PacketIn>> loginIn) { public static void setLoginIn(Map<Integer, Class<? extends PacketIn>> loginIn) {
LoginIn = loginIn; Packet.loginIn = loginIn;
} }
public static Map<Class<? extends PacketOut>, Integer> getLoginOut() { public static Map<Class<? extends PacketOut>, Integer> getLoginOut() {
return LoginOut; return loginOut;
} }
public static void setLoginOut(Map<Class<? extends PacketOut>, Integer> loginOut) { public static void setLoginOut(Map<Class<? extends PacketOut>, Integer> loginOut) {
LoginOut = loginOut; Packet.loginOut = loginOut;
} }
public static Map<Integer, Class<? extends PacketIn>> getPlayIn() { public static Map<Integer, Class<? extends PacketIn>> getPlayIn() {
return PlayIn; return playIn;
} }
public static void setPlayIn(Map<Integer, Class<? extends PacketIn>> playIn) { public static void setPlayIn(Map<Integer, Class<? extends PacketIn>> playIn) {
PlayIn = playIn; Packet.playIn = playIn;
} }
public static Map<Class<? extends PacketOut>, Integer> getPlayOut() { public static Map<Class<? extends PacketOut>, Integer> getPlayOut() {
return PlayOut; return playOut;
} }
public static void setPlayOut(Map<Class<? extends PacketOut>, Integer> playOut) { public static void setPlayOut(Map<Class<? extends PacketOut>, Integer> playOut) {
PlayOut = playOut; Packet.playOut = playOut;
} }
} }

View File

@ -22,7 +22,7 @@ public class PacketPlayInPluginMessaging extends PacketIn {
channel = new NamespacedKey(rawChannel); channel = new NamespacedKey(rawChannel);
int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8); int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8);
data = new byte[dataLength]; data = new byte[dataLength];
in.read(data); in.readFully(data);
} }
public NamespacedKey getChannel() { public NamespacedKey getChannel() {

View File

@ -52,7 +52,7 @@ public class DataTypeIO {
public static int getStringLength(String string, Charset charset) throws IOException { public static int getStringLength(String string, Charset charset) throws IOException {
byte[] bytes = string.getBytes(charset); byte[] bytes = string.getBytes(charset);
return bytes.length; return getVarIntLength(bytes.length) + bytes.length;
} }
public static void writeString(DataOutputStream out, String string, Charset charset) throws IOException { public static void writeString(DataOutputStream out, String string, Charset charset) throws IOException {

View File

@ -0,0 +1,40 @@
package com.loohp.limbo.utils;
import java.util.Objects;
public class Pair<F, S> {
private final F first;
private final S second;
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}