From 8111b7f756ee614ca3b878683d29521019d8470d Mon Sep 17 00:00:00 2001 From: LAX1DUDE Date: Thu, 18 Aug 2022 00:19:30 -0700 Subject: [PATCH] wrote 90% of the server side (host) code --- javascript/eagswebrtc.js | 8 +- sp-relay/protocol.txt | 24 ++ .../eaglercraft/sp/relay/EaglerSPRelay.java | 2 +- .../sp/relay/pkt/IPacket00Handshake.java | 2 +- .../relay/pkt/IPacketFEDisconnectClient.java | 1 + .../sp/ipc/IPCPacket17ConfigureLAN.java | 44 +++ .../eaglercraft/sp/ipc/IPCPacketManager.java | 1 + .../eaglercraft/sp/IntegratedServer.java | 5 + .../src/ServerConfigurationManager.java | 28 +- .../eaglercraft/IntegratedServer.java | 18 +- .../eaglercraft/IntegratedServerLAN.java | 308 ++++++++++++++++++ .../eaglercraft/LANClientNetworkManager.java | 12 + .../lax1dude/eaglercraft/LANPeerEvent.java | 88 +++++ .../lax1dude/eaglercraft/RelayManager.java | 12 +- .../eaglercraft/RelayServerSocket.java | 2 + .../sp/relay/pkt/IPacket00Handshake.java | 2 +- .../relay/pkt/IPacketFEDisconnectClient.java | 1 + .../java/net/minecraft/src/GuiIngameMenu.java | 7 +- .../java/net/minecraft/src/GuiShareToLan.java | 14 +- .../adapter/EaglerAdapterImpl2.java | 112 ++++--- .../adapter/teavm/EaglercraftLANServer.java | 9 +- 21 files changed, 606 insertions(+), 94 deletions(-) create mode 100644 sp-relay/protocol.txt create mode 100644 sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacket17ConfigureLAN.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/LANPeerEvent.java diff --git a/javascript/eagswebrtc.js b/javascript/eagswebrtc.js index 2fa5f64..8a012e2 100644 --- a/javascript/eagswebrtc.js +++ b/javascript/eagswebrtc.js @@ -601,10 +601,6 @@ window.initializeLANServer = (() => { // nothing to do! } - setRecieveCodeHandler(cb) { - // not yet implemented! - } - setICEServers(urls) { this.ICEServers.length = 0; for(var i = 0; i < urls.length; ++i) { @@ -701,7 +697,7 @@ window.initializeLANServer = (() => { try { thePeer.disconnect(); }catch(e) {} - this.remoteClientDisconnectHandler(peerId, quiet); + this.remoteClientDisconnectHandler(peerId); } } this.peerList.clear(); @@ -713,7 +709,7 @@ window.initializeLANServer = (() => { try { thePeer.disconnect(); }catch(e) {} - this.remoteClientDisconnectHandler(peerId, quiet); + this.remoteClientDisconnectHandler(peerId); } } diff --git a/sp-relay/protocol.txt b/sp-relay/protocol.txt new file mode 100644 index 0000000..e6c7398 --- /dev/null +++ b/sp-relay/protocol.txt @@ -0,0 +1,24 @@ + + Relay Packet Order: + ~~~~~~~~~~~~~~~~~~ + + Opening LAN world: + + [Server -> Relay] Open WebSocket + [Server -> Relay] PKT 0x00: Send protocol id, identify as server + [Relay -> Server] PKT 0x00: Reply protocol id, assign join code + [Relay -> Server] PKT 0x01: Send ICE server list to server + + Client connects: + + [Client -> Relay] Open WebSocket + [Client -> Relay] PKT 0x00: Send protocol id, identify as client, send server join code + [Relay -> Client] PKT 0x00: Reply protocol id, assign client id + [Relay -> Client] PKT 0x01: Send ICE server list to client + [Relay -> Server] PKT 0x02: Notify server of the client, send client's id to server + [Client -> Relay -> Server] PKT 0x03: Send client ICE Candidate to server + [Server -> Relay -> Client] PKT 0x03: Send server ICE Candidate to client + [Client -> Relay -> Server] PKT 0x04: Send client description to server + [Server -> Relay -> Client] PKT 0x04: Send server description to client + [Client -> Relay -> Server] PKT 0x05 or 0x06: Client signals sucess or failure + [Relay -> Client] PKT 0xFE: Signal to close WebSocket 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 b9c0fb3..16f8b3b 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 @@ -208,7 +208,7 @@ public class EaglerSPRelay extends WebSocketServer { IPacket00Handshake ipkt = (IPacket00Handshake)pkt; if(ipkt.connectionVersion != Constants.protocolVersion) { logger.debug("[{}]: Connected with unsupported protocol version: {} (supported " - + "version: {})", arg0.getAttachment(), Constants.protocolVersion); + + "version: {})", arg0.getAttachment(), ipkt.connectionVersion, Constants.protocolVersion); if(ipkt.connectionVersion < Constants.protocolVersion) { arg0.send(IPacket.writePacket(new IPacketFFErrorCode(IPacketFFErrorCode.TYPE_PROTOCOL_VERSION, "Outdated Client! (v" + Constants.protocolVersion + " req)"))); diff --git a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java index 3702881..609d9bc 100644 --- a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java +++ b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java @@ -35,7 +35,7 @@ public class IPacket00Handshake extends IPacket { @Override public int packetLength() { - return 1 + 1 + (connectionCode != null ? 1 + connectionCode.length() : 0); + return 1 + (connectionCode != null ? 1 + connectionCode.length() : 0); } } diff --git a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java index 34d6163..d67894a 100644 --- a/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java +++ b/sp-relay/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java @@ -12,6 +12,7 @@ public class IPacketFEDisconnectClient extends IPacket { public static final int TYPE_TIMEOUT = 0x02; public static final int TYPE_INVALID_OPERATION = 0x03; public static final int TYPE_INTERNAL_ERROR = 0x04; + public static final int TYPE_SERVER_DISCONNECT = 0x05; public static final int TYPE_UNKNOWN = 0xFF; public String clientId; diff --git a/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacket17ConfigureLAN.java b/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacket17ConfigureLAN.java new file mode 100644 index 0000000..b4ff78d --- /dev/null +++ b/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacket17ConfigureLAN.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.sp.ipc; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +public class IPCPacket17ConfigureLAN implements IPCPacketBase { + + public static final int ID = 0x17; + + public int gamemode; + public boolean cheats; + + public IPCPacket17ConfigureLAN() { + } + + public IPCPacket17ConfigureLAN(int gamemode, boolean cheats) { + this.gamemode = gamemode; + this.cheats = cheats; + } + + @Override + public void deserialize(DataInput bin) throws IOException { + gamemode = bin.readUnsignedByte(); + cheats = bin.readBoolean(); + } + + @Override + public void serialize(DataOutput bin) throws IOException { + bin.writeByte(gamemode); + bin.writeBoolean(cheats); + } + + @Override + public int id() { + return ID; + } + + @Override + public int size() { + return 2; + } + +} diff --git a/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacketManager.java b/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacketManager.java index 06e75d1..469f563 100644 --- a/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacketManager.java +++ b/sp-server/src/ipc/java/net/lax1dude/eaglercraft/sp/ipc/IPCPacketManager.java @@ -38,6 +38,7 @@ public class IPCPacketManager { mappings.put(IPCPacket14StringList.ID, () -> new IPCPacket14StringList()); mappings.put(IPCPacket15ThrowException.ID, () -> new IPCPacket15ThrowException()); mappings.put(IPCPacket16NBTList.ID, () -> new IPCPacket16NBTList()); + mappings.put(IPCPacket17ConfigureLAN.ID, () -> new IPCPacket17ConfigureLAN()); mappings.put(IPCPacketFFProcessKeepAlive.ID, () -> new IPCPacketFFProcessKeepAlive()); } diff --git a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java index aaa8fa5..aef0e6e 100644 --- a/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java +++ b/sp-server/src/main/java/net/lax1dude/eaglercraft/sp/IntegratedServer.java @@ -658,6 +658,11 @@ public class IntegratedServer { } } break; + case IPCPacket17ConfigureLAN.ID: { + IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)packet; + currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats); + } + break; default: System.err.println("IPC packet type 0x" + Integer.toHexString(id) + " class '" + packet.getClass().getSimpleName() + "' was not handled"); sendTaskFailed(); diff --git a/sp-server/src/main/java/net/minecraft/src/ServerConfigurationManager.java b/sp-server/src/main/java/net/minecraft/src/ServerConfigurationManager.java index b52406a..cab9bb7 100644 --- a/sp-server/src/main/java/net/minecraft/src/ServerConfigurationManager.java +++ b/sp-server/src/main/java/net/minecraft/src/ServerConfigurationManager.java @@ -48,6 +48,9 @@ public class ServerConfigurationManager { * hardcoded to max at 200 players */ private int playerPingIndex = 0; + + private EnumGameType lanGamemode = EnumGameType.SURVIVAL; + private boolean lanCheats = false; public ServerConfigurationManager(MinecraftServer par1MinecraftServer) { this.mcServer = par1MinecraftServer; @@ -521,7 +524,7 @@ public class ServerConfigurationManager { * Returns true if the specific player is allowed to use commands. */ public boolean areCommandsAllowed(String par1Str) { - return this.ops.contains(par1Str.trim().toLowerCase()) + return lanCheats || this.ops.contains(par1Str.trim().toLowerCase()) || this.mcServer.isSinglePlayer() && this.mcServer.worldServers[0].getWorldInfo().areCommandsAllowed() && this.mcServer.getServerOwner().equalsIgnoreCase(par1Str) || this.commandsAllowedForAll; @@ -820,14 +823,18 @@ public class ServerConfigurationManager { } private void func_72381_a(EntityPlayerMP par1EntityPlayerMP, EntityPlayerMP par2EntityPlayerMP, World par3World) { - if (par2EntityPlayerMP != null) { - par1EntityPlayerMP.theItemInWorldManager - .setGameType(par2EntityPlayerMP.theItemInWorldManager.getGameType()); - } else if (this.gameType != null) { - par1EntityPlayerMP.theItemInWorldManager.setGameType(this.gameType); - } + if(par1EntityPlayerMP.username.equals(mcServer.getServerOwner())) { + if (par2EntityPlayerMP != null) { + par1EntityPlayerMP.theItemInWorldManager + .setGameType(par2EntityPlayerMP.theItemInWorldManager.getGameType()); + } else if (this.gameType != null) { + par1EntityPlayerMP.theItemInWorldManager.setGameType(this.gameType); + } - par1EntityPlayerMP.theItemInWorldManager.initializeGameType(par3World.getWorldInfo().getGameType()); + par1EntityPlayerMP.theItemInWorldManager.initializeGameType(par3World.getWorldInfo().getGameType()); + }else { + par1EntityPlayerMP.theItemInWorldManager.setGameType(lanGamemode); + } } /** @@ -846,4 +853,9 @@ public class ServerConfigurationManager { this.mcServer.logInfo(par1Str); this.sendPacketToAllPlayers(new Packet3Chat(par1Str)); } + + public void configureLAN(int gamemode, boolean cheats) { + lanGamemode = EnumGameType.getByID(gamemode); + lanCheats = cheats; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/IntegratedServer.java b/src/main/java/net/lax1dude/eaglercraft/IntegratedServer.java index 6c4a6ff..3f24b26 100644 --- a/src/main/java/net/lax1dude/eaglercraft/IntegratedServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/IntegratedServer.java @@ -432,18 +432,8 @@ public class IntegratedServer { sendIPCPacket(new IPCPacket0CPlayerChannel(channel, false)); } - private static boolean hostingLAN = false; - - public static final String shareToLAN(EnumGameType gameType, boolean allowCommands) { - hostingLAN = true; - return "yee"; + public static void configureLAN(EnumGameType enumGameType, boolean allowCommands) { + sendIPCPacket(new IPCPacket17ConfigureLAN(enumGameType.getID(), allowCommands)); } - - public static final void closeLAN() { - hostingLAN = false; - } - - public static final boolean isHostingLAN() { - return hostingLAN; - } -} + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java b/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java new file mode 100644 index 0000000..b7b4168 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/IntegratedServerLAN.java @@ -0,0 +1,308 @@ +package net.lax1dude.eaglercraft; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import net.lax1dude.eaglercraft.sp.relay.pkt.*; + +public class IntegratedServerLAN { + + private static RelayServerSocket lanRelaySocket = null; + + public static String shareToLAN(Consumer progressCallback, String worldName, boolean worldHidden) { + RelayServerSocket sock = IntegratedServer.relayManager.getWorkingRelay((str) -> progressCallback.accept("Connecting: " + str), + IntegratedServer.preferredRelayVersion, worldName + (worldHidden ? ";1" : ";0")); + if(sock == null) { + lanRelaySocket = null; + return null; + }else { + progressCallback.accept("Opening: " + sock.getURI()); + IPacket00Handshake hs = (IPacket00Handshake)sock.readPacket(); + lanRelaySocket = sock; + String code = hs.connectionCode; + System.out.println("Relay [" + sock.getURI() + "] connected as 'server', code: " + code); + progressCallback.accept("Opened '" + code + "' on " + sock.getURI()); + long millis = System.currentTimeMillis(); + do { + if(sock.isClosed()) { + System.out.println("Relay [" + sock.getURI() + "] connection lost"); + lanRelaySocket = null; + return null; + } + IPacket pkt = sock.readPacket(); + if(pkt != null) { + if(pkt instanceof IPacket01ICEServers) { + IPacket01ICEServers ipkt = (IPacket01ICEServers)pkt; + System.out.println("Relay [" + sock.getURI() + "] provided ICE servers:"); + List servers = new ArrayList(); + for(net.lax1dude.eaglercraft.sp.relay.pkt.ICEServerSet.RelayServer srv : ipkt.servers) { + System.out.println("Relay [" + sock.getURI() + "] " + srv.type.name() + + ": " + srv.address); + servers.add(srv.getICEString()); + } + EaglerAdapter.serverLANInitializeServer(servers.toArray(new String[servers.size()])); + return code; + }else { + System.err.println("Relay [" + sock.getURI() + "] unexpected packet: " + pkt.getClass().getSimpleName()); + closeLAN(); + return null; + } + } + try { + Thread.sleep(50l); + } catch (InterruptedException e) { + } + }while(System.currentTimeMillis() - millis > 1000l); + System.out.println("Relay [" + sock.getURI() + "] relay provide ICE servers timeout"); + closeLAN(); + return null; + } + } + + public static void closeLAN() { + if(lanRelaySocket != null) { + lanRelaySocket.close(); + lanRelaySocket = null; + } + cleanupLAN(); + } + + static void cleanupLAN() { + Iterator itr = clients.values().iterator(); + while(itr.hasNext()) { + itr.next().disconnect(); + } + clients.clear(); + } + + public static boolean isHostingLAN() { + return lanRelaySocket != null; + } + + private static final Map clients = new HashMap(); + + public static void updateLANServer() { + if(lanRelaySocket != null) { + IPacket pkt; + while((pkt = lanRelaySocket.nextPacket()) != null) { + if(pkt instanceof IPacket02NewClient) { + IPacket02NewClient ipkt = (IPacket02NewClient) pkt; + if(clients.containsKey(ipkt.clientId)) { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] relay provided duplicate client '" + ipkt.clientId + "'"); + }else { + clients.put(ipkt.clientId, new LANClient(ipkt.clientId)); + } + }else if(pkt instanceof IPacket03ICECandidate) { + IPacket03ICECandidate ipkt = (IPacket03ICECandidate) pkt; + LANClient c = clients.get(ipkt.peerId); + if(c != null) { + c.handleICECandidates(ipkt.candidate); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] relay sent IPacket03ICECandidate for unknown client '" + ipkt.peerId + "'"); + } + }else if(pkt instanceof IPacket04Description) { + IPacket04Description ipkt = (IPacket04Description) pkt; + LANClient c = clients.get(ipkt.peerId); + if(c != null) { + c.handleDescription(ipkt.description); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] relay sent IPacket04Description for unknown client '" + ipkt.peerId + "'"); + } + }else if(pkt instanceof IPacket05ClientSuccess) { + IPacket05ClientSuccess ipkt = (IPacket05ClientSuccess) pkt; + LANClient c = clients.get(ipkt.clientId); + if(c != null) { + c.handleSuccess(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] relay sent IPacket05ClientSuccess for unknown client '" + ipkt.clientId + "'"); + } + }else if(pkt instanceof IPacket06ClientFailure) { + IPacket06ClientFailure ipkt = (IPacket06ClientFailure) pkt; + LANClient c = clients.get(ipkt.clientId); + if(c != null) { + c.handleFailure(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] relay sent IPacket06ClientFailure for unknown client '" + ipkt.clientId + "'"); + } + }else if(pkt instanceof IPacketFFErrorCode) { + IPacketFFErrorCode ipkt = (IPacketFFErrorCode) pkt; + System.err.println("Relay [" + lanRelaySocket.getURI() + "] error code thrown: " + + IPacketFFErrorCode.code2string(ipkt.code) + "(" + ipkt.code + "): " + ipkt.desc); + Throwable t; + while((t = lanRelaySocket.getException()) != null) { + t.printStackTrace(); + } + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected packet: " + pkt.getClass().getSimpleName()); + } + } + if(lanRelaySocket.isClosed()) { + lanRelaySocket = null; + cleanupLAN(); + }else { + Iterator itr = clients.values().iterator(); + while(itr.hasNext()) { + LANClient cl = itr.next(); + cl.update(); + if(cl.dead) { + itr.remove(); + } + } + } + } + } + + private static final class LANClient { + + private static final int PRE = 0, SENT_ICE_CANDIDATE = 2, SENT_DESCRIPTION = 3, CONNECTED = 4, CLOSED = 5; + + protected final String clientId; + + protected int state = PRE; + protected boolean dead = false; + + protected LANClient(String clientId) { + this.clientId = clientId; + } + + protected void handleICECandidates(String candidates) { + if(state == PRE) { + EaglerAdapter.serverLANPeerICECandidates(clientId, candidates); + long millis = System.currentTimeMillis(); + do { + LANPeerEvent evt; + if((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { + if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { + lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates)); + state = SENT_ICE_CANDIDATE; + return; + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + System.err.println("LAN client '" + clientId + "' disconnected while waiting for server ICE candidates"); + }else { + System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName()); + } + disconnect(); + return; + } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } + }while(System.currentTimeMillis() - millis > 3000l); + System.err.println("Getting server ICE candidates for '" + clientId + "' timed out!"); + disconnect(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket03ICECandidate for '" + clientId + "'"); + } + } + + protected void handleDescription(String description) { + if(state == SENT_ICE_CANDIDATE) { + EaglerAdapter.serverLANPeerDescription(clientId, description); + long millis = System.currentTimeMillis(); + do { + LANPeerEvent evt; + if((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { + if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) { + lanRelaySocket.writePacket(new IPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description)); + state = SENT_DESCRIPTION; + return; + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + System.err.println("LAN client '" + clientId + "' disconnected while waiting for server description"); + }else { + System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName()); + } + disconnect(); + return; + } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } + }while(System.currentTimeMillis() - millis > 3000l); + System.err.println("Getting server description for '" + clientId + "' timed out!"); + disconnect(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket04Description for '" + clientId + "'"); + } + } + + protected void handleSuccess() { + if(state == SENT_DESCRIPTION) { + long millis = System.currentTimeMillis(); + do { + LANPeerEvent evt; + if((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { + if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { + EaglerAdapter.enableChannel("NET|" + clientId); + state = CONNECTED; + return; + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + System.err.println("LAN client '" + clientId + "' disconnected while waiting for connection"); + }else { + System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName()); + } + disconnect(); + return; + } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } + }while(System.currentTimeMillis() - millis > 3000l); + System.err.println("Getting server description for '" + clientId + "' timed out!"); + disconnect(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket05ClientSuccess for '" + clientId + "'"); + } + } + + protected void handleFailure() { + if(state == SENT_DESCRIPTION) { + System.err.println("Client '" + clientId + "' failed to connect"); + disconnect(); + }else { + System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket06ClientFailure for '" + clientId + "'"); + } + } + + protected void update() { + if(state == CONNECTED) { + LANPeerEvent evt; + while((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { + if(state == CONNECTED) { + if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { + EaglerAdapter.sendToIntegratedServer(clientId, ((LANPeerEvent.LANPeerPacketEvent)evt).payload); + }else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { + System.err.println("LAN client '" + clientId + "' disconnected"); + disconnect(); + }else { + System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName()); + disconnect(); + } + } + } + if(state == CONNECTED) { + PKT pk = EaglerAdapter.recieveFromIntegratedServer(clientId); + EaglerAdapter.serverLANWritePacket(clientId, pk.data); + } + } + } + + protected void disconnect() { + if(!dead) { + if(state == CONNECTED) { + EaglerAdapter.disableChannel("NET|" + clientId); + } + state = CLOSED; + lanRelaySocket.writePacket(new IPacketFEDisconnectClient(clientId, IPacketFEDisconnectClient.TYPE_SERVER_DISCONNECT, "Connection Closed")); + dead = true; + } + } + + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java index 14cb2cd..d572c6e 100644 --- a/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/LANClientNetworkManager.java @@ -90,6 +90,10 @@ public class LANClientNetworkManager implements INetworkManager { connectState = SENT_ICE_CANDIDATE; continue mainLoop; } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } }while(System.currentTimeMillis() - lm > 3000l); // no ice candidates were sent @@ -127,6 +131,10 @@ public class LANClientNetworkManager implements INetworkManager { connectState = SENT_DESCRIPTION; continue mainLoop; } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } }while(System.currentTimeMillis() - lm > 3000l); // no description was sent @@ -163,6 +171,10 @@ public class LANClientNetworkManager implements INetworkManager { return new LANClientNetworkManager(displayCode, displayRelay); } + try { + Thread.sleep(20l); + } catch (InterruptedException e) { + } }while(System.currentTimeMillis() - lm > 3000l); // no channel was opened diff --git a/src/main/java/net/lax1dude/eaglercraft/LANPeerEvent.java b/src/main/java/net/lax1dude/eaglercraft/LANPeerEvent.java new file mode 100644 index 0000000..6d2e974 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/LANPeerEvent.java @@ -0,0 +1,88 @@ +package net.lax1dude.eaglercraft; + +public interface LANPeerEvent { + + String getPeerId(); + + public static class LANPeerICECandidateEvent implements LANPeerEvent { + + public final String clientId; + public final String candidates; + + public LANPeerICECandidateEvent(String clientId, String candidates) { + this.clientId = clientId; + this.candidates = candidates; + } + + @Override + public String getPeerId() { + return clientId; + } + + } + + public static class LANPeerDescriptionEvent implements LANPeerEvent { + + public final String clientId; + public final String description; + + public LANPeerDescriptionEvent(String clientId, String description) { + this.clientId = clientId; + this.description = description; + } + + @Override + public String getPeerId() { + return clientId; + } + + } + + public static class LANPeerDataChannelEvent implements LANPeerEvent { + + public final String clientId; + + public LANPeerDataChannelEvent(String clientId) { + this.clientId = clientId; + } + + @Override + public String getPeerId() { + return clientId; + } + + } + + public static class LANPeerPacketEvent implements LANPeerEvent { + + public final String clientId; + public final byte[] payload; + + public LANPeerPacketEvent(String clientId, byte[] payload) { + this.clientId = clientId; + this.payload = payload; + } + + @Override + public String getPeerId() { + return clientId; + } + + } + + public static class LANPeerDisconnectEvent implements LANPeerEvent { + + public final String clientId; + + public LANPeerDisconnectEvent(String clientId) { + this.clientId = clientId; + } + + @Override + public String getPeerId() { + return clientId; + } + + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/RelayManager.java b/src/main/java/net/lax1dude/eaglercraft/RelayManager.java index 5a170af..9d8c00f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/RelayManager.java +++ b/src/main/java/net/lax1dude/eaglercraft/RelayManager.java @@ -241,7 +241,9 @@ public class RelayManager { progressCallback.accept(relay.address); RelayServerSocket sock = connectHandshake(relay, type, code); if(sock != null) { - return sock; + if(!sock.isFailed()) { + return sock; + } }else { brokenServers.add(relay); } @@ -253,7 +255,9 @@ public class RelayManager { progressCallback.accept(relayEtr.address); RelayServerSocket sock = connectHandshake(relayEtr, type, code); if(sock != null) { - return sock; + if(!sock.isFailed()) { + return sock; + } }else { brokenServers.add(relayEtr); } @@ -275,7 +279,9 @@ public class RelayManager { progressCallback.accept(srv.address); RelayServerSocket sock = connectHandshake(srv, type, code); if(sock != null) { - return sock; + if(!sock.isFailed()) { + return sock; + } }else { brokenServers.add(srv); } diff --git a/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java b/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java index e9cd3d9..1394acf 100644 --- a/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java +++ b/src/main/java/net/lax1dude/eaglercraft/RelayServerSocket.java @@ -19,4 +19,6 @@ public interface RelayServerSocket { RateLimit getRatelimitHistory(); + String getURI(); + } diff --git a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java index 3702881..609d9bc 100644 --- a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java +++ b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacket00Handshake.java @@ -35,7 +35,7 @@ public class IPacket00Handshake extends IPacket { @Override public int packetLength() { - return 1 + 1 + (connectionCode != null ? 1 + connectionCode.length() : 0); + return 1 + (connectionCode != null ? 1 + connectionCode.length() : 0); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java index 34d6163..d67894a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java +++ b/src/main/java/net/lax1dude/eaglercraft/sp/relay/pkt/IPacketFEDisconnectClient.java @@ -12,6 +12,7 @@ public class IPacketFEDisconnectClient extends IPacket { public static final int TYPE_TIMEOUT = 0x02; public static final int TYPE_INVALID_OPERATION = 0x03; public static final int TYPE_INTERNAL_ERROR = 0x04; + public static final int TYPE_SERVER_DISCONNECT = 0x05; public static final int TYPE_UNKNOWN = 0xFF; public String clientId; diff --git a/src/main/java/net/minecraft/src/GuiIngameMenu.java b/src/main/java/net/minecraft/src/GuiIngameMenu.java index 9d6f614..16d4b2b 100644 --- a/src/main/java/net/minecraft/src/GuiIngameMenu.java +++ b/src/main/java/net/minecraft/src/GuiIngameMenu.java @@ -6,6 +6,7 @@ import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.GuiScreenSkinCapeSettings; import net.lax1dude.eaglercraft.GuiVoiceMenu; import net.lax1dude.eaglercraft.IntegratedServer; +import net.lax1dude.eaglercraft.IntegratedServerLAN; import net.minecraft.client.Minecraft; public class GuiIngameMenu extends GuiScreen { @@ -31,7 +32,7 @@ public class GuiIngameMenu extends GuiScreen { this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 4 + 24 + var1, StatCollector.translateToLocal("menu.returnToGame"))); this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + var1, 98, 20, StatCollector.translateToLocal("menu.options"))); GuiButton var3; - this.buttonList.add(var3 = new GuiButton(7, this.width / 2 + 2, this.height / 4 + 96 + var1, 98, 20, StatCollector.translateToLocal(IntegratedServer.isHostingLAN() ? "menu.closeLan" : "menu.shareToLan"))); + this.buttonList.add(var3 = new GuiButton(7, this.width / 2 + 2, this.height / 4 + 96 + var1, 98, 20, StatCollector.translateToLocal(IntegratedServerLAN.isHostingLAN() ? "menu.closeLan" : "menu.shareToLan"))); var3.enabled = mc.isSingleplayer(); this.buttonList.add(new GuiButton(8, 3, 3, 120, 20, StatCollector.translateToLocal("menu.skinCapeSettings"))); } @@ -64,8 +65,8 @@ public class GuiIngameMenu extends GuiScreen { break; case 7: - if (IntegratedServer.isHostingLAN()) { - IntegratedServer.closeLAN(); + if (IntegratedServerLAN.isHostingLAN()) { + IntegratedServerLAN.closeLAN(); this.mc.displayGuiScreen((GuiScreen) null); this.mc.setIngameFocus(); this.mc.sndManager.resumeAllSounds(); diff --git a/src/main/java/net/minecraft/src/GuiShareToLan.java b/src/main/java/net/minecraft/src/GuiShareToLan.java index a6c8300..9e65886 100644 --- a/src/main/java/net/minecraft/src/GuiShareToLan.java +++ b/src/main/java/net/minecraft/src/GuiShareToLan.java @@ -1,6 +1,7 @@ package net.minecraft.src; import net.lax1dude.eaglercraft.IntegratedServer; +import net.lax1dude.eaglercraft.IntegratedServerLAN; public class GuiShareToLan extends GuiScreen { /** @@ -77,11 +78,16 @@ public class GuiShareToLan extends GuiScreen { this.func_74088_g(); } else if (par1GuiButton.id == 101) { this.mc.displayGuiScreen((GuiScreen) null); - String var2 = IntegratedServer.shareToLAN(EnumGameType.getByName(this.gameMode), this.allowCommands); + LoadingScreenRenderer ls = mc.loadingScreen; + IntegratedServer.configureLAN(EnumGameType.getByName(this.gameMode), this.allowCommands); + String code = IntegratedServerLAN.shareToLAN((str) -> ls.displayProgressMessage(str), null, false); + + //TODO: handle code success or failure, redirect to relay list on failure, on success store code and relay and display in pause menu + //TODO: must call IntegratedServer.configureLAN(mc.theWorld.getGameType(), false); when world is closed + String var3; - - if (var2 != null) { - var3 = StatCollector.translateToLocalFormatted("commands.publish.started", var2); + if (code != null) { + var3 = StatCollector.translateToLocalFormatted("commands.publish.started", code); } else { var3 = StatCollector.translateToLocal("commands.publish.failed"); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index 8334012..45aab7e 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -85,11 +85,13 @@ import org.teavm.jso.workers.Worker; import net.lax1dude.eaglercraft.AssetRepository; import net.lax1dude.eaglercraft.Base64; +import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.EaglerImage; import net.lax1dude.eaglercraft.EaglerProfile; import net.lax1dude.eaglercraft.EarlyLoadScreen; import net.lax1dude.eaglercraft.ExpiringSet; import net.lax1dude.eaglercraft.IntegratedServer; +import net.lax1dude.eaglercraft.LANPeerEvent; import net.lax1dude.eaglercraft.LocalStorageManager; import net.lax1dude.eaglercraft.PKT; import net.lax1dude.eaglercraft.RelayQuery; @@ -1848,6 +1850,9 @@ public class EaglerAdapterImpl2 { if(IntegratedServer.doesChannelExist(EaglerProfile.username) && IntegratedServer.isWorldRunning()) { return true; } + if(!EaglerAdapter.clientLANClosed()) { + return true; + } if(sock == null || sock.getReadyState() == 3) { sockIsConnecting = false; } @@ -3599,6 +3604,11 @@ public class EaglerAdapterImpl2 { return RateLimit.NONE; } + @Override + public String getURI() { + return uri; + } + } private static class RelayServerSocketRatelimitDummy implements RelayServerSocket { @@ -3652,6 +3662,11 @@ public class EaglerAdapterImpl2 { return limit; } + @Override + public String getURI() { + return ""; + } + } public static final RelayServerSocket openRelayConnection(String addr) { @@ -3812,38 +3827,34 @@ public class EaglerAdapterImpl2 { private static native EaglercraftLANServer startRTCLANServer(); private static boolean serverLANinit = false; - private static final List serverLANPacketBuffer = new ArrayList<>(); + private static final List serverLANEventBuffer = new LinkedList<>(); public static final boolean serverLANSupported() { return rtcLANServer.LANServerSupported(); } - public static final String serverLANInitializeServer(String relay) { + public static final void serverLANInitializeServer(String[] servers) { + serverLANEventBuffer.clear(); + rtcLANServer.setICEServers(servers); rtcLANServer.initializeServer(); if(!serverLANinit) { serverLANinit = true; rtcLANServer.setDescriptionHandler(new EaglercraftLANServer.DescriptionHandler() { @Override public void call(String peerId, String candidate) { - + serverLANEventBuffer.add(new LANPeerEvent.LANPeerDisconnectEvent(peerId)); } }); rtcLANServer.setICECandidateHandler(new EaglercraftLANServer.ICECandidateHandler() { @Override public void call(String peerId, String candidate) { - - } - }); - rtcLANServer.setRecieveCodeHandler(new EaglercraftLANServer.CodeHandler() { - @Override - public void call(String code) { - + serverLANEventBuffer.add(new LANPeerEvent.LANPeerDisconnectEvent(peerId)); } }); rtcLANServer.setRemoteClientDataChannelHandler(new EaglercraftLANServer.ClientSignalHandler() { @Override - public void call() { - // unused + public void call(String peerId) { + serverLANEventBuffer.add(new LANPeerEvent.LANPeerDataChannelEvent(peerId)); } }); rtcLANServer.setRemoteClientPacketHandler(new EaglercraftLANServer.PeerPacketHandler() { @@ -3854,18 +3865,16 @@ public class EaglerAdapterImpl2 { for(int i = 0; i < ret.length; ++i) { ret[i] = (byte) array.get(i); } - serverLANPacketBuffer.add(new LANPeerPacket(peerId, ret)); + serverLANEventBuffer.add(new LANPeerEvent.LANPeerPacketEvent(peerId, ret)); } }); rtcLANServer.setRemoteClientDisconnectHandler(new EaglercraftLANServer.ClientSignalHandler() { @Override - public void call() { - clientDataChannelClosed = true; + public void call(String peerId) { + serverLANEventBuffer.add(new LANPeerEvent.LANPeerDisconnectEvent(peerId)); } }); } - // todo: java-side: register in relay & return code!! - return null; } public static final boolean serverLANServerOpen() { @@ -3877,39 +3886,40 @@ public class EaglerAdapterImpl2 { // todo: java-side: disconnect from relay server } - public static interface LANConnectionEvent { - - public String getPeerName(); - - public void accept(); - - public void reject(); - + public static final LANPeerEvent serverLANGetEvent() { + return serverLANEventBuffer.size() > 0 ? serverLANEventBuffer.remove(0) : null; } - public static final LANConnectionEvent serverLANGetConnectionEvent() { - return null; - } - - public static final String serverLANGetDisconnectEvent() { - return null; - } - - public static class LANPeerPacket { - - public final String peer; - - public final byte[] packet; - - protected LANPeerPacket(String peer, byte[] packet) { - this.peer = peer; - this.packet = packet; + public static final LANPeerEvent serverLANGetEvent(String clientId) { + if(serverLANEventBuffer.size() > 0) { + Iterator i = serverLANEventBuffer.iterator(); + while(i.hasNext()) { + LANPeerEvent evt = i.next(); + if(evt.getPeerId().equals(clientId)) { + i.remove(); + return evt; + } + } + return null; + }else { + return null; } - } - public static final LANPeerPacket serverLANReadPacket() { - return serverLANPacketBuffer.size() > 0 ? serverLANPacketBuffer.remove(0) : null; + public static final T serverLANGetEvent(String clientId, Class eventType) { + if(serverLANEventBuffer.size() > 0) { + Iterator i = serverLANEventBuffer.iterator(); + while(i.hasNext()) { + LANPeerEvent evt = i.next(); + if(evt.getPeerId().equals(clientId) && eventType.isInstance(evt)) { + i.remove(); + return (T)evt; + } + } + return null; + }else { + return null; + } } public static final void serverLANWritePacket(String peer, byte[] data) { @@ -3918,6 +3928,18 @@ public class EaglerAdapterImpl2 { rtcLANServer.sendPacketToRemoteClient(peer, arr); } + public static final void serverLANCreatePeer(String peer) { + rtcLANServer.signalRemoteConnect(peer); + } + + public static final void serverLANPeerICECandidates(String peer, String iceCandidates) { + rtcLANServer.signalRemoteICECandidate(peer, iceCandidates); + } + + public static final void serverLANPeerDescription(String peer, String description) { + rtcLANServer.signalRemoteDescription(peer, description); + } + public static final void serverLANDisconnectPeer(String peer) { rtcLANServer.signalRemoteDisconnect(peer); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/EaglercraftLANServer.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/EaglercraftLANServer.java index 624e974..762fa97 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/EaglercraftLANServer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/teavm/EaglercraftLANServer.java @@ -14,8 +14,6 @@ public interface EaglercraftLANServer extends JSObject { void initializeServer(); - void setRecieveCodeHandler(CodeHandler cb); - void setICEServers(String[] urls); void setICECandidateHandler(ICECandidateHandler cb); @@ -55,11 +53,6 @@ public interface EaglercraftLANServer extends JSObject { void call(String peerId, String candidate); } - @JSFunctor - public static interface CodeHandler extends JSObject { - void call(String code); - } - @JSFunctor public static interface DescriptionHandler extends JSObject { void call(String peerId, String candidate); @@ -67,7 +60,7 @@ public interface EaglercraftLANServer extends JSObject { @JSFunctor public static interface ClientSignalHandler extends JSObject { - void call(); + void call(String peerId); } @JSFunctor