88import io .netty .channel .ChannelHandlerContext ;
99import io .netty .handler .codec .MessageToMessageCodec ;
1010import io .netty .handler .codec .http .websocketx .BinaryWebSocketFrame ;
11+ import net .jodah .expiringmap .ExpiringMap ;
1112import net .raphimc .netminecraft .constants .MCPackets ;
1213import net .raphimc .netminecraft .netty .connection .NetClient ;
1314import net .raphimc .netminecraft .packet .PacketTypes ;
1920import net .raphimc .viaproxy .proxy .session .ProxyConnection ;
2021import net .raphimc .viaproxy .proxy .util .ExceptionUtil ;
2122
23+ import java .io .IOException ;
2224import java .nio .charset .StandardCharsets ;
23- import java .util .ArrayList ;
25+ import java .util .HashMap ;
2426import java .util .List ;
27+ import java .util .Map ;
2528import java .util .UUID ;
29+ import java .util .concurrent .TimeUnit ;
2630
2731public 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 );
0 commit comments