diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/data/ProtocolStack.java b/src/main/java/com/projectswg/forwarder/resources/networking/data/ProtocolStack.java index 60db69b..0254d30 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/data/ProtocolStack.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/data/ProtocolStack.java @@ -4,6 +4,7 @@ import com.projectswg.forwarder.resources.networking.ClientServer; import com.projectswg.forwarder.resources.networking.packets.Fragmented; import com.projectswg.forwarder.resources.networking.packets.Packet; import com.projectswg.forwarder.resources.networking.packets.SequencedPacket; +import me.joshlarson.jlcommon.log.Log; import org.jetbrains.annotations.NotNull; import java.net.InetSocketAddress; @@ -46,6 +47,7 @@ public class ProtocolStack { } public void send(Packet packet) { + Log.t("Sending %s", packet); send(packet.encode().array()); } @@ -166,4 +168,9 @@ public class ProtocolStack { return Collections.unmodifiableCollection(outboundPackaged); } + @Override + public String toString() { + return String.format("ProtocolStack[server=%s, source=%s, connectionId=%d]", server, source, connectionId); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Acknowledge.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Acknowledge.java index 2eea00a..b71e060 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Acknowledge.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Acknowledge.java @@ -62,4 +62,9 @@ public class Acknowledge extends Packet { public short getSequence() { return sequence; } + @Override + public String toString() { + return String.format("Acknowledge[%d]", sequence); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/ClientNetworkStatusUpdate.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/ClientNetworkStatusUpdate.java index 5c4c080..a6ae217 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/ClientNetworkStatusUpdate.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/ClientNetworkStatusUpdate.java @@ -102,4 +102,10 @@ public class ClientNetworkStatusUpdate extends Packet { public void setLastServerUpdate(int last) { this.lastServerUpdate = last; } public void setPacketsSent(long sent) { this.packetSent = sent; } public void setPacketsRecv(long recv) { this.packetRecv = recv; } + + @Override + public String toString() { + return String.format("ClientNetworkStatusUpdate[tick=%d, lastUpdate=%d, avgUpdate=%d, shortestUpdate=%d, longestUpdate=%d, lastServerUpdate=%d, sent=%d, recv=%d]", clientTickCount, lastUpdate, avgUpdate, shortUpdate, longUpdate, lastServerUpdate, packetSent, packetRecv); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/DataChannel.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/DataChannel.java index b2782a0..a5bd9ad 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/DataChannel.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/DataChannel.java @@ -213,4 +213,9 @@ public class DataChannel extends Packet implements SequencedPacket { } } + @Override + public String toString() { + return String.format("DataChannel[seq=%d, packets=%d]", sequence, content.size()); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Disconnect.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Disconnect.java index ab393cd..0f1665b 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Disconnect.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Disconnect.java @@ -99,4 +99,10 @@ public class Disconnect extends Packet { return reason; } } + + @Override + public String toString() { + return String.format("Disconnect[id=%d, reason=%s]", connectionId, reason); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Fragmented.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Fragmented.java index de01538..c7538c1 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/Fragmented.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/Fragmented.java @@ -133,4 +133,9 @@ public class Fragmented extends Packet implements SequencedPacket { return f; } + @Override + public String toString() { + return String.format("Fragmented[seq=%d, len=%d]", sequence, length); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/KeepAlive.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/KeepAlive.java index 9a42263..61ef7a2 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/KeepAlive.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/KeepAlive.java @@ -22,4 +22,9 @@ public class KeepAlive extends Packet { return data; } + @Override + public String toString() { + return String.format("KeepAlive[]"); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/MultiPacket.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/MultiPacket.java index 2c3b053..ab6d48f 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/MultiPacket.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/MultiPacket.java @@ -112,4 +112,9 @@ public class MultiPacket extends Packet { return length; } + @Override + public String toString() { + return String.format("MultiPacket[packets=%d]", content.size()); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/OutOfOrder.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/OutOfOrder.java index 0e59fb6..1a46d36 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/OutOfOrder.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/OutOfOrder.java @@ -58,4 +58,10 @@ public class OutOfOrder extends Packet { } public short getSequence() { return sequence; } + + @Override + public String toString() { + return String.format("OutOfOrder[%d]", sequence); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/ServerNetworkStatusUpdate.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/ServerNetworkStatusUpdate.java index ba79b15..4ef692e 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/ServerNetworkStatusUpdate.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/ServerNetworkStatusUpdate.java @@ -91,4 +91,10 @@ public class ServerNetworkStatusUpdate extends Packet { public void setClientPacketsRecv(long recv) { this.clientPacketsRecv = recv; } public void setServerPacketsSent(long sent) { this.serverPacketsSent = sent; } public void setServerPacketsRecv(long recv) { this.serverPacketsRecv = recv; } + + @Override + public String toString() { + return String.format("ServerNetworkStatusUpdate[ticks=%d, syncStamp=%d, clientSent=%d, clientRecv=%d, serverSent=%d, serverRecv=%d]", clientTickCount, serverSyncStampLong, clientPacketsSent, clientPacketsRecv, serverPacketsSent, serverPacketsRecv); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionRequest.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionRequest.java index a3fa988..1217797 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionRequest.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionRequest.java @@ -71,4 +71,10 @@ public class SessionRequest extends Packet { public int getCrcLength() { return crcLength; } public int getConnectionId() { return connectionId; } public int getUdpSize() { return udpSize; } + + @Override + public String toString() { + return String.format("SessionRequest[connectionId=%d, crcLength=%d, udpSize=%d]", connectionId, crcLength, udpSize); + } + } diff --git a/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionResponse.java b/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionResponse.java index 47a4ef3..4a13f87 100644 --- a/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionResponse.java +++ b/src/main/java/com/projectswg/forwarder/resources/networking/packets/SessionResponse.java @@ -92,4 +92,10 @@ public class SessionResponse extends Packet { public void setEncryptionFlag(short flag) { this.encryptionFlag = (byte) flag; } public void setXorLength(byte xorLength) { this.xorLength = xorLength; } public void setUdpSize(int size) { this.udpSize = size; } + + @Override + public String toString() { + return String.format("SessionResponse[connectionId=%d, crcSeed=%d, crcLength=%d, encryptionFlag=%d, xorLength=%d, udpSize=%d]", connectionId, crcSeed, crcLength, encryptionFlag, xorLength, udpSize); + } + } diff --git a/src/main/java/com/projectswg/forwarder/services/client/ClientServerService.java b/src/main/java/com/projectswg/forwarder/services/client/ClientServerService.java index 0285eaa..569b936 100644 --- a/src/main/java/com/projectswg/forwarder/services/client/ClientServerService.java +++ b/src/main/java/com/projectswg/forwarder/services/client/ClientServerService.java @@ -140,6 +140,8 @@ public class ClientServerService extends Service { private void broadcast(InetSocketAddress source, ClientServer server, Packet parsed) { ProtocolStack stack = this.stack.get(); if (parsed instanceof SessionRequest) { + if (stack != null && stack.getServer() == ClientServer.ZONE) + return; stack = new ProtocolStack(source, server, (remote, data) -> send(remote, server, data)); stack.setConnectionId(((SessionRequest) parsed).getConnectionId()); setStack(stack); @@ -154,20 +156,18 @@ public class ClientServerService extends Service { Log.t("[%s]@%s sent: %s", source, server, parsed); intentChain.broadcastAfter(getIntentManager(), new SonyPacketInboundIntent(parsed)); if (parsed instanceof Disconnect) { + Log.d("Received client disconnect with id %d and reason %s", ((Disconnect) parsed).getConnectionId(), ((Disconnect) parsed).getReason()); setStack(null); } } private void setStack(ProtocolStack stack) { + Log.d("Updating stack: %s", stack); ProtocolStack oldStack = this.stack.getAndSet(stack); - if (oldStack != null) { - oldStack.send(new Disconnect(oldStack.getConnectionId(), DisconnectReason.MANAGER_DELETED)); - if (stack == null) - intentChain.broadcastAfter(getIntentManager(), new ClientDisconnectedIntent()); - } - if (stack != null && stack.getServer() == ClientServer.LOGIN) { + if (oldStack != null && stack == null) + intentChain.broadcastAfter(getIntentManager(), new ClientDisconnectedIntent()); + if (stack != null && stack.getServer() == ClientServer.LOGIN) intentChain.broadcastAfter(getIntentManager(), new ClientConnectedIntent()); - } intentChain.broadcastAfter(getIntentManager(), new UpdateStackIntent(stack)); } diff --git a/src/main/java/com/projectswg/forwarder/services/server/ServerConnectionService.java b/src/main/java/com/projectswg/forwarder/services/server/ServerConnectionService.java index c80682d..b271179 100644 --- a/src/main/java/com/projectswg/forwarder/services/server/ServerConnectionService.java +++ b/src/main/java/com/projectswg/forwarder/services/server/ServerConnectionService.java @@ -1,8 +1,10 @@ package com.projectswg.forwarder.services.server; +import com.projectswg.common.network.packets.PacketType; +import com.projectswg.common.utilities.ByteUtilities; import com.projectswg.connection.HolocoreSocket; -import com.projectswg.connection.ServerConnectionChangedReason; import com.projectswg.connection.RawPacket; +import com.projectswg.connection.ServerConnectionChangedReason; import com.projectswg.forwarder.Forwarder.ForwarderData; import com.projectswg.forwarder.intents.client.ClientConnectedIntent; import com.projectswg.forwarder.intents.client.ClientDisconnectedIntent; @@ -13,20 +15,27 @@ import com.projectswg.forwarder.intents.control.StopForwarderIntent; import com.projectswg.forwarder.intents.server.ServerConnectedIntent; import com.projectswg.forwarder.intents.server.ServerDisconnectedIntent; import com.projectswg.forwarder.resources.networking.NetInterceptor; -import me.joshlarson.jlcommon.concurrency.BasicThread; import me.joshlarson.jlcommon.concurrency.Delay; +import me.joshlarson.jlcommon.concurrency.ThreadPool; import me.joshlarson.jlcommon.control.IntentChain; import me.joshlarson.jlcommon.control.IntentHandler; import me.joshlarson.jlcommon.control.Service; import me.joshlarson.jlcommon.log.Log; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; public class ServerConnectionService extends Service { private final IntentChain intentChain; + private final AtomicReference activeThread; private final AtomicBoolean running; - private final BasicThread thread; + private final ThreadPool thread; + private final Lock runLock; + private final Lock sleepLock; private HolocoreSocket holocore; private NetInterceptor interceptor; @@ -34,16 +43,27 @@ public class ServerConnectionService extends Service { public ServerConnectionService() { this.intentChain = new IntentChain(); + this.activeThread = new AtomicReference<>(null); this.running = new AtomicBoolean(false); - this.thread = new BasicThread("server-connection", this::runningLoop); + this.thread = new ThreadPool(2, "server-connection"); + this.runLock = new ReentrantLock(true); + this.sleepLock = new ReentrantLock(true); this.holocore = null; this.interceptor = null; this.data = null; } + @Override + public boolean start() { + thread.start(); + return true; + } + @Override public boolean stop() { - return stopRunningLoop(); + running.set(false); + thread.stop(true); + return thread.awaitTermination(1000); } @IntentHandler @@ -59,8 +79,7 @@ public class ServerConnectionService extends Service { @IntentHandler private void handleClientConnectedIntent(ClientConnectedIntent cci) { - stopRunningLoop(); - thread.start(); + queueConnectionLoop(0); } @IntentHandler @@ -72,58 +91,95 @@ public class ServerConnectionService extends Service { private void handleDataPacketInboundIntent(DataPacketInboundIntent dpii) { if (running.get()) holocore.send(interceptor.interceptClient(dpii.getData())); + else + Log.w("Dropping packet destined for server: %s", ByteUtilities.getHexString(dpii.getData())); } - private boolean stopRunningLoop() { - if (running.getAndSet(false)) { - thread.stop(true); - return thread.awaitTermination(500); + private void stopRunningLoop() { + running.set(false); + Thread activeThread = this.activeThread.get(); + if (activeThread != null) + activeThread.interrupt(); + try { + if (runLock.tryLock(1, TimeUnit.SECONDS)) + runLock.unlock(); + } catch (InterruptedException e) { + // Ignored } - return true; } - private void runningLoop() { - holocore = new HolocoreSocket(data.getAddress().getAddress(), data.getAddress().getPort()); - running.set(true); - while (running.get()) { + private void queueConnectionLoop(long delay) { + thread.execute(() -> startConnectionLoop(delay)); + } + + private void startConnectionLoop(long delay) { + if (sleepLock.tryLock()) { try { - connectedLoop(); + if (!Delay.sleepMilli(delay)) + return; + } finally { + sleepLock.unlock(); + } + } else if (delay > 0) { + return; + } + if (runLock.tryLock()) { + try { + activeThread.set(Thread.currentThread()); + holocore = new HolocoreSocket(data.getAddress().getAddress(), data.getAddress().getPort()); + running.set(true); + if (attemptConnection()) { + while (holocore.isConnected()) { + if (!connectionLoop()) + break; + } + } } catch (Throwable t) { Log.w(t); Log.i("Disconnected from server. Sleeping 3 seconds"); holocore.disconnect(ServerConnectionChangedReason.UNKNOWN); - Delay.sleepMilli(3000); + queueConnectionLoop(3000); + } finally { + cleanupConnection(); + activeThread.set(null); + runLock.unlock(); } } - Log.t("Destroying holocore connection"); - holocore.terminate(); - holocore = null; } - private void connectedLoop() { + private boolean attemptConnection() { Log.t("Attempting to connect to server at %s", holocore.getRemoteAddress()); if (!holocore.connect(5000)) { Log.t("Failed to connect to server. Sleeping 3 seconds"); - Delay.sleepMilli(3000); - return; + queueConnectionLoop(3000); + return false; } intentChain.broadcastAfter(getIntentManager(), new ServerConnectedIntent()); Log.i("Successfully connected to server at %s", holocore.getRemoteAddress()); - while (holocore.isConnected()) { - if (!running.get()) { - holocore.disconnect(ServerConnectionChangedReason.CLIENT_DISCONNECT); - break; - } - - RawPacket inbound = holocore.receive(); - if (inbound == null) { - holocore.disconnect(ServerConnectionChangedReason.SOCKET_CLOSED); - break; - } - intentChain.broadcastAfter(getIntentManager(), new DataPacketOutboundIntent(interceptor.interceptServer(inbound.getData()))); + return true; + } + + private boolean connectionLoop() { + if (!running.get()) { + holocore.disconnect(ServerConnectionChangedReason.CLIENT_DISCONNECT); + return false; } + + RawPacket inbound = holocore.receive(); + if (inbound == null) { + holocore.disconnect(ServerConnectionChangedReason.SOCKET_CLOSED); + return false; + } + intentChain.broadcastAfter(getIntentManager(), new DataPacketOutboundIntent(interceptor.interceptServer(inbound.getData()))); + return true; + } + + private void cleanupConnection() { intentChain.broadcastAfter(getIntentManager(), new ServerDisconnectedIntent()); Log.i("Disconnected from server"); + holocore.terminate(); + holocore = null; + running.set(false); } }