update a LOT

todo: 1.8 capes and fnaw skins
This commit is contained in:
ayunami2000 2024-04-26 10:49:18 -04:00
parent 6cc8a79c8b
commit 790b33ad68
8 changed files with 731 additions and 313 deletions

View File

@ -10,5 +10,5 @@ repositories {
} }
dependencies { dependencies {
implementation files("libs/ViaProxy-3.0.22-SNAPSHOT+java8.jar") implementation files("libs/ViaProxy-3.2.0-SNAPSHOT+java8.jar")
} }

View File

@ -5,6 +5,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.util.ChatColorUtil; import com.viaversion.viaversion.util.ChatColorUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
@ -24,7 +25,6 @@ import net.raphimc.vialegacy.api.LegacyProtocolVersion;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ClientboundPackets1_5_2; import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ClientboundPackets1_5_2;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundPackets1_5_2; import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundPackets1_5_2;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.proxy.session.LegacyProxyConnection; import net.raphimc.viaproxy.proxy.session.LegacyProxyConnection;
import net.raphimc.viaproxy.proxy.session.ProxyConnection; import net.raphimc.viaproxy.proxy.session.ProxyConnection;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil; import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
@ -43,7 +43,7 @@ import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, ByteBuf> { public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, ByteBuf> {
private final VersionEnum version; private final ProtocolVersion version;
private final String password; private final String password;
private final NetClient proxyConnection; private final NetClient proxyConnection;
private final Map<UUID, String> uuidStringMap = new HashMap<>(); private final Map<UUID, String> uuidStringMap = new HashMap<>();
@ -53,7 +53,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
private ByteBuf serverBoundPartialPacket = Unpooled.EMPTY_BUFFER; private ByteBuf serverBoundPartialPacket = Unpooled.EMPTY_BUFFER;
private ByteBuf clientBoundPartialPacket = Unpooled.EMPTY_BUFFER; private ByteBuf clientBoundPartialPacket = Unpooled.EMPTY_BUFFER;
public EaglerServerHandler(NetClient proxyConnection, String password) { public EaglerServerHandler(NetClient proxyConnection, String password) {
this.version = proxyConnection instanceof ProxyConnection ? ((ProxyConnection) proxyConnection).getServerVersion() : VersionEnum.r1_5_2; this.version = proxyConnection instanceof ProxyConnection ? ((ProxyConnection) proxyConnection).getServerVersion() : LegacyProtocolVersion.r1_5_2;
this.password = password; this.password = password;
this.proxyConnection = proxyConnection; this.proxyConnection = proxyConnection;
} }
@ -71,7 +71,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
out.add(Unpooled.EMPTY_BUFFER); out.add(Unpooled.EMPTY_BUFFER);
return; return;
} }
if (version.isNewerThan(VersionEnum.r1_6_4)) { if (version.newerThanOrEqualTo(ProtocolVersion.v1_7_2)) {
if (in.readableBytes() >= 2 && in.getUnsignedByte(0) == 0xFE && in.getUnsignedByte(1) == 0x01) { if (in.readableBytes() >= 2 && in.getUnsignedByte(0) == 0xFE && in.getUnsignedByte(1) == 0x01) {
handshakeState = -1; handshakeState = -1;
out.add(new TextWebSocketFrame("Accept: MOTD")); out.add(new TextWebSocketFrame("Accept: MOTD"));
@ -282,7 +282,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
ByteBuf bb = ctx.alloc().buffer(); ByteBuf bb = ctx.alloc().buffer();
bb.writeByte((byte) 0xFF); bb.writeByte((byte) 0xFF);
StringBuilder sb = new StringBuilder("\u00A71\0"); StringBuilder sb = new StringBuilder("\u00A71\0");
sb.append(LegacyProtocolVersion.getRealProtocolVersion(version.getVersion())).append("\0"); sb.append(version.getVersion()).append("\0");
sb.append(version.getName()).append("\0"); sb.append(version.getName()).append("\0");
sb.append(motdSb).append("\0"); sb.append(motdSb).append("\0");
sb.append(online).append("\0"); sb.append(online).append("\0");
@ -307,7 +307,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
JsonObject resp = new JsonObject(); JsonObject resp = new JsonObject();
JsonObject versionObj = new JsonObject(); JsonObject versionObj = new JsonObject();
versionObj.addProperty("name", version.getName()); versionObj.addProperty("name", version.getName());
versionObj.addProperty("protocol", LegacyProtocolVersion.getRealProtocolVersion(version.getVersion())); versionObj.addProperty("protocol", version.getVersion());
resp.add("version", versionObj); resp.add("version", versionObj);
JsonObject playersObj = new JsonObject(); JsonObject playersObj = new JsonObject();
playersObj.addProperty("max", max); playersObj.addProperty("max", max);
@ -358,7 +358,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
JsonObject resp = new JsonObject(); JsonObject resp = new JsonObject();
JsonObject versionObj = new JsonObject(); JsonObject versionObj = new JsonObject();
versionObj.addProperty("name", version.getName()); versionObj.addProperty("name", version.getName());
versionObj.addProperty("protocol", LegacyProtocolVersion.getRealProtocolVersion(version.getVersion())); versionObj.addProperty("protocol", version.getVersion());
resp.add("version", versionObj); resp.add("version", versionObj);
JsonObject playersObj = new JsonObject(); JsonObject playersObj = new JsonObject();
playersObj.addProperty("max", serverInfo.max); playersObj.addProperty("max", serverInfo.max);
@ -401,7 +401,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
handshakeState = -1; handshakeState = -1;
return; return;
} }
if (version.isNewerThan(VersionEnum.r1_6_4)) { if (version.newerThanOrEqualTo(ProtocolVersion.v1_7_2)) {
if (handshakeState == 0) { if (handshakeState == 0) {
out.add(Unpooled.EMPTY_BUFFER); out.add(Unpooled.EMPTY_BUFFER);
} else if (handshakeState == 1) { } else if (handshakeState == 1) {
@ -440,7 +440,7 @@ public class EaglerServerHandler extends MessageToMessageCodec<WebSocketFrame, B
bb.writeBytes(clientBoundPartialPacket); bb.writeBytes(clientBoundPartialPacket);
clientBoundPartialPacket.release(); clientBoundPartialPacket.release();
clientBoundPartialPacket = Unpooled.EMPTY_BUFFER; clientBoundPartialPacket = Unpooled.EMPTY_BUFFER;
bb.writeBytes(in.content()); bb.writeBytes(in.content()); // todo: do i need to reset this??????
int readerIndex = 0; int readerIndex = 0;
try { try {
while (bb.isReadable()) { while (bb.isReadable()) {

View File

@ -4,38 +4,32 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.util.AttributeKey; import net.raphimc.netminecraft.packet.PacketTypes;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil; import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
public class EaglerVoiceHandler extends ChannelInboundHandlerAdapter { public class EaglerVoiceHandler extends ChannelInboundHandlerAdapter {
private static final ConcurrentHashMap<String, ChannelHandlerContext> voicePlayers;
private static final ConcurrentHashMap<String, ExpiringSet<String>> voiceRequests;
private static final CopyOnWriteArraySet<String[]> voicePairs;
private final String user;
private static final Collection<String> iceServers;
private static final AttributeKey<Boolean> VOICE_ENABLED;
private static void sendData(final ChannelHandlerContext ctx, final byte[] data) throws IOException { public static final VoiceServerImpl voiceService = new VoiceServerImpl();
final ByteBuf bb = ctx.alloc().buffer();
bb.writeByte(250); public String user;
try { public UUID uuid;
Types1_6_4.STRING.write(bb, "EAG|Voice"); public final boolean old;
} catch (Exception e) { private int pluginMessageId = -1;
throw new IOException(e);
}
bb.writeShort(data.length);
bb.writeBytes(data);
ctx.writeAndFlush(new BinaryWebSocketFrame(bb));
}
public EaglerVoiceHandler(final String username) { public EaglerVoiceHandler(final String username) {
this.user = username; this.user = username;
if (this.user == null) {
this.uuid = null;
old = false;
} else {
old = true;
this.uuid = nameToUUID(this.user);
}
} }
@Override @Override
@ -43,187 +37,107 @@ public class EaglerVoiceHandler extends ChannelInboundHandlerAdapter {
ExceptionUtil.handleNettyException(ctx, cause, null); ExceptionUtil.handleNettyException(ctx, cause, null);
} }
private static UUID nameToUUID(String name) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8));
}
private boolean sent = false;
@Override @Override
public void channelRead(final ChannelHandlerContext ctx, final Object obj) throws Exception { public void channelRead(final ChannelHandlerContext ctx, final Object obj) throws Exception {
if (((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).state == EaglercraftHandler.State.LOGIN_COMPLETE && !ctx.channel().hasAttr(EaglerVoiceHandler.VOICE_ENABLED)) { if (!FunnyConfig.eaglerVoice) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); super.channelRead(ctx, obj);
final DataOutputStream dos = new DataOutputStream(baos); return;
dos.write(0); }
dos.writeBoolean(true); if (((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).state == EaglercraftHandler.State.LOGIN_COMPLETE && !sent) {
dos.write(EaglerVoiceHandler.iceServers.size()); if (this.user == null) {
for (final String str : EaglerVoiceHandler.iceServers) { this.user = ((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).username;
dos.writeUTF(str); this.uuid = nameToUUID(this.user);
} }
sendData(ctx, baos.toByteArray()); sent = true;
this.sendVoicePlayers(this.user); voiceService.handlePlayerLoggedIn(ctx);
ctx.channel().attr(EaglerVoiceHandler.VOICE_ENABLED).set(true);
} }
if (obj instanceof BinaryWebSocketFrame) { if (obj instanceof BinaryWebSocketFrame) {
final ByteBuf bb = ((BinaryWebSocketFrame) obj).content(); final ByteBuf bb = ((BinaryWebSocketFrame) obj).content();
if (bb.readableBytes() >= 3 && bb.readByte() == -6) { if (old) {
String tag; if (bb.readableBytes() >= 3 && bb.readByte() == -6) {
byte[] msg; String tag;
try { byte[] msg;
tag = Types1_6_4.STRING.read(bb); try {
msg = new byte[bb.readShort()]; tag = Types1_6_4.STRING.read(bb);
bb.readBytes(msg); msg = new byte[bb.readShort()];
} catch (Exception e) { bb.readBytes(msg);
bb.resetReaderIndex(); } catch (Exception e) {
super.channelRead(ctx, obj);
return;
}
try {
if (!tag.equals("EAG|Voice")) {
bb.resetReaderIndex(); bb.resetReaderIndex();
super.channelRead(ctx, obj); super.channelRead(ctx, obj);
return; return;
} }
if (!FunnyConfig.eaglerVoice) { try {
if (!tag.equals("EAG|Voice")) {
bb.resetReaderIndex();
super.channelRead(ctx, obj);
return;
}
final DataInputStream streamIn = new DataInputStream(new ByteArrayInputStream(msg));
final int sig = streamIn.read();
switch (sig) {
case 0: {
voiceService.handleVoiceSignalPacketTypeRequest(nameToUUID(streamIn.readUTF()), ctx);
bb.release();
return;
}
case 1: {
voiceService.handleVoiceSignalPacketTypeConnect(ctx);
bb.release();
return;
}
case 2: {
try {
voiceService.handleVoiceSignalPacketTypeDisconnect(nameToUUID(streamIn.readUTF()), ctx);
} catch (EOFException e) {
voiceService.handleVoiceSignalPacketTypeDisconnect(null, ctx);
}
bb.release();
return;
}
case 3: {
voiceService.handleVoiceSignalPacketTypeICE(nameToUUID(streamIn.readUTF()), streamIn.readUTF(), ctx);
bb.release();
return;
}
case 4: {
voiceService.handleVoiceSignalPacketTypeDesc(nameToUUID(streamIn.readUTF()), streamIn.readUTF(), ctx);
bb.release();
return;
}
default: {
bb.release();
return;
}
}
} catch (Throwable var8) {
voiceService.handlePlayerLoggedOut(uuid);
bb.release(); bb.release();
return; return;
} }
final DataInputStream streamIn = new DataInputStream(new ByteArrayInputStream(msg));
final int sig = streamIn.read();
switch (sig) {
case 0: {
if (!EaglerVoiceHandler.voicePlayers.containsKey(this.user)) {
bb.release();
return;
}
final String targetUser = streamIn.readUTF();
if (this.user.equals(targetUser)) {
bb.release();
return;
}
if (this.checkVoicePair(this.user, targetUser)) {
bb.release();
return;
}
if (!EaglerVoiceHandler.voicePlayers.containsKey(targetUser)) {
bb.release();
return;
}
if (!EaglerVoiceHandler.voiceRequests.containsKey(this.user)) {
EaglerVoiceHandler.voiceRequests.put(this.user, new ExpiringSet<>(2000L));
}
if (!EaglerVoiceHandler.voiceRequests.get(this.user).contains(targetUser)) {
EaglerVoiceHandler.voiceRequests.get(this.user).add(targetUser);
if (EaglerVoiceHandler.voiceRequests.containsKey(targetUser) && EaglerVoiceHandler.voiceRequests.get(targetUser).contains(this.user)) {
if (EaglerVoiceHandler.voiceRequests.containsKey(targetUser)) {
EaglerVoiceHandler.voiceRequests.get(targetUser).remove(this.user);
if (EaglerVoiceHandler.voiceRequests.get(targetUser).isEmpty()) {
EaglerVoiceHandler.voiceRequests.remove(targetUser);
}
}
if (EaglerVoiceHandler.voiceRequests.containsKey(this.user)) {
EaglerVoiceHandler.voiceRequests.get(this.user).remove(targetUser);
if (EaglerVoiceHandler.voiceRequests.get(this.user).isEmpty()) {
EaglerVoiceHandler.voiceRequests.remove(this.user);
}
}
EaglerVoiceHandler.voicePairs.add(new String[]{this.user, targetUser});
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
DataOutputStream dos2 = new DataOutputStream(baos2);
dos2.write(1);
dos2.writeUTF(this.user);
dos2.writeBoolean(false);
sendData(EaglerVoiceHandler.voicePlayers.get(targetUser), baos2.toByteArray());
baos2 = new ByteArrayOutputStream();
dos2 = new DataOutputStream(baos2);
dos2.write(1);
dos2.writeUTF(targetUser);
dos2.writeBoolean(true);
sendData(ctx, baos2.toByteArray());
}
bb.release();
return;
}
bb.release();
return;
}
case 1: {
if (!EaglerVoiceHandler.voicePlayers.containsKey(this.user)) {
final ByteArrayOutputStream baos3 = new ByteArrayOutputStream();
final DataOutputStream dos3 = new DataOutputStream(baos3);
dos3.write(1);
dos3.writeUTF(this.user);
final byte[] out = baos3.toByteArray();
for (final ChannelHandlerContext conn : EaglerVoiceHandler.voicePlayers.values()) {
sendData(conn, out);
}
EaglerVoiceHandler.voicePlayers.put(this.user, ctx);
for (final String username : EaglerVoiceHandler.voicePlayers.keySet()) {
this.sendVoicePlayers(username);
}
bb.release();
return;
}
bb.release();
return;
}
case 2: {
if (!EaglerVoiceHandler.voicePlayers.containsKey(this.user)) {
bb.release();
return;
}
try {
final String targetUser = streamIn.readUTF();
if (!EaglerVoiceHandler.voicePlayers.containsKey(targetUser)) {
bb.release();
return;
}
if (EaglerVoiceHandler.voicePairs.removeIf(pair -> (pair[0].equals(this.user) && pair[1].equals(targetUser)) || (pair[0].equals(targetUser) && pair[1].equals(this.user)))) {
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
DataOutputStream dos2 = new DataOutputStream(baos2);
dos2.write(2);
dos2.writeUTF(this.user);
sendData(EaglerVoiceHandler.voicePlayers.get(targetUser), baos2.toByteArray());
baos2 = new ByteArrayOutputStream();
dos2 = new DataOutputStream(baos2);
dos2.write(2);
dos2.writeUTF(targetUser);
sendData(ctx, baos2.toByteArray());
break;
}
} catch (EOFException var7) {
this.removeUser(this.user);
}
bb.release();
return;
}
case 3:
case 4: {
if (EaglerVoiceHandler.voicePlayers.containsKey(this.user)) {
final String username = streamIn.readUTF();
if (this.checkVoicePair(this.user, username)) {
final String data = streamIn.readUTF();
final ByteArrayOutputStream baos4 = new ByteArrayOutputStream();
final DataOutputStream dos4 = new DataOutputStream(baos4);
dos4.write(sig);
dos4.writeUTF(this.user);
dos4.writeUTF(data);
sendData(EaglerVoiceHandler.voicePlayers.get(username), baos4.toByteArray());
}
bb.release();
return;
}
bb.release();
return;
}
default: {
bb.release();
return;
}
}
} catch (Throwable var8) {
this.removeUser(this.user);
bb.release();
return;
} }
bb.release(); bb.resetReaderIndex();
return; } else {
if (this.pluginMessageId <= 0) {
this.pluginMessageId = ((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).pluginMessageId;
}
try {
if (PacketTypes.readVarInt(bb) == this.pluginMessageId && PacketTypes.readString(bb, 32767).equals("EAG|Voice-1.8")) {
final byte[] data = new byte[bb.readableBytes()];
bb.readBytes(data);
VoiceSignalPackets.processPacket(data, ctx);
bb.release();
return;
}
} catch (Exception ignored) {
}
bb.resetReaderIndex();
} }
bb.resetReaderIndex();
} }
super.channelRead(ctx, obj); super.channelRead(ctx, obj);
} }
@ -231,95 +145,6 @@ public class EaglerVoiceHandler extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception { public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx); super.channelInactive(ctx);
this.removeUser(this.user); voiceService.handlePlayerLoggedOut(uuid);
}
public void sendVoicePlayers(final String name) {
if (EaglerVoiceHandler.voicePlayers.containsKey(name)) {
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.write(5);
final Set<String> mostlyGlobalPlayers = ConcurrentHashMap.newKeySet();
for (final String username : EaglerVoiceHandler.voicePlayers.keySet()) {
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)))) {
mostlyGlobalPlayers.add(username);
}
}
if (!mostlyGlobalPlayers.isEmpty()) {
dos.writeInt(mostlyGlobalPlayers.size());
for (final String username : mostlyGlobalPlayers) {
dos.writeUTF(username);
}
sendData(EaglerVoiceHandler.voicePlayers.get(name), baos.toByteArray());
}
} catch (IOException ignored) {
}
}
}
public void removeUser(final String name) {
EaglerVoiceHandler.voicePlayers.remove(name);
for (final String username : EaglerVoiceHandler.voicePlayers.keySet()) {
if (!name.equals(username)) {
this.sendVoicePlayers(username);
}
}
for (final String[] voicePair : EaglerVoiceHandler.voicePairs) {
String target = null;
if (voicePair[0].equals(name)) {
target = voicePair[1];
} else if (voicePair[1].equals(name)) {
target = voicePair[0];
}
if (target != null && EaglerVoiceHandler.voicePlayers.containsKey(target)) {
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.write(2);
dos.writeUTF(name);
sendData(EaglerVoiceHandler.voicePlayers.get(target), baos.toByteArray());
} catch (IOException ignored) {
}
}
}
EaglerVoiceHandler.voicePairs.removeIf(pair -> pair[0].equals(name) || pair[1].equals(name));
}
private boolean checkVoicePair(final String user1, final String user2) {
return EaglerVoiceHandler.voicePairs.stream().anyMatch(pair -> (pair[0].equals(user1) && pair[1].equals(user2)) || (pair[0].equals(user2) && pair[1].equals(user1)));
}
static {
voicePlayers = new ConcurrentHashMap<>();
voiceRequests = new ConcurrentHashMap<>();
voicePairs = new CopyOnWriteArraySet<>();
(iceServers = new ArrayList<>()).add("stun:stun.l.google.com:19302");
EaglerVoiceHandler.iceServers.add("stun:stun1.l.google.com:19302");
EaglerVoiceHandler.iceServers.add("stun:stun2.l.google.com:19302");
EaglerVoiceHandler.iceServers.add("stun:stun3.l.google.com:19302");
EaglerVoiceHandler.iceServers.add("stun:stun4.l.google.com:19302");
EaglerVoiceHandler.iceServers.add("stun:openrelay.metered.ca:80");
final Map<String, Map<String, String>> turnServerList = new HashMap<>();
HashMap<String, String> n = new HashMap<>();
n.put("url", "turn:openrelay.metered.ca:80");
n.put("username", "openrelayproject");
n.put("password", "openrelayproject");
turnServerList.put("openrelay1", n);
n = new HashMap<>();
n.put("url", "turn:openrelay.metered.ca:443");
n.put("username", "openrelayproject");
n.put("password", "openrelayproject");
turnServerList.put("openrelay2", n);
n = new HashMap<>();
n.put("url", "turn:openrelay.metered.ca:443?transport=tcp");
n.put("username", "openrelayproject");
n.put("password", "openrelayproject");
turnServerList.put("openrelay3", n);
for (final Map.Entry<String, Map<String, String>> trn : turnServerList.entrySet()) {
final Map<String, String> o = trn.getValue();
EaglerVoiceHandler.iceServers.add(o.get("url") + ";" + o.get("username") + ";" + o.get("password"));
}
VOICE_ENABLED = AttributeKey.valueOf("ayun-voice-enabled");
} }
} }

View File

@ -1,6 +1,7 @@
package me.ayunami2000.ayunViaProxyEagUtils; package me.ayunami2000.ayunViaProxyEagUtils;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.libs.gson.JsonArray; import com.viaversion.viaversion.libs.gson.JsonArray;
import com.viaversion.viaversion.libs.gson.JsonObject; import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.libs.gson.JsonParser; import com.viaversion.viaversion.libs.gson.JsonParser;
@ -22,9 +23,9 @@ import net.raphimc.netminecraft.constants.ConnectionState;
import net.raphimc.netminecraft.constants.MCPackets; import net.raphimc.netminecraft.constants.MCPackets;
import net.raphimc.netminecraft.constants.MCPipeline; import net.raphimc.netminecraft.constants.MCPipeline;
import net.raphimc.netminecraft.packet.PacketTypes; import net.raphimc.netminecraft.packet.PacketTypes;
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundPackets1_5_2; import net.raphimc.vialegacy.protocols.release.protocol1_6_1to1_5_2.ServerboundPackets1_5_2;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.ViaProxy; import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer; import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
import net.raphimc.viaproxy.proxy.util.ExceptionUtil; import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
@ -53,7 +54,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
public static final AttributeKey<HttpHeaders> httpHeadersKey = AttributeKey.newInstance("eag-http-headers"); public static final AttributeKey<HttpHeaders> httpHeadersKey = AttributeKey.newInstance("eag-http-headers");
private HostAndPort host; private HostAndPort host;
public State state; public State state;
public VersionEnum version; public ProtocolVersion version;
public int pluginMessageId; public int pluginMessageId;
public String username; public String username;
@ -143,7 +144,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
if (data.readableBytes() >= 2 && data.getByte(0) == 2 && data.getByte(1) == 69) { if (data.readableBytes() >= 2 && data.getByte(0) == 2 && data.getByte(1) == 69) {
data.setByte(1, 61); data.setByte(1, 61);
this.state = State.LOGIN_COMPLETE; this.state = State.LOGIN_COMPLETE;
this.version = VersionEnum.r1_5_2; this.version = LegacyProtocolVersion.r1_5_2;
out.add(data.retain()); out.add(data.retain());
break; break;
} }
@ -197,8 +198,8 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
} }
Logger.LOGGER.info("Eaglercraft client connected: " + clientBrand + " " + clientVersionString); Logger.LOGGER.info("Eaglercraft client connected: " + clientBrand + " " + clientVersionString);
this.state = State.HANDSHAKE_COMPLETE; this.state = State.HANDSHAKE_COMPLETE;
this.version = VersionEnum.fromProtocolId(minecraftVersion); this.version = ProtocolVersion.getProtocol(minecraftVersion);
if (this.version.equals(VersionEnum.UNKNOWN)) { if (this.version.equals(ProtocolVersion.unknown)) {
Logger.LOGGER.error("Unsupported protocol version: " + minecraftVersion); Logger.LOGGER.error("Unsupported protocol version: " + minecraftVersion);
ctx.close(); ctx.close();
return; return;
@ -278,7 +279,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
break; break;
} }
case LOGIN_COMPLETE: { case LOGIN_COMPLETE: {
if (this.version.equals(VersionEnum.r1_5_2)) { if (this.version.equals(LegacyProtocolVersion.r1_5_2)) {
final int packetId = data.readUnsignedByte(); final int packetId = data.readUnsignedByte();
if (packetId == ServerboundPackets1_5_2.SHARED_KEY.getId()) { if (packetId == ServerboundPackets1_5_2.SHARED_KEY.getId()) {
ctx.channel().writeAndFlush(new BinaryWebSocketFrame(data.readerIndex(0).retain())); ctx.channel().writeAndFlush(new BinaryWebSocketFrame(data.readerIndex(0).retain()));
@ -289,7 +290,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
break; break;
} }
} }
} else if (this.version.isNewerThanOrEqualTo(VersionEnum.r1_7_2tor1_7_5) && (!ctx.channel().hasAttr(Main.secureWs) || ctx.channel().attr(Main.secureWs) == null)) { } else if (this.version.newerThanOrEqualTo(ProtocolVersion.v1_7_2) && (!ctx.channel().hasAttr(Main.secureWs) || ctx.channel().attr(Main.secureWs) == null)) {
final int packetId = PacketTypes.readVarInt(data); final int packetId = PacketTypes.readVarInt(data);
if (packetId == this.pluginMessageId && PacketTypes.readString(data, 32767).startsWith("EAG|")) { if (packetId == this.pluginMessageId && PacketTypes.readString(data, 32767).startsWith("EAG|")) {
break; break;
@ -315,7 +316,7 @@ public class EaglercraftHandler extends MessageToMessageCodec<WebSocketFrame, By
return; return;
} }
this.state = State.STATUS; this.state = State.STATUS;
this.version = VersionEnum.r1_8; this.version = ProtocolVersion.v1_8;
if (ctx.pipeline().get(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME) != null) { if (ctx.pipeline().get(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME) != null) {
ctx.pipeline().remove(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME); ctx.pipeline().remove(Client2ProxyChannelInitializer.LEGACY_PASSTHROUGH_INITIAL_HANDLER_NAME);
} }

View File

@ -11,7 +11,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import net.raphimc.viaproxy.plugins.PluginManager; import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.plugins.events.Client2ProxyHandlerCreationEvent; import net.raphimc.viaproxy.plugins.events.Client2ProxyHandlerCreationEvent;
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer; import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
import net.raphimc.viaproxy.proxy.client2proxy.passthrough.LegacyPassthroughInitialHandler; import net.raphimc.viaproxy.proxy.client2proxy.passthrough.LegacyPassthroughInitialHandler;
@ -40,8 +40,9 @@ public class EaglercraftInitialHandler extends ByteToMessageDecoder {
return; return;
} }
if (in.readableBytes() >= 3 || in.getByte(0) != 71) { if (in.readableBytes() >= 3 || in.getByte(0) != 71) {
if ((in.readableBytes() >= 3 && in.getCharSequence(0, 3, StandardCharsets.UTF_8).equals("GET")) || (in.readableBytes() >= 4 && in.getCharSequence(0, 4, StandardCharsets.UTF_8).equals("POST"))) { boolean ssl = EaglercraftInitialHandler.sslContext != null && in.readableBytes() >= 3 && in.getByte(0) == 22;
if (EaglercraftInitialHandler.sslContext != null) { if (ssl || (in.readableBytes() >= 3 && in.getCharSequence(0, 3, StandardCharsets.UTF_8).equals("GET")) || (in.readableBytes() >= 4 && in.getCharSequence(0, 4, StandardCharsets.UTF_8).equals("POST"))) {
if (ssl) {
ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-ssl-handler", EaglercraftInitialHandler.sslContext.newHandler(ctx.alloc())); ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-ssl-handler", EaglercraftInitialHandler.sslContext.newHandler(ctx.alloc()));
} }
ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-http-codec", new HttpServerCodec()); ctx.pipeline().addBefore("eaglercraft-initial-handler", "ws-http-codec", new HttpServerCodec());
@ -69,7 +70,7 @@ public class EaglercraftInitialHandler extends ByteToMessageDecoder {
} }
} }
Supplier<ChannelHandler> handlerSupplier = () -> PluginManager.EVENT_MANAGER.call(new Client2ProxyHandlerCreationEvent(new PassthroughClient2ProxyHandler(), true)).getHandler(); Supplier<ChannelHandler> handlerSupplier = () -> ViaProxy.EVENT_MANAGER.call(new Client2ProxyHandlerCreationEvent(new PassthroughClient2ProxyHandler(), true)).getHandler();
PassthroughClient2ProxyChannelInitializer channelInitializer = new PassthroughClient2ProxyChannelInitializer(handlerSupplier); PassthroughClient2ProxyChannelInitializer channelInitializer = new PassthroughClient2ProxyChannelInitializer(handlerSupplier);
try { try {
initChannelMethod.invoke(channelInitializer, ctx.channel()); initChannelMethod.invoke(channelInitializer, ctx.channel());

View File

@ -1,23 +1,25 @@
package me.ayunami2000.ayunViaProxyEagUtils; package me.ayunami2000.ayunViaProxyEagUtils;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion; import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameClientExtensionHandshaker;
import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateClientExtensionHandshaker;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import net.lenni0451.lambdaevents.EventHandler; import net.lenni0451.lambdaevents.EventHandler;
import net.raphimc.netminecraft.constants.MCPipeline; import net.raphimc.netminecraft.constants.MCPipeline;
import net.raphimc.netminecraft.netty.connection.NetClient; import net.raphimc.netminecraft.netty.connection.NetClient;
import net.raphimc.netminecraft.util.ServerAddress;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import net.raphimc.vialoader.util.VersionEnum; import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.plugins.PluginManager;
import net.raphimc.viaproxy.plugins.ViaProxyPlugin; import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent; import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent;
import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent; import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent;
@ -31,6 +33,8 @@ import javax.net.ssl.*;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
@ -70,8 +74,8 @@ public class Main extends ViaProxyPlugin {
} }
public void onEnable() { public void onEnable() {
PluginManager.EVENT_MANAGER.register(this); ViaProxy.EVENT_MANAGER.register(this);
(new FunnyConfig(new File("ViaLoader", "vpeagutils.yml"))).reloadConfig(); (new FunnyConfig(new File("ViaLoader", "vpeagutils.yml"))).reload();
EaglerXSkinHandler.skinService = new SkinService(); EaglerXSkinHandler.skinService = new SkinService();
} }
@ -82,7 +86,7 @@ public class Main extends ViaProxyPlugin {
NetClient proxyConnection; NetClient proxyConnection;
Channel c2p; Channel c2p;
ServerAddress addr; SocketAddress addr;
if (event.isLegacyPassthrough()) { if (event.isLegacyPassthrough()) {
proxyConnection = LegacyProxyConnection.fromChannel(ch); proxyConnection = LegacyProxyConnection.fromChannel(ch);
c2p = ((LegacyProxyConnection) proxyConnection).getC2P(); c2p = ((LegacyProxyConnection) proxyConnection).getC2P();
@ -99,8 +103,8 @@ public class Main extends ViaProxyPlugin {
c2p.attr(secureWs).set(true); c2p.attr(secureWs).set(true);
} }
if (c2p.hasAttr(secureWs) && c2p.attr(secureWs).get() != null) { if (c2p.hasAttr(secureWs) && c2p.attr(secureWs).get() != null && addr instanceof InetSocketAddress) {
doWsServerStuff(ch, proxyConnection, c2p, addr); doWsServerStuff(ch, proxyConnection, c2p, (InetSocketAddress) addr);
if (!event.isLegacyPassthrough()) { if (!event.isLegacyPassthrough()) {
ch.pipeline().addFirst("handshake-waiter", new ChannelOutboundHandlerAdapter() { ch.pipeline().addFirst("handshake-waiter", new ChannelOutboundHandlerAdapter() {
private boolean hasHandshake = false; private boolean hasHandshake = false;
@ -142,9 +146,9 @@ public class Main extends ViaProxyPlugin {
} }
} }
private static void doWsServerStuff(Channel ch, NetClient proxyConnection, Channel c2p, ServerAddress addr) throws URISyntaxException { private static void doWsServerStuff(Channel ch, NetClient proxyConnection, Channel c2p, InetSocketAddress addr) throws URISyntaxException {
ch.attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(-2); ch.attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(-2);
if (proxyConnection instanceof ProxyConnection && ((ProxyConnection) proxyConnection).getServerVersion().isNewerThan(VersionEnum.r1_6_4)) { if (proxyConnection instanceof ProxyConnection && ((ProxyConnection) proxyConnection).getServerVersion().newerThanOrEqualTo(ProtocolVersion.v1_7_2)) {
ch.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME); ch.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME);
} else if (ch.pipeline().get(MCPipeline.ENCRYPTION_HANDLER_NAME) != null) { } else if (ch.pipeline().get(MCPipeline.ENCRYPTION_HANDLER_NAME) != null) {
ch.pipeline().remove(MCPipeline.ENCRYPTION_HANDLER_NAME); ch.pipeline().remove(MCPipeline.ENCRYPTION_HANDLER_NAME);
@ -164,7 +168,7 @@ public class Main extends ViaProxyPlugin {
StringBuilder url = new StringBuilder("ws"); StringBuilder url = new StringBuilder("ws");
boolean secure = c2p.attr(secureWs).get(); boolean secure = c2p.attr(secureWs).get();
if (secure) { if (secure) {
final SSLEngine sslEngine = sc.createSSLEngine(addr.getAddress(), addr.getPort()); final SSLEngine sslEngine = sc.createSSLEngine(addr.getHostString(), addr.getPort());
sslEngine.setUseClientMode(true); sslEngine.setUseClientMode(true);
sslEngine.setNeedClientAuth(false); sslEngine.setNeedClientAuth(false);
ch.pipeline().addFirst("eag-server-ssl", new SslHandler(sslEngine) { ch.pipeline().addFirst("eag-server-ssl", new SslHandler(sslEngine) {
@ -181,7 +185,7 @@ public class Main extends ViaProxyPlugin {
} else { } else {
ch.pipeline().addFirst("eag-server-http-codec", new HttpClientCodec()); ch.pipeline().addFirst("eag-server-http-codec", new HttpClientCodec());
} }
url.append("://").append(addr.getAddress()); url.append("://").append(addr.getHostString());
if ((secure && addr.getPort() != 443) || (!secure && addr.getPort() != 80)) { if ((secure && addr.getPort() != 443) || (!secure && addr.getPort() != 80)) {
url.append(":").append(addr.getPort()); url.append(":").append(addr.getPort());
} }
@ -193,7 +197,7 @@ public class Main extends ViaProxyPlugin {
URI uri = new URI(url.toString()); URI uri = new URI(url.toString());
HttpHeaders headers = c2p.hasAttr(EaglercraftHandler.httpHeadersKey) ? c2p.attr(EaglercraftHandler.httpHeadersKey).get() : new DefaultHttpHeaders().clear().set(HttpHeaderNames.ORIGIN, "https://via.shhnowisnottheti.me"); HttpHeaders headers = c2p.hasAttr(EaglercraftHandler.httpHeadersKey) ? c2p.attr(EaglercraftHandler.httpHeadersKey).get() : new DefaultHttpHeaders().clear().set(HttpHeaderNames.ORIGIN, "https://via.shhnowisnottheti.me");
ch.pipeline().addAfter("eag-server-http-codec", "eag-server-http-aggregator", new HttpObjectAggregator(2097152, true)); ch.pipeline().addAfter("eag-server-http-codec", "eag-server-http-aggregator", new HttpObjectAggregator(2097152, true));
ch.pipeline().addAfter("eag-server-http-aggregator", "eag-server-ws-compression", WebSocketClientCompressionHandler.INSTANCE); ch.pipeline().addAfter("eag-server-http-aggregator", "eag-server-ws-compression", new WebSocketClientExtensionHandler(new PerMessageDeflateClientExtensionHandshaker(1, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), 15, false, false), new DeflateFrameClientExtensionHandshaker(1, false), new DeflateFrameClientExtensionHandshaker(1, true)));
ch.pipeline().addAfter("eag-server-ws-compression", "eag-server-ws-handshaker", new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, true, headers, 2097152))); ch.pipeline().addAfter("eag-server-ws-compression", "eag-server-ws-handshaker", new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, true, headers, 2097152)));
ch.pipeline().addAfter("eag-server-ws-handshaker", "eag-server-ws-ready", new WebSocketConnectedNotifier()); ch.pipeline().addAfter("eag-server-ws-handshaker", "eag-server-ws-ready", new WebSocketConnectedNotifier());
ch.pipeline().addAfter("eag-server-ws-ready", "eag-server-handler", new EaglerServerHandler(proxyConnection, c2p.attr(eagxPass).get())); ch.pipeline().addAfter("eag-server-ws-ready", "eag-server-handler", new EaglerServerHandler(proxyConnection, c2p.attr(eagxPass).get()));
@ -239,6 +243,7 @@ public class Main extends ViaProxyPlugin {
} else { } else {
ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-x-login", new EaglerXLoginHandler()); ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-x-login", new EaglerXLoginHandler());
ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-skin-x", new EaglerXSkinHandler()); ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-skin-x", new EaglerXSkinHandler());
ctx.pipeline().addBefore("eaglercraft-handler", "ayun-eag-voice", new EaglerVoiceHandler(null));
} }
} catch (Exception ignored) { } catch (Exception ignored) {
} }

View File

@ -0,0 +1,400 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import net.raphimc.netminecraft.constants.MCPackets;
import net.raphimc.netminecraft.packet.PacketTypes;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class VoiceServerImpl {
public static final Map<UUID, String> uuidToUsernameMap = new HashMap<>();
private static void fuckFuckFuck(ByteBuf out, String str) {
int strlen = str.length();
int utflen = 0;
int c, count = 0;
/* use charAt instead of copying String to char array */
for (int i = 0; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
if (utflen > 65535)
throw new RuntimeException(
"encoded string too long: " + utflen + " bytes");
byte[] bytearr = new byte[utflen+2];
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
int i=0;
for (i=0; i<strlen; i++) {
c = str.charAt(i);
if (!((c >= 0x0001) && (c <= 0x007F))) break;
bytearr[count++] = (byte) c;
}
for (;i < strlen; i++){
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
bytearr[count++] = (byte) c;
} else if (c > 0x07FF) {
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
} else {
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
}
}
out.writeBytes(bytearr, 0, utflen+2);
}
private int pluginMessageId = -1;
private void sendData(final ChannelHandlerContext ctx, final byte[] data) {
final ByteBuf bb = ctx.alloc().buffer();
if (((EaglerVoiceHandler) ctx.pipeline().get("ayun-eag-voice")).old) {
bb.writeByte(250);
try {
Types1_6_4.STRING.write(bb, "EAG|Voice");
} catch (Exception e) {
e.printStackTrace();
return;
}
bb.writeShort(data.length);
bb.writeBytes(data);
bb.readerIndex(23);
switch (bb.readByte()) {
case 0:
if (bb.readableBytes() == 1) break;
bb.skipBytes(1);
int vi = PacketTypes.readVarInt(bb);
String[] fard = new String[vi];
for (int i = 0; i < vi; i++) {
int xd = PacketTypes.readVarInt(bb);
byte[] arr = new byte[xd];
bb.readBytes(arr);
fard[i] = new String(arr, StandardCharsets.UTF_8);
}
bb.readerIndex(0);
bb.writerIndex(25);
bb.writeByte(vi);
for (String s : fard) {
fuckFuckFuck(bb, s);
}
break;
case 1:
UUID uuid = PacketTypes.readUuid(bb);
boolean o = bb.readBoolean();
bb.readerIndex(0);
bb.writerIndex(24);
fuckFuckFuck(bb, uuidToUsernameMap.get(uuid));
bb.writeBoolean(o);
break;
case 2:
if (bb.readableBytes() == 0) break;
uuid = PacketTypes.readUuid(bb);
bb.readerIndex(0);
bb.writerIndex(24);
fuckFuckFuck(bb, uuidToUsernameMap.get(uuid));
break;
case 3:
case 4:
uuid = PacketTypes.readUuid(bb);
vi = PacketTypes.readVarInt(bb);
byte[] arr = new byte[vi];
bb.readBytes(arr);
bb.readerIndex(0);
bb.writerIndex(24);
fuckFuckFuck(bb, uuidToUsernameMap.get(uuid));
fuckFuckFuck(bb, new String(arr, StandardCharsets.UTF_8));
break;
case 5:
vi = PacketTypes.readVarInt(bb);
bb.skipBytes(vi * 16);
fard = new String[vi];
for (int i = 0; i < vi; i++) {
int xd = PacketTypes.readVarInt(bb);
arr = new byte[xd];
bb.readBytes(arr);
fard[i] = new String(arr, StandardCharsets.UTF_8);
}
bb.readerIndex(0);
bb.writerIndex(24);
bb.writeInt(vi);
for (String s : fard) {
fuckFuckFuck(bb, s);
}
break;
}
bb.readerIndex(0);
bb.setShort(21, bb.writerIndex() - 23);
} else {
if (pluginMessageId <= 0) {
pluginMessageId = MCPackets.S2C_PLUGIN_MESSAGE.getId((((EaglercraftHandler) ctx.pipeline().get("eaglercraft-handler")).version).getVersion());
}
PacketTypes.writeVarInt(bb, pluginMessageId);
PacketTypes.writeString(bb, "EAG|Voice-1.8");
bb.writeBytes(data);
}
ctx.writeAndFlush(new BinaryWebSocketFrame(bb));
}
private final byte[] iceServersPacket;
private final Map<UUID, ChannelHandlerContext> voicePlayers = new HashMap<>();
private final Map<UUID, ExpiringSet<UUID>> voiceRequests = new HashMap<>();
private final Set<VoicePair> voicePairs = new HashSet<>();
private static class VoicePair {
private final UUID uuid1;
private final UUID uuid2;
@Override
public int hashCode() {
return uuid1.hashCode() ^ uuid2.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VoicePair other = (VoicePair) obj;
return (uuid1.equals(other.uuid1) && uuid2.equals(other.uuid2))
|| (uuid1.equals(other.uuid2) && uuid2.equals(other.uuid1));
}
private VoicePair(UUID uuid1, UUID uuid2) {
this.uuid1 = uuid1;
this.uuid2 = uuid2;
}
private boolean anyEquals(UUID uuid) {
return uuid1.equals(uuid) || uuid2.equals(uuid);
}
}
VoiceServerImpl() {
if (FunnyConfig.eaglerVoice) {
Set<String> list = new HashSet<>();
list.add("stun:stun.l.google.com:19302");
list.add("stun:stun1.l.google.com:19302");
list.add("stun:stun2.l.google.com:19302");
list.add("stun:stun3.l.google.com:19302");
list.add("stun:stun4.l.google.com:19302");
list.add("turn:relay1.expressturn.com:3478;ef2KWF55ZPQWCLBMIG;MGn8T66L0GhgDodf");
this.iceServersPacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(true, list.toArray(new String[0]));
} else {
this.iceServersPacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(false, null);
}
}
public void handlePlayerLoggedIn(ChannelHandlerContext player) {
EaglerVoiceHandler evh = (EaglerVoiceHandler) player.pipeline().get("ayun-eag-voice");
uuidToUsernameMap.put(evh.uuid, evh.user);
sendData(player, iceServersPacket);
}
public void handlePlayerLoggedOut(UUID playerUUID) {
removeUser(playerUUID);
uuidToUsernameMap.remove(playerUUID);
}
void handleVoiceSignalPacketTypeRequest(UUID player, ChannelHandlerContext sender) {
UUID senderUUID = ((EaglerVoiceHandler) sender.pipeline().get("ayun-eag-voice")).uuid;
synchronized (voicePlayers) {
if (senderUUID.equals(player))
return; // prevent duplicates
if (!voicePlayers.containsKey(senderUUID))
return;
ChannelHandlerContext targetPlayerCon = voicePlayers.get(player);
if (targetPlayerCon == null)
return;
VoicePair newPair = new VoicePair(player, senderUUID);
if (voicePairs.contains(newPair))
return; // already paired
ExpiringSet<UUID> senderRequestSet = voiceRequests.get(senderUUID);
if (senderRequestSet == null) {
voiceRequests.put(senderUUID, senderRequestSet = new ExpiringSet<>(2000));
}
if (!senderRequestSet.add(player)) {
return;
}
// check if other has requested earlier
ExpiringSet<UUID> theSet;
if ((theSet = voiceRequests.get(player)) != null && theSet.contains(senderUUID)) {
theSet.remove(senderUUID);
if (theSet.isEmpty())
voiceRequests.remove(player);
senderRequestSet.remove(player);
if (senderRequestSet.isEmpty())
voiceRequests.remove(senderUUID);
// send each other add data
voicePairs.add(newPair);
sendData(targetPlayerCon,
VoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false));
sendData(sender, VoiceSignalPackets.makeVoiceSignalPacketConnect(player, true));
}
}
}
void handleVoiceSignalPacketTypeConnect(ChannelHandlerContext sender) {
UUID senderUUID = ((EaglerVoiceHandler) sender.pipeline().get("ayun-eag-voice")).uuid;
synchronized (voicePlayers) {
if (voicePlayers.containsKey(senderUUID)) {
return;
}
boolean hasNoOtherPlayers = voicePlayers.isEmpty();
voicePlayers.put(senderUUID, sender);
if (hasNoOtherPlayers) {
return;
}
byte[] packetToBroadcast = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
for (ChannelHandlerContext userCon : voicePlayers.values()) {
sendData(userCon, packetToBroadcast);
}
}
}
void handleVoiceSignalPacketTypeICE(UUID player, String str, ChannelHandlerContext sender) {
UUID senderUUID = ((EaglerVoiceHandler) sender.pipeline().get("ayun-eag-voice")).uuid;
ChannelHandlerContext pass;
VoicePair pair = new VoicePair(player, senderUUID);
synchronized (voicePlayers) {
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
}
if (pass != null) {
sendData(pass, VoiceSignalPackets.makeVoiceSignalPacketICE(senderUUID, str));
}
}
void handleVoiceSignalPacketTypeDesc(UUID player, String str, ChannelHandlerContext sender) {
UUID senderUUID = ((EaglerVoiceHandler) sender.pipeline().get("ayun-eag-voice")).uuid;
ChannelHandlerContext pass;
VoicePair pair = new VoicePair(player, senderUUID);
synchronized (voicePlayers) {
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
}
if (pass != null) {
sendData(pass,
VoiceSignalPackets.makeVoiceSignalPacketDesc(senderUUID, str));
}
}
void handleVoiceSignalPacketTypeDisconnect(UUID player, ChannelHandlerContext sender) {
UUID senderUUID = ((EaglerVoiceHandler) sender.pipeline().get("ayun-eag-voice")).uuid;
if (player != null) {
synchronized (voicePlayers) {
if (!voicePlayers.containsKey(player)) {
return;
}
byte[] userDisconnectPacket = null;
Iterator<VoicePair> pairsItr = voicePairs.iterator();
while (pairsItr.hasNext()) {
VoicePair voicePair = pairsItr.next();
UUID target = null;
if (voicePair.uuid1.equals(player)) {
target = voicePair.uuid2;
} else if (voicePair.uuid2.equals(player)) {
target = voicePair.uuid1;
}
if (target != null) {
pairsItr.remove();
ChannelHandlerContext conn = voicePlayers.get(target);
if (conn != null) {
if (userDisconnectPacket == null) {
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(player);
}
sendData(conn, userDisconnectPacket);
}
sendData(sender,
VoiceSignalPackets.makeVoiceSignalPacketDisconnect(target));
}
}
}
} else {
removeUser(senderUUID);
}
}
public void removeUser(UUID user) {
synchronized (voicePlayers) {
if (voicePlayers.remove(user) == null) {
return;
}
voiceRequests.remove(user);
if (voicePlayers.size() > 0) {
byte[] voicePlayersPkt = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
for (Map.Entry<UUID, ChannelHandlerContext> userCon : voicePlayers.entrySet()) {
if (!user.equals(userCon.getKey())) {
sendData(userCon.getValue(), voicePlayersPkt);
}
}
}
byte[] userDisconnectPacket = null;
Iterator<VoicePair> pairsItr = voicePairs.iterator();
while (pairsItr.hasNext()) {
VoicePair voicePair = pairsItr.next();
UUID target = null;
if (voicePair.uuid1.equals(user)) {
target = voicePair.uuid2;
} else if (voicePair.uuid2.equals(user)) {
target = voicePair.uuid1;
}
if (target != null) {
pairsItr.remove();
if (voicePlayers.size() > 0) {
ChannelHandlerContext conn = voicePlayers.get(target);
if (conn != null) {
if (userDisconnectPacket == null) {
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(user);
}
sendData(conn, userDisconnectPacket);
}
}
}
}
}
}
}

View File

@ -0,0 +1,186 @@
package me.ayunami2000.ayunViaProxyEagUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.UUID;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import net.raphimc.netminecraft.packet.PacketTypes;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class VoiceSignalPackets {
static final int VOICE_SIGNAL_ALLOWED = 0;
static final int VOICE_SIGNAL_REQUEST = 0;
static final int VOICE_SIGNAL_CONNECT = 1;
static final int VOICE_SIGNAL_DISCONNECT = 2;
static final int VOICE_SIGNAL_ICE = 3;
static final int VOICE_SIGNAL_DESC = 4;
static final int VOICE_SIGNAL_GLOBAL = 5;
public static void processPacket(byte[] data, ChannelHandlerContext sender) throws IOException {
int packetId = -1;
if(data.length == 0) {
throw new IOException("Zero-length packet recieved");
}
try {
ByteBuf buffer = Unpooled.wrappedBuffer(data).writerIndex(data.length);
packetId = buffer.readUnsignedByte();
switch(packetId) {
case VOICE_SIGNAL_REQUEST: {
EaglerVoiceHandler.voiceService.handleVoiceSignalPacketTypeRequest(PacketTypes.readUuid(buffer), sender);
break;
}
case VOICE_SIGNAL_CONNECT: {
EaglerVoiceHandler.voiceService.handleVoiceSignalPacketTypeConnect(sender);
break;
}
case VOICE_SIGNAL_ICE: {
EaglerVoiceHandler.voiceService.handleVoiceSignalPacketTypeICE(PacketTypes.readUuid(buffer), PacketTypes.readString(buffer, 32767), sender);
break;
}
case VOICE_SIGNAL_DESC: {
EaglerVoiceHandler.voiceService.handleVoiceSignalPacketTypeDesc(PacketTypes.readUuid(buffer), PacketTypes.readString(buffer, 32767), sender);
break;
}
case VOICE_SIGNAL_DISCONNECT: {
EaglerVoiceHandler.voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? PacketTypes.readUuid(buffer) : null, sender);
break;
}
default: {
throw new IOException("Unknown packet type " + packetId);
}
}
if(buffer.readableBytes() > 0) {
throw new IOException("Voice packet is too long!");
}
}catch(IOException ex) {
throw ex;
}catch(Throwable t) {
throw new IOException("Unhandled exception handling voice packet type " + packetId, t);
}
}
static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) {
if (iceServers == null) {
byte[] ret = new byte[2];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
wrappedBuffer.writeBoolean(allowed);
return ret;
}
byte[][] iceServersBytes = new byte[iceServers.length][];
int totalLen = 2 + getVarIntSize(iceServers.length);
for(int i = 0; i < iceServers.length; ++i) {
byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8);
totalLen += getVarIntSize(b.length) + b.length;
}
byte[] ret = new byte[totalLen];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
wrappedBuffer.writeBoolean(allowed);
PacketTypes.writeVarInt(wrappedBuffer, iceServersBytes.length);
for(int i = 0; i < iceServersBytes.length; ++i) {
byte[] b = iceServersBytes[i];
PacketTypes.writeVarInt(wrappedBuffer, b.length);
wrappedBuffer.writeBytes(b);
}
return ret;
}
static byte[] makeVoiceSignalPacketGlobal(Collection<ChannelHandlerContext> users) {
int cnt = users.size();
byte[][] displayNames = new byte[cnt][];
int i = 0;
for(ChannelHandlerContext user : users) {
String name = ((EaglerVoiceHandler) user.pipeline().get("ayun-eag-voice")).user;
if(name.length() > 16) name = name.substring(0, 16);
displayNames[i++] = name.getBytes(StandardCharsets.UTF_8);
}
int totalLength = 1 + getVarIntSize(cnt) + (cnt << 4);
for(i = 0; i < cnt; ++i) {
totalLength += getVarIntSize(displayNames[i].length) + displayNames[i].length;
}
byte[] ret = new byte[totalLength];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL);
PacketTypes.writeVarInt(wrappedBuffer, cnt);
for(ChannelHandlerContext user : users) {
PacketTypes.writeUuid(wrappedBuffer, ((EaglerVoiceHandler) user.pipeline().get("ayun-eag-voice")).uuid);
}
for(i = 0; i < cnt; ++i) {
PacketTypes.writeVarInt(wrappedBuffer, displayNames[i].length);
wrappedBuffer.writeBytes(displayNames[i]);
}
return ret;
}
static byte[] makeVoiceSignalPacketConnect(UUID player, boolean offer) {
byte[] ret = new byte[18];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
PacketTypes.writeUuid(wrappedBuffer, player);
wrappedBuffer.writeBoolean(offer);
return ret;
}
static byte[] makeVoiceSignalPacketDisconnect(UUID player) {
if(player == null) {
return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT };
}
byte[] ret = new byte[17];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
PacketTypes.writeUuid(wrappedBuffer, player);
return ret;
}
static byte[] makeVoiceSignalPacketICE(UUID player, String str) {
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_ICE);
PacketTypes.writeUuid(wrappedBuffer, player);
PacketTypes.writeVarInt(wrappedBuffer, strBytes.length);
wrappedBuffer.writeBytes(strBytes);
return ret;
}
static byte[] makeVoiceSignalPacketDesc(UUID player, String str) {
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
wrappedBuffer.writeByte(VOICE_SIGNAL_DESC);
PacketTypes.writeUuid(wrappedBuffer, player);
PacketTypes.writeVarInt(wrappedBuffer, strBytes.length);
wrappedBuffer.writeBytes(strBytes);
return ret;
}
public static int getVarIntSize(int input) {
for (int i = 1; i < 5; ++i) {
if ((input & -1 << i * 7) == 0) {
return i;
}
}
return 5;
}
}