made most client relay handshake code

This commit is contained in:
LAX1DUDE 2022-08-16 00:19:52 -07:00
parent 5eef920554
commit caad879284
8 changed files with 369 additions and 21 deletions

View File

@ -82,7 +82,7 @@ public class EaglerSPRelay extends WebSocketServer {
Iterator<EaglerSPClient> itr = clientConnections.values().iterator(); Iterator<EaglerSPClient> itr = clientConnections.values().iterator();
while(itr.hasNext()) { while(itr.hasNext()) {
EaglerSPClient cl = itr.next(); EaglerSPClient cl = itr.next();
if(millis - cl.createdOn > 5000l) { if(millis - cl.createdOn > 6900l) {
cl.disconnect(IPacketFEDisconnectClient.TYPE_TIMEOUT, "Took too long to connect!"); cl.disconnect(IPacketFEDisconnectClient.TYPE_TIMEOUT, "Took too long to connect!");
} }
} }

View File

@ -0,0 +1,196 @@
package net.lax1dude.eaglercraft;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket00Handshake;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket01ICEServers;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket03ICECandidate;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket04Description;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacketFFErrorCode;
import net.minecraft.src.INetworkManager;
import net.minecraft.src.LoadingScreenRenderer;
import net.minecraft.src.NetHandler;
import net.minecraft.src.Packet;
public class LANClientNetworkManager implements INetworkManager {
private static final int PRE = 0, INIT = 1, SENT_ICE_CANDIDATE = 2, SENT_DESCRIPTION = 3;
private static final String[] initStateNames = new String[] { "PRE", "INIT", "SENT_ICE_CANDIDATE", "SENT_DESCRIPTION" };
public final String displayCode;
public final String displayRelay;
private LANClientNetworkManager(String displayCode, String displayRelay) {
this.displayCode = displayCode;
this.displayRelay = displayRelay;
}
public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, LoadingScreenRenderer loadingScreen,
String displayCode, String displayRelay) {
EaglerAdapter.clearLANClientState();
loadingScreen.displayProgressMessage("Connecting to '" + displayCode + "' via " + displayRelay + "...");
int connectState = -1;
IPacket pkt;
mainLoop: while(!sock.isClosed()) {
if((pkt = sock.readPacket()) != null) {
if(pkt instanceof IPacket00Handshake) {
if(connectState == PRE) {
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] recieved handshake, "
+ "client id: " + ((IPacket00Handshake)pkt).connectionCode);
connectState = INIT;
}else {
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] unexpected packet: "
+ "IPacket00Handshake in state " + initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket01ICEServers) {
if(connectState == INIT) {
IPacket01ICEServers ipkt = (IPacket01ICEServers) pkt;
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] provided ICE servers:");
List<String> servers = new ArrayList();
for(net.lax1dude.eaglercraft.sp.relay.pkt.ICEServerSet.RelayServer srv : ipkt.servers) {
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] " + srv.type.name()
+ ": " + srv.address);
servers.add(srv.getICEString());
}
EaglerAdapter.clientLANSetICEServersAndConnect(servers.toArray(new String[servers.size()]));
long lm = System.currentTimeMillis();
do {
String c = EaglerAdapter.clientLANAwaitICECandidate();
if(c != null) {
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] client sent ICE candidate");
sock.writePacket(new IPacket03ICECandidate("", c));
connectState = SENT_ICE_CANDIDATE;
continue mainLoop;
}
}while(System.currentTimeMillis() - lm > 3000l);
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] client provide ICE candidate timeout");
return null;
}else {
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] unexpected packet: "
+ "IPacket01ICEServers in state " + initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket03ICECandidate) {
if(connectState == SENT_ICE_CANDIDATE) {
IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt;
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] recieved server ICE candidate");
EaglerAdapter.clientLANSetICECandidate(ipkt.candidate);
long lm = System.currentTimeMillis();
do {
String c = EaglerAdapter.clientLANAwaitDescription();
if(c != null) {
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] client sent description");
sock.writePacket(new IPacket04Description("", c));
connectState = SENT_DESCRIPTION;
continue mainLoop;
}
}while(System.currentTimeMillis() - lm > 3000l);
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] client provide description timeout");
return null;
}else {
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] unexpected packet: "
+ "IPacket03ICECandidate in state " + initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacket04Description) {
if(connectState == SENT_DESCRIPTION) {
IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt;
System.out.println("Relay [" + displayRelay + "|" + displayCode + "] recieved server description");
EaglerAdapter.clientLANSetDescription(ipkt.candidate);
// TODO: handle description, return packet 05 or 06
}else {
sock.close();
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] unexpected packet: "
+ "IPacket04Description in state " + initStateNames[connectState]);
return null;
}
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt;
System.err.println("Relay [" + displayRelay + "|" + displayCode + "] connection failed: " +
IPacketFFErrorCode.code2string(ipkt.code) + "(" + ipkt.code + "): " + ipkt.desc);
Throwable t;
while((t = sock.getException()) != null) {
t.printStackTrace();
}
sock.close();
return null;
}else {
System.err.println("Relay [" + displayRelay + "] unexpected packet: " + pkt.getClass().getSimpleName());
sock.close();
return null;
}
}
try {
Thread.sleep(20l);
} catch (InterruptedException e) {
}
}
return null;
}
@Override
public void setNetHandler(NetHandler var1) {
// TODO Auto-generated method stub
}
@Override
public void addToSendQueue(Packet var1) {
// TODO Auto-generated method stub
}
@Override
public void wakeThreads() {
// TODO Auto-generated method stub
}
@Override
public void processReadPackets() {
// TODO Auto-generated method stub
}
@Override
public void serverShutdown() {
// TODO Auto-generated method stub
}
@Override
public int packetSize() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void networkShutdown(String var1, Object... var2) {
// TODO Auto-generated method stub
}
@Override
public void closeConnections() {
// TODO Auto-generated method stub
}
@Override
public String getServerURI() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -1,9 +1,13 @@
package net.lax1dude.eaglercraft; package net.lax1dude.eaglercraft;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket00Handshake;
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacketFFErrorCode;
import net.minecraft.src.NBTBase; import net.minecraft.src.NBTBase;
import net.minecraft.src.NBTTagCompound; import net.minecraft.src.NBTTagCompound;
import net.minecraft.src.NBTTagList; import net.minecraft.src.NBTTagList;
@ -180,44 +184,100 @@ public class RelayManager {
} }
} }
public RelayServer getWorkingRelay(Consumer<String> progressCallback) { private RelayServerSocket connectHandshake(RelayServer relay, int type, String code) {
RelayServerSocket sock = relay.openSocket();
while(!sock.isClosed()) {
if(sock.isOpen()) {
sock.writePacket(new IPacket00Handshake(type, IntegratedServer.preferredRelayVersion, code));
while(!sock.isClosed()) {
IPacket pkt = sock.nextPacket();
if(pkt != null) {
if(pkt instanceof IPacket00Handshake) {
return sock;
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt;
System.err.println("Relay [" + relay.address + "] failed: " + IPacketFFErrorCode.code2string(ipkt.code) +
"(" + ipkt.code + "): " + ipkt.desc);
Throwable t;
while((t = sock.getException()) != null) {
t.printStackTrace();
}
sock.close();
return null;
}else {
System.err.println("Relay [" + relay.address + "] unexpected packet: " + pkt.getClass().getSimpleName());
sock.close();
return null;
}
}
try {
Thread.sleep(20l);
} catch (InterruptedException e) {
}
}
}
try {
Thread.sleep(20l);
} catch (InterruptedException e) {
}
}
System.err.println("Relay [" + relay.address + "] connection failed!");
Throwable t;
while((t = sock.getException()) != null) {
t.printStackTrace();
}
return null;
}
private final List<RelayServer> brokenServers = new LinkedList();
public RelayServerSocket getWorkingRelay(Consumer<String> progressCallback, int type, String code) {
brokenServers.clear();
if(relays.size() > 0) { if(relays.size() > 0) {
long millis = System.currentTimeMillis(); long millis = System.currentTimeMillis();
if(millis - lastPingThrough > 10000l) { if(millis - lastPingThrough < 10000l) {
RelayServer relay = getPrimary(); RelayServer relay = getPrimary();
if(relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) { if(relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) {
return relay; progressCallback.accept(relay.address);
RelayServerSocket sock = connectHandshake(relay, type, code);
if(sock != null) {
return sock;
}else {
brokenServers.add(relay);
}
} }
for(int i = 0, l = relays.size(); i < l; ++i) { for(int i = 0, l = relays.size(); i < l; ++i) {
RelayServer relayEtr = relays.get(i); RelayServer relayEtr = relays.get(i);
if(relayEtr != relay) { if(relayEtr != relay) {
if(relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) { if(relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) {
return relayEtr; progressCallback.accept(relayEtr.address);
RelayServerSocket sock = connectHandshake(relayEtr, type, code);
if(sock != null) {
return sock;
}else {
brokenServers.add(relayEtr);
}
} }
} }
} }
} }
return getWorkingRelayActive(progressCallback); return getWorkingCodeRelayActive(progressCallback, type, code);
}else { }else {
return null; return null;
} }
} }
private RelayServer getWorkingRelayActive(Consumer<String> progressCallback) { private RelayServerSocket getWorkingCodeRelayActive(Consumer<String> progressCallback, int type, String code) {
if(relays.size() > 0) { if(relays.size() > 0) {
RelayServer relay = getPrimary();
progressCallback.accept(relay.address);
relay.pingBlocking();
if(relay.getPing() > 0l && relay.getPingCompatible().isCompatible()) {
return relay;
}
for(int i = 0, l = relays.size(); i < l; ++i) { for(int i = 0, l = relays.size(); i < l; ++i) {
RelayServer relayEtr = relays.get(i); RelayServer srv = relays.get(i);
if(relayEtr != relay) { if(!brokenServers.contains(srv)) {
progressCallback.accept(relayEtr.address); progressCallback.accept(srv.address);
relayEtr.pingBlocking(); RelayServerSocket sock = connectHandshake(srv, type, code);
if(relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) { if(sock != null) {
return relayEtr; return sock;
}else {
brokenServers.add(srv);
} }
} }
} }

View File

@ -15,6 +15,7 @@ public class RelayServer {
private VersionMismatch queriedCompatible; private VersionMismatch queriedCompatible;
private long ping = 0l; private long ping = 0l;
private long workingPing = 0l; private long workingPing = 0l;
public long lastPing = 0l;
public RelayServer(String address, String comment, boolean primary) { public RelayServer(String address, String comment, boolean primary) {
this.address = address; this.address = address;
@ -95,6 +96,7 @@ public class RelayServer {
queriedCompatible = query.getCompatible(); queriedCompatible = query.getCompatible();
workingPing = ping; workingPing = ping;
} }
lastPing = System.currentTimeMillis();
query = null; query = null;
} }
} }
@ -111,4 +113,8 @@ public class RelayServer {
} }
} }
public RelayServerSocket openSocket() {
return EaglerAdapter.openRelayConnection(address);
}
} }

View File

@ -15,6 +15,7 @@ public interface RelayServerSocket {
void writePacket(IPacket pkt); void writePacket(IPacket pkt);
IPacket readPacket(); IPacket readPacket();
IPacket nextPacket();
RateLimit getRatelimitHistory(); RateLimit getRatelimitHistory();

View File

@ -26,6 +26,16 @@ public class ICEServerSet {
this.username = null; this.username = null;
this.password = null; this.password = null;
} }
public String getICEString() {
if(type == RelayType.STUN) {
return address;
}else if(type == RelayType.TURN) {
return address + ";" + username + ";" + password;
}else {
throw new IllegalStateException("Unknown relay type: " + (type == null ? "null" : type.name()));
}
}
} }

View File

@ -15,6 +15,27 @@ public class IPacketFFErrorCode extends IPacket {
public static final int TYPE_SERVER_DISCONNECTED = 0x06; public static final int TYPE_SERVER_DISCONNECTED = 0x06;
public static final int TYPE_UNKNOWN_CLIENT = 0x07; public static final int TYPE_UNKNOWN_CLIENT = 0x07;
public static final String[] packetTypes = new String[0x08];
static {
packetTypes[TYPE_INTERNAL_ERROR] = "TYPE_INTERNAL_ERROR";
packetTypes[TYPE_PROTOCOL_VERSION] = "TYPE_PROTOCOL_VERSION";
packetTypes[TYPE_INVALID_PACKET] = "TYPE_INVALID_PACKET";
packetTypes[TYPE_ILLEGAL_OPERATION] = "TYPE_ILLEGAL_OPERATION";
packetTypes[TYPE_CODE_LENGTH] = "TYPE_CODE_LENGTH";
packetTypes[TYPE_INCORRECT_CODE] = "TYPE_INCORRECT_CODE";
packetTypes[TYPE_SERVER_DISCONNECTED] = "TYPE_SERVER_DISCONNECTED";
packetTypes[TYPE_UNKNOWN_CLIENT] = "TYPE_UNKNOWN_CLIENT";
}
public static String code2string(int i) {
if(i >= 0 || i < packetTypes.length) {
return packetTypes[i];
}else {
return "UNKNOWN";
}
}
public int code; public int code;
public String desc; public String desc;

View File

@ -3578,6 +3578,15 @@ public class EaglerAdapterImpl2 {
return null; return null;
} }
} }
@Override
public IPacket nextPacket() {
if(packets.size() > 0) {
return packets.get(0);
}else {
return null;
}
}
@Override @Override
public RateLimit getRatelimitHistory() { public RateLimit getRatelimitHistory() {
@ -3632,6 +3641,11 @@ public class EaglerAdapterImpl2 {
public IPacket readPacket() { public IPacket readPacket() {
return null; return null;
} }
@Override
public IPacket nextPacket() {
return null;
}
@Override @Override
public RateLimit getRatelimitHistory() { public RateLimit getRatelimitHistory() {
@ -3670,6 +3684,9 @@ public class EaglerAdapterImpl2 {
public static final int LAN_CLIENT_CONNECTING = 1; public static final int LAN_CLIENT_CONNECTING = 1;
public static final int LAN_CLIENT_CONNECTED = 2; public static final int LAN_CLIENT_CONNECTED = 2;
private static String clientICECandidate = null;
private static String clientDescription = null;
public static final boolean clientLANSupported() { public static final boolean clientLANSupported() {
return rtcLANClient.LANClientSupported(); return rtcLANClient.LANClientSupported();
} }
@ -3680,13 +3697,13 @@ public class EaglerAdapterImpl2 {
rtcLANClient.setDescriptionHandler(new EaglercraftLANClient.DescriptionHandler() { rtcLANClient.setDescriptionHandler(new EaglercraftLANClient.DescriptionHandler() {
@Override @Override
public void call(String description) { public void call(String description) {
clientDescription = description;
} }
}); });
rtcLANClient.setICECandidateHandler(new EaglercraftLANClient.ICECandidateHandler() { rtcLANClient.setICECandidateHandler(new EaglercraftLANClient.ICECandidateHandler() {
@Override @Override
public void call(String candidate) { public void call(String candidate) {
clientICECandidate = candidate;
} }
}); });
rtcLANClient.setRemoteDataChannelHandler(new EaglercraftLANClient.ClientSignalHandler() { rtcLANClient.setRemoteDataChannelHandler(new EaglercraftLANClient.ClientSignalHandler() {
@ -3734,6 +3751,43 @@ public class EaglerAdapterImpl2 {
return clientLANPacketBuffer.size() > 0 ? clientLANPacketBuffer.remove(0) : null; return clientLANPacketBuffer.size() > 0 ? clientLANPacketBuffer.remove(0) : null;
} }
public static final void clientLANSetICEServersAndConnect(String[] servers) {
rtcLANClient.setICEServers(servers);
}
public static final void clearLANClientState() {
clientICECandidate = null;
clientDescription = null;
}
public static final String clientLANAwaitICECandidate() {
if(clientICECandidate != null) {
String ret = clientICECandidate;
clientICECandidate = null;
return ret;
}else {
return null;
}
}
public static final String clientLANAwaitDescription() {
if(clientDescription != null) {
String ret = clientDescription;
clientDescription = null;
return ret;
}else {
return null;
}
}
public static final void clientLANSetICECandidate(String candidate) {
rtcLANClient.signalRemoteICECandidate(candidate);
}
public static final void clientLANSetDescription(String description) {
rtcLANClient.signalRemoteDescription(description);
}
private static EaglercraftLANServer rtcLANServer = null; private static EaglercraftLANServer rtcLANServer = null;
@JSBody(params = { }, script = "return window.startLANServer();") @JSBody(params = { }, script = "return window.startLANServer();")