Changed raw server communication code to go through ClientHolocore

This commit is contained in:
Obique PSWG
2017-02-01 11:39:31 -06:00
parent 12393299e8
commit 77a4c43779
15 changed files with 28 additions and 440 deletions

View File

@@ -5,5 +5,10 @@
<classpathentry kind="lib" path="lib/lz4-1.3.0.jar"/>
<classpathentry kind="lib" path="lib/findbugs.jar"/>
<classpathentry kind="lib" path="lib/Holocore.jar"/>
<classpathentry kind="lib" path="lib/ClientHolocore.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/Forwarder/lib/ClientHolocore-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>

Binary file not shown.

BIN
lib/ClientHolocore.jar Normal file

Binary file not shown.

View File

@@ -6,6 +6,8 @@ import java.util.concurrent.atomic.AtomicLong;
import network.packets.swg.ErrorMessage;
import com.projectswg.connection.ServerConnectionChangedReason;
import com.projectswg.connection.ServerConnectionStatus;
import com.projectswg.control.IntentManager;
import com.projectswg.control.Manager;
import com.projectswg.intents.ClientConnectionChangedIntent;
@@ -15,9 +17,7 @@ import com.projectswg.intents.ServerToClientPacketIntent;
import com.projectswg.networking.NetInterceptor.InterceptorProperties;
import com.projectswg.networking.client.ClientConnectionService;
import com.projectswg.networking.server.ServerConnectionService;
import com.projectswg.networking.server.ServerConnectionChangedReason;
import com.projectswg.networking.soe.Disconnect.DisconnectReason;
import com.projectswg.resources.ServerConnectionStatus;
import com.projectswg.services.PacketRecordingService;
import com.projectswg.utilities.Log;
import com.projectswg.utilities.ThreadUtilities;

View File

@@ -15,8 +15,9 @@ import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import com.projectswg.Connections.ConnectionCallback;
import com.projectswg.resources.ServerConnectionStatus;
import com.projectswg.connection.ServerConnectionStatus;
import com.projectswg.utilities.Log;
public class Forwarder extends Application implements ConnectionCallback {

View File

@@ -1,8 +1,8 @@
package com.projectswg.intents;
import com.projectswg.connection.ServerConnectionChangedReason;
import com.projectswg.connection.ServerConnectionStatus;
import com.projectswg.control.Intent;
import com.projectswg.networking.server.ServerConnectionChangedReason;
import com.projectswg.resources.ServerConnectionStatus;
public class ServerConnectionChangedIntent extends Intent {

View File

@@ -1,32 +1,30 @@
package com.projectswg.intents;
import network.PacketType;
import com.projectswg.control.Intent;
public class ServerToClientPacketIntent extends Intent {
public static final String TYPE = "ServerToClientPacketIntent";
private PacketType type;
private int crc;
private byte [] rawData;
public ServerToClientPacketIntent(PacketType type, byte [] rawData) {
public ServerToClientPacketIntent(int crc, byte [] rawData) {
super(TYPE);
setPacketType(type);
setCrc(crc);
setRawData(rawData);
}
public PacketType getPacketType() {
return type;
public int getCrc() {
return crc;
}
public byte [] getRawData() {
return rawData;
}
public void setPacketType(PacketType type) {
this.type = type;
public void setCrc(int crc) {
this.crc = crc;
}
public void setRawData(byte [] rawData) {

View File

@@ -1,93 +0,0 @@
package com.projectswg.networking.server;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import resources.network.NetBufferStream;
import com.projectswg.networking.encryption.Compression;
public class HolocoreProtocol {
private static final byte [] EMPTY_PACKET = new byte[0];
private final NetBufferStream inboundStream;
public HolocoreProtocol() {
this.inboundStream = new NetBufferStream();
}
public void reset() {
inboundStream.reset();
}
public ByteBuffer assemble(byte [] raw) {
int decompressedLength = raw.length;
boolean compressed = raw.length >= 16;
if (compressed) {
byte [] compressedData = Compression.compress(raw);
if (compressedData.length >= raw.length)
compressed = false;
else
raw = compressedData;
}
ByteBuffer data = ByteBuffer.allocate(raw.length + 5).order(ByteOrder.LITTLE_ENDIAN);
data.put(createBitmask(compressed, true));
data.putShort((short) raw.length);
data.putShort((short) decompressedLength);
data.put(raw);
data.flip();
return data;
}
public boolean addToBuffer(ByteBuffer data) {
synchronized (inboundStream) {
inboundStream.write(data);
inboundStream.mark();
try {
if (inboundStream.remaining() < 5)
return false;
inboundStream.getByte();
short messageLength = inboundStream.getShort();
inboundStream.getShort();
if (inboundStream.remaining() < messageLength) {
inboundStream.rewind();
return false;
}
return true;
} finally {
inboundStream.rewind();
}
}
}
public byte [] disassemble() {
synchronized (inboundStream) {
inboundStream.mark();
if (inboundStream.remaining() < 5) {
inboundStream.rewind();
return EMPTY_PACKET;
}
byte bitmask = inboundStream.getByte();
short messageLength = inboundStream.getShort();
short decompressedLength = inboundStream.getShort();
if (inboundStream.remaining() < messageLength) {
inboundStream.rewind();
return EMPTY_PACKET;
}
byte [] message = inboundStream.getArray(messageLength);
if ((bitmask & 1) != 0) // Compressed
message = Compression.decompress(message, decompressedLength);
inboundStream.compact();
return message;
}
}
private byte createBitmask(boolean compressed, boolean swg) {
byte bitfield = 0;
bitfield |= (compressed?1:0) << 0;
bitfield |= (swg?1:0) << 1;
return bitfield;
}
}

View File

@@ -1,243 +0,0 @@
package com.projectswg.networking.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.SocketChannel;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import network.PacketType;
import network.packets.swg.holo.HoloConnectionStarted;
import network.packets.swg.holo.HoloConnectionStopped;
import network.packets.swg.holo.HoloSetProtocolVersion;
import network.packets.swg.holo.HoloConnectionStopped.ConnectionStoppedReason;
import com.projectswg.control.Assert;
import com.projectswg.networking.server.SWGProtocol.RawPacket;
import com.projectswg.resources.ServerConnectionStatus;
import com.projectswg.utilities.Log;
public class HolocoreSocket {
private static final String PROTOCOL = "2016-04-13";
private final Object socketMutex;
private final ByteBuffer buffer;
private final SWGProtocol swgProtocol;
private final AtomicReference<ServerConnectionStatus> status;
private SocketChannel socket;
private StatusChangedCallback callback;
private InetAddress addr;
private int port;
public HolocoreSocket(InetAddress addr, int port) {
this.socketMutex = new Object();
this.buffer = ByteBuffer.allocateDirect(128*1024);
this.swgProtocol = new SWGProtocol();
this.socket = null;
this.status = new AtomicReference<>(ServerConnectionStatus.DISCONNECTED);
this.callback = null;
this.addr = addr;
this.port = port;
}
public void setStatusChangedCallback(StatusChangedCallback callback) {
this.callback = callback;
}
public void setRemoteAddress(InetAddress addr, int port) {
this.addr = addr;
this.port = port;
}
public InetSocketAddress getRemoteAddress() {
return new InetSocketAddress(addr, port);
}
public ServerConnectionStatus getConnectionState() {
return status.get();
}
public boolean isDisconnected() {
return status.get() == ServerConnectionStatus.DISCONNECTED;
}
public boolean isConnecting() {
return status.get() == ServerConnectionStatus.CONNECTING;
}
public boolean isConnected() {
return status.get() == ServerConnectionStatus.CONNECTED;
}
public boolean connect() {
synchronized (socketMutex) {
Assert.test(isDisconnected());
try {
Log.out(this, "Connecting to %s at port %d", addr, port);
swgProtocol.reset();
socket = SocketChannel.open();
updateStatus(ServerConnectionStatus.CONNECTING, ServerConnectionChangedReason.NONE);
socket.socket().setKeepAlive(true);
socket.socket().setPerformancePreferences(0, 1, 2);
socket.socket().setTrafficClass(0x10); // Low Delay bit
socket.configureBlocking(true);
socket.connect(new InetSocketAddress(addr, port));
if (!socket.finishConnect())
return false;
waitForConnect();
Log.out(this, "Connected to Server");
return true;
} catch (IOException e) {
if (e instanceof AsynchronousCloseException) {
disconnect(ServerConnectionChangedReason.SOCKET_CLOSED);
} else if (e.getMessage() == null) {
disconnect(ServerConnectionChangedReason.UNKNOWN);
Log.err(this, e);
} else {
disconnect(getReason(e.getMessage()));
}
return false;
}
}
}
public boolean disconnect(ServerConnectionChangedReason reason) {
synchronized (socketMutex) {
if (isDisconnected())
return true;
Assert.notNull(socket);
updateStatus(ServerConnectionStatus.DISCONNECTED, reason);
Log.out(this, "Disconnected from server with reason %s", reason);
try {
if (socket.isOpen())
socket.write(swgProtocol.assemble(new HoloConnectionStopped(ConnectionStoppedReason.APPLICATION).encode().array()));
socket.close();
socket = null;
return true;
} catch (IOException e) {
Log.err(this, e);
return false;
}
}
}
public boolean send(byte [] raw) {
return sendRaw(swgProtocol.assemble(raw));
}
public RawPacket read() {
RawPacket packet = null;
do {
packet = swgProtocol.disassemble();
if (packet != null)
return packet;
readRaw(buffer);
swgProtocol.addToBuffer(buffer);
} while (!isDisconnected());
return null;
}
private boolean sendRaw(ByteBuffer data) {
if (isDisconnected()) {
Log.err(this, "Cannot send! Not connected.");
return false;
}
try {
synchronized (socketMutex) {
socket.write(data);
}
return true;
} catch (IOException e) {
Log.err(this, e);
disconnect(ServerConnectionChangedReason.OTHER_SIDE_TERMINATED);
}
return false;
}
private boolean readRaw(ByteBuffer data) {
try {
data.position(0);
data.limit(data.capacity());
int n = socket.read(data);
if (n < 0) {
disconnect(ServerConnectionChangedReason.OTHER_SIDE_TERMINATED);
} else {
data.flip();
return true;
}
} catch (Exception e) {
if (e instanceof AsynchronousCloseException) {
disconnect(ServerConnectionChangedReason.SOCKET_CLOSED);
} else if (e.getMessage() != null) {
disconnect(getReason(e.getMessage()));
} else {
disconnect(ServerConnectionChangedReason.UNKNOWN);
Log.err(this, e);
}
}
return false;
}
private void waitForConnect() {
send(new HoloSetProtocolVersion(PROTOCOL).encode().array());
while (isConnecting()) {
RawPacket packet = read();
if (packet == null)
continue;
handlePacket(packet.getPacketType(), packet.getData());
}
if (isConnected())
send(new HoloConnectionStarted().encode().array());
}
private void handlePacket(PacketType type, byte [] raw) {
if (type == PacketType.HOLO_CONNECTION_STARTED) {
updateStatus(ServerConnectionStatus.CONNECTED, ServerConnectionChangedReason.NONE);
} else if (type == PacketType.HOLO_CONNECTION_STOPPED) {
HoloConnectionStopped packet = new HoloConnectionStopped();
packet.decode(ByteBuffer.wrap(raw));
switch (packet.getReason()) {
case INVALID_PROTOCOL:
disconnect(ServerConnectionChangedReason.INVALID_PROTOCOL);
break;
default:
disconnect(ServerConnectionChangedReason.NONE);
break;
}
}
}
private void updateStatus(ServerConnectionStatus status, ServerConnectionChangedReason reason) {
ServerConnectionStatus old = this.status.getAndSet(status);
Log.out(this, "Server Status: %s -> %s", old, status);
if (old != status && callback != null)
callback.onConnectionStatusChanged(old, status, reason);
}
private ServerConnectionChangedReason getReason(String message) {
message = message.toLowerCase(Locale.US);
if (message.contains("broken pipe"))
return ServerConnectionChangedReason.BROKEN_PIPE;
if (message.contains("connection reset"))
return ServerConnectionChangedReason.CONNECTION_RESET;
if (message.contains("connection refused"))
return ServerConnectionChangedReason.CONNECTION_REFUSED;
if (message.contains("address in use"))
return ServerConnectionChangedReason.ADDR_IN_USE;
if (message.contains("socket closed"))
return ServerConnectionChangedReason.SOCKET_CLOSED;
if (message.contains("no route to host"))
return ServerConnectionChangedReason.NO_ROUTE_TO_HOST;
Log.err(this, "Unknown reason: " + message);
return ServerConnectionChangedReason.UNKNOWN;
}
public interface StatusChangedCallback {
void onConnectionStatusChanged(ServerConnectionStatus oldStatus, ServerConnectionStatus newStatus, ServerConnectionChangedReason reason);
}
}

View File

@@ -1,57 +0,0 @@
package com.projectswg.networking.server;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import network.PacketType;
public class SWGProtocol {
private final HolocoreProtocol holocore;
public SWGProtocol() {
holocore = new HolocoreProtocol();
}
public void reset() {
holocore.reset();
}
public ByteBuffer assemble(byte [] packet) {
return holocore.assemble(packet);
}
public boolean addToBuffer(ByteBuffer network) {
return holocore.addToBuffer(network);
}
public RawPacket disassemble() {
byte [] packet = holocore.disassemble();
if (packet.length < 6)
return null;
ByteBuffer data = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN);
PacketType type = PacketType.fromCrc(data.getInt(2));
return new RawPacket(type, packet);
}
public static class RawPacket {
private final PacketType type;
private final byte [] data;
public RawPacket(PacketType type, byte [] data) {
this.type = type;
this.data = data;
}
public PacketType getPacketType() {
return type;
}
public byte [] getData() {
return data;
}
}
}

View File

@@ -1,15 +0,0 @@
package com.projectswg.networking.server;
public enum ServerConnectionChangedReason {
NONE,
CLIENT_DISCONNECT,
SOCKET_CLOSED,
INVALID_PROTOCOL,
BROKEN_PIPE,
CONNECTION_RESET,
CONNECTION_REFUSED,
ADDR_IN_USE,
NO_ROUTE_TO_HOST,
OTHER_SIDE_TERMINATED,
UNKNOWN
}

View File

@@ -8,11 +8,13 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import network.PacketType;
import network.packets.swg.zone.HeartBeat;
import com.projectswg.concurrency.PswgBasicScheduledThread;
import com.projectswg.concurrency.PswgBasicThread;
import com.projectswg.connection.HolocoreSocket;
import com.projectswg.connection.ServerConnectionChangedReason;
import com.projectswg.connection.packets.RawPacket;
import com.projectswg.control.Assert;
import com.projectswg.control.IntentManager;
import com.projectswg.control.Service;
@@ -20,7 +22,6 @@ import com.projectswg.intents.ClientConnectionChangedIntent;
import com.projectswg.intents.ClientToServerPacketIntent;
import com.projectswg.intents.ServerConnectionChangedIntent;
import com.projectswg.intents.ServerToClientPacketIntent;
import com.projectswg.networking.server.SWGProtocol.RawPacket;
import com.projectswg.utilities.IntentChain;
import com.projectswg.utilities.Log;
import com.projectswg.utilities.ThreadUtilities;
@@ -151,10 +152,10 @@ public class ServerConnectionService extends Service {
}
Assert.test(connection.isConnected());
RawPacket packet = null;
while ((packet = connection.read()) != null) {
if (packet.getPacketType() == PacketType.HEART_BEAT_MESSAGE)
while ((packet = connection.receive()) != null) {
if (packet.getCrc() == HeartBeat.CRC)
lastHeartbeat.set(System.nanoTime());
recvIntentChain.broadcastAfter(new ServerToClientPacketIntent(packet.getPacketType(), packet.getData()));
recvIntentChain.broadcastAfter(new ServerToClientPacketIntent(packet.getCrc(), packet.getData()));
}
}
} catch (Throwable t) {

View File

@@ -2,14 +2,12 @@ package com.projectswg.networking.server;
import java.net.InetAddress;
import network.PacketType;
import com.projectswg.connection.ServerConnectionStatus;
import com.projectswg.control.IntentManager;
import com.projectswg.intents.ClientConnectionChangedIntent;
import com.projectswg.intents.ServerConnectionChangedIntent;
import com.projectswg.intents.ServerToClientPacketIntent;
import com.projectswg.resources.ClientConnectionStatus;
import com.projectswg.resources.ServerConnectionStatus;
public class ServerConnectionWrapper {
@@ -58,12 +56,12 @@ public class ServerConnectionWrapper {
private void onServerData(ServerToClientPacketIntent i) {
if (callback != null)
callback.onServerPacket(i.getPacketType(), i.getRawData());
callback.onServerPacket(i.getCrc(), i.getRawData());
}
public interface ConnectionCallback {
void onServerConnectionChanged(ServerConnectionStatus old, ServerConnectionStatus status);
void onServerPacket(PacketType type, byte [] data);
void onServerPacket(int crc, byte [] data);
}
}

View File

@@ -1,7 +0,0 @@
package com.projectswg.resources;
public enum ServerConnectionStatus {
CONNECTING,
CONNECTED,
DISCONNECTED
}

View File

@@ -4,13 +4,13 @@ import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import com.projectswg.connection.ServerConnectionStatus;
import com.projectswg.control.Assert;
import com.projectswg.control.Service;
import com.projectswg.intents.ClientToServerPacketIntent;
import com.projectswg.intents.ServerConnectionChangedIntent;
import com.projectswg.intents.ServerToClientPacketIntent;
import com.projectswg.recording.PacketRecorder;
import com.projectswg.resources.ServerConnectionStatus;
import com.projectswg.utilities.Log;
public class PacketRecordingService extends Service {