Skip to content

Commit 8a154e8

Browse files
committed
Add optional online-mode skin support
1 parent 0dba9a0 commit 8a154e8

13 files changed

+319
-49
lines changed

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package me.ayunami2000.ayunViaProxyEagUtils;
22

3+
import com.google.gson.JsonObject;
4+
import com.viaversion.viaversion.util.GsonUtil;
35
import io.netty.buffer.ByteBuf;
46
import io.netty.channel.ChannelHandlerContext;
57
import io.netty.channel.ChannelInboundHandlerAdapter;
68
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
79
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
10+
import net.raphimc.vialoader.util.VersionEnum;
11+
import net.raphimc.viaproxy.ViaProxy;
12+
import net.raphimc.viaproxy.cli.options.Options;
813
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
914

15+
import javax.imageio.ImageIO;
16+
import java.awt.image.DataBufferByte;
1017
import java.io.ByteArrayOutputStream;
1118
import java.io.DataOutputStream;
1219
import java.io.IOException;
20+
import java.io.InputStreamReader;
21+
import java.net.URL;
22+
import java.net.URLConnection;
1323
import java.nio.charset.StandardCharsets;
14-
import java.util.HashMap;
1524
import java.util.UUID;
1625
import java.util.concurrent.ConcurrentHashMap;
1726

@@ -41,10 +50,12 @@ public EaglerSkinHandler(final String username) {
4150
this.user = username;
4251
}
4352

53+
@Override
4454
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
4555
ExceptionUtil.handleNettyException(ctx, cause, null);
4656
}
4757

58+
@Override
4859
public void channelRead(final ChannelHandlerContext ctx, final Object obj) throws Exception {
4960
final UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.user).getBytes(StandardCharsets.UTF_8));
5061
if (!EaglerSkinHandler.users.containsKey(uuid) && ctx.channel().isActive()) {
@@ -102,6 +113,48 @@ public void channelRead(final ChannelHandlerContext ctx, final Object obj) throw
102113
conc = conc2;
103114
}
104115
sendData(ctx, "EAG|UserSkin", conc);
116+
} else if (EaglerXSkinHandler.skinService.loadPremiumSkins) {
117+
try {
118+
URL url = new URL("https://playerdb.co/api/player/minecraft/" + fetch);
119+
URLConnection urlConnection = url.openConnection();
120+
urlConnection.setRequestProperty("user-agent", "Mozilla/5.0 ViaProxy/" + ViaProxy.VERSION);
121+
JsonObject json = GsonUtil.getGson().fromJson(new InputStreamReader(urlConnection.getInputStream()), JsonObject.class);
122+
if (json.get("success").getAsBoolean()) {
123+
String premiumUUID = json.getAsJsonObject("data").getAsJsonObject("player").getAsJsonObject("meta").get("id").getAsString();
124+
byte[] tmp = EaglerXSkinHandler.skinService.fetchSkinPacket(uuidFetch, "https://crafatar.com/skins/" + premiumUUID);
125+
if (tmp != null) {
126+
EaglerXSkinHandler.skinService.registerEaglercraftPlayer(uuidFetch, tmp);
127+
if ((data = EaglerSkinHandler.skinCollection.get(uuidFetch)) != null) {
128+
byte[] conc = new byte[data.length + 2];
129+
conc[0] = msg[0];
130+
conc[1] = msg[1];
131+
System.arraycopy(data, 0, conc, 2, data.length);
132+
if ((data = EaglerSkinHandler.capeCollection.get(uuidFetch)) != null) {
133+
final byte[] conc2 = new byte[conc.length + data.length];
134+
System.arraycopy(conc, 0, conc2, 0, conc.length);
135+
System.arraycopy(data, 0, conc2, conc.length, data.length);
136+
conc = conc2;
137+
} else {
138+
try {
139+
tmp = ((DataBufferByte) ImageIO.read(new URL("https://crafatar.com/capes/" + premiumUUID)).getRaster().getDataBuffer()).getData();
140+
data = new byte[4098];
141+
data[0] = data[1] = 0;
142+
// todo: figure out if we need to shuffle around colors
143+
System.arraycopy(tmp, 0, data, 2, tmp.length);
144+
EaglerSkinHandler.capeCollection.put(uuid, data);
145+
final byte[] conc2 = new byte[conc.length + data.length];
146+
System.arraycopy(conc, 0, conc2, 0, conc.length);
147+
System.arraycopy(data, 0, conc2, conc.length, data.length);
148+
conc = conc2;
149+
} catch (Exception ignored) {
150+
}
151+
}
152+
sendData(ctx, "EAG|UserSkin", conc);
153+
}
154+
}
155+
}
156+
} catch (Exception ignored) {
157+
}
105158
}
106159
}
107160
bb.release();
@@ -143,6 +196,7 @@ public void channelRead(final ChannelHandlerContext ctx, final Object obj) throw
143196
super.channelRead(ctx, obj);
144197
}
145198

199+
@Override
146200
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
147201
super.channelInactive(ctx);
148202
final UUID uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.user).getBytes(StandardCharsets.UTF_8));

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ public EaglerVoiceHandler(final String username) {
3838
this.user = username;
3939
}
4040

41+
@Override
4142
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
4243
ExceptionUtil.handleNettyException(ctx, cause, null);
4344
}
4445

46+
@Override
4547
public void channelRead(final ChannelHandlerContext ctx, final Object obj) throws Exception {
46-
if (((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).state == EaglercraftHandler.State.LOGIN_COMPLETE && !ctx.channel().hasAttr((AttributeKey) EaglerVoiceHandler.VOICE_ENABLED)) {
48+
if (((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).state == EaglercraftHandler.State.LOGIN_COMPLETE && !ctx.channel().hasAttr(EaglerVoiceHandler.VOICE_ENABLED)) {
4749
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
4850
final DataOutputStream dos = new DataOutputStream(baos);
4951
dos.write(0);
@@ -54,7 +56,7 @@ public void channelRead(final ChannelHandlerContext ctx, final Object obj) throw
5456
}
5557
sendData(ctx, baos.toByteArray());
5658
this.sendVoicePlayers(this.user);
57-
ctx.channel().attr((AttributeKey) EaglerVoiceHandler.VOICE_ENABLED).set(true);
59+
ctx.channel().attr(EaglerVoiceHandler.VOICE_ENABLED).set(true);
5860
}
5961
if (obj instanceof BinaryWebSocketFrame) {
6062
final ByteBuf bb = ((BinaryWebSocketFrame) obj).content();
@@ -222,6 +224,7 @@ public void channelRead(final ChannelHandlerContext ctx, final Object obj) throw
222224
super.channelRead(ctx, obj);
223225
}
224226

227+
@Override
225228
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
226229
super.channelInactive(ctx);
227230
this.removeUser(this.user);
@@ -233,13 +236,13 @@ public void sendVoicePlayers(final String name) {
233236
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
234237
final DataOutputStream dos = new DataOutputStream(baos);
235238
dos.write(5);
236-
final Set<String> mostlyGlobalPlayers = new HashSet<>();
239+
final Set<String> mostlyGlobalPlayers = ConcurrentHashMap.newKeySet();
237240
for (final String username : EaglerVoiceHandler.voicePlayers.keySet()) {
238241
if (!username.equals(name) && EaglerVoiceHandler.voicePairs.stream().noneMatch(pair -> (pair[0].equals(name) && pair[1].equals(username)) || (pair[0].equals(username) && pair[1].equals(name)))) {
239242
mostlyGlobalPlayers.add(username);
240243
}
241244
}
242-
if (mostlyGlobalPlayers.size() > 0) {
245+
if (!mostlyGlobalPlayers.isEmpty()) {
243246
dos.writeInt(mostlyGlobalPlayers.size());
244247
for (final String username : mostlyGlobalPlayers) {
245248
dos.writeUTF(username);
@@ -293,8 +296,8 @@ private boolean checkVoicePair(final String user1, final String user2) {
293296
EaglerVoiceHandler.iceServers.add("stun:stun3.l.google.com:19302");
294297
EaglerVoiceHandler.iceServers.add("stun:stun4.l.google.com:19302");
295298
EaglerVoiceHandler.iceServers.add("stun:openrelay.metered.ca:80");
296-
final Map<String, Object> turnServerList = new HashMap<>();
297-
HashMap<String, Object> n = new HashMap<>();
299+
final Map<String, Map<String, String>> turnServerList = new HashMap<>();
300+
HashMap<String, String> n = new HashMap<>();
298301
n.put("url", "turn:openrelay.metered.ca:80");
299302
n.put("username", "openrelayproject");
300303
n.put("password", "openrelayproject");
@@ -309,12 +312,9 @@ private boolean checkVoicePair(final String user1, final String user2) {
309312
n.put("username", "openrelayproject");
310313
n.put("password", "openrelayproject");
311314
turnServerList.put("openrelay3", n);
312-
for (final Map.Entry<String, Object> trn : turnServerList.entrySet()) {
313-
final Object o = trn.getValue();
314-
if (o instanceof Map) {
315-
final Map o2 = (Map) o;
316-
EaglerVoiceHandler.iceServers.add("" + o2.get("url") + ";" + o2.get("username") + ";" + o2.get("password"));
317-
}
315+
for (final Map.Entry<String, Map<String, String>> trn : turnServerList.entrySet()) {
316+
final Map<String, String> o = trn.getValue();
317+
EaglerVoiceHandler.iceServers.add(o.get("url") + ";" + o.get("username") + ";" + o.get("password"));
318318
}
319319
VOICE_ENABLED = AttributeKey.valueOf("ayun-voice-enabled");
320320
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ public EaglerXLoginHandler() {
1515
this.counter = 0;
1616
}
1717

18+
@Override
19+
@SuppressWarnings("deprecation")
1820
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
1921
ExceptionUtil.handleNettyException(ctx, cause, null);
2022
}
2123

24+
@Override
2225
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception {
2326
if (msg instanceof BinaryWebSocketFrame) {
2427
final ByteBuf bb = ((BinaryWebSocketFrame) msg).content();

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
99

1010
import java.nio.charset.StandardCharsets;
11-
import java.util.HashMap;
12-
import java.util.Map;
1311
import java.util.UUID;
1412
import java.util.concurrent.ConcurrentHashMap;
1513

@@ -24,10 +22,12 @@ public EaglerXSkinHandler() {
2422
this.pluginMessageId = -1;
2523
}
2624

25+
@Override
2726
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
2827
ExceptionUtil.handleNettyException(ctx, cause, null);
2928
}
3029

30+
@Override
3131
public void channelRead(final ChannelHandlerContext ctx, final Object obj) throws Exception {
3232
final EaglercraftHandler.State state = ((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).state;
3333
if (state == EaglercraftHandler.State.LOGIN && obj instanceof BinaryWebSocketFrame) {
@@ -94,6 +94,7 @@ public void channelRead(final ChannelHandlerContext ctx, final Object obj) throw
9494
super.channelRead(ctx, obj);
9595
}
9696

97+
@Override
9798
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
9899
super.channelInactive(ctx);
99100
if (this.user != null) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ protected void decode(final ChannelHandlerContext ctx, final WebSocketFrame in,
206206
throw new IllegalStateException("Unexpected packet id " + packetId + " in state " + this.state);
207207
}
208208
final String username = data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
209-
data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
209+
data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII);
210210
data.skipBytes(data.readUnsignedByte());
211211
if (data.isReadable()) {
212212
throw new IllegalArgumentException("Too much data in packet: " + data.readableBytes() + " bytes");
@@ -224,7 +224,7 @@ protected void decode(final ChannelHandlerContext ctx, final WebSocketFrame in,
224224
case LOGIN: {
225225
final int packetId = data.readUnsignedByte();
226226
if (packetId == 7) {
227-
data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
227+
data.readCharSequence(data.readUnsignedByte(), StandardCharsets.US_ASCII);
228228
final byte[] dataBytes = new byte[data.readUnsignedShort()];
229229
data.readBytes(dataBytes);
230230
if (data.isReadable()) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
public class EaglercraftInitialHandler extends ByteToMessageDecoder {
1919
private static SslContext sslContext;
2020

21+
@Override
2122
protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) {
2223
if (!ctx.channel().isOpen()) {
2324
return;
@@ -64,6 +65,7 @@ public static final class EaglercraftClientConnected {
6465
}
6566
}
6667

68+
@Override
6769
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
6870
ExceptionUtil.handleNettyException(ctx, cause, null);
6971
}
Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,41 @@
11
package me.ayunami2000.ayunViaProxyEagUtils;
22

3-
import java.util.HashMap;
4-
import java.util.HashSet;
3+
import java.util.AbstractSet;
54
import java.util.Iterator;
65
import java.util.Map;
6+
import java.util.Set;
7+
import java.util.concurrent.ConcurrentHashMap;
78

8-
public class ExpiringSet<T> extends HashSet<T> {
9+
public class ExpiringSet<T> extends AbstractSet<T> {
10+
private final Set<T> realSet;
911
private final long expiration;
10-
private final ExpiringEvent<T> event;
1112
private final Map<T, Long> timestamps;
1213

1314
public ExpiringSet(final long expiration) {
14-
this.timestamps = new HashMap<>();
15+
this.realSet = ConcurrentHashMap.newKeySet();
16+
this.timestamps = new ConcurrentHashMap<>();
1517
this.expiration = expiration;
16-
this.event = null;
17-
}
18-
19-
public ExpiringSet(final long expiration, final ExpiringEvent<T> event) {
20-
this.timestamps = new HashMap<>();
21-
this.expiration = expiration;
22-
this.event = event;
2318
}
2419

2520
public void checkForExpirations() {
2621
final Iterator<T> iterator = this.timestamps.keySet().iterator();
2722
final long now = System.currentTimeMillis();
2823
while (iterator.hasNext()) {
2924
final T element = iterator.next();
30-
if (super.contains(element)) {
25+
if (this.realSet.contains(element)) {
3126
if (this.timestamps.get(element) + this.expiration >= now) {
3227
continue;
3328
}
34-
if (this.event != null) {
35-
this.event.onExpiration(element);
36-
}
3729
}
3830
iterator.remove();
39-
super.remove(element);
31+
this.realSet.remove(element);
4032
}
4133
}
4234

4335
@Override
4436
public boolean add(final T o) {
4537
this.checkForExpirations();
46-
final boolean success = super.add(o);
38+
final boolean success = this.realSet.add(o);
4739
if (success) {
4840
this.timestamps.put(o, System.currentTimeMillis());
4941
}
@@ -53,7 +45,7 @@ public boolean add(final T o) {
5345
@Override
5446
public boolean remove(final Object o) {
5547
this.checkForExpirations();
56-
final boolean success = super.remove(o);
48+
final boolean success = this.realSet.remove(o);
5749
if (success) {
5850
this.timestamps.remove(o);
5951
}
@@ -63,16 +55,24 @@ public boolean remove(final Object o) {
6355
@Override
6456
public void clear() {
6557
this.timestamps.clear();
66-
super.clear();
58+
this.realSet.clear();
6759
}
6860

6961
@Override
70-
public boolean contains(final Object o) {
62+
public Iterator<T> iterator() {
63+
this.checkForExpirations();
64+
return this.realSet.iterator();
65+
}
66+
67+
@Override
68+
public int size() {
7169
this.checkForExpirations();
72-
return super.contains(o);
70+
return this.realSet.size();
7371
}
7472

75-
public interface ExpiringEvent<T> {
76-
void onExpiration(final T p0);
73+
@Override
74+
public boolean contains(final Object o) {
75+
this.checkForExpirations();
76+
return this.realSet.contains(o);
7777
}
7878
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package me.ayunami2000.ayunViaProxyEagUtils;
2+
3+
import com.viaversion.viaversion.util.Config;
4+
5+
import java.io.File;
6+
import java.net.URL;
7+
import java.util.Collections;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public class FunnyConfig extends Config {
12+
private boolean premiumSkins = false;
13+
14+
protected FunnyConfig(File configFile) {
15+
super(configFile);
16+
}
17+
18+
@Override
19+
public URL getDefaultConfigURL() {
20+
return Main.class.getResource("/eaglerskins.yml");
21+
}
22+
23+
@Override
24+
protected void handleConfig(Map<String, Object> map) {
25+
Object item = map.get("premium-skins");
26+
if (item instanceof Boolean) {
27+
this.premiumSkins = (Boolean) item;
28+
}
29+
}
30+
31+
@Override
32+
public List<String> getUnsupportedOptions() {
33+
return Collections.emptyList();
34+
}
35+
36+
public boolean getPremiumSkins() {
37+
return this.premiumSkins;
38+
}
39+
}

0 commit comments

Comments
 (0)