mirror of
https://bitbucket.org/projectswg/forwarder.git
synced 2026-01-16 23:04:26 -05:00
Improved network efficiency
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/">
|
||||
<accessrules>
|
||||
<accessrule kind="accessible" pattern="javafx/**"/>
|
||||
</accessrules>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
bin/
|
||||
/.gradle/
|
||||
/build/
|
||||
|
||||
Submodule Client Holocore updated: be5b53b4ea...41a5df892b
Submodule PSWGCommon updated: 016f219e80...8505a996c3
BIN
lib/Holocore.jar
BIN
lib/Holocore.jar
Binary file not shown.
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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<byte []> 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<byte []> 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<SequencedOutbound> 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<byte []> 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<SequencedOutbound> 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<byte []> inboundQueue, Queue<SequencedOutbound> 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<SequencedOutbound> outboundQueue) {
|
||||
if (channel.getPacketCount() == 0)
|
||||
return;
|
||||
|
||||
channel.setSequence(clientData.getAndIncrementTxSequence());
|
||||
outboundQueue.add(new SequencedOutbound(channel.getSequence(), channel.encode().array()));
|
||||
reset();
|
||||
}
|
||||
|
||||
private void sendFragmented(Queue<SequencedOutbound> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<SequencedOutbound> 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<SequencedOutbound> 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<SequencedOutbound> 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<SequencedOutbound> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***********************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***********************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user