mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #37 - Touch support without userscript, many other feats
This commit is contained in:
@ -4,14 +4,22 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.ArrayUtils;
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState;
|
||||
import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
|
||||
@ -42,20 +50,40 @@ import net.minecraft.util.IChatComponent;
|
||||
*/
|
||||
public class ConnectionHandshake {
|
||||
|
||||
private static final long baseTimeout = 15000l;
|
||||
private static final long baseTimeout = 10000l;
|
||||
|
||||
private static final int protocolV2 = 2;
|
||||
private static final int protocolV3 = 3;
|
||||
private static final int protocolV4 = 4;
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
public static String pluginVersion = null;
|
||||
public static String pluginBrand = null;
|
||||
public static int protocolVersion = -1;
|
||||
|
||||
public static boolean attemptHandshake(Minecraft mc, GuiConnecting connecting, GuiScreen ret, String password, boolean allowPlaintext) {
|
||||
public static byte[] getSPHandshakeProtocolData() {
|
||||
try {
|
||||
EaglerOutputStream bao = new EaglerOutputStream();
|
||||
DataOutputStream d = new DataOutputStream(bao);
|
||||
d.writeShort(3); // supported eagler protocols count
|
||||
d.writeShort(protocolV2); // client supports v2
|
||||
d.writeShort(protocolV3); // client supports v3
|
||||
d.writeShort(protocolV4); // client supports v4
|
||||
return bao.toByteArray();
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean attemptHandshake(Minecraft mc, IWebSocketClient client, GuiConnecting connecting,
|
||||
GuiScreen ret, String password, boolean allowPlaintext, boolean enableCookies, byte[] cookieData) {
|
||||
try {
|
||||
EaglerProfile.clearServerSkinOverride();
|
||||
PauseMenuCustomizeState.reset();
|
||||
pluginVersion = null;
|
||||
pluginBrand = null;
|
||||
protocolVersion = -1;
|
||||
EaglerOutputStream bao = new EaglerOutputStream();
|
||||
DataOutputStream d = new DataOutputStream(bao);
|
||||
|
||||
@ -63,9 +91,7 @@ public class ConnectionHandshake {
|
||||
|
||||
d.writeByte(2); // legacy protocol version
|
||||
|
||||
d.writeShort(2); // supported eagler protocols count
|
||||
d.writeShort(protocolV2); // client supports v2
|
||||
d.writeShort(protocolV3); // client supports v3
|
||||
d.write(getSPHandshakeProtocolData()); // write supported eagler protocol versions
|
||||
|
||||
d.writeShort(1); // supported game protocols count
|
||||
d.writeShort(47); // client supports 1.8 protocol
|
||||
@ -84,9 +110,9 @@ public class ConnectionHandshake {
|
||||
d.writeByte(username.length());
|
||||
d.writeBytes(username);
|
||||
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
client.send(bao.toByteArray());
|
||||
|
||||
byte[] read = awaitNextPacket(baseTimeout);
|
||||
byte[] read = awaitNextPacket(client, baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for server protocol response!");
|
||||
return false;
|
||||
@ -115,7 +141,7 @@ public class ConnectionHandshake {
|
||||
games.append("mc").append(di.readShort());
|
||||
}
|
||||
|
||||
logger.info("Incompatible client: v2 & mc47");
|
||||
logger.info("Incompatible client: v2/v3/v4 & mc47");
|
||||
logger.info("Server supports: {}", protocols);
|
||||
logger.info("Server supports: {}", games);
|
||||
|
||||
@ -128,11 +154,11 @@ public class ConnectionHandshake {
|
||||
|
||||
return false;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) {
|
||||
int serverVers = di.readShort();
|
||||
protocolVersion = di.readShort();
|
||||
|
||||
if(serverVers != protocolV2 && serverVers != protocolV3) {
|
||||
logger.info("Incompatible server version: {}", serverVers);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(serverVers < protocolV2 ? "Outdated Server" : "Outdated Client")));
|
||||
if(protocolVersion != protocolV2 && protocolVersion != protocolV3 && protocolVersion != protocolV4) {
|
||||
logger.info("Incompatible server version: {}", protocolVersion);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(protocolVersion < protocolV2 ? "Outdated Server" : "Outdated Client")));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -143,7 +169,7 @@ public class ConnectionHandshake {
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info("Server protocol: {}", serverVers);
|
||||
logger.info("Server protocol: {}", protocolVersion);
|
||||
|
||||
int msgLen = di.read();
|
||||
byte[] dat = new byte[msgLen];
|
||||
@ -260,10 +286,19 @@ public class ConnectionHandshake {
|
||||
}else {
|
||||
d.writeByte(0);
|
||||
}
|
||||
if(protocolVersion >= protocolV4) {
|
||||
d.writeBoolean(enableCookies);
|
||||
if(enableCookies && cookieData != null) {
|
||||
d.writeByte(cookieData.length);
|
||||
d.write(cookieData);
|
||||
}else {
|
||||
d.writeByte(0);
|
||||
}
|
||||
}
|
||||
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
client.send(bao.toByteArray());
|
||||
|
||||
read = awaitNextPacket(baseTimeout);
|
||||
read = awaitNextPacket(client, baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for login negotiation response!");
|
||||
return false;
|
||||
@ -280,52 +315,79 @@ public class ConnectionHandshake {
|
||||
|
||||
Minecraft.getMinecraft().getSession().update(serverUsername, new EaglercraftUUID(di.readLong(), di.readLong()));
|
||||
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
|
||||
String profileDataType = "skin_v1";
|
||||
d.writeByte(profileDataType.length());
|
||||
d.writeBytes(profileDataType);
|
||||
byte[] packetSkin = EaglerProfile.getSkinPacket();
|
||||
Map<String,byte[]> profileDataToSend = new HashMap<>();
|
||||
|
||||
if(protocolVersion >= 4) {
|
||||
bao.reset();
|
||||
d.writeLong(EaglercraftVersion.clientBrandUUID.msb);
|
||||
d.writeLong(EaglercraftVersion.clientBrandUUID.lsb);
|
||||
profileDataToSend.put("brand_uuid_v1", bao.toByteArray());
|
||||
}
|
||||
|
||||
byte[] packetSkin = EaglerProfile.getSkinPacket(protocolVersion);
|
||||
if(packetSkin.length > 0xFFFF) {
|
||||
throw new IOException("Skin packet is too long: " + packetSkin.length);
|
||||
}
|
||||
d.writeShort(packetSkin.length);
|
||||
d.write(packetSkin);
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
profileDataToSend.put(protocolVersion >= 4 ? "skin_v2" : "skin_v1", packetSkin);
|
||||
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
|
||||
profileDataType = "cape_v1";
|
||||
d.writeByte(profileDataType.length());
|
||||
d.writeBytes(profileDataType);
|
||||
byte[] packetCape = EaglerProfile.getCapePacket();
|
||||
if(packetCape.length > 0xFFFF) {
|
||||
throw new IOException("Cape packet is too long: " + packetCape.length);
|
||||
}
|
||||
d.writeShort(packetCape.length);
|
||||
d.write(packetCape);
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
profileDataToSend.put("cape_v1", packetCape);
|
||||
|
||||
byte[] packetSignatureData = UpdateService.getClientSignatureData();
|
||||
if(packetSignatureData != null) {
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
|
||||
profileDataType = "update_cert_v1";
|
||||
d.writeByte(profileDataType.length());
|
||||
d.writeBytes(profileDataType);
|
||||
if(packetSignatureData.length > 0xFFFF) {
|
||||
throw new IOException("Update certificate login packet is too long: " + packetSignatureData.length);
|
||||
profileDataToSend.put("update_cert_v1", packetSignatureData);
|
||||
}
|
||||
|
||||
if(protocolVersion >= 4) {
|
||||
List<Entry<String,byte[]>> toSend = new ArrayList<>(profileDataToSend.entrySet());
|
||||
while(!toSend.isEmpty()) {
|
||||
int sendLen = 2;
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
|
||||
d.writeByte(0); // will be replaced
|
||||
int packetCount = 0;
|
||||
while(!toSend.isEmpty() && packetCount < 255) {
|
||||
Entry<String,byte[]> etr = toSend.get(toSend.size() - 1);
|
||||
int i = 3 + etr.getKey().length() + etr.getValue().length;
|
||||
if(sendLen + i < 0xFF00) {
|
||||
String profileDataType = etr.getKey();
|
||||
d.writeByte(profileDataType.length());
|
||||
d.writeBytes(profileDataType);
|
||||
byte[] data = etr.getValue();
|
||||
d.writeShort(data.length);
|
||||
d.write(data);
|
||||
toSend.remove(toSend.size() - 1);
|
||||
++packetCount;
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
byte[] send = bao.toByteArray();
|
||||
send[1] = (byte)packetCount;
|
||||
client.send(send);
|
||||
}
|
||||
}else {
|
||||
for(Entry<String,byte[]> etr : profileDataToSend.entrySet()) {
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA);
|
||||
String profileDataType = etr.getKey();
|
||||
d.writeByte(profileDataType.length());
|
||||
d.writeBytes(profileDataType);
|
||||
byte[] data = etr.getValue();
|
||||
d.writeShort(data.length);
|
||||
d.write(data);
|
||||
client.send(bao.toByteArray());
|
||||
}
|
||||
d.writeShort(packetSignatureData.length);
|
||||
d.write(packetSignatureData);
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
}
|
||||
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN);
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
client.send(bao.toByteArray());
|
||||
|
||||
read = awaitNextPacket(baseTimeout);
|
||||
read = awaitNextPacket(client, baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for login confirmation response!");
|
||||
return false;
|
||||
@ -336,13 +398,13 @@ public class ConnectionHandshake {
|
||||
if(type == HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN) {
|
||||
return true;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di, serverVers == protocolV2);
|
||||
showError(mc, client, connecting, ret, di, protocolVersion == protocolV2);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) {
|
||||
if(serverVers == protocolV2) {
|
||||
if(protocolVersion == protocolV2) {
|
||||
msgLen = di.read();
|
||||
}else {
|
||||
msgLen = di.readUnsignedShort();
|
||||
@ -353,13 +415,13 @@ public class ConnectionHandshake {
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr)));
|
||||
return false;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di, serverVers == protocolV2);
|
||||
showError(mc, client, connecting, ret, di, protocolVersion == protocolV2);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di, true);
|
||||
showError(mc, client, connecting, ret, di, true);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
@ -372,37 +434,51 @@ public class ConnectionHandshake {
|
||||
|
||||
}
|
||||
|
||||
private static byte[] awaitNextPacket(long timeout) {
|
||||
long millis = System.currentTimeMillis();
|
||||
byte[] b;
|
||||
while((b = PlatformNetworking.readPlayPacket()) == null) {
|
||||
if(PlatformNetworking.playConnectionState().isClosed()) {
|
||||
private static byte[] awaitNextPacket(IWebSocketClient client, long timeout) {
|
||||
long millis = EagRuntime.steadyTimeMillis();
|
||||
IWebSocketFrame b;
|
||||
while((b = client.getNextBinaryFrame()) == null) {
|
||||
if(client.getState().isClosed()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(50l);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
if(System.currentTimeMillis() - millis > timeout) {
|
||||
PlatformNetworking.playDisconnect();
|
||||
if(EagRuntime.steadyTimeMillis() - millis > timeout) {
|
||||
client.close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
return b.getByteArray();
|
||||
}
|
||||
|
||||
private static void showError(Minecraft mc, GuiConnecting connecting, GuiScreen scr, DataInputStream err, boolean v2) throws IOException {
|
||||
private static void showError(Minecraft mc, IWebSocketClient client, GuiConnecting connecting, GuiScreen scr, DataInputStream err, boolean v2) throws IOException {
|
||||
int errorCode = err.read();
|
||||
int msgLen = v2 ? err.read() : err.readUnsignedShort();
|
||||
|
||||
// workaround for bug in EaglerXBungee 1.2.7 and below
|
||||
if(msgLen == 0) {
|
||||
if(v2) {
|
||||
if(err.available() == 256) {
|
||||
msgLen = 256;
|
||||
}
|
||||
}else {
|
||||
if(err.available() == 65536) {
|
||||
msgLen = 65536;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] dat = new byte[msgLen];
|
||||
err.read(dat);
|
||||
String errStr = new String(dat, StandardCharsets.UTF_8);
|
||||
logger.info("Server Error Code {}: {}", errorCode, errStr);
|
||||
if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_BLOCKED) {
|
||||
RateLimitTracker.registerBlock(PlatformNetworking.getCurrentURI());
|
||||
RateLimitTracker.registerBlock(client.getCurrentURI());
|
||||
mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr));
|
||||
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) {
|
||||
RateLimitTracker.registerLockOut(PlatformNetworking.getCurrentURI());
|
||||
RateLimitTracker.registerLockOut(client.getCurrentURI());
|
||||
mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr));
|
||||
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) {
|
||||
mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", IChatComponent.Serializer.jsonToComponent(errStr)));
|
||||
|
@ -1,24 +1,19 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf;
|
||||
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.INetHandler;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
@ -32,7 +27,7 @@ import net.minecraft.util.IChatComponent;
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class EaglercraftNetworkManager {
|
||||
public abstract class EaglercraftNetworkManager {
|
||||
|
||||
protected final String address;
|
||||
protected INetHandler nethandler = null;
|
||||
@ -63,103 +58,23 @@ public class EaglercraftNetworkManager {
|
||||
return pluginVersion;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
PlatformNetworking.startPlayConnection(address);
|
||||
public abstract void connect();
|
||||
|
||||
public abstract EnumEaglerConnectionState getConnectStatus();
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public EnumEaglerConnectionState getConnectStatus() {
|
||||
return PlatformNetworking.playConnectionState();
|
||||
}
|
||||
|
||||
public void closeChannel(IChatComponent reason) {
|
||||
PlatformNetworking.playDisconnect();
|
||||
if(nethandler != null) {
|
||||
nethandler.onDisconnect(reason);
|
||||
}
|
||||
clientDisconnected = true;
|
||||
}
|
||||
public abstract void closeChannel(IChatComponent reason);
|
||||
|
||||
public void setConnectionState(EnumConnectionState state) {
|
||||
packetState = state;
|
||||
}
|
||||
|
||||
public void processReceivedPackets() throws IOException {
|
||||
if(nethandler == null) return;
|
||||
List<byte[]> pkts = PlatformNetworking.readAllPacket();
|
||||
public abstract void processReceivedPackets() throws IOException;
|
||||
|
||||
if(pkts == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0, l = pkts.size(); i < l; ++i) {
|
||||
byte[] next = pkts.get(i);
|
||||
++debugPacketCounter;
|
||||
try {
|
||||
ByteBuf nettyBuffer = Unpooled.buffer(next, next.length);
|
||||
nettyBuffer.writerIndex(next.length);
|
||||
PacketBuffer input = new PacketBuffer(nettyBuffer);
|
||||
int pktId = input.readVarIntFromBuffer();
|
||||
|
||||
Packet pkt;
|
||||
try {
|
||||
pkt = packetState.getPacket(EnumPacketDirection.CLIENTBOUND, pktId);
|
||||
}catch(IllegalAccessException | InstantiationException ex) {
|
||||
throw new IOException("Recieved a packet with type " + pktId + " which is invalid!");
|
||||
}
|
||||
|
||||
if(pkt == null) {
|
||||
throw new IOException("Recieved packet type " + pktId + " which is undefined in state " + packetState);
|
||||
}
|
||||
|
||||
try {
|
||||
pkt.readPacketData(input);
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t);
|
||||
}
|
||||
|
||||
try {
|
||||
pkt.processPacket(nethandler);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to process {}! It'll be skipped for debug purposes.", pkt.getClass().getSimpleName());
|
||||
logger.error(t);
|
||||
}
|
||||
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to process websocket frame {}! It'll be skipped for debug purposes.", debugPacketCounter);
|
||||
logger.error(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket(Packet pkt) {
|
||||
if(!isChannelOpen()) {
|
||||
logger.error("Packet was sent on a closed connection: {}", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
try {
|
||||
i = packetState.getPacketId(EnumPacketDirection.SERVERBOUND, pkt);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
temporaryBuffer.clear();
|
||||
temporaryBuffer.writeVarIntToBuffer(i);
|
||||
try {
|
||||
pkt.writePacketData(temporaryBuffer);
|
||||
}catch(IOException ex) {
|
||||
logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
int len = temporaryBuffer.writerIndex();
|
||||
byte[] bytes = new byte[len];
|
||||
temporaryBuffer.getBytes(0, bytes);
|
||||
|
||||
PlatformNetworking.writePlayPacket(bytes);
|
||||
}
|
||||
public abstract void sendPacket(Packet pkt);
|
||||
|
||||
public void setNetHandler(INetHandler nethandler) {
|
||||
this.nethandler = nethandler;
|
||||
@ -181,18 +96,7 @@ public class EaglercraftNetworkManager {
|
||||
throw new CompressionNotSupportedException();
|
||||
}
|
||||
|
||||
public boolean checkDisconnected() {
|
||||
if(PlatformNetworking.playConnectionState().isClosed()) {
|
||||
try {
|
||||
processReceivedPackets(); // catch kick message
|
||||
} catch (IOException e) {
|
||||
}
|
||||
doClientDisconnect(new ChatComponentTranslation("disconnect.endOfStream"));
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public abstract boolean checkDisconnected();
|
||||
|
||||
protected boolean clientDisconnected = false;
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class GuiHandshakeApprove extends GuiScreen {
|
||||
public void initGui() {
|
||||
this.buttonList.clear();
|
||||
titleString = I18n.format("handshakeApprove." + message + ".title");
|
||||
bodyLines = new ArrayList();
|
||||
bodyLines = new ArrayList<>();
|
||||
int i = 0;
|
||||
boolean wasNull = true;
|
||||
while(true) {
|
||||
|
@ -4,6 +4,8 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
@ -23,12 +25,12 @@ public class RateLimitTracker {
|
||||
|
||||
private static long lastTickUpdate = 0l;
|
||||
|
||||
private static final Map<String, Long> blocks = new HashMap();
|
||||
private static final Map<String, Long> lockout = new HashMap();
|
||||
private static final Map<String, Long> blocks = new HashMap<>();
|
||||
private static final Map<String, Long> lockout = new HashMap<>();
|
||||
|
||||
public static boolean isLockedOut(String addr) {
|
||||
Long lockoutStatus = lockout.get(addr);
|
||||
return lockoutStatus != null && System.currentTimeMillis() - lockoutStatus.longValue() < 300000l;
|
||||
return lockoutStatus != null && EagRuntime.steadyTimeMillis() - lockoutStatus.longValue() < 300000l;
|
||||
}
|
||||
|
||||
public static boolean isProbablyLockedOut(String addr) {
|
||||
@ -36,17 +38,17 @@ public class RateLimitTracker {
|
||||
}
|
||||
|
||||
public static void registerBlock(String addr) {
|
||||
blocks.put(addr, System.currentTimeMillis());
|
||||
blocks.put(addr, EagRuntime.steadyTimeMillis());
|
||||
}
|
||||
|
||||
public static void registerLockOut(String addr) {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EagRuntime.steadyTimeMillis();
|
||||
blocks.put(addr, millis);
|
||||
lockout.put(addr, millis);
|
||||
}
|
||||
|
||||
public static void tick() {
|
||||
long millis = System.currentTimeMillis();
|
||||
long millis = EagRuntime.steadyTimeMillis();
|
||||
if(millis - lastTickUpdate > 5000l) {
|
||||
lastTickUpdate = millis;
|
||||
Iterator<Long> blocksItr = blocks.values().iterator();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
@ -26,7 +27,8 @@ public class ServerQueryDispatch {
|
||||
|
||||
public static IServerQuery sendServerQuery(String uri, String accept) {
|
||||
logger.info("Sending {} query to: \"{}\"", accept, uri);
|
||||
return PlatformNetworking.sendServerQuery(uri, accept);
|
||||
IWebSocketClient sockClient = PlatformNetworking.openWebSocket(uri);
|
||||
return sockClient != null ? new ServerQueryImpl(sockClient, accept) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,179 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
class ServerQueryImpl implements IServerQuery {
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("WebSocketQuery");
|
||||
|
||||
private final List<QueryResponse> queryResponses = new LinkedList<>();
|
||||
private final List<byte[]> queryResponsesBytes = new LinkedList<>();
|
||||
|
||||
protected final IWebSocketClient websocketClient;
|
||||
protected final String uri;
|
||||
protected final String accept;
|
||||
protected boolean hasSentAccept = false;
|
||||
protected boolean open = true;
|
||||
protected boolean alive = false;
|
||||
protected long pingStart = -1l;
|
||||
protected long pingTimer = -1l;
|
||||
private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK;
|
||||
|
||||
ServerQueryImpl(IWebSocketClient websocketClient, String accept) {
|
||||
this.websocketClient = websocketClient;
|
||||
this.uri = websocketClient.getCurrentURI();
|
||||
this.accept = accept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if(!hasSentAccept && websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) {
|
||||
hasSentAccept = true;
|
||||
websocketClient.send("Accept: " + accept);
|
||||
}
|
||||
List<IWebSocketFrame> lst = websocketClient.getNextFrames();
|
||||
if(lst != null) {
|
||||
for(int i = 0, l = lst.size(); i < l; ++i) {
|
||||
IWebSocketFrame frame = lst.get(i);
|
||||
alive = true;
|
||||
if(pingTimer == -1) {
|
||||
pingTimer = PlatformRuntime.steadyTimeMillis() - pingStart;
|
||||
if(pingTimer < 1) {
|
||||
pingTimer = 1;
|
||||
}
|
||||
}
|
||||
if(frame.isString()) {
|
||||
String str = frame.getString();
|
||||
if(str.equalsIgnoreCase("BLOCKED")) {
|
||||
logger.error("Reached full IP ratelimit for {}!", uri);
|
||||
rateLimit = EnumServerRateLimit.BLOCKED;
|
||||
return;
|
||||
}
|
||||
if(str.equalsIgnoreCase("LOCKED")) {
|
||||
logger.error("Reached full IP ratelimit lockout for {}!", uri);
|
||||
rateLimit = EnumServerRateLimit.LOCKED_OUT;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JSONObject obj = new JSONObject(str);
|
||||
if("blocked".equalsIgnoreCase(obj.optString("type", null))) {
|
||||
logger.error("Reached query ratelimit for {}!", uri);
|
||||
rateLimit = EnumServerRateLimit.BLOCKED;
|
||||
}else if("locked".equalsIgnoreCase(obj.optString("type", null))) {
|
||||
logger.error("Reached query ratelimit lockout for {}!", uri);
|
||||
rateLimit = EnumServerRateLimit.LOCKED_OUT;
|
||||
}else {
|
||||
queryResponses.add(new QueryResponse(obj, pingTimer));
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
logger.error("Exception thrown parsing websocket query response from \"" + uri + "\"!");
|
||||
logger.error(t);
|
||||
}
|
||||
}else {
|
||||
queryResponsesBytes.add(frame.getByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(websocketClient.isClosed()) {
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String str) {
|
||||
if(websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) {
|
||||
websocketClient.send(str);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(byte[] bytes) {
|
||||
if(websocketClient.getState() == EnumEaglerConnectionState.CONNECTED) {
|
||||
websocketClient.send(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int responsesAvailable() {
|
||||
synchronized(queryResponses) {
|
||||
return queryResponses.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResponse getResponse() {
|
||||
synchronized(queryResponses) {
|
||||
if(queryResponses.size() > 0) {
|
||||
return queryResponses.remove(0);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int binaryResponsesAvailable() {
|
||||
synchronized(queryResponsesBytes) {
|
||||
return queryResponsesBytes.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBinaryResponse() {
|
||||
synchronized(queryResponsesBytes) {
|
||||
if(queryResponsesBytes.size() > 0) {
|
||||
return queryResponsesBytes.remove(0);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryReadyState readyState() {
|
||||
return open ? (alive ? QueryReadyState.OPEN : QueryReadyState.CONNECTING)
|
||||
: (alive ? QueryReadyState.CLOSED : QueryReadyState.FAILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if(open) {
|
||||
open = false;
|
||||
websocketClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumServerRateLimit getRateLimit() {
|
||||
return rateLimit;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
|
||||
import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf;
|
||||
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class WebSocketNetworkManager extends EaglercraftNetworkManager {
|
||||
|
||||
protected final IWebSocketClient webSocketClient;
|
||||
|
||||
public WebSocketNetworkManager(IWebSocketClient webSocketClient) {
|
||||
super(webSocketClient.getCurrentURI());
|
||||
this.webSocketClient = webSocketClient;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
}
|
||||
|
||||
public EnumEaglerConnectionState getConnectStatus() {
|
||||
return webSocketClient.getState();
|
||||
}
|
||||
|
||||
public void closeChannel(IChatComponent reason) {
|
||||
webSocketClient.close();
|
||||
if(nethandler != null) {
|
||||
nethandler.onDisconnect(reason);
|
||||
}
|
||||
clientDisconnected = true;
|
||||
}
|
||||
|
||||
public void processReceivedPackets() throws IOException {
|
||||
if(nethandler == null) return;
|
||||
if(webSocketClient.availableStringFrames() > 0) {
|
||||
logger.warn("discarding {} string frames recieved on a binary connection", webSocketClient.availableStringFrames());
|
||||
webSocketClient.clearStringFrames();
|
||||
}
|
||||
List<IWebSocketFrame> pkts = webSocketClient.getNextBinaryFrames();
|
||||
|
||||
if(pkts == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0, l = pkts.size(); i < l; ++i) {
|
||||
IWebSocketFrame next = pkts.get(i);
|
||||
++debugPacketCounter;
|
||||
try {
|
||||
byte[] asByteArray = next.getByteArray();
|
||||
ByteBuf nettyBuffer = Unpooled.buffer(asByteArray, asByteArray.length);
|
||||
nettyBuffer.writerIndex(asByteArray.length);
|
||||
PacketBuffer input = new PacketBuffer(nettyBuffer);
|
||||
int pktId = input.readVarIntFromBuffer();
|
||||
|
||||
Packet pkt;
|
||||
try {
|
||||
pkt = packetState.getPacket(EnumPacketDirection.CLIENTBOUND, pktId);
|
||||
}catch(IllegalAccessException | InstantiationException ex) {
|
||||
throw new IOException("Recieved a packet with type " + pktId + " which is invalid!");
|
||||
}
|
||||
|
||||
if(pkt == null) {
|
||||
throw new IOException("Recieved packet type " + pktId + " which is undefined in state " + packetState);
|
||||
}
|
||||
|
||||
try {
|
||||
pkt.readPacketData(input);
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Failed to read packet type '" + pkt.getClass().getSimpleName() + "'", t);
|
||||
}
|
||||
|
||||
try {
|
||||
pkt.processPacket(nethandler);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to process {}! It'll be skipped for debug purposes.", pkt.getClass().getSimpleName());
|
||||
logger.error(t);
|
||||
}
|
||||
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to process websocket frame {}! It'll be skipped for debug purposes.", debugPacketCounter);
|
||||
logger.error(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket(Packet pkt) {
|
||||
if(!isChannelOpen()) {
|
||||
logger.error("Packet was sent on a closed connection: {}", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
try {
|
||||
i = packetState.getPacketId(EnumPacketDirection.SERVERBOUND, pkt);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Incorrect packet for state: {}", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
temporaryBuffer.clear();
|
||||
temporaryBuffer.writeVarIntToBuffer(i);
|
||||
try {
|
||||
pkt.writePacketData(temporaryBuffer);
|
||||
}catch(IOException ex) {
|
||||
logger.error("Failed to write packet {}!", pkt.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
int len = temporaryBuffer.writerIndex();
|
||||
byte[] bytes = new byte[len];
|
||||
temporaryBuffer.getBytes(0, bytes);
|
||||
|
||||
webSocketClient.send(bytes);
|
||||
}
|
||||
|
||||
public boolean checkDisconnected() {
|
||||
if(webSocketClient.isClosed()) {
|
||||
try {
|
||||
processReceivedPackets(); // catch kick message
|
||||
} catch (IOException e) {
|
||||
}
|
||||
doClientDisconnect(new ChatComponentTranslation("disconnect.endOfStream"));
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.SkinModel;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
|
||||
import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ClientV3MessageHandler implements GameMessageHandler {
|
||||
|
||||
private final NetHandlerPlayClient netHandler;
|
||||
|
||||
public ClientV3MessageHandler(NetHandlerPlayClient netHandler) {
|
||||
this.netHandler = netHandler;
|
||||
}
|
||||
|
||||
public void handleServer(SPacketEnableFNAWSkinsEAG packet) {
|
||||
netHandler.currentFNAWSkinAllowedState = packet.enableSkins;
|
||||
netHandler.currentFNAWSkinForcedState = packet.enableSkins;
|
||||
Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(netHandler.currentFNAWSkinForcedState
|
||||
|| (netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins));
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherCapeCustomEAG packet) {
|
||||
netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
packet.customCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherCapePresetEAG packet) {
|
||||
netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
packet.presetCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherSkinCustomV3EAG packet) {
|
||||
EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast);
|
||||
SkinModel modelId;
|
||||
if(packet.modelID == (byte)0xFF) {
|
||||
modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID);
|
||||
}else {
|
||||
modelId = SkinModel.getModelFromId(packet.modelID & 0x7F);
|
||||
if((packet.modelID & 0x80) != 0 && modelId.sanitize) {
|
||||
modelId = SkinModel.STEVE;
|
||||
}
|
||||
}
|
||||
if(modelId.highPoly != null) {
|
||||
modelId = SkinModel.STEVE;
|
||||
}
|
||||
this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, packet.customSkin, modelId);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherSkinPresetEAG packet) {
|
||||
this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
packet.presetSkin);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketUpdateCertEAG packet) {
|
||||
if (EagRuntime.getConfiguration().allowUpdateSvc()) {
|
||||
UpdateService.addCertificateToSet(packet.updateCert);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalAllowedEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeAllowed(packet.allowed, packet.iceServers);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalConnectV3EAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
if (packet.isAnnounceType) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
|
||||
} else {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeConnect(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.offer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalDescEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeDescription(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
new String(packet.desc, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeDisconnect(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalGlobalEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeGlobalNew(packet.users);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalICEEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeICECandidate(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
new String(packet.ice, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.ClientUUIDLoadingCache;
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
import net.lax1dude.eaglercraft.v1_8.PauseMenuCustomizeState;
|
||||
import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.SkinModel;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.*;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache;
|
||||
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
|
||||
import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController;
|
||||
import net.lax1dude.eaglercraft.v1_8.webview.ServerInfoCache;
|
||||
import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ClientV4MessageHandler implements GameMessageHandler {
|
||||
|
||||
private final NetHandlerPlayClient netHandler;
|
||||
|
||||
public ClientV4MessageHandler(NetHandlerPlayClient netHandler) {
|
||||
this.netHandler = netHandler;
|
||||
}
|
||||
|
||||
public void handleServer(SPacketEnableFNAWSkinsEAG packet) {
|
||||
netHandler.currentFNAWSkinAllowedState = packet.enableSkins;
|
||||
netHandler.currentFNAWSkinForcedState = packet.force;
|
||||
Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(netHandler.currentFNAWSkinForcedState
|
||||
|| (netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins));
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherCapeCustomEAG packet) {
|
||||
netHandler.getCapeCache().cacheCapeCustom(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
packet.customCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherCapePresetEAG packet) {
|
||||
netHandler.getCapeCache().cacheCapePreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
packet.presetCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherSkinCustomV4EAG packet) {
|
||||
EaglercraftUUID responseUUID = new EaglercraftUUID(packet.uuidMost, packet.uuidLeast);
|
||||
SkinModel modelId;
|
||||
if(packet.modelID == (byte)0xFF) {
|
||||
modelId = this.netHandler.getSkinCache().getRequestedSkinType(responseUUID);
|
||||
}else {
|
||||
modelId = SkinModel.getModelFromId(packet.modelID & 0x7F);
|
||||
if((packet.modelID & 0x80) != 0 && modelId.sanitize) {
|
||||
modelId = SkinModel.STEVE;
|
||||
}
|
||||
}
|
||||
if(modelId.highPoly != null) {
|
||||
modelId = SkinModel.STEVE;
|
||||
}
|
||||
this.netHandler.getSkinCache().cacheSkinCustom(responseUUID, SkinPacketVersionCache.convertToV3Raw(packet.customSkin), modelId);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherSkinPresetEAG packet) {
|
||||
this.netHandler.getSkinCache().cacheSkinPreset(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.presetSkin);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketUpdateCertEAG packet) {
|
||||
if (EagRuntime.getConfiguration().allowUpdateSvc()) {
|
||||
UpdateService.addCertificateToSet(packet.updateCert);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalAllowedEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeAllowed(packet.allowed, packet.iceServers);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalConnectV4EAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeConnect(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast), packet.offer);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalConnectAnnounceV4EAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalDescEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeDescription(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
new String(packet.desc, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalDisconnectPeerEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeDisconnect(new EaglercraftUUID(packet.uuidMost, packet.uuidLeast));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalGlobalEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeGlobalNew(packet.users);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketVoiceSignalICEEAG packet) {
|
||||
if (VoiceClientController.isClientSupported()) {
|
||||
VoiceClientController.handleVoiceSignalPacketTypeICECandidate(
|
||||
new EaglercraftUUID(packet.uuidMost, packet.uuidLeast),
|
||||
new String(packet.ice, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketForceClientSkinPresetV4EAG packet) {
|
||||
EaglerProfile.handleForceSkinPreset(packet.presetSkin);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketForceClientSkinCustomV4EAG packet) {
|
||||
EaglerProfile.handleForceSkinCustom(packet.modelID, SkinPacketVersionCache.convertToV3Raw(packet.customSkin));
|
||||
}
|
||||
|
||||
public void handleServer(SPacketSetServerCookieV4EAG packet) {
|
||||
if(!netHandler.isClientInEaglerSingleplayerOrLAN() && Minecraft.getMinecraft().getCurrentServerData().enableCookies) {
|
||||
ServerCookieDataStore.saveCookie(netHandler.getNetworkManager().getAddress(), packet.expires, packet.data,
|
||||
packet.revokeQuerySupported, packet.saveCookieToDisk);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketRedirectClientV4EAG packet) {
|
||||
Minecraft.getMinecraft().handleReconnectPacket(packet.redirectURI);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketOtherPlayerClientUUIDV4EAG packet) {
|
||||
ClientUUIDLoadingCache.handleResponse(packet.requestId, new EaglercraftUUID(packet.clientUUIDMost, packet.clientUUIDLeast));
|
||||
}
|
||||
|
||||
public void handleServer(SPacketForceClientCapePresetV4EAG packet) {
|
||||
EaglerProfile.handleForceCapePreset(packet.presetCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketForceClientCapeCustomV4EAG packet) {
|
||||
EaglerProfile.handleForceCapeCustom(packet.customCape);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketInvalidatePlayerCacheV4EAG packet) {
|
||||
if(packet.players != null && packet.players.size() > 0) {
|
||||
for(SPacketInvalidatePlayerCacheV4EAG.InvalidateRequest req : packet.players) {
|
||||
EaglercraftUUID uuid = new EaglercraftUUID(req.uuidMost, req.uuidLeast);
|
||||
if(req.invalidateSkin) {
|
||||
this.netHandler.getSkinCache().handleInvalidate(uuid);
|
||||
}
|
||||
if(req.invalidateCape) {
|
||||
this.netHandler.getCapeCache().handleInvalidate(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketUnforceClientV4EAG packet) {
|
||||
if(packet.resetSkin) {
|
||||
EaglerProfile.isServerSkinOverride = false;
|
||||
}
|
||||
if(packet.resetCape) {
|
||||
EaglerProfile.isServerCapeOverride = false;
|
||||
}
|
||||
if(packet.resetFNAW) {
|
||||
netHandler.currentFNAWSkinForcedState = false;
|
||||
Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(
|
||||
netHandler.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServer(SPacketCustomizePauseMenuV4EAG packet) {
|
||||
PauseMenuCustomizeState.loadPacket(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketServerInfoDataChunkV4EAG packet) {
|
||||
ServerInfoCache.handleChunk(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketWebViewMessageV4EAG packet) {
|
||||
WebViewOverlayController.handleMessagePacket(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketNotifIconsRegisterV4EAG packet) {
|
||||
netHandler.getNotifManager().processPacketAddIcons(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketNotifIconsReleaseV4EAG packet) {
|
||||
netHandler.getNotifManager().processPacketRemIcons(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketNotifBadgeShowV4EAG packet) {
|
||||
netHandler.getNotifManager().processPacketShowBadge(packet);
|
||||
}
|
||||
|
||||
public void handleServer(SPacketNotifBadgeHideV4EAG packet) {
|
||||
netHandler.getNotifManager().processPacketHideBadge(packet);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
import net.lax1dude.eaglercraft.v1_8.netty.Unpooled;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageConstants;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV3MessageHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.socket.protocol.ServerV4MessageHandler;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.network.NetHandlerPlayServer;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class GameProtocolMessageController {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger("GameProtocolMessageController");
|
||||
|
||||
public final GamePluginMessageProtocol protocol;
|
||||
public final int sendDirection;
|
||||
public final int receiveDirection;
|
||||
private final PacketBufferInputWrapper inputStream = new PacketBufferInputWrapper(null);
|
||||
private final PacketBufferOutputWrapper outputStream = new PacketBufferOutputWrapper(null);
|
||||
private final GameMessageHandler handler;
|
||||
private final IPluginMessageSendFunction sendFunction;
|
||||
private final List<PacketBuffer> sendQueueV4;
|
||||
private final boolean noDelay;
|
||||
|
||||
public GameProtocolMessageController(GamePluginMessageProtocol protocol, int sendDirection, GameMessageHandler handler,
|
||||
IPluginMessageSendFunction sendCallback) {
|
||||
this.protocol = protocol;
|
||||
this.sendDirection = sendDirection;
|
||||
this.receiveDirection = GamePluginMessageConstants.oppositeDirection(sendDirection);
|
||||
this.handler = handler;
|
||||
this.sendFunction = sendCallback;
|
||||
this.noDelay = protocol.ver < 4 || EagRuntime.getConfiguration().isEaglerNoDelay();
|
||||
this.sendQueueV4 = !noDelay ? new LinkedList<>() : null;
|
||||
}
|
||||
|
||||
public boolean handlePacket(String channel, PacketBuffer data) throws IOException {
|
||||
GameMessagePacket pkt;
|
||||
if(protocol.ver >= 4 && data.readableBytes() > 0 && data.getByte(data.readerIndex()) == (byte) 0xFF
|
||||
&& channel.equals(GamePluginMessageConstants.V4_CHANNEL)) {
|
||||
data.readByte();
|
||||
inputStream.buffer = data;
|
||||
int count = inputStream.readVarInt();
|
||||
for(int i = 0, j, k; i < count; ++i) {
|
||||
j = data.readVarIntFromBuffer();
|
||||
k = data.readerIndex() + j;
|
||||
if(j > data.readableBytes()) {
|
||||
throw new IOException("Packet fragment is too long: " + j + " > " + data.readableBytes());
|
||||
}
|
||||
pkt = protocol.readPacket(channel, receiveDirection, inputStream);
|
||||
if(pkt != null) {
|
||||
try {
|
||||
pkt.handlePacket(handler);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(),
|
||||
GamePluginMessageConstants.getDirectionString(receiveDirection), handler);
|
||||
logger.error(t);
|
||||
}
|
||||
}else {
|
||||
logger.warn("Could not read packet fragment {} of {}, unknown packet", count, i);
|
||||
}
|
||||
if(data.readerIndex() != k) {
|
||||
logger.warn("Packet fragment {} was the wrong length: {} != {}",
|
||||
(pkt != null ? pkt.getClass().getSimpleName() : "unknown"), j + data.readerIndex() - k, j);
|
||||
data.readerIndex(k);
|
||||
}
|
||||
}
|
||||
if(data.readableBytes() > 0) {
|
||||
logger.warn("Leftover data after reading multi-packet! ({} bytes)", data.readableBytes());
|
||||
}
|
||||
inputStream.buffer = null;
|
||||
return true;
|
||||
}
|
||||
inputStream.buffer = data;
|
||||
pkt = protocol.readPacket(channel, receiveDirection, inputStream);
|
||||
if(pkt != null && inputStream.available() > 0) {
|
||||
logger.warn("Leftover data after reading packet {}! ({} bytes)", pkt.getClass().getSimpleName(), inputStream.available());
|
||||
}
|
||||
inputStream.buffer = null;
|
||||
if(pkt != null) {
|
||||
try {
|
||||
pkt.handlePacket(handler);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Failed to handle packet {} in direction {} using handler {}!", pkt.getClass().getSimpleName(),
|
||||
GamePluginMessageConstants.getDirectionString(receiveDirection), handler);
|
||||
logger.error(t);
|
||||
}
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket(GameMessagePacket packet) throws IOException {
|
||||
int len = packet.length() + 1;
|
||||
PacketBuffer buf = new PacketBuffer(len != 0 ? Unpooled.buffer(len) : Unpooled.buffer(64));
|
||||
outputStream.buffer = buf;
|
||||
String chan = protocol.writePacket(sendDirection, outputStream, packet);
|
||||
outputStream.buffer = null;
|
||||
int j = buf.writerIndex();
|
||||
if(len != 0 && j != len && j + 1 != len) {
|
||||
logger.warn("Packet {} was expected to be {} bytes but was serialized to {} bytes!",
|
||||
packet.getClass().getSimpleName(), len, j);
|
||||
}
|
||||
if(sendQueueV4 != null && chan.equals(GamePluginMessageConstants.V4_CHANNEL)) {
|
||||
sendQueueV4.add(buf);
|
||||
}else {
|
||||
sendFunction.sendPluginMessage(chan, buf);
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if(sendQueueV4 != null) {
|
||||
int queueLen = sendQueueV4.size();
|
||||
PacketBuffer pkt;
|
||||
if(queueLen == 0) {
|
||||
return;
|
||||
}else if(queueLen == 1) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt);
|
||||
}else {
|
||||
int i, j, sendCount = 0, totalLen = 0;
|
||||
PacketBuffer sendBuffer;
|
||||
while(sendQueueV4.size() > 0) {
|
||||
do {
|
||||
i = sendQueueV4.get(sendCount++).readableBytes();
|
||||
totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i;
|
||||
}while(totalLen < 32760 && sendCount < sendQueueV4.size());
|
||||
if(totalLen >= 32760) {
|
||||
--sendCount;
|
||||
}
|
||||
if(sendCount <= 1) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, pkt);
|
||||
continue;
|
||||
}
|
||||
sendBuffer = new PacketBuffer(Unpooled.buffer(1 + totalLen + GamePacketOutputBuffer.getVarIntSize(sendCount)));
|
||||
sendBuffer.writeByte(0xFF);
|
||||
sendBuffer.writeVarIntToBuffer(sendCount);
|
||||
for(j = 0; j < sendCount; ++j) {
|
||||
pkt = sendQueueV4.remove(0);
|
||||
sendBuffer.writeVarIntToBuffer(pkt.readableBytes());
|
||||
sendBuffer.writeBytes(pkt);
|
||||
}
|
||||
sendFunction.sendPluginMessage(GamePluginMessageConstants.V4_CHANNEL, sendBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameMessageHandler createClientHandler(int protocolVersion, NetHandlerPlayClient netHandler) {
|
||||
switch(protocolVersion) {
|
||||
case 2:
|
||||
case 3:
|
||||
return new ClientV3MessageHandler(netHandler);
|
||||
case 4:
|
||||
return new ClientV4MessageHandler(netHandler);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public static GameMessageHandler createServerHandler(int protocolVersion, NetHandlerPlayServer netHandler) {
|
||||
switch(protocolVersion) {
|
||||
case 2:
|
||||
case 3:
|
||||
return new ServerV3MessageHandler(netHandler);
|
||||
case 4:
|
||||
return new ServerV4MessageHandler(netHandler);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown protocol verison: " + protocolVersion);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public interface IPluginMessageSendFunction {
|
||||
|
||||
void sendPluginMessage(String channel, PacketBuffer contents);
|
||||
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.DecoderException;
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketInputBuffer;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class PacketBufferInputWrapper implements GamePacketInputBuffer {
|
||||
|
||||
protected PacketBuffer buffer;
|
||||
|
||||
public PacketBufferInputWrapper(PacketBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public PacketBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public void setBuffer(PacketBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
try {
|
||||
buffer.readBytes(b);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||
try {
|
||||
buffer.readBytes(b, off, len);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(int n) throws IOException {
|
||||
int r = buffer.readableBytes();
|
||||
if(n > r) {
|
||||
n = r;
|
||||
}
|
||||
buffer.readerIndex(buffer.readerIndex() + n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean() throws IOException {
|
||||
try {
|
||||
return buffer.readBoolean();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
try {
|
||||
return buffer.readByte();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
try {
|
||||
return buffer.readUnsignedByte();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
try {
|
||||
return buffer.readShort();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
try {
|
||||
return buffer.readUnsignedShort();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
try {
|
||||
return buffer.readChar();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
try {
|
||||
return buffer.readInt();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
try {
|
||||
return buffer.readLong();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat() throws IOException {
|
||||
try {
|
||||
return buffer.readFloat();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
try {
|
||||
return buffer.readDouble();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
return DataInputStream.readUTF(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipAllBytes(int n) throws IOException {
|
||||
if(buffer.readableBytes() < n) {
|
||||
throw new EOFException();
|
||||
}
|
||||
buffer.readerIndex(buffer.readerIndex() + n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readVarInt() throws IOException {
|
||||
try {
|
||||
return buffer.readVarIntFromBuffer();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readVarLong() throws IOException {
|
||||
try {
|
||||
return buffer.readVarLong();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStringMC(int maxLen) throws IOException {
|
||||
try {
|
||||
return buffer.readStringFromBuffer(maxLen);
|
||||
}catch(DecoderException ex) {
|
||||
throw new IOException(ex.getMessage());
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStringEaglerASCII8() throws IOException {
|
||||
int len = readUnsignedByte();
|
||||
char[] ret = new char[len];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
ret[i] = (char)readByte();
|
||||
}
|
||||
return new String(ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStringEaglerASCII16() throws IOException {
|
||||
int len = readUnsignedShort();
|
||||
char[] ret = new char[len];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
ret[i] = (char)readByte();
|
||||
}
|
||||
return new String(ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readByteArrayMC() throws IOException {
|
||||
try {
|
||||
return buffer.readByteArray();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return buffer.readableBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream stream() {
|
||||
return new InputStream() {
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if(buffer.readableBytes() > 0) {
|
||||
return buffer.readUnsignedShort();
|
||||
}else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
int avail = buffer.readableBytes();
|
||||
if(avail == 0) return -1;
|
||||
len = avail > len ? avail : len;
|
||||
buffer.readBytes(b, off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
return PacketBufferInputWrapper.this.skipBytes((int)n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return buffer.readableBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
buffer.markReaderIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
try {
|
||||
buffer.resetReaderIndex();
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() throws IOException {
|
||||
byte[] ret = new byte[buffer.readableBytes()];
|
||||
buffer.readBytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,316 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket.protocol.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePacketOutputBuffer;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class PacketBufferOutputWrapper implements GamePacketOutputBuffer {
|
||||
|
||||
protected PacketBuffer buffer;
|
||||
|
||||
public PacketBufferOutputWrapper(PacketBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public PacketBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public void setBuffer(PacketBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
try {
|
||||
buffer.writeByte(b);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
try {
|
||||
buffer.writeBytes(b);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
try {
|
||||
buffer.writeBytes(b, off, len);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean v) throws IOException {
|
||||
try {
|
||||
buffer.writeBoolean(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int v) throws IOException {
|
||||
try {
|
||||
buffer.writeByte(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(int v) throws IOException {
|
||||
try {
|
||||
buffer.writeShort(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChar(int v) throws IOException {
|
||||
try {
|
||||
buffer.writeChar(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(int v) throws IOException {
|
||||
try {
|
||||
buffer.writeInt(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(long v) throws IOException {
|
||||
try {
|
||||
buffer.writeLong(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(float v) throws IOException {
|
||||
try {
|
||||
buffer.writeFloat(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double v) throws IOException {
|
||||
try {
|
||||
buffer.writeDouble(v);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(String s) throws IOException {
|
||||
try {
|
||||
int l = s.length();
|
||||
for(int i = 0; i < l; ++i) {
|
||||
buffer.writeByte((int)s.charAt(i));
|
||||
}
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChars(String s) throws IOException {
|
||||
try {
|
||||
int l = s.length();
|
||||
for(int i = 0; i < l; ++i) {
|
||||
buffer.writeChar(s.charAt(i));
|
||||
}
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeUTF(String str) throws IOException {
|
||||
long utfCount = countUTFBytes(str);
|
||||
if (utfCount > 65535) {
|
||||
throw new IOException("String is longer than 65535 bytes when encoded as UTF8!");
|
||||
}
|
||||
byte[] arr = new byte[(int) utfCount + 2];
|
||||
int offset = 2;
|
||||
arr[0] = (byte)(((int)utfCount >>> 8) & 0xFF);
|
||||
arr[1] = (byte)((int)utfCount & 0xFF);
|
||||
offset = writeUTFBytesToBuffer(str, arr, offset);
|
||||
try {
|
||||
buffer.writeBytes(arr, 0, offset);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
private static long countUTFBytes(String str) {
|
||||
int utfCount = 0;
|
||||
int length = str.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int charValue = str.charAt(i);
|
||||
if (charValue > 0 && charValue <= 127) {
|
||||
utfCount++;
|
||||
} else if (charValue <= 2047) {
|
||||
utfCount += 2;
|
||||
} else {
|
||||
utfCount += 3;
|
||||
}
|
||||
}
|
||||
return utfCount;
|
||||
}
|
||||
|
||||
private static int writeUTFBytesToBuffer(String str, byte[] buffer, int offset) throws IOException {
|
||||
int length = str.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int charValue = str.charAt(i);
|
||||
if (charValue > 0 && charValue <= 127) {
|
||||
buffer[offset++] = (byte) charValue;
|
||||
} else if (charValue <= 2047) {
|
||||
buffer[offset++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
|
||||
buffer[offset++] = (byte) (0x80 | (0x3f & charValue));
|
||||
} else {
|
||||
buffer[offset++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
|
||||
buffer[offset++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
|
||||
buffer[offset++] = (byte) (0x80 | (0x3f & charValue));
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarInt(int i) throws IOException {
|
||||
try {
|
||||
buffer.writeVarIntToBuffer(i);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarLong(long i) throws IOException {
|
||||
try {
|
||||
buffer.writeVarLong(i);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStringMC(String str) throws IOException {
|
||||
try {
|
||||
buffer.writeString(str);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStringEaglerASCII8(String str) throws IOException {
|
||||
int len = str.length();
|
||||
if(len > 255) {
|
||||
throw new IOException("String is longer than 255 chars! (" + len + ")");
|
||||
}
|
||||
try {
|
||||
buffer.writeByte(len);
|
||||
for(int i = 0, j; i < len; ++i) {
|
||||
j = (int)str.charAt(i);
|
||||
if(j > 255) {
|
||||
j = (int)'?';
|
||||
}
|
||||
buffer.writeByte(j);
|
||||
}
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStringEaglerASCII16(String str) throws IOException {
|
||||
int len = str.length();
|
||||
if(len > 65535) {
|
||||
throw new IOException("String is longer than 65535 chars! (" + len + ")");
|
||||
}
|
||||
try {
|
||||
buffer.writeShort(len);
|
||||
for(int i = 0, j; i < len; ++i) {
|
||||
j = (int)str.charAt(i);
|
||||
if(j > 255) {
|
||||
j = (int)'?';
|
||||
}
|
||||
buffer.writeByte(j);
|
||||
}
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByteArrayMC(byte[] bytes) throws IOException {
|
||||
try {
|
||||
buffer.writeByteArray(bytes);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream stream() {
|
||||
return new OutputStream() {
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
try {
|
||||
buffer.writeByte(b);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
try {
|
||||
buffer.writeBytes(b, off, len);
|
||||
}catch(IndexOutOfBoundsException ex) {
|
||||
throw new IOException("Packet buffer overflowed!");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user