diff --git a/.classpath b/.classpath index 8b7d01e..2ebdd55 100644 --- a/.classpath +++ b/.classpath @@ -1,11 +1,7 @@ - - - - - + diff --git a/.gitignore b/.gitignore index e660fd9..a80544b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bin/ +/.gradle/ +/build/ diff --git a/Client Holocore b/Client Holocore index be5b53b..41a5df8 160000 --- a/Client Holocore +++ b/Client Holocore @@ -1 +1 @@ -Subproject commit be5b53b4ea08260c945566938ab0f887b0d86ba9 +Subproject commit 41a5df892bf71bebad5085bc45d3b9e3107b5dfc diff --git a/PSWGCommon b/PSWGCommon index 016f219..8505a99 160000 --- a/PSWGCommon +++ b/PSWGCommon @@ -1 +1 @@ -Subproject commit 016f219e8092a55863118644a42e66d7253dcc39 +Subproject commit 8505a996c3758d5b1d4054d302474d4cd6979808 diff --git a/lib/Holocore.jar b/lib/Holocore.jar deleted file mode 100644 index fd7e338..0000000 Binary files a/lib/Holocore.jar and /dev/null differ diff --git a/src/com/projectswg/Connections.java b/src/com/projectswg/Connections.java index 8b3106d..98ea2cd 100644 --- a/src/com/projectswg/Connections.java +++ b/src/com/projectswg/Connections.java @@ -8,6 +8,7 @@ import com.projectswg.common.concurrency.Delay; import com.projectswg.common.control.IntentManager; import com.projectswg.common.control.Manager; import com.projectswg.common.debug.Log; +import com.projectswg.common.network.packets.swg.ErrorMessage; import com.projectswg.connection.ServerConnectionChangedReason; import com.projectswg.connection.ServerConnectionStatus; import com.projectswg.intents.ClientConnectionChangedIntent; @@ -20,8 +21,6 @@ import com.projectswg.networking.server.ServerConnectionService; import com.projectswg.networking.soe.Disconnect.DisconnectReason; import com.projectswg.services.PacketRecordingService; -import network.packets.swg.ErrorMessage; - public class Connections extends Manager { public static final String VERSION = "0.9.8"; diff --git a/src/com/projectswg/networking/NetInterceptor.java b/src/com/projectswg/networking/NetInterceptor.java index 21b6162..6c36778 100644 --- a/src/com/projectswg/networking/NetInterceptor.java +++ b/src/com/projectswg/networking/NetInterceptor.java @@ -1,11 +1,14 @@ package com.projectswg.networking; -import com.projectswg.common.network.NetBuffer; -import com.projectswg.networking.client.ClientData; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; -import network.packets.swg.login.LoginClientId; -import network.packets.swg.login.LoginClusterStatus; -import resources.Galaxy; +import com.projectswg.common.data.encodables.galaxy.Galaxy; +import com.projectswg.common.network.NetBuffer; +import com.projectswg.common.network.packets.PacketType; +import com.projectswg.common.network.packets.swg.login.LoginClientId; +import com.projectswg.common.network.packets.swg.login.LoginClusterStatus; +import com.projectswg.networking.client.ClientData; public class NetInterceptor { @@ -24,12 +27,12 @@ public class NetInterceptor { public byte [] interceptClient(byte [] data) { if (data.length < 6) return data; - NetBuffer buffer = NetBuffer.wrap(data); - buffer.getShort(); - switch (buffer.getInt()) { - case 0x41131F96: // LoginClientId - return setAutoLogin(buffer); - case 0x43FD1C22: // CmdSceneReady + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + PacketType type = PacketType.fromCrc(bb.getInt(2)); + switch (type) { + case LOGIN_CLIENT_ID: + return setAutoLogin(NetBuffer.wrap(bb)); + case CMD_SCENE_READY: clientData.setZoning(false); return data; default: @@ -40,11 +43,11 @@ public class NetInterceptor { public byte [] interceptServer(byte [] data) { if (data.length < 6) return data; - NetBuffer buffer = NetBuffer.wrap(data); - buffer.getShort(); - switch (buffer.getInt()) { - case 0x3436AEB6: // LoginClusterStatus - return getServerList(buffer); + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + PacketType type = PacketType.fromCrc(bb.getInt(2)); + switch (type) { + case LOGIN_CLUSTER_STATUS: + return getServerList(NetBuffer.wrap(bb)); default: return data; } diff --git a/src/com/projectswg/networking/client/ClientConnectionService.java b/src/com/projectswg/networking/client/ClientConnectionService.java index 9e39045..48a8a99 100644 --- a/src/com/projectswg/networking/client/ClientConnectionService.java +++ b/src/com/projectswg/networking/client/ClientConnectionService.java @@ -4,6 +4,8 @@ import com.projectswg.common.concurrency.Delay; import com.projectswg.common.concurrency.PswgBasicScheduledThread; import com.projectswg.common.control.Manager; import com.projectswg.common.debug.Log; +import com.projectswg.common.network.packets.SWGPacket; +import com.projectswg.common.network.packets.swg.zone.HeartBeat; import com.projectswg.intents.ServerToClientPacketIntent; import com.projectswg.networking.NetInterceptor; import com.projectswg.networking.NetInterceptor.InterceptorProperties; @@ -15,9 +17,6 @@ import com.projectswg.networking.soe.Disconnect; import com.projectswg.networking.soe.Disconnect.DisconnectReason; import com.projectswg.resources.ClientConnectionStatus; -import network.packets.swg.SWGPacket; -import com.projectswg.common.network.packets.swg.zone.HeartBeat; - public class ClientConnectionService extends Manager implements ClientPacketSender { private final NetInterceptor interceptor; diff --git a/src/com/projectswg/networking/client/ClientPacketSender.java b/src/com/projectswg/networking/client/ClientPacketSender.java index ee7bf55..00e81ad 100644 --- a/src/com/projectswg/networking/client/ClientPacketSender.java +++ b/src/com/projectswg/networking/client/ClientPacketSender.java @@ -1,9 +1,8 @@ package com.projectswg.networking.client; +import com.projectswg.common.network.packets.SWGPacket; import com.projectswg.networking.Packet; -import network.packets.swg.SWGPacket; - public interface ClientPacketSender { /** Adds the specified packets to a buffer to guarantee sending in-order */ diff --git a/src/com/projectswg/networking/client/sender/ClientPackager.java b/src/com/projectswg/networking/client/sender/ClientPackager.java index 8688dcb..0837731 100644 --- a/src/com/projectswg/networking/client/sender/ClientPackager.java +++ b/src/com/projectswg/networking/client/sender/ClientPackager.java @@ -3,11 +3,14 @@ package com.projectswg.networking.client.sender; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Queue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import com.projectswg.common.concurrency.Delay; import com.projectswg.common.concurrency.PswgBasicThread; -import com.projectswg.common.concurrency.SmartLock; -import com.projectswg.common.concurrency.SynchronizedQueue; import com.projectswg.common.control.IntentManager; import com.projectswg.common.debug.Assert; import com.projectswg.intents.ClientConnectionChangedIntent; @@ -21,133 +24,196 @@ import com.projectswg.resources.ClientConnectionStatus; public class ClientPackager { private final NetInterceptor interceptor; - private final ClientData clientData; private final ClientPacketResender packetResender; - private final PswgBasicThread packagerThread; - private final Queue inboundQueue; - private final SmartLock inboundQueueLock; - private final DataChannel channel; - private int size; + private final PackagingWrapper packager; public ClientPackager(NetInterceptor interceptor, ClientData clientData, ClientPacketSender sender) { this.interceptor = interceptor; - this.clientData = clientData; - this.inboundQueue = new SynchronizedQueue<>(new ArrayDeque<>(128)); - this.inboundQueueLock = new SmartLock(); - this.packagerThread = new PswgBasicThread("packet-packager", () -> packagerRunnable()); this.packetResender = new ClientPacketResender(sender); - this.channel = new DataChannel(); - reset(); + this.packager = new PackagingWrapper(packetResender, clientData); } public void start(IntentManager intentManager) { - Assert.test(inboundQueue.isEmpty(), "Inbound queue must be empty when starting!"); - packagerThread.start(); packetResender.start(intentManager); + packager.start(); intentManager.registerForIntent(ClientConnectionChangedIntent.class, ccci -> handleClientStatusChanged(ccci.getStatus())); } public void stop() { + packager.stop(); packetResender.stop(); - packagerThread.stop(true); - packagerThread.awaitTermination(1000); - inboundQueue.clear(); } public void clear() { - inboundQueue.clear(); + packager.reset(); } public void addToPackage(byte [] data) { Assert.test(data.length > 0, "Array length must be greater than 0!"); - inboundQueue.add(data); - inboundQueueLock.signal(); + packager.add(interceptor.interceptServer(data)); } private void handleClientStatusChanged(ClientConnectionStatus status) { clear(); } - private void packagerRunnable() { - while (packagerThread.isRunning()) { - if (handle(inboundQueue) > 0) { - if (Delay.sleepMicro(25)) + private static class PackagingWrapper { + + private final AtomicBoolean running; + private final PswgBasicThread packagerThread; + private final ClientPacketResender packetResender; + private final Packager packager; + private final Queue inboundQueue; + private final Lock inboundQueueLock; + private final Condition inboundQueueCondition; + + public PackagingWrapper(ClientPacketResender packetResender, ClientData clientData) { + this.running = new AtomicBoolean(false); + this.packagerThread = new PswgBasicThread("packet-packager", this::loop); + this.packetResender = packetResender; + this.packager = new Packager(clientData); + this.inboundQueue = new ArrayDeque<>(128); + this.inboundQueueLock = new ReentrantLock(false); + this.inboundQueueCondition = inboundQueueLock.newCondition(); + } + + public void start() { + running.set(true); + packagerThread.start(); + } + + public void stop() { + running.set(false); + packagerThread.stop(true); + packagerThread.awaitTermination(500); + } + + public void reset() { + inboundQueue.clear(); + } + + public void add(byte [] packet) { + inboundQueueLock.lock(); + try { + inboundQueue.add(packet); + inboundQueueCondition.signal(); + } finally { + inboundQueueLock.unlock(); + } + } + + private void loop() { + Queue outboundQueue = new ArrayDeque<>(64); + while (running.get()) { + if (!waitForInbound()) break; - } else { + processInbound(outboundQueue); + Delay.sleepMicro(25); // Accumulates some packets + } + } + + /** + * Waits for the queue to be non-empty and acquires the lock + * @return TRUE if the lock is acquired and the queue is non-empty, FALSE otherwise + */ + private boolean waitForInbound() { + inboundQueueLock.lock(); + while (inboundQueue.isEmpty()) { try { - while (inboundQueue.isEmpty()) { - inboundQueueLock.await(); - } + inboundQueueCondition.await(); } catch (InterruptedException e) { + running.set(false); + return false; } } + return true; } - } - - /** - * Packages all packets in the queue appropriately - * @param queue the queue of raw packets - */ - private synchronized int handle(Queue queue) { - byte [] packet; - int packetSize; - int packets = 0; - Assert.test(size == 8, "Internal Packager size must equal 8 at start of loop!"); - Assert.test(channel.getPacketCount() == 0, "Internal Packager DataChannel must be empty at start of loop!"); - while (!queue.isEmpty()) { - packet = interceptor.interceptServer(queue.poll()); - packetSize = getPacketLength(packet); - if (size + packetSize >= 496) { - handleDataChannelOverflow(packet, packetSize); - } else { - addToChannel(packet, packetSize); + + private void processInbound(Queue outboundQueue) { + try { + packager.handle(inboundQueue, outboundQueue); + } finally { + inboundQueueLock.unlock(); + } + + while (!outboundQueue.isEmpty()) { + packetResender.add(outboundQueue.poll()); } - packets++; } - sendDataChannel(); - return packets; + } - private void handleDataChannelOverflow(byte [] packet, int packetSize) { - sendDataChannel(); - if (packetSize >= 496) { - sendFragmented(packet); - return; + private static class Packager { + + private final AtomicInteger size; + private final DataChannel channel; + private final ClientData clientData; + + public Packager(ClientData clientData) { + this.size = new AtomicInteger(8); + this.channel = new DataChannel(); + this.clientData = clientData; } - addToChannel(packet, packetSize); - } - - private void addToChannel(byte [] packet, int packetSize) { - channel.addPacket(packet); - size += packetSize; - } - - private void sendDataChannel() { - if (channel.getPacketCount() == 0) - return; - channel.setSequence(clientData.getAndIncrementTxSequence()); - packetResender.add(channel.getSequence(), channel.encode().array()); - reset(); - } - - private void sendFragmented(byte [] packet) { - Fragmented [] frags = Fragmented.encode(ByteBuffer.wrap(packet), clientData.getTxSequence()); - clientData.setTxSequence((short) (clientData.getTxSequence() + frags.length)); - for (Fragmented frag : frags) { - packetResender.add(frag.getSequence(), frag.encode().array()); + + /** + * Processes the inbound queue, and then sends it to the outbound queue + * @param inboundQueue inbound queue from the server + * @param outboundQueue outbound queue to the client + */ + public void handle(Queue inboundQueue, Queue outboundQueue) { + byte [] packet; + int packetSize; + + while (!inboundQueue.isEmpty()) { + packet = inboundQueue.poll(); + packetSize = getPacketLength(packet); + + if (size.get() + packetSize >= 496) // overflowed previous packet + sendDataChannel(outboundQueue); + + if (packetSize < 496) { + addToDataChannel(packet, packetSize); + } else { + sendFragmented(outboundQueue, packet); + } + } + sendDataChannel(outboundQueue); } - } - - private int getPacketLength(byte [] data) { - int len = data.length; - if (len >= 255) - return len + 3; - return len + 1; - } - - private void reset() { - channel.clearPackets(); - size = 8; + + private void addToDataChannel(byte [] packet, int packetSize) { + channel.addPacket(packet); + size.getAndAdd(packetSize); + } + + private void sendDataChannel(Queue outboundQueue) { + if (channel.getPacketCount() == 0) + return; + + channel.setSequence(clientData.getAndIncrementTxSequence()); + outboundQueue.add(new SequencedOutbound(channel.getSequence(), channel.encode().array())); + reset(); + } + + private void sendFragmented(Queue outboundQueue, byte [] packet) { + Fragmented [] frags = Fragmented.encode(ByteBuffer.wrap(packet), clientData.getTxSequence()); + clientData.setTxSequence((short) (clientData.getTxSequence() + frags.length)); + for (Fragmented frag : frags) { + outboundQueue.add(new SequencedOutbound(frag.getSequence(), frag.encode().array())); + } + } + + private void reset() { + channel.clearPackets(); + size.set(8); + } + + private static int getPacketLength(byte [] data) { + int len = data.length; + if (len >= 255) + return len + 3; + return len + 1; + } + } } diff --git a/src/com/projectswg/networking/client/sender/ClientPacketResender.java b/src/com/projectswg/networking/client/sender/ClientPacketResender.java index e406990..7728460 100644 --- a/src/com/projectswg/networking/client/sender/ClientPacketResender.java +++ b/src/com/projectswg/networking/client/sender/ClientPacketResender.java @@ -1,8 +1,11 @@ package com.projectswg.networking.client.sender; -import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import com.projectswg.common.concurrency.Delay; import com.projectswg.common.concurrency.PswgBasicThread; @@ -14,7 +17,6 @@ import com.projectswg.networking.Packet; import com.projectswg.networking.client.ClientPacketSender; import com.projectswg.networking.soe.Acknowledge; import com.projectswg.networking.soe.OutOfOrder; -import com.projectswg.networking.soe.SequencedPacket; import com.projectswg.resources.ClientConnectionStatus; /** @@ -22,195 +24,229 @@ import com.projectswg.resources.ClientConnectionStatus; */ public class ClientPacketResender { - private final List sentPackets; - private final ClientPacketSender sender; - private final PswgBasicThread resender; - private final CongestionAvoidance congAvoidance; - private final AtomicLong lastSent; + private final ResenderWrapper resender; + private final PswgBasicThread executor; + private final AtomicBoolean running; public ClientPacketResender(ClientPacketSender sender) { - this.sender = sender; - this.sentPackets = new ArrayList<>(128); - this.resender = new PswgBasicThread("packet-resender", () -> resendRunnable()); - this.congAvoidance = new CongestionAvoidance(); - this.lastSent = new AtomicLong(0); + this.resender = new ResenderWrapper(sender); + this.executor = new PswgBasicThread("packet-resender", this::resender); + this.running = new AtomicBoolean(false); } public void start(IntentManager intentManager) { - resender.start(); intentManager.registerForIntent(ClientSonyPacketIntent.class, cspi -> handleClientPacket(cspi.getPacket())); intentManager.registerForIntent(ClientConnectionChangedIntent.class, ccci -> handleClientStatusChanged(ccci.getStatus())); + + running.set(true); + executor.start(); } public void stop() { - resender.stop(true); - resender.awaitTermination(1000); - synchronized (sentPackets) { - sentPackets.clear(); - } + running.set(false); + executor.stop(true); + executor.awaitTermination(500); + resender.reset(); } - public void restart() { - synchronized (sentPackets) { - sentPackets.clear(); - } + public void add(SequencedOutbound out) { + resender.add(out); } - public void add(short sequence, byte [] data) { - SequencedOutbound out = new SequencedOutbound(sequence, data); - synchronized (sentPackets) { - sentPackets.add(out); - sentPackets.notifyAll(); + private void resender() { + while (running.get()) { + resender.resend(); } } private void handleClientPacket(Packet p) { if (p instanceof Acknowledge) - onAcknowledge(((Acknowledge) p).getSequence()); + resender.onAcknowledge(((Acknowledge) p).getSequence()); else if (p instanceof OutOfOrder) - onOutOfOrder(((OutOfOrder) p).getSequence()); + resender.onOutOfOrder(((OutOfOrder) p).getSequence()); } private void handleClientStatusChanged(ClientConnectionStatus status) { - restart(); + resender.reset(); } - private void onOutOfOrder(short sequence) { - Log.w("OOO %d", sequence); - synchronized (sentPackets) { - congAvoidance.onOutOfOrder(); - updateRtt(); + private static class ResenderWrapper { + + private final List sentPackets; + private final CongestionAvoidance congAvoidance; + private final Lock sentPacketsLock; + private final Condition sentPacketsCondition; + private final Resender resender; + + public ResenderWrapper(ClientPacketSender sender) { + this.sentPackets = new LinkedList<>(); + this.congAvoidance = new CongestionAvoidance(); + this.sentPacketsLock = new ReentrantLock(false); + this.sentPacketsCondition = sentPacketsLock.newCondition(); + this.resender = new Resender(congAvoidance, sender); } - } - - private void onAcknowledge(short sequence) { - int seqInt = sequence & 0xFFFF; - SequencedOutbound out; - synchronized (sentPackets) { - while (!sentPackets.isEmpty()) { - out = sentPackets.get(0); - if (out.getSequenceInt() <= seqInt || (seqInt < 100 && out.getSequenceInt() > Short.MAX_VALUE-100)) { - sentPackets.remove(0); - } else { - break; - } + + public void add(SequencedOutbound seq) { + sentPacketsLock.lock(); + try { + sentPackets.add(seq); + sentPacketsCondition.signal(); + } finally { + sentPacketsLock.unlock(); } - updateRtt(); - congAvoidance.onAcknowledgement(); } - } - - private void resendRunnable() { - while (resender.isRunning()) { - synchronized (sentPackets) { - congAvoidance.markBeginningOfWindow(); - if (congAvoidance.getWindow() != 50 && congAvoidance.getAverageRTT() != Double.MAX_VALUE) { - Log.d("Congestion Window: %d Average RTT: %.3fms", congAvoidance.getWindow(), congAvoidance.getAverageRTT()/1E6); + + public void reset() { + sentPacketsLock.lock(); + try { + sentPackets.clear(); + } finally { + sentPacketsLock.unlock(); + } + } + + public void onOutOfOrder(short sequence) { + Log.w("OOO %d", sequence); + sentPacketsLock.lock(); + try { + congAvoidance.onOutOfOrder(); + } finally { + sentPacketsLock.unlock(); + } + } + + public void onAcknowledge(short sequence) { + sentPacketsLock.lock(); + try { + int seqInt = sequence & 0xFFFF; + SequencedOutbound out; + while (!sentPackets.isEmpty()) { + out = sentPackets.get(0); + if (out.getSequenceInt() <= seqInt || (seqInt < 100 && out.getSequenceInt() > Short.MAX_VALUE-100)) { + sentPackets.remove(0); + } else { + break; + } } - boolean lossEvent = !sentPackets.isEmpty() && (congAvoidance.isTimedOut() || congAvoidance.isTripleACK()); - if (lossEvent) { - Log.w("Resender: Loss Event!"); - } - sendAllInWindow(); - if (sentPackets.size() < 100) { - casualWindowIteration(lossEvent); - } else { - intenseWindowIteration(lossEvent); - } - congAvoidance.markEndOfWindow(); - if (waitForPacket()) - continue; + congAvoidance.onAcknowledgement(); + } finally { + sentPacketsLock.unlock(); + } + } + + public void resend() { + sentPacketsLock.lock(); + try { + waitForPacket(); + resender.handle(sentPackets); + } finally { + sentPacketsLock.unlock(); } waitForTimeoutOrAck(); } - } - - private void updateRtt() { - long lastRtt = getTimeSinceSent(); - clearTimeSinceSent(); - if (lastRtt > 0) - congAvoidance.updateRtt(lastRtt); - } - - private void waitForTimeoutOrAck() { - double avg = congAvoidance.getAverageRTT(); - if (avg == Double.MAX_VALUE) - Delay.sleepMicro(5); - else - Delay.sleepNano((long) Math.min(1E9, Math.max(5E6, avg))); - } - - private boolean waitForPacket() { - if (!sentPackets.isEmpty()) - return false; - try { - while (sentPackets.isEmpty()) { - sentPackets.wait(); + + private void waitForPacket() { + if (!sentPackets.isEmpty()) + return; + try { + while (sentPackets.isEmpty()) { + sentPacketsCondition.await(); + } + } catch (InterruptedException e) { + } - } catch (InterruptedException e) { + congAvoidance.clearTimeSinceSent(); } - clearTimeSinceSent(); - return true; - } - - /** - * This congestion avoidance algorithm focuses more on slow window - * increases/decreases. This is ideal for casual gameplay where there - * aren't many packets being sent at once. - */ - private void casualWindowIteration(boolean lossEvent) { - if (lossEvent) { - congAvoidance.setWindow(Math.max(10, congAvoidance.getWindow() - 5)); - } else { - congAvoidance.setWindow(Math.min(50, congAvoidance.getWindow() + 5)); + + private void waitForTimeoutOrAck() { + double avg = congAvoidance.getAverageRTT(); + if (avg == Double.MAX_VALUE) + Delay.sleepMicro(5); + else + Delay.sleepNano((long) Math.min(1E9, Math.max(5E6, avg))); } + } - /** - * This congestion avoidance algorithm focuses on fast window - * increases/decreases. This is ideal for zone-in where as many packets as - * possible need to be sent as fast as possible. - */ - private void intenseWindowIteration(boolean lossEvent) { - int window = congAvoidance.getWindow(); - if (window > sentPackets.size()) - return; - if (lossEvent) { - congAvoidance.setWindow(Math.max(50, window / 4)); - } else { - congAvoidance.setWindow((int) (window * 1.5)); + private static class Resender { + + private final CongestionAvoidance congAvoidance; + private final ClientPacketSender sender; + + public Resender(CongestionAvoidance congAvoidance, ClientPacketSender sender) { + this.congAvoidance = congAvoidance; + this.sender = sender; } - } - - private void sendAllInWindow() { - int max = congAvoidance.getWindow(); - for (SequencedOutbound out : sentPackets) { - if (--max < 0) - break; - sender.sendRaw(out.getData()); - congAvoidance.onSentPacket(); + + public void handle(List sentPackets) { + congAvoidance.markBeginningOfWindow(); + int sentPacketCount = sentPackets.size(); + boolean lossEvent = sentPacketCount > 0 && (congAvoidance.isTimedOut() || congAvoidance.isTripleACK()); + + printStatus(lossEvent); + sendAllInWindow(sentPackets); + if (sentPacketCount < 100) { + casualWindowIteration(lossEvent); + } else { + intenseWindowIteration(lossEvent, sentPacketCount); + } + congAvoidance.markEndOfWindow(); } - updateTimeSinceSent(); - } - - private void updateTimeSinceSent() { - lastSent.compareAndSet(0, System.nanoTime()); - } - - private long getTimeSinceSent() { - long sent = lastSent.get(); - if (sent == 0) - return -1; - return System.nanoTime() - sent; - } - - private void clearTimeSinceSent() { - lastSent.set(0); + + private void printStatus(boolean lossEvent) { + if (congAvoidance.getWindow() != 50 && congAvoidance.getAverageRTT() != Double.MAX_VALUE) + Log.d("Resender: Congestion Window: %d Average RTT: %.3fms", congAvoidance.getWindow(), congAvoidance.getAverageRTT()/1E6); + + if (lossEvent) + Log.w("Resender: Loss Event!"); + } + + /** + * This congestion avoidance algorithm focuses more on slow window + * increases/decreases. This is ideal for casual gameplay where there + * aren't many packets being sent at once. + */ + private void casualWindowIteration(boolean lossEvent) { + if (lossEvent) { + congAvoidance.setWindow(Math.max(10, congAvoidance.getWindow() - 5)); + } else { + congAvoidance.setWindow(Math.min(50, congAvoidance.getWindow() + 5)); + } + } + + /** + * This congestion avoidance algorithm focuses on fast window + * increases/decreases. This is ideal for zone-in where as many packets as + * possible need to be sent as fast as possible. + */ + private void intenseWindowIteration(boolean lossEvent, int sentPackets) { + int window = congAvoidance.getWindow(); + if (window > sentPackets) + return; + if (lossEvent) { + congAvoidance.setWindow(Math.max(50, window / 4)); + } else { + congAvoidance.setWindow((int) (window * 1.5)); + } + } + + private void sendAllInWindow(List sentPackets) { + int max = congAvoidance.getWindow(); + for (SequencedOutbound out : sentPackets) { + if (--max < 0) + break; + sender.sendRaw(out.getData()); + congAvoidance.onSentPacket(); + } + congAvoidance.updateTimeSinceSent(); + } + } private static class CongestionAvoidance { // Higher-level properties + private long lastSent; private double averageRtt; private int congestionWindow; @@ -226,25 +262,22 @@ public class ClientPacketResender { } public void reset() { + this.lastSent = 0; + this.congestionWindow = 1; + this.outOfOrders = 0; this.acknowledgements = 0; this.sentPackets = 0; this.missedWindows = 0; - this.congestionWindow = 1; } public void onOutOfOrder() { + updateRtt(); this.outOfOrders++; } - public void updateRtt(long lastRtt) { - if (this.averageRtt == Double.MAX_VALUE) - this.averageRtt = lastRtt; - else - this.averageRtt = averageRtt * 0.875 + lastRtt * 0.125; - } - public void onAcknowledgement() { + updateRtt(); this.outOfOrders = 0; this.acknowledgements++; } @@ -285,43 +318,27 @@ public class ClientPacketResender { return missedWindows >= 2 && averageRtt != Double.MAX_VALUE; } - } - - private static class SequencedOutbound implements SequencedPacket { - - private int sequence; - private byte [] data; - - public SequencedOutbound(short sequence, byte [] data) { - this.sequence = sequence & 0xFFFF; - this.data = data; + public void updateTimeSinceSent() { + if (lastSent == 0) + lastSent = System.nanoTime(); } - @Override - public short getSequence() { return (short) sequence; } - public int getSequenceInt() { return sequence; } - public byte [] getData() { return data; } - - @Override - public int compareTo(SequencedPacket p) { - if (getSequence() < p.getSequence()) - return -1; - if (getSequence() == p.getSequence()) - return 0; - return 1; + private void updateRtt() { + long lastRtt = lastSent; + lastSent = 0; + if (lastRtt <= 0) + return; + lastRtt = System.nanoTime() - lastRtt; + + if (this.averageRtt == Double.MAX_VALUE) + this.averageRtt = lastRtt; + else + this.averageRtt = averageRtt * 0.875 + lastRtt * 0.125; } - @Override - public boolean equals(Object o) { - if (!(o instanceof SequencedOutbound)) - return super.equals(o); - return ((SequencedOutbound) o).getSequence() == sequence; + public void clearTimeSinceSent() { + lastSent = 0; } - @Override - public int hashCode() { - return sequence; - } - } - + } } diff --git a/src/com/projectswg/networking/client/sender/SequencedOutbound.java b/src/com/projectswg/networking/client/sender/SequencedOutbound.java new file mode 100644 index 0000000..0f35cca --- /dev/null +++ b/src/com/projectswg/networking/client/sender/SequencedOutbound.java @@ -0,0 +1,40 @@ +package com.projectswg.networking.client.sender; + +import com.projectswg.networking.soe.SequencedPacket; + +public class SequencedOutbound implements SequencedPacket { + + private final int sequence; + private final byte [] data; + + public SequencedOutbound(short sequence, byte [] data) { + this.sequence = sequence & 0xFFFF; + this.data = data; + } + + @Override + public short getSequence() { return (short) sequence; } + public int getSequenceInt() { return sequence; } + public byte [] getData() { return data; } + + @Override + public int compareTo(SequencedPacket p) { + if (getSequence() < p.getSequence()) + return -1; + if (getSequence() == p.getSequence()) + return 0; + return 1; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SequencedOutbound)) + return super.equals(o); + return ((SequencedOutbound) o).getSequence() == sequence; + } + + @Override + public int hashCode() { + return sequence; + } +} diff --git a/src/com/projectswg/networking/encryption/CRC.java b/src/com/projectswg/networking/encryption/CRC.java deleted file mode 100644 index 5b3354e..0000000 --- a/src/com/projectswg/networking/encryption/CRC.java +++ /dev/null @@ -1,181 +0,0 @@ -/*********************************************************************************** -* Copyright (c) 2015 /// Project SWG /// www.projectswg.com * -* * -* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * -* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * -* Our goal is to create an emulator which will provide a server for players to * -* continue playing a game similar to the one they used to play. We are basing * -* it on the final publish of the game prior to end-game events. * -* * -* This file is part of Holocore. * -* * -* -------------------------------------------------------------------------------- * -* * -* Holocore is free software: you can redistribute it and/or modify * -* it under the terms of the GNU Affero General Public License as * -* published by the Free Software Foundation, either version 3 of the * -* License, or (at your option) any later version. * -* * -* Holocore is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU Affero General Public License for more details. * -* * -* You should have received a copy of the GNU Affero General Public License * -* along with Holocore. If not, see . * -* * -***********************************************************************************/ -package com.projectswg.networking.encryption; - -import java.nio.charset.StandardCharsets; - -public class CRC { - - private static final int CRC_TABLE[] = { - 0x0000000, - 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, - 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, - 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, - 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, - 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, - 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, - 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, - 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, - 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, - 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE, - 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, - 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, - 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, - 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, - 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, - 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, - 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, - 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, - 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, - 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, - 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, - 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, - 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, - 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, - 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, - 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, - 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, - 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, - 0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629, - 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, - 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, - 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, - 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, - 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, - 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, - 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, - 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, - 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, - 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, - 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21, - 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, - 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087, - 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, - 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, - 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, - 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, - 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, - 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09, - 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, - 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, - 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4, - }; - - private static final int[] CRC_ZIP_TABLE = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; - - public static int getCrc(String input) { - byte [] bytes = input.getBytes(StandardCharsets.UTF_8); - int crc = 0xffffffff; - - for (int i = 0; i < bytes.length; ++i) { - crc = CRC_TABLE[(int) (bytes[i] ^ (crc >> 24)) & 0x000000FF] ^ (crc << 8); - } - return (int) ~crc; - } - - public static int memcrc(String input) { - byte [] bytes = input.getBytes(StandardCharsets.UTF_8); - - int crc = 0xffffffff; - for (int i = 0; i < bytes.length; ++i) { - crc = CRC_TABLE[(int) (bytes[i] ^ (crc >> 24)) & 0x000000FF] ^ (crc << 8); - } - - return (int) ~crc; - } - - public static int memcrc(byte [] src_buffer, int offset, int length, int seed) { - int index; - int newCRC = 0; - - newCRC = CRC_ZIP_TABLE[(~seed) & 0xFF]; - newCRC ^= 0x00FFFFFF; - index = (seed >> 8) ^ newCRC; - newCRC = (newCRC >> 8) & 0x00FFFFFF; - newCRC ^= CRC_ZIP_TABLE[index & 0xFF]; - index = (seed >> 16) ^ newCRC; - newCRC = (newCRC >> 8) & 0x00FFFFFF; - newCRC ^= CRC_ZIP_TABLE[index & 0xFF]; - index = (seed >> 24) ^ newCRC; - newCRC = (newCRC >> 8) & 0x00FFFFFF; - newCRC ^= CRC_ZIP_TABLE[index & 0xFF]; - - for (int i = offset; i < length; i++) { - index = (src_buffer[i]) ^ newCRC; - newCRC = (newCRC >> 8) & 0x00FFFFFF; - newCRC ^= CRC_ZIP_TABLE[index & 0xFF]; - } - - return ~newCRC; - } - -} diff --git a/src/com/projectswg/networking/resources/Galaxy.java b/src/com/projectswg/networking/resources/Galaxy.java deleted file mode 100644 index 8491824..0000000 --- a/src/com/projectswg/networking/resources/Galaxy.java +++ /dev/null @@ -1,112 +0,0 @@ -/*********************************************************************************** -* Copyright (c) 2015 /// Project SWG /// www.projectswg.com * -* * -* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * -* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * -* Our goal is to create an emulator which will provide a server for players to * -* continue playing a game similar to the one they used to play. We are basing * -* it on the final publish of the game prior to end-game events. * -* * -* This file is part of Holocore. * -* * -* -------------------------------------------------------------------------------- * -* * -* Holocore is free software: you can redistribute it and/or modify * -* it under the terms of the GNU Affero General Public License as * -* published by the Free Software Foundation, either version 3 of the * -* License, or (at your option) any later version. * -* * -* Holocore is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU Affero General Public License for more details. * -* * -* You should have received a copy of the GNU Affero General Public License * -* along with Holocore. If not, see . * -* * -***********************************************************************************/ -package com.projectswg.networking.resources; - - -public class Galaxy { - - private int id = 0; - private String name = ""; - private String address = ""; - private short zonePort = (short) 44463; - private short pingPort = (short) 44462; - private int population = 0; - private int popStatus = 0; - private GalaxyStatus status = GalaxyStatus.DOWN; - private int timeZone = 0; - private int maxCharacters = 0; - private int onlinePlayerLimit = 0; - private int onlineFreeTrialLimit = 0; - private boolean recommended = true; - - public Galaxy() { - - } - - public int getId() { return id; } - public String getName() { return name; } - public String getAddress() { return address; } - public short getZonePort() { return zonePort; } - public short getPingPort() { return pingPort; } - public int getPopulation() { return population; } - public int getPopulationStatus() { return popStatus; } - public GalaxyStatus getStatus() { return status; } - public int getTimeZone() { return timeZone; } - public int getMaxCharacters() { return maxCharacters; } - public int getOnlinePlayerLimit() { return onlinePlayerLimit; } - public int getOnlineFreeTrialLimit() { return onlineFreeTrialLimit; } - public boolean isRecommended() { return recommended; } - - public void setId(int id) { this.id = id; } - public void setName(String name) { this.name = name; } - public void setAddress(String addr) { this.address = addr; } - public void setZonePort(short port) { this.zonePort = port; } - public void setPingPort(short port) { this.pingPort = port; } - public void setPopulation(int population) { this.population = population; } - public void setPopulationStatus(int status) { this.popStatus = status; } - public void setStatus(GalaxyStatus status) { this.status = status; } - public void setTimeZone(int timeZone) { this.timeZone = timeZone; } - public void setMaxCharacters(int max) { this.maxCharacters = max; } - public void setOnlinePlayerLimit(int max) { this.onlinePlayerLimit = max; } - public void setOnlineFreeTrialLimit(int max) { this.onlineFreeTrialLimit = max; } - public void setRecommended(boolean r) { this.recommended = r; } - - public String toString() { - return String.format("Galaxy[ID=%d Name=%s Address=%s Zone=%d Ping=%d Pop=%d PopStat=%d Status=%s Time=%d Max=%d Rec=%b]", - id, name, address, zonePort, pingPort, population, popStatus, status, timeZone, maxCharacters, recommended); - } - - public void setStatus(int status) { - for (GalaxyStatus gs : GalaxyStatus.values()) { - if (gs.getStatus() == status) { - setStatus(gs); - return; - } - } - } - - public enum GalaxyStatus { - DOWN (0x00), - LOADING (0x01), - UP (0x02), - LOCKED (0x03), - RESTRICTED (0x04), - FULL (0x05); - - private byte status; - - GalaxyStatus(int status) { - this.status = (byte) status; - } - - public byte getStatus() { - return status; - } - } - -} diff --git a/src/com/projectswg/networking/server/ServerConnectionService.java b/src/com/projectswg/networking/server/ServerConnectionService.java index 2b02da7..1b8c35f 100644 --- a/src/com/projectswg/networking/server/ServerConnectionService.java +++ b/src/com/projectswg/networking/server/ServerConnectionService.java @@ -15,6 +15,7 @@ import com.projectswg.common.control.IntentManager; import com.projectswg.common.control.Service; import com.projectswg.common.debug.Assert; import com.projectswg.common.debug.Log; +import com.projectswg.common.network.packets.swg.zone.HeartBeat; import com.projectswg.connection.HolocoreSocket; import com.projectswg.connection.ServerConnectionChangedReason; import com.projectswg.connection.packets.RawPacket; @@ -23,8 +24,6 @@ import com.projectswg.intents.ClientToServerPacketIntent; import com.projectswg.intents.ServerConnectionChangedIntent; import com.projectswg.intents.ServerToClientPacketIntent; -import com.projectswg.common.network.packets.swg.zone.HeartBeat; - public class ServerConnectionService extends Service { private static final long HOLOCORE_TIMEOUT = TimeUnit.SECONDS.toNanos(21);