Skip to content

Commit 1f86cb9

Browse files
committed
finish eag1.8-->eag1.5 skin support
also make it work on latest dev viaproxy
1 parent 5761ac3 commit 1f86cb9

File tree

3 files changed

+173
-37
lines changed

3 files changed

+173
-37
lines changed

src/main/java/me/ayunami2000/ayunViaProxyEagUtils/EaglerServerHandler.java

Lines changed: 120 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.netty.channel.ChannelHandlerContext;
99
import io.netty.handler.codec.MessageToMessageCodec;
1010
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
11+
import net.jodah.expiringmap.ExpiringMap;
1112
import net.raphimc.netminecraft.constants.MCPackets;
1213
import net.raphimc.netminecraft.netty.connection.NetClient;
1314
import net.raphimc.netminecraft.packet.PacketTypes;
@@ -19,16 +20,22 @@
1920
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
2021
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
2122

23+
import java.io.IOException;
2224
import java.nio.charset.StandardCharsets;
23-
import java.util.ArrayList;
25+
import java.util.HashMap;
2426
import java.util.List;
27+
import java.util.Map;
2528
import java.util.UUID;
29+
import java.util.concurrent.TimeUnit;
2630

2731
public class EaglerServerHandler extends MessageToMessageCodec<BinaryWebSocketFrame, ByteBuf> {
2832
private final VersionEnum version;
2933
private final String password;
3034
private final NetClient proxyConnection;
31-
private final List<UUID> skinsBeingFetched = new ArrayList<>();
35+
private final Map<UUID, String> uuidStringMap = new HashMap<>();
36+
private final Map<Integer, UUID> eidUuidMap = new HashMap<>();
37+
private final ExpiringMap<Short, UUID> skinsBeingFetched = ExpiringMap.builder().expiration(1, TimeUnit.MINUTES).build();
38+
private short skinFetchCounter = 0;
3239
private ByteBuf serverBoundPartialPacket = Unpooled.EMPTY_BUFFER;
3340
private ByteBuf clientBoundPartialPacket = Unpooled.EMPTY_BUFFER;
3441
public EaglerServerHandler(NetClient proxyConnection, String password) {
@@ -47,6 +54,30 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
4754
@Override
4855
public void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
4956
if (version.isNewerThan(VersionEnum.r1_6_4)) {
57+
if (in.readableBytes() == 2 && in.getUnsignedByte(0) == 0xFE && in.getUnsignedByte(1) == 0x01) {
58+
// todo: legacy ping
59+
ctx.close();
60+
return;
61+
}
62+
63+
int len = PacketTypes.readVarInt(in);
64+
ByteBuf bb = ctx.alloc().buffer(len);
65+
bb.writeBytes(in);
66+
int id = PacketTypes.readVarInt(bb);
67+
if (id == 0x00) {
68+
PacketTypes.readVarInt(bb);
69+
PacketTypes.readString(bb, 32767);
70+
bb.readUnsignedShort();
71+
int nextState = PacketTypes.readVarInt(bb);
72+
if (nextState == 1) {
73+
// todo: ping
74+
ctx.close();
75+
return;
76+
}
77+
}
78+
bb.release();
79+
in.resetReaderIndex();
80+
5081
if (handshakeState == 0) {
5182
handshakeState = 1;
5283
if (((ProxyConnection) proxyConnection).getGameProfile() == null) {
@@ -80,6 +111,10 @@ public void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
80111
bb.readerIndex(readerIndex);
81112
bb.readBytes(packet, len);
82113
encodeOld(ctx, packet, out);
114+
if (!ctx.channel().isOpen()) {
115+
out.add(Unpooled.EMPTY_BUFFER);
116+
return;
117+
}
83118
}
84119
} catch (Exception e) {
85120
bb.readerIndex(readerIndex);
@@ -98,18 +133,61 @@ public void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
98133
}
99134

100135
public void encodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
136+
if (in.readableBytes() == 2 && in.getUnsignedByte(0) == 0xFE && in.getUnsignedByte(1) == 0x01) {
137+
// todo: legacy ping
138+
ctx.close();
139+
return;
140+
}
101141
if (in.readableBytes() >= 2 && in.getUnsignedByte(0) == 2) {
102142
in.setByte(1, in.getUnsignedByte(1) + 8);
103143
}
104-
if (in.readableBytes() >= 1 && in.getUnsignedByte(0) == 0xFD) {
144+
if (in.getUnsignedByte(0) == 0xFD) {
105145
return;
106146
}
107147
if (in.readableBytes() >= 3 && in.getUnsignedByte(0) == 250) {
108148
in.skipBytes(1);
109149
String tag;
150+
byte[] msg;
110151
try {
111152
tag = Types1_6_4.STRING.read(in);
112153
if (tag.equals("EAG|Skins-1.8")) {
154+
msg = new byte[in.readShort()];
155+
in.readBytes(msg);
156+
if (msg.length == 0) {
157+
throw new IOException("Zero-length packet recieved");
158+
}
159+
final int packetId = msg[0] & 0xFF;
160+
switch (packetId) {
161+
case 3: {
162+
if (msg.length != 17) {
163+
throw new IOException("Invalid length " + msg.length + " for skin request packet");
164+
}
165+
final UUID searchUUID = SkinPackets.bytesToUUID(msg, 1);
166+
if (uuidStringMap.containsKey(searchUUID)) {
167+
short id = skinFetchCounter++;
168+
skinsBeingFetched.put(id, searchUUID);
169+
String name = uuidStringMap.get(searchUUID);
170+
ByteBuf bb = ctx.alloc().buffer();
171+
bb.writeByte((byte) 250);
172+
Types1_6_4.STRING.write(bb, "EAG|FetchSkin");
173+
ByteBuf bbb = ctx.alloc().buffer();
174+
bbb.writeByte((byte) ((id >> 8) & 0xFF));
175+
bbb.writeByte((byte) (id & 0xFF));
176+
bbb.writeBytes(name.getBytes(StandardCharsets.UTF_8));
177+
bb.writeShort(bbb.readableBytes());
178+
bb.writeBytes(bbb);
179+
bbb.release();
180+
out.add(new BinaryWebSocketFrame(bb));
181+
}
182+
break;
183+
}
184+
case 6: {
185+
break;
186+
}
187+
default: {
188+
throw new IOException("Unknown packet type " + packetId);
189+
}
190+
}
113191
return;
114192
}
115193
} catch (Exception ignored) {
@@ -190,26 +268,35 @@ public void decode(ChannelHandlerContext ctx, BinaryWebSocketFrame in, List<Obje
190268
}
191269
public void decodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
192270
if (in.getUnsignedByte(0) == 0x14) {
193-
in.skipBytes(5);
194271
try {
272+
in.skipBytes(1);
273+
int eid = in.readInt();
195274
String name = Types1_6_4.STRING.read(in);
196275
UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8));
197-
skinsBeingFetched.add(uuid);
198-
ByteBuf bb = ctx.alloc().buffer();
199-
bb.writeByte((byte) 250);
200-
Types1_6_4.STRING.write(bb, "EAG|FetchSkin");
201-
ByteBuf bbb = ctx.alloc().buffer();
202-
bbb.writeByte((byte) 0);
203-
bbb.writeByte((byte) 0);
204-
bbb.writeBytes(name.getBytes(StandardCharsets.UTF_8));
205-
bb.writeShort(bbb.readableBytes());
206-
bb.writeBytes(bbb);
207-
bbb.release();
208-
ctx.writeAndFlush(new BinaryWebSocketFrame(bb));
276+
eidUuidMap.put(eid, uuid);
277+
uuidStringMap.put(uuid, name);
278+
} catch (Exception ignored) {
279+
}
280+
in.resetReaderIndex();
281+
}
282+
if (in.getUnsignedByte(0) == 0x1D) {
283+
try {
284+
in.skipBytes(1);
285+
short count = in.readUnsignedByte();
286+
for (short i = 0; i < count; i++) {
287+
int eid = in.readInt();
288+
if (eidUuidMap.containsKey(eid)) {
289+
uuidStringMap.remove(eidUuidMap.remove(eid));
290+
}
291+
}
209292
} catch (Exception ignored) {
210293
}
211294
in.resetReaderIndex();
212295
}
296+
if (in.getUnsignedByte(0) == 0x09) {
297+
eidUuidMap.clear();
298+
uuidStringMap.clear();
299+
}
213300
if (in.getUnsignedByte(0) == 0xFD) {
214301
in.writerIndex(0);
215302
in.writeByte((byte) 0xCD);
@@ -224,19 +311,15 @@ public void decodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
224311
try {
225312
tag = Types1_6_4.STRING.read(in);
226313
if (tag.equals("EAG|UserSkin")) {
227-
if (skinsBeingFetched.isEmpty()) {
228-
return;
229-
}
230314
msg = new byte[in.readShort()];
231315
in.readBytes(msg);
232-
if (msg.length < 8192) {
316+
short id = (short) ((msg[0] << 8) + msg[1]);
317+
if (!skinsBeingFetched.containsKey(id)) {
233318
return;
234319
}
235-
// TODO: FIX LOL!!
236-
// also todo: handle status packets
237-
byte[] res = new byte[msg.length > 16384 ? 16384 : 8192];
238-
System.arraycopy(msg, 1, res, 0, res.length);
239-
if (res.length < 16384) {
320+
byte[] res = new byte[msg.length - 3];
321+
System.arraycopy(msg, 3, res, 0, res.length);
322+
if (res.length == 8192) {
240323
final int[] tmp1 = new int[2048];
241324
final int[] tmp2 = new int[4096];
242325
for (int i = 0; i < tmp1.length; ++i) {
@@ -247,19 +330,29 @@ public void decodeOld(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
247330
for (int i = 0; i < tmp2.length; ++i) {
248331
System.arraycopy(Ints.toByteArray(tmp2[i]), 0, res, i * 4, 4);
249332
}
250-
} else {
333+
} else if (res.length == 16384) {
251334
for (int j = 0; j < res.length; j += 4) {
252335
final byte tmp3 = res[j + 3];
253336
res[j + 3] = res[j + 2];
254337
res[j + 2] = res[j + 1];
255338
res[j + 1] = res[j];
256339
res[j] = tmp3;
257340
}
341+
} else {
342+
ByteBuf bb = ctx.alloc().buffer();
343+
bb.writeByte((byte) 250);
344+
Types1_6_4.STRING.write(bb, "EAG|Skins-1.8");
345+
byte[] data = SkinPackets.makePresetResponse(skinsBeingFetched.remove(id));
346+
bb.writeShort(data.length);
347+
bb.writeBytes(data);
348+
out.add(bb);
349+
return;
258350
}
351+
UUID uuid = skinsBeingFetched.remove(id);
259352
ByteBuf bb = ctx.alloc().buffer();
260353
bb.writeByte((byte) 250);
261354
Types1_6_4.STRING.write(bb, "EAG|Skins-1.8");
262-
byte[] data = SkinPackets.makeCustomResponse(skinsBeingFetched.remove(0), 0, res);
355+
byte[] data = SkinPackets.makeCustomResponse(uuid, 0, res);
263356
bb.writeShort(data.length);
264357
bb.writeBytes(data);
265358
out.add(bb);

src/main/java/me/ayunami2000/ayunViaProxyEagUtils/Main.java

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package me.ayunami2000.ayunViaProxyEagUtils;
22

33
import io.netty.buffer.ByteBuf;
4-
import io.netty.channel.Channel;
5-
import io.netty.channel.ChannelHandlerContext;
6-
import io.netty.channel.ChannelInboundHandlerAdapter;
4+
import io.netty.channel.*;
75
import io.netty.handler.codec.http.*;
86
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
97
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
@@ -25,6 +23,7 @@
2523
import net.raphimc.viaproxy.plugins.events.types.ITyped;
2624
import net.raphimc.viaproxy.proxy.session.LegacyProxyConnection;
2725
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
26+
import net.raphimc.viaproxy.proxy.util.ChannelUtil;
2827
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
2928

3029
import javax.net.ssl.*;
@@ -94,15 +93,37 @@ public void onEvent(final Proxy2ServerChannelInitializeEvent event) throws URISy
9493

9594

9695

97-
98-
// c2p.attr(secureWs).set(true);
96+
c2p.attr(secureWs).set(true); // todo: THIS!!
9997

10098

10199

102100

103101

104102
if (c2p.hasAttr(secureWs)) {
105103
doWsServerStuff(ch, proxyConnection, c2p, addr);
104+
if (!event.isLegacyPassthrough()) {
105+
ch.pipeline().addFirst("handshake-waiter", new ChannelOutboundHandlerAdapter() {
106+
private boolean hasHandshake = false;
107+
108+
@Override
109+
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
110+
if (msg instanceof ByteBuf) {
111+
hasHandshake = true;
112+
ChannelUtil.restoreAutoRead(c2p);
113+
}
114+
super.write(ctx, msg, promise);
115+
}
116+
117+
@Override
118+
public void flush(ChannelHandlerContext ctx) throws Exception {
119+
super.flush(ctx);
120+
if (hasHandshake) {
121+
ch.pipeline().remove(this);
122+
ChannelUtil.disableAutoRead(c2p);
123+
}
124+
}
125+
});
126+
}
106127
}
107128
}
108129
}
@@ -114,13 +135,33 @@ private static void doWsServerStuff(Channel ch, NetClient proxyConnection, Chann
114135
} else if (ch.pipeline().get(MCPipeline.ENCRYPTION_HANDLER_NAME) != null) {
115136
ch.pipeline().remove(MCPipeline.ENCRYPTION_HANDLER_NAME);
116137
}
138+
if (c2p.pipeline().get("ayun-eag-voice") != null) {
139+
c2p.pipeline().remove("ayun-eag-voice");
140+
}
141+
if (c2p.pipeline().get("ayun-eag-skin") != null) {
142+
c2p.pipeline().remove("ayun-eag-skin");
143+
}
144+
if (c2p.pipeline().get("ayun-eag-x-login") != null) {
145+
c2p.pipeline().remove("ayun-eag-x-login");
146+
}
147+
if (c2p.pipeline().get("ayun-eag-skin-x") != null) {
148+
c2p.pipeline().remove("ayun-eag-skin-x");
149+
}
117150
StringBuilder url = new StringBuilder("ws");
118151
boolean secure = c2p.attr(secureWs).get();
119152
if (secure) {
120153
final SSLEngine sslEngine = sc.createSSLEngine(addr.getAddress(), addr.getPort());
121154
sslEngine.setUseClientMode(true);
122155
sslEngine.setNeedClientAuth(false);
123-
ch.pipeline().addFirst("eag-server-ssl", new SslHandler(sslEngine));
156+
ch.pipeline().addFirst("eag-server-ssl", new SslHandler(sslEngine) {
157+
@Override
158+
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
159+
if (this.handshakeFuture().cause() != null) {
160+
ExceptionUtil.handleNettyException(ctx, this.handshakeFuture().cause(), null);
161+
}
162+
super.closeOutbound();
163+
}
164+
});
124165
url.append("s");
125166
ch.pipeline().addAfter("eag-server-ssl", "eag-server-http-codec", new HttpClientCodec());
126167
} else {
@@ -163,9 +204,7 @@ public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt
163204
super.userEventTriggered(ctx, evt);
164205
if (evt instanceof EaglercraftInitialHandler.EaglercraftClientConnected) {
165206
ctx.pipeline().remove("ayun-eag-detector");
166-
if (!ctx.channel().hasAttr(secureWs)) {
167-
ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-utils-init", new EaglerUtilsInitHandler());
168-
}
207+
ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-utils-init", new EaglerUtilsInitHandler());
169208
}
170209
}
171210

src/main/java/me/ayunami2000/ayunViaProxyEagUtils/SkinService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
77
import net.raphimc.netminecraft.constants.MCPackets;
88
import net.raphimc.netminecraft.packet.PacketTypes;
9+
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
910

1011
import javax.imageio.ImageIO;
1112
import java.awt.image.DataBufferByte;
@@ -50,14 +51,17 @@ public void processGetOtherSkin(final UUID searchUUID, final ChannelHandlerConte
5051
for (int i = 0; i < tmp2.length; ++i) {
5152
System.arraycopy(Ints.toByteArray(tmp2[i]), 0, res, i * 4, 4);
5253
}
53-
} else {
54+
} else if (res.length == 16384) {
5455
for (int j = 0; j < res.length; j += 4) {
5556
final byte tmp3 = res[j + 3];
5657
res[j + 3] = res[j + 2];
5758
res[j + 2] = res[j + 1];
5859
res[j + 1] = res[j];
5960
res[j] = tmp3;
6061
}
62+
} else {
63+
sendData(sender, SkinPackets.makePresetResponse(searchUUID));
64+
return;
6165
}
6266
sendData(sender, SkinPackets.makeCustomResponse(searchUUID, 0, res));
6367
} else {

0 commit comments

Comments
 (0)