mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #0 - First Release
This commit is contained in:
@ -0,0 +1,61 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.minecraft.client.multiplayer.ServerAddress;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class AddressResolver {
|
||||
|
||||
public static String resolveURI(ServerData input) {
|
||||
return resolveURI(input.serverIP);
|
||||
}
|
||||
|
||||
public static String resolveURI(String input) {
|
||||
String lc = input.toLowerCase();
|
||||
if(!lc.startsWith("ws://") && !lc.startsWith("wss://")) {
|
||||
if(EagRuntime.requireSSL()) {
|
||||
input = "wss://" + input;
|
||||
}else {
|
||||
input = "ws://" + input;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public static ServerAddress resolveAddressFromURI(String input) {
|
||||
String uri = resolveURI(input);
|
||||
String lc = input.toLowerCase();
|
||||
if(lc.startsWith("ws://")) {
|
||||
input = input.substring(5);
|
||||
}else if(lc.startsWith("wss://")) {
|
||||
input = input.substring(6);
|
||||
}
|
||||
int port = EagRuntime.requireSSL() ? 443: 80;
|
||||
int i = input.indexOf('/');
|
||||
if(i != -1) {
|
||||
input = input.substring(0, i);
|
||||
}
|
||||
i = input.lastIndexOf(':');
|
||||
if(i != -1) {
|
||||
try {
|
||||
port = Integer.parseInt(input.substring(i + 1));
|
||||
}catch(Throwable t) {
|
||||
}
|
||||
}
|
||||
return new ServerAddress(uri, port);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class CompressionNotSupportedException extends UnsupportedOperationException {
|
||||
|
||||
public CompressionNotSupportedException() {
|
||||
super("Compression is not supported by Eaglercraft, set " +
|
||||
"'network-compression-threshold=-1' in server.properties to " +
|
||||
"allow Eaglercraft connections to this server");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,387 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.ArrayUtils;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest;
|
||||
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.profile.EaglerProfile;
|
||||
import net.lax1dude.eaglercraft.v1_8.profile.GuiAuthenticationScreen;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiDisconnected;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.multiplayer.GuiConnecting;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class ConnectionHandshake {
|
||||
|
||||
private static final long baseTimeout = 5000l;
|
||||
|
||||
private static final int baseVersion = 2; // ProtocolVersions.V_02
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
public static boolean attemptHandshake(Minecraft mc, GuiConnecting connecting, GuiScreen ret, String password, boolean allowPlaintext) {
|
||||
try {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
DataOutputStream d = new DataOutputStream(bao);
|
||||
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_VERSION);
|
||||
|
||||
d.writeByte(2); // legacy protocol version
|
||||
|
||||
d.writeShort(1); // supported eagler protocols count
|
||||
d.writeShort(baseVersion); // client supports v2
|
||||
|
||||
d.writeShort(1); // supported game protocols count
|
||||
d.writeShort(47); // client supports 1.8 protocol
|
||||
|
||||
String clientBrand = EaglercraftVersion.projectForkName;
|
||||
d.writeByte(clientBrand.length());
|
||||
d.writeBytes(clientBrand);
|
||||
|
||||
String clientVers = EaglercraftVersion.projectOriginVersion;
|
||||
d.writeByte(clientVers.length());
|
||||
d.writeBytes(clientVers);
|
||||
|
||||
d.writeBoolean(password != null);
|
||||
|
||||
String username = mc.getSession().getProfile().getName();
|
||||
d.writeByte(username.length());
|
||||
d.writeBytes(username);
|
||||
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
|
||||
byte[] read = awaitNextPacket(baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for server protocol response!");
|
||||
return false;
|
||||
}
|
||||
|
||||
DataInputStream di = new DataInputStream(new EaglerInputStream(read));
|
||||
|
||||
int type = di.read();
|
||||
if(type == HandshakePacketTypes.PROTOCOL_VERSION_MISMATCH) {
|
||||
|
||||
StringBuilder protocols = new StringBuilder();
|
||||
int c = di.readShort();
|
||||
for(int i = 0; i < c; ++i) {
|
||||
if(i > 0) {
|
||||
protocols.append(", ");
|
||||
}
|
||||
protocols.append("v").append(di.readShort());
|
||||
}
|
||||
|
||||
StringBuilder games = new StringBuilder();
|
||||
c = di.readShort();
|
||||
for(int i = 0; i < c; ++i) {
|
||||
if(i > 0) {
|
||||
games.append(", ");
|
||||
}
|
||||
games.append("mc").append(di.readShort());
|
||||
}
|
||||
|
||||
logger.info("Incompatible client: v2 & mc47");
|
||||
logger.info("Server supports: {}", protocols);
|
||||
logger.info("Server supports: {}", games);
|
||||
|
||||
int msgLen = di.read();
|
||||
byte[] dat = new byte[msgLen];
|
||||
di.read(dat);
|
||||
String msg = new String(dat, StandardCharsets.UTF_8);
|
||||
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(msg)));
|
||||
|
||||
return false;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_VERSION) {
|
||||
int serverVers = di.readShort();
|
||||
|
||||
if(serverVers != baseVersion) {
|
||||
logger.info("Incompatible server version: {}", serverVers);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(serverVers < baseVersion ? "Outdated Server" : "Outdated Client")));
|
||||
return false;
|
||||
}
|
||||
|
||||
int gameVers = di.readShort();
|
||||
if(gameVers != 47) {
|
||||
logger.info("Incompatible minecraft protocol version: {}", gameVers);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText("This server does not support 1.8!")));
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info("Server protocol: {}", serverVers);
|
||||
|
||||
int msgLen = di.read();
|
||||
byte[] dat = new byte[msgLen];
|
||||
di.read(dat);
|
||||
String brandStr = ArrayUtils.asciiString(dat);
|
||||
|
||||
msgLen = di.read();
|
||||
dat = new byte[msgLen];
|
||||
di.read(dat);
|
||||
String versionStr = ArrayUtils.asciiString(dat);
|
||||
|
||||
logger.info("Server version: {}", versionStr);
|
||||
logger.info("Server brand: {}", brandStr);
|
||||
|
||||
int authType = di.read();
|
||||
int saltLength = (int)di.readShort() & 0xFFFF;
|
||||
|
||||
byte[] salt = new byte[saltLength];
|
||||
di.read(salt);
|
||||
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_REQUEST_LOGIN);
|
||||
|
||||
d.writeByte(username.length());
|
||||
d.writeBytes(username);
|
||||
|
||||
String requestedServer = "default";
|
||||
d.writeByte(requestedServer.length());
|
||||
d.writeBytes(requestedServer);
|
||||
|
||||
if(authType != 0 && password != null && password.length() > 0) {
|
||||
if(authType == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) {
|
||||
if(allowPlaintext) {
|
||||
logger.warn("Server is using insecure plaintext authentication");
|
||||
d.writeByte(password.length() << 1);
|
||||
d.writeChars(password);
|
||||
}else {
|
||||
logger.error("Plaintext authentication was attempted but no user confirmation has been given to proceed");
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed",
|
||||
new ChatComponentText(EnumChatFormatting.RED + "Plaintext authentication was attempted but no user confirmation has been given to proceed")));
|
||||
return false;
|
||||
}
|
||||
}else if(authType == HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256) {
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
|
||||
int passLen = password.length();
|
||||
|
||||
digest.update((byte)((passLen >> 8) & 0xFF));
|
||||
digest.update((byte)(passLen & 0xFF));
|
||||
|
||||
for(int i = 0; i < passLen; ++i) {
|
||||
char codePoint = password.charAt(i);
|
||||
digest.update((byte)((codePoint >> 8) & 0xFF));
|
||||
digest.update((byte)(codePoint & 0xFF));
|
||||
}
|
||||
|
||||
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_SAVE, 0, 32);
|
||||
|
||||
byte[] hashed = new byte[32];
|
||||
digest.doFinal(hashed, 0);
|
||||
|
||||
digest.reset();
|
||||
|
||||
digest.update(hashed, 0, 32);
|
||||
digest.update(salt, 0, 32);
|
||||
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32);
|
||||
|
||||
digest.doFinal(hashed, 0);
|
||||
|
||||
digest.reset();
|
||||
|
||||
digest.update(hashed, 0, 32);
|
||||
digest.update(salt, 32, 32);
|
||||
digest.update(HandshakePacketTypes.EAGLER_SHA256_SALT_BASE, 0, 32);
|
||||
|
||||
digest.doFinal(hashed, 0);
|
||||
|
||||
d.writeByte(32);
|
||||
d.write(hashed);
|
||||
}else if(authType == HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) {
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
|
||||
byte[] passwd = password.getBytes(StandardCharsets.UTF_8);
|
||||
digest.update(passwd, 0, passwd.length);
|
||||
|
||||
byte[] hashed = new byte[32];
|
||||
digest.doFinal(hashed, 0);
|
||||
|
||||
byte[] toHexAndSalt = new byte[64];
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF];
|
||||
toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF];
|
||||
}
|
||||
|
||||
digest.reset();
|
||||
digest.update(toHexAndSalt, 0, 64);
|
||||
digest.update(salt, 0, salt.length);
|
||||
|
||||
digest.doFinal(hashed, 0);
|
||||
|
||||
for(int i = 0; i < 32; ++i) {
|
||||
toHexAndSalt[i << 1] = HEX[(hashed[i] >> 4) & 0xF];
|
||||
toHexAndSalt[(i << 1) + 1] = HEX[hashed[i] & 0xF];
|
||||
}
|
||||
|
||||
d.writeByte(64);
|
||||
d.write(toHexAndSalt);
|
||||
}else {
|
||||
logger.error("Unsupported authentication type: {}", authType);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed",
|
||||
new ChatComponentText(EnumChatFormatting.RED + "Unsupported authentication type: " + authType + "\n\n" + EnumChatFormatting.GRAY + "(Use a newer version of the client)")));
|
||||
return false;
|
||||
}
|
||||
}else {
|
||||
d.writeByte(0);
|
||||
}
|
||||
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
|
||||
read = awaitNextPacket(baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for login negotiation response!");
|
||||
return false;
|
||||
}
|
||||
|
||||
di = new DataInputStream(new EaglerInputStream(read));
|
||||
type = di.read();
|
||||
if(type == HandshakePacketTypes.PROTOCOL_SERVER_ALLOW_LOGIN) {
|
||||
msgLen = di.read();
|
||||
dat = new byte[msgLen];
|
||||
di.read(dat);
|
||||
|
||||
String serverUsername = ArrayUtils.asciiString(dat);
|
||||
|
||||
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();
|
||||
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());
|
||||
|
||||
bao.reset();
|
||||
d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_FINISH_LOGIN);
|
||||
PlatformNetworking.writePlayPacket(bao.toByteArray());
|
||||
|
||||
read = awaitNextPacket(baseTimeout);
|
||||
if(read == null) {
|
||||
logger.error("Read timed out while waiting for login confirmation response!");
|
||||
return false;
|
||||
}
|
||||
|
||||
di = new DataInputStream(new EaglerInputStream(read));
|
||||
type = di.read();
|
||||
if(type == HandshakePacketTypes.PROTOCOL_SERVER_FINISH_LOGIN) {
|
||||
return true;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_DENY_LOGIN) {
|
||||
msgLen = di.read();
|
||||
dat = new byte[msgLen];
|
||||
di.read(dat);
|
||||
String errStr = new String(dat, StandardCharsets.UTF_8);
|
||||
mc.displayGuiScreen(new GuiDisconnected(ret, "connect.failed", new ChatComponentText(errStr)));
|
||||
return false;
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}else if(type == HandshakePacketTypes.PROTOCOL_SERVER_ERROR) {
|
||||
showError(mc, connecting, ret, di);
|
||||
return false;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
logger.error("Exception in handshake");
|
||||
logger.error(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static byte[] awaitNextPacket(long timeout) {
|
||||
long millis = System.currentTimeMillis();
|
||||
byte[] b;
|
||||
while((b = PlatformNetworking.readPlayPacket()) == null) {
|
||||
if(PlatformNetworking.playConnectionState().isClosed()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(50l);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
if(System.currentTimeMillis() - millis > timeout) {
|
||||
PlatformNetworking.playDisconnect();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private static void showError(Minecraft mc, GuiConnecting connecting, GuiScreen scr, DataInputStream err) throws IOException {
|
||||
int errorCode = err.read();
|
||||
int msgLen = err.read();
|
||||
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());
|
||||
mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr));
|
||||
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_RATELIMIT_LOCKED) {
|
||||
RateLimitTracker.registerLockOut(PlatformNetworking.getCurrentURI());
|
||||
mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(scr));
|
||||
}else if(errorCode == HandshakePacketTypes.SERVER_ERROR_CUSTOM_MESSAGE) {
|
||||
mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", new ChatComponentText(errStr)));
|
||||
}else if(connecting != null && errorCode == HandshakePacketTypes.SERVER_ERROR_AUTHENTICATION_REQUIRED) {
|
||||
mc.displayGuiScreen(new GuiAuthenticationScreen(connecting, scr, errStr));
|
||||
}else {
|
||||
mc.displayGuiScreen(new GuiDisconnected(scr, "connect.failed", new ChatComponentText("Server Error Code " + errorCode + "\n" + errStr)));
|
||||
}
|
||||
}
|
||||
|
||||
public static GuiScreen displayAuthProtocolConfirm(int protocol, GuiScreen no, GuiScreen yes) {
|
||||
if(protocol == HandshakePacketTypes.AUTH_METHOD_PLAINTEXT) {
|
||||
return new GuiHandshakeApprove("plaintext", no, yes);
|
||||
}else if(protocol != HandshakePacketTypes.AUTH_METHOD_EAGLER_SHA256 && protocol != HandshakePacketTypes.AUTH_METHOD_AUTHME_SHA256) {
|
||||
return new GuiHandshakeApprove("unsupportedAuth", no);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] HEX = new byte[] {
|
||||
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'
|
||||
};
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class EaglercraftNetworkManager {
|
||||
|
||||
private final String address;
|
||||
private INetHandler nethandler = null;
|
||||
private EnumConnectionState packetState = EnumConnectionState.HANDSHAKING;
|
||||
private final PacketBuffer temporaryBuffer;
|
||||
private int debugPacketCounter = 0;
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("NetworkManager");
|
||||
|
||||
public EaglercraftNetworkManager(String address) {
|
||||
this.address = address;
|
||||
this.temporaryBuffer = new PacketBuffer(Unpooled.buffer(0x1FFFF));
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
PlatformNetworking.startPlayConnection(address);
|
||||
}
|
||||
|
||||
public EnumEaglerConnectionState getConnectStatus() {
|
||||
return PlatformNetworking.playConnectionState();
|
||||
}
|
||||
|
||||
public void closeChannel(IChatComponent reason) {
|
||||
PlatformNetworking.playDisconnect();
|
||||
if(nethandler != null) {
|
||||
nethandler.onDisconnect(reason);
|
||||
}
|
||||
clientDisconnected = true;
|
||||
}
|
||||
|
||||
public void setConnectionState(EnumConnectionState state) {
|
||||
packetState = state;
|
||||
}
|
||||
|
||||
public void processReceivedPackets() throws IOException {
|
||||
if(nethandler == null) return;
|
||||
byte[] next;
|
||||
|
||||
while((next = PlatformNetworking.readPlayPacket()) != null) {
|
||||
++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!");
|
||||
}
|
||||
|
||||
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 void setNetHandler(INetHandler nethandler) {
|
||||
this.nethandler = nethandler;
|
||||
}
|
||||
|
||||
public boolean isLocalChannel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isChannelOpen() {
|
||||
return getConnectStatus() == EnumEaglerConnectionState.CONNECTED;
|
||||
}
|
||||
|
||||
public boolean getIsencrypted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setCompressionTreshold(int compressionTreshold) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean clientDisconnected = false;
|
||||
|
||||
private void doClientDisconnect(IChatComponent msg) {
|
||||
if(!clientDisconnected) {
|
||||
clientDisconnected = true;
|
||||
if(nethandler != null) {
|
||||
this.nethandler.onDisconnect(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class EncryptionNotSupportedException extends UnsupportedOperationException {
|
||||
|
||||
public EncryptionNotSupportedException() {
|
||||
super("Encryption is not supported by Eaglercraft, set " +
|
||||
"online-mode=false' in server.properties to " +
|
||||
"allow Eaglercraft connections to this server");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class GuiHandshakeApprove extends GuiScreen {
|
||||
|
||||
protected String message;
|
||||
protected GuiScreen no;
|
||||
protected GuiScreen yes;
|
||||
|
||||
protected String titleString;
|
||||
protected List<String> bodyLines;
|
||||
|
||||
protected int bodyY;
|
||||
|
||||
public GuiHandshakeApprove(String message, GuiScreen no, GuiScreen yes) {
|
||||
this.message = message;
|
||||
this.no = no;
|
||||
this.yes = yes;
|
||||
}
|
||||
|
||||
public GuiHandshakeApprove(String message, GuiScreen back) {
|
||||
this(message, back, null);
|
||||
}
|
||||
|
||||
public void initGui() {
|
||||
this.buttonList.clear();
|
||||
titleString = I18n.format("handshakeApprove." + message + ".title");
|
||||
bodyLines = new ArrayList();
|
||||
int i = 0;
|
||||
boolean wasNull = true;
|
||||
while(true) {
|
||||
String line = getI18nOrNull("handshakeApprove." + message + ".body." + (i++));
|
||||
if(line == null) {
|
||||
if(wasNull) {
|
||||
break;
|
||||
}else {
|
||||
bodyLines.add("");
|
||||
wasNull = true;
|
||||
}
|
||||
}else {
|
||||
bodyLines.add(line);
|
||||
wasNull = false;
|
||||
}
|
||||
}
|
||||
int totalHeight = 10 + 10 + bodyLines.size() * 10 + 10 + 20;
|
||||
bodyY = (height - totalHeight) / 2 - 15;
|
||||
int buttonY = bodyY + totalHeight - 20;
|
||||
if(yes != null) {
|
||||
this.buttonList.add(new GuiButton(0, width / 2 + 3, buttonY, 100, 20, I18n.format("gui.no")));
|
||||
this.buttonList.add(new GuiButton(1, width / 2 - 103, buttonY, 100, 20, I18n.format("gui.yes")));
|
||||
}else {
|
||||
this.buttonList.add(new GuiButton(0, width / 2 - 100, buttonY, 200, 20, I18n.format("gui.back")));
|
||||
}
|
||||
}
|
||||
|
||||
protected void actionPerformed(GuiButton parGuiButton) {
|
||||
if(parGuiButton.id == 0) {
|
||||
mc.displayGuiScreen(no);
|
||||
}else if(parGuiButton.id == 1) {
|
||||
mc.displayGuiScreen(yes);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawScreen(int xx, int yy, float partialTicks) {
|
||||
drawBackground(0);
|
||||
drawCenteredString(fontRendererObj, titleString, width / 2, bodyY, 16777215);
|
||||
for(int i = 0, l = bodyLines.size(); i < l; ++i) {
|
||||
String s = bodyLines.get(i);
|
||||
if(s.length() > 0) {
|
||||
drawCenteredString(fontRendererObj, s, width / 2, bodyY + 20 + i * 10, 16777215);
|
||||
}
|
||||
}
|
||||
super.drawScreen(xx, yy, partialTicks);
|
||||
}
|
||||
|
||||
private String getI18nOrNull(String key) {
|
||||
String ret = I18n.format(key);
|
||||
if(key.equals(ret)) {
|
||||
return null;
|
||||
}else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class HandshakePacketTypes {
|
||||
|
||||
public static final String AUTHENTICATION_REQUIRED = "Authentication Required:";
|
||||
|
||||
public static final int PROTOCOL_CLIENT_VERSION = 0x01;
|
||||
public static final int PROTOCOL_SERVER_VERSION = 0x02;
|
||||
public static final int PROTOCOL_VERSION_MISMATCH = 0x03;
|
||||
public static final int PROTOCOL_CLIENT_REQUEST_LOGIN = 0x04;
|
||||
public static final int PROTOCOL_SERVER_ALLOW_LOGIN = 0x05;
|
||||
public static final int PROTOCOL_SERVER_DENY_LOGIN = 0x06;
|
||||
public static final int PROTOCOL_CLIENT_PROFILE_DATA = 0x07;
|
||||
public static final int PROTOCOL_CLIENT_FINISH_LOGIN = 0x08;
|
||||
public static final int PROTOCOL_SERVER_FINISH_LOGIN = 0x09;
|
||||
public static final int PROTOCOL_SERVER_ERROR = 0xFF;
|
||||
|
||||
public static final int STATE_OPENED = 0x00;
|
||||
public static final int STATE_CLIENT_VERSION = 0x01;
|
||||
public static final int STATE_CLIENT_LOGIN = 0x02;
|
||||
public static final int STATE_CLIENT_COMPLETE = 0x03;
|
||||
|
||||
public static final int SERVER_ERROR_UNKNOWN_PACKET = 0x01;
|
||||
public static final int SERVER_ERROR_INVALID_PACKET = 0x02;
|
||||
public static final int SERVER_ERROR_WRONG_PACKET = 0x03;
|
||||
public static final int SERVER_ERROR_EXCESSIVE_PROFILE_DATA = 0x04;
|
||||
public static final int SERVER_ERROR_DUPLICATE_PROFILE_DATA = 0x05;
|
||||
public static final int SERVER_ERROR_RATELIMIT_BLOCKED = 0x06;
|
||||
public static final int SERVER_ERROR_RATELIMIT_LOCKED = 0x07;
|
||||
public static final int SERVER_ERROR_CUSTOM_MESSAGE = 0x08;
|
||||
public static final int SERVER_ERROR_AUTHENTICATION_REQUIRED = 0x09;
|
||||
|
||||
public static final int AUTH_METHOD_NONE = 0x0;
|
||||
public static final int AUTH_METHOD_EAGLER_SHA256 = 0x01;
|
||||
public static final int AUTH_METHOD_AUTHME_SHA256 = 0x02;
|
||||
public static final int AUTH_METHOD_PLAINTEXT = 0xFF;
|
||||
|
||||
public static final byte[] EAGLER_SHA256_SALT_BASE = new byte[] { (byte) 117, (byte) 43, (byte) 1, (byte) 112,
|
||||
(byte) 75, (byte) 3, (byte) 188, (byte) 61, (byte) 121, (byte) 31, (byte) 34, (byte) 181, (byte) 234,
|
||||
(byte) 31, (byte) 247, (byte) 72, (byte) 12, (byte) 168, (byte) 138, (byte) 45, (byte) 143, (byte) 77,
|
||||
(byte) 118, (byte) 245, (byte) 187, (byte) 242, (byte) 188, (byte) 219, (byte) 160, (byte) 235, (byte) 235,
|
||||
(byte) 68 };
|
||||
|
||||
public static final byte[] EAGLER_SHA256_SALT_SAVE = new byte[] { (byte) 49, (byte) 25, (byte) 39, (byte) 38,
|
||||
(byte) 253, (byte) 85, (byte) 70, (byte) 245, (byte) 71, (byte) 150, (byte) 253, (byte) 206, (byte) 4,
|
||||
(byte) 26, (byte) 198, (byte) 249, (byte) 145, (byte) 251, (byte) 232, (byte) 174, (byte) 186, (byte) 98,
|
||||
(byte) 27, (byte) 232, (byte) 55, (byte) 144, (byte) 83, (byte) 21, (byte) 36, (byte) 55, (byte) 170,
|
||||
(byte) 118 };
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
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();
|
||||
|
||||
public static boolean isLockedOut(String addr) {
|
||||
Long lockoutStatus = lockout.get(addr);
|
||||
return lockoutStatus != null && System.currentTimeMillis() - lockoutStatus.longValue() < 300000l;
|
||||
}
|
||||
|
||||
public static boolean isProbablyLockedOut(String addr) {
|
||||
return blocks.containsKey(addr) || lockout.containsKey(addr);
|
||||
}
|
||||
|
||||
public static void registerBlock(String addr) {
|
||||
blocks.put(addr, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static void registerLockOut(String addr) {
|
||||
long millis = System.currentTimeMillis();
|
||||
blocks.put(addr, millis);
|
||||
lockout.put(addr, millis);
|
||||
}
|
||||
|
||||
public static void tick() {
|
||||
long millis = System.currentTimeMillis();
|
||||
if(millis - lastTickUpdate > 5000l) {
|
||||
lastTickUpdate = millis;
|
||||
Iterator<Long> blocksItr = blocks.values().iterator();
|
||||
while(blocksItr.hasNext()) {
|
||||
if(millis - blocksItr.next().longValue() > 900000l) {
|
||||
blocksItr.remove();
|
||||
}
|
||||
}
|
||||
blocksItr = lockout.values().iterator();
|
||||
while(blocksItr.hasNext()) {
|
||||
if(millis - blocksItr.next().longValue() > 900000l) {
|
||||
blocksItr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.socket;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
|
||||
*
|
||||
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
|
||||
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
|
||||
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
|
||||
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
|
||||
*
|
||||
* NOT FOR COMMERCIAL OR MALICIOUS USE
|
||||
*
|
||||
* (please read the 'LICENSE' file this repo's root directory for more info)
|
||||
*
|
||||
*/
|
||||
public class ServerQueryDispatch {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger("QueryDispatch");
|
||||
|
||||
public static IServerQuery sendServerQuery(String uri, String accept) {
|
||||
logger.info("Sending {} query to: \"{}\"", accept, uri);
|
||||
return PlatformNetworking.sendServerQuery(uri, accept);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user