diff --git a/javascript/eagswebrtc.js b/javascript/eagswebrtc.js index 0002f13..2fa5f64 100644 --- a/javascript/eagswebrtc.js +++ b/javascript/eagswebrtc.js @@ -398,6 +398,9 @@ window.initializeLANClient = (() => { signalRemoteConnect() { const self = this; + if(self.readyState === READYSTATE_CONNECTED || self.readyState === READYSTATE_CONNECTING) { + signalRemoteDisconnect(); + } this.peerConnection.addEventListener("icecandidate", (evt) => { if(evt.candidate) { self.iceCandidateHandler(JSON.stringify({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate })); diff --git a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java index b762b69..14cb2cd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java @@ -1,5 +1,10 @@ package net.lax1dude.eaglercraft; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -8,9 +13,10 @@ 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.IPacket05ClientSuccess; +import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket06ClientFailure; 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; @@ -23,24 +29,29 @@ public class LANClientNetworkManager implements INetworkManager { public final String displayCode; public final String displayRelay; + private NetHandler theNetHandler; + private LANClientNetworkManager(String displayCode, String displayRelay) { this.displayCode = displayCode; this.displayRelay = displayRelay; + this.theNetHandler = null; } - public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, LoadingScreenRenderer loadingScreen, - String displayCode, String displayRelay) { + public static LANClientNetworkManager connectToWorld(RelayServerSocket sock, 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) { + + // %%%%%% Process IPacket00Handshake %%%%%% + 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: " @@ -49,7 +60,12 @@ public class LANClientNetworkManager implements INetworkManager { } }else if(pkt instanceof IPacket01ICEServers) { if(connectState == INIT) { + + // %%%%%% Process IPacket01ICEServers %%%%%% + IPacket01ICEServers ipkt = (IPacket01ICEServers) pkt; + + // print servers 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) { @@ -57,20 +73,30 @@ public class LANClientNetworkManager implements INetworkManager { + ": " + srv.address); servers.add(srv.getICEString()); } + + // process EaglerAdapter.clientLANSetICEServersAndConnect(servers.toArray(new String[servers.size()])); + + // await result long lm = System.currentTimeMillis(); do { String c = EaglerAdapter.clientLANAwaitICECandidate(); if(c != null) { System.out.println("Relay [" + displayRelay + "|" + displayCode + "] client sent ICE candidate"); + + // 'this.iceCandidateHandler' was called, send result: sock.writePacket(new IPacket03ICECandidate("", c)); + connectState = SENT_ICE_CANDIDATE; continue mainLoop; } }while(System.currentTimeMillis() - lm > 3000l); + + // no ice candidates were sent 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: " @@ -79,22 +105,35 @@ public class LANClientNetworkManager implements INetworkManager { } }else if(pkt instanceof IPacket03ICECandidate) { if(connectState == SENT_ICE_CANDIDATE) { + + // %%%%%% Process IPacket03ICECandidate %%%%%% + IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt; + + // process System.out.println("Relay [" + displayRelay + "|" + displayCode + "] recieved server ICE candidate"); EaglerAdapter.clientLANSetICECandidate(ipkt.candidate); + + // await result long lm = System.currentTimeMillis(); do { String c = EaglerAdapter.clientLANAwaitDescription(); if(c != null) { System.out.println("Relay [" + displayRelay + "|" + displayCode + "] client sent description"); + + // 'this.descriptionHandler' was called, send result: sock.writePacket(new IPacket04Description("", c)); + connectState = SENT_DESCRIPTION; continue mainLoop; } }while(System.currentTimeMillis() - lm > 3000l); + + // no description was sent 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: " @@ -103,11 +142,35 @@ public class LANClientNetworkManager implements INetworkManager { } }else if(pkt instanceof IPacket04Description) { if(connectState == SENT_DESCRIPTION) { + + // %%%%%% Process IPacket04Description %%%%%% + IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt; + + // process System.out.println("Relay [" + displayRelay + "|" + displayCode + "] recieved server description"); EaglerAdapter.clientLANSetDescription(ipkt.candidate); + + // await result + long lm = System.currentTimeMillis(); + do { + if(EaglerAdapter.clientLANAwaitChannel()) { + System.out.println("Relay [" + displayRelay + "|" + displayCode + "] client opened data channel"); + + // 'this.remoteDataChannelHandler' was called, success + sock.writePacket(new IPacket05ClientSuccess()); + sock.close(); + return new LANClientNetworkManager(displayCode, displayRelay); + + } + }while(System.currentTimeMillis() - lm > 3000l); - // TODO: handle description, return packet 05 or 06 + // no channel was opened + sock.writePacket(new IPacket06ClientFailure()); + sock.close(); + System.err.println("Relay [" + displayRelay + "|" + displayCode + "] client open data channel timeout"); + + return null; }else { sock.close(); @@ -116,6 +179,9 @@ public class LANClientNetworkManager implements INetworkManager { return null; } }else if(pkt instanceof IPacketFFErrorCode) { + + // %%%%%% Process IPacketFFErrorCode %%%%%% + IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; System.err.println("Relay [" + displayRelay + "|" + displayCode + "] connection failed: " + IPacketFFErrorCode.code2string(ipkt.code) + "(" + ipkt.code + "): " + ipkt.desc); @@ -125,7 +191,11 @@ public class LANClientNetworkManager implements INetworkManager { } sock.close(); return null; + }else { + + // %%%%%% Unexpected Packet %%%%%% + System.err.println("Relay [" + displayRelay + "] unexpected packet: " + pkt.getClass().getSimpleName()); sock.close(); return null; @@ -141,56 +211,92 @@ public class LANClientNetworkManager implements INetworkManager { @Override public void setNetHandler(NetHandler var1) { - // TODO Auto-generated method stub - + theNetHandler = var1; } @Override public void addToSendQueue(Packet var1) { - // TODO Auto-generated method stub - + try { + ByteArrayOutputStream bao = new ByteArrayOutputStream(var1.getPacketSize() + 1); + Packet.writePacket(var1, new DataOutputStream(bao)); + EaglerAdapter.clientLANSendPacket(bao.toByteArray()); + }catch(IOException e) { + System.err.println("Failed to serialize minecraft packet '" + var1.getClass().getSimpleName() + "' to remote LAN world"); + e.printStackTrace(); + } } @Override public void wakeThreads() { - // TODO Auto-generated method stub - + // no } @Override public void processReadPackets() { - // TODO Auto-generated method stub - + if(this.theNetHandler != null) { + byte[] data; + while((data = EaglerAdapter.clientLANReadPacket()) != null) { + try { + ByteArrayInputStream bai = new ByteArrayInputStream(data); + int pktId = bai.read(); + + if(pktId == -1) { + System.err.println("Recieved invalid '-1' packet"); + continue; + } + + Packet pkt = Packet.getNewPacket(pktId); + + if(pkt == null) { + System.err.println("Recieved invalid '" + pktId + "' packet"); + continue; + } + + pkt.readPacketData(new DataInputStream(bai)); + + try { + pkt.processPacket(theNetHandler); + }catch(Throwable t) { + System.err.println("Could not process minecraft packet 0x" + Integer.toHexString(pkt.getPacketId()) + " class '" + pkt.getClass().getSimpleName() + "' from remote LAN world"); + t.printStackTrace(); + } + + }catch(IOException ex) { + System.err.println("Could not deserialize a " + data.length + " byte long minecraft packet of type '" + (data.length <= 0 ? -1 : (int)(data[0] & 0xFF)) + "' from remote LAN world"); + } + } + } } @Override public void serverShutdown() { - // TODO Auto-generated method stub - + if(!EaglerAdapter.clientLANClosed()) { + EaglerAdapter.clientLANCloseConnection(); + } } @Override public int packetSize() { - // TODO Auto-generated method stub return 0; } @Override public void networkShutdown(String var1, Object... var2) { - // TODO Auto-generated method stub - + if(!EaglerAdapter.clientLANClosed()) { + EaglerAdapter.clientLANCloseConnection(); + } } @Override public void closeConnections() { - // TODO Auto-generated method stub - + if(!EaglerAdapter.clientLANClosed()) { + EaglerAdapter.clientLANCloseConnection(); + } } @Override public String getServerURI() { - // TODO Auto-generated method stub - return null; + return "[lan:" + displayRelay + ":" + displayCode + "]"; } } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index 1bd7408..8334012 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -3686,6 +3686,8 @@ public class EaglerAdapterImpl2 { private static String clientICECandidate = null; private static String clientDescription = null; + private static boolean clientDataChannelOpen = false; + private static boolean clientDataChannelClosed = false; public static final boolean clientLANSupported() { return rtcLANClient.LANClientSupported(); @@ -3709,7 +3711,7 @@ public class EaglerAdapterImpl2 { rtcLANClient.setRemoteDataChannelHandler(new EaglercraftLANClient.ClientSignalHandler() { @Override public void call() { - // basically useless, ignore for now. + clientDataChannelOpen = true; } }); rtcLANClient.setRemotePacketHandler(new EaglercraftLANClient.RemotePacketHandler() { @@ -3726,7 +3728,7 @@ public class EaglerAdapterImpl2 { rtcLANClient.setRemoteDisconnectHandler(new EaglercraftLANClient.ClientSignalHandler() { @Override public void call() { - // disconnected + clientDataChannelClosed = true; } }); } @@ -3753,11 +3755,14 @@ public class EaglerAdapterImpl2 { public static final void clientLANSetICEServersAndConnect(String[] servers) { rtcLANClient.setICEServers(servers); + rtcLANClient.signalRemoteConnect(); } - + public static final void clearLANClientState() { clientICECandidate = null; clientDescription = null; + clientDataChannelOpen = false; + clientDataChannelClosed = false; } public static final String clientLANAwaitICECandidate() { @@ -3780,6 +3785,19 @@ public class EaglerAdapterImpl2 { } } + public static final boolean clientLANAwaitChannel() { + if(clientDataChannelOpen) { + clientDataChannelOpen = false; + return true; + }else { + return false; + } + } + + public static final boolean clientLANClosed() { + return clientDataChannelClosed; + } + public static final void clientLANSetICECandidate(String candidate) { rtcLANClient.signalRemoteICECandidate(candidate); } @@ -3842,7 +3860,7 @@ public class EaglerAdapterImpl2 { rtcLANServer.setRemoteClientDisconnectHandler(new EaglercraftLANServer.ClientSignalHandler() { @Override public void call() { - // disconnected + clientDataChannelClosed = true; } }); }