diff --git a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/EaglerSPRelay.java b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/EaglerSPRelay.java index cabb04c..b9c0fb3 100644 --- a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/EaglerSPRelay.java +++ b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/EaglerSPRelay.java @@ -82,7 +82,7 @@ public class EaglerSPRelay extends WebSocketServer { Iterator itr = clientConnections.values().iterator(); while(itr.hasNext()) { EaglerSPClient cl = itr.next(); - if(millis - cl.createdOn > 5000l) { + if(millis - cl.createdOn > 6900l) { cl.disconnect(IPacketFEDisconnectClient.TYPE_TIMEOUT, "Took too long to connect!"); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java new file mode 100644 index 0000000..b762b69 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java @@ -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 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; + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/RelayManager.java b/src/main/java/net/lax1dude/eaglercraft/RelayManager.java index 75d911b..5a170af 100644 --- a/src/main/java/net/lax1dude/eaglercraft/RelayManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/RelayManager.java @@ -1,9 +1,13 @@ package net.lax1dude.eaglercraft; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; 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.NBTTagCompound; import net.minecraft.src.NBTTagList; @@ -180,44 +184,100 @@ public class RelayManager { } } - public RelayServer getWorkingRelay(Consumer 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 brokenServers = new LinkedList(); + + public RelayServerSocket getWorkingRelay(Consumer progressCallback, int type, String code) { + brokenServers.clear(); if(relays.size() > 0) { long millis = System.currentTimeMillis(); - if(millis - lastPingThrough > 10000l) { + if(millis - lastPingThrough < 10000l) { RelayServer relay = getPrimary(); 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) { RelayServer relayEtr = relays.get(i); if(relayEtr != relay) { 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 { return null; } } - private RelayServer getWorkingRelayActive(Consumer progressCallback) { + private RelayServerSocket getWorkingCodeRelayActive(Consumer progressCallback, int type, String code) { 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) { - RelayServer relayEtr = relays.get(i); - if(relayEtr != relay) { - progressCallback.accept(relayEtr.address); - relayEtr.pingBlocking(); - if(relayEtr.getPing() > 0l && relayEtr.getPingCompatible().isCompatible()) { - return relayEtr; + RelayServer srv = relays.get(i); + if(!brokenServers.contains(srv)) { + progressCallback.accept(srv.address); + RelayServerSocket sock = connectHandshake(srv, type, code); + if(sock != null) { + return sock; + }else { + brokenServers.add(srv); } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/RelayServer.java b/src/main/java/net/lax1dude/eaglercraft/RelayServer.java index 6f86aea..b7ada2b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/RelayServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/RelayServer.java @@ -15,6 +15,7 @@ public class RelayServer { private VersionMismatch queriedCompatible; private long ping = 0l; private long workingPing = 0l; + public long lastPing = 0l; public RelayServer(String address, String comment, boolean primary) { this.address = address; @@ -95,6 +96,7 @@ public class RelayServer { queriedCompatible = query.getCompatible(); workingPing = ping; } + lastPing = System.currentTimeMillis(); query = null; } } @@ -111,4 +113,8 @@ public class RelayServer { } } + public RelayServerSocket openSocket() { + return EaglerAdapter.openRelayConnection(address); + } + } diff --git a/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java b/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java index 066ea0d..e9cd3d9 100644 --- a/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java +++ b/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java @@ -15,6 +15,7 @@ public interface RelayServerSocket { void writePacket(IPacket pkt); IPacket readPacket(); + IPacket nextPacket(); RateLimit getRatelimitHistory(); diff --git a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/ICEServerSet.java b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/ICEServerSet.java index 675e892..0a17b63 100644 --- a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/ICEServerSet.java +++ b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/ICEServerSet.java @@ -26,6 +26,16 @@ public class ICEServerSet { this.username = 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())); + } + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFFErrorCode.java b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFFErrorCode.java index 8effe08..b9f2617 100644 --- a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFFErrorCode.java +++ b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFFErrorCode.java @@ -15,6 +15,27 @@ public class IPacketFFErrorCode extends IPacket { public static final int TYPE_SERVER_DISCONNECTED = 0x06; 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 String desc; diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index bab1edf..1bd7408 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -3578,6 +3578,15 @@ public class EaglerAdapterImpl2 { return null; } } + + @Override + public IPacket nextPacket() { + if(packets.size() > 0) { + return packets.get(0); + }else { + return null; + } + } @Override public RateLimit getRatelimitHistory() { @@ -3632,6 +3641,11 @@ public class EaglerAdapterImpl2 { public IPacket readPacket() { return null; } + + @Override + public IPacket nextPacket() { + return null; + } @Override public RateLimit getRatelimitHistory() { @@ -3670,6 +3684,9 @@ public class EaglerAdapterImpl2 { public static final int LAN_CLIENT_CONNECTING = 1; public static final int LAN_CLIENT_CONNECTED = 2; + private static String clientICECandidate = null; + private static String clientDescription = null; + public static final boolean clientLANSupported() { return rtcLANClient.LANClientSupported(); } @@ -3680,13 +3697,13 @@ public class EaglerAdapterImpl2 { rtcLANClient.setDescriptionHandler(new EaglercraftLANClient.DescriptionHandler() { @Override public void call(String description) { - + clientDescription = description; } }); rtcLANClient.setICECandidateHandler(new EaglercraftLANClient.ICECandidateHandler() { @Override public void call(String candidate) { - + clientICECandidate = candidate; } }); rtcLANClient.setRemoteDataChannelHandler(new EaglercraftLANClient.ClientSignalHandler() { @@ -3734,6 +3751,43 @@ public class EaglerAdapterImpl2 { 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; @JSBody(params = { }, script = "return window.startLANServer();")