diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java index 8a1a0e8..ea762df 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/eaglercraft/EaglercraftHandler.java @@ -18,6 +18,13 @@ package net.raphimc.viaproxy.proxy.client2proxy.eaglercraft; import com.google.common.net.HostAndPort; +import com.viaversion.viaversion.libs.gson.JsonArray; +import com.viaversion.viaversion.libs.gson.JsonObject; +import com.viaversion.viaversion.libs.gson.JsonParser; +import com.viaversion.viaversion.protocols.base.ClientboundStatusPackets; +import com.viaversion.viaversion.protocols.base.ServerboundHandshakePackets; +import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets; +import com.viaversion.viaversion.protocols.base.ServerboundStatusPackets; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; @@ -25,6 +32,7 @@ import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer; import net.raphimc.netminecraft.constants.ConnectionState; import net.raphimc.netminecraft.constants.MCPackets; import net.raphimc.netminecraft.constants.MCPipeline; @@ -36,8 +44,13 @@ import net.raphimc.viaproxy.ViaProxy; import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer; import net.raphimc.viaproxy.util.logging.Logger; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import java.util.List; import java.util.UUID; @@ -69,12 +82,69 @@ public class EaglercraftHandler extends MessageToMessageCodec out) { - if (this.state != State.LOGIN_COMPLETE) { + protected void encode(ChannelHandlerContext ctx, ByteBuf in, List out) throws IOException { + if (this.state == State.STATUS) { + final int packetId = PacketTypes.readVarInt(in); + if (packetId != ClientboundStatusPackets.STATUS_RESPONSE.getId()) { + throw new IllegalStateException("Unexpected packet id " + packetId); + } + final JsonObject root = JsonParser.parseString(PacketTypes.readString(in, Short.MAX_VALUE)).getAsJsonObject(); + + final JsonObject response = new JsonObject(); + response.addProperty("name", "ViaProxy"); + response.addProperty("brand", "ViaProxy"); + if (root.has("version")) { + response.add("vers", root.getAsJsonObject("version").get("name")); + } else { + response.addProperty("vers", "Unknown"); + } + response.addProperty("cracked", true); + response.addProperty("secure", false); + response.addProperty("time", System.currentTimeMillis()); + response.addProperty("uuid", UUID.randomUUID().toString()); + response.addProperty("type", "motd"); + + final JsonObject data = new JsonObject(); + data.addProperty("cache", false); + final JsonArray motd = new JsonArray(); + if (root.has("description")) { + final String[] motdLines = TextComponentSerializer.V1_8.deserialize(root.get("description").toString()).asLegacyFormatString().split("\n"); + for (String motdLine : motdLines) { + motd.add(motdLine); + } + } + data.add("motd", motd); + data.addProperty("icon", root.has("favicon")); + if (root.has("players")) { + final JsonObject javaPlayers = root.getAsJsonObject("players"); + data.add("online", javaPlayers.get("online")); + data.add("max", javaPlayers.get("max")); + final JsonArray players = new JsonArray(); + if (javaPlayers.has("sample")) { + javaPlayers.getAsJsonArray("sample").forEach(player -> players.add(TextComponentSerializer.V1_8.deserialize(player.getAsJsonObject().get("name").getAsString()).asLegacyFormatString())); + } + data.add("players", players); + } + response.add("data", data); + out.add(new TextWebSocketFrame(response.toString())); + + if (root.has("favicon")) { + final BufferedImage icon = ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(root.get("favicon").getAsString().substring(22).replace("\n", "").getBytes(StandardCharsets.UTF_8)))); + final int[] pixels = icon.getRGB(0, 0, 64, 64, null, 0, 64); + final byte[] iconPixels = new byte[64 * 64 * 4]; + for (int i = 0; i < 64 * 64; ++i) { + iconPixels[i * 4] = (byte) ((pixels[i] >> 16) & 0xFF); + iconPixels[i * 4 + 1] = (byte) ((pixels[i] >> 8) & 0xFF); + iconPixels[i * 4 + 2] = (byte) (pixels[i] & 0xFF); + iconPixels[i * 4 + 3] = (byte) ((pixels[i] >> 24) & 0xFF); + } + out.add(new BinaryWebSocketFrame(ctx.alloc().buffer().writeBytes(iconPixels))); + } + } else if (this.state == State.LOGIN_COMPLETE) { + out.add(new BinaryWebSocketFrame(in.retain())); + } else { throw new IllegalStateException("Cannot send packets before login is completed"); } - - out.add(new BinaryWebSocketFrame(in.retain())); } @Override @@ -207,11 +277,9 @@ public class EaglercraftHandler extends MessageToMessageCodec