diff --git a/.classpath b/.classpath index eae1a3d..542097f 100644 --- a/.classpath +++ b/.classpath @@ -5,5 +5,10 @@ + + + + + diff --git a/lib/ClientHolocore-javadoc.jar b/lib/ClientHolocore-javadoc.jar new file mode 100644 index 0000000..e3fed79 Binary files /dev/null and b/lib/ClientHolocore-javadoc.jar differ diff --git a/lib/ClientHolocore.jar b/lib/ClientHolocore.jar new file mode 100644 index 0000000..1d59255 Binary files /dev/null and b/lib/ClientHolocore.jar differ diff --git a/src/com/projectswg/Connections.java b/src/com/projectswg/Connections.java index cc00289..488041a 100644 --- a/src/com/projectswg/Connections.java +++ b/src/com/projectswg/Connections.java @@ -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; diff --git a/src/com/projectswg/Forwarder.java b/src/com/projectswg/Forwarder.java index 7c6724c..b4191aa 100644 --- a/src/com/projectswg/Forwarder.java +++ b/src/com/projectswg/Forwarder.java @@ -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 { diff --git a/src/com/projectswg/intents/ServerConnectionChangedIntent.java b/src/com/projectswg/intents/ServerConnectionChangedIntent.java index 6d611a5..071d94c 100644 --- a/src/com/projectswg/intents/ServerConnectionChangedIntent.java +++ b/src/com/projectswg/intents/ServerConnectionChangedIntent.java @@ -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 { diff --git a/src/com/projectswg/intents/ServerToClientPacketIntent.java b/src/com/projectswg/intents/ServerToClientPacketIntent.java index 50501fc..4fdeafc 100644 --- a/src/com/projectswg/intents/ServerToClientPacketIntent.java +++ b/src/com/projectswg/intents/ServerToClientPacketIntent.java @@ -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) { diff --git a/src/com/projectswg/networking/server/HolocoreProtocol.java b/src/com/projectswg/networking/server/HolocoreProtocol.java deleted file mode 100644 index 4913eb6..0000000 --- a/src/com/projectswg/networking/server/HolocoreProtocol.java +++ /dev/null @@ -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; - } - -} diff --git a/src/com/projectswg/networking/server/HolocoreSocket.java b/src/com/projectswg/networking/server/HolocoreSocket.java deleted file mode 100644 index 80f4519..0000000 --- a/src/com/projectswg/networking/server/HolocoreSocket.java +++ /dev/null @@ -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 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); - } - -} diff --git a/src/com/projectswg/networking/server/SWGProtocol.java b/src/com/projectswg/networking/server/SWGProtocol.java deleted file mode 100644 index 8ae86e0..0000000 --- a/src/com/projectswg/networking/server/SWGProtocol.java +++ /dev/null @@ -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; - } - - } - -} diff --git a/src/com/projectswg/networking/server/ServerConnectionChangedReason.java b/src/com/projectswg/networking/server/ServerConnectionChangedReason.java deleted file mode 100644 index 3b4c582..0000000 --- a/src/com/projectswg/networking/server/ServerConnectionChangedReason.java +++ /dev/null @@ -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 -} diff --git a/src/com/projectswg/networking/server/ServerConnectionService.java b/src/com/projectswg/networking/server/ServerConnectionService.java index 85d6656..6a3310a 100644 --- a/src/com/projectswg/networking/server/ServerConnectionService.java +++ b/src/com/projectswg/networking/server/ServerConnectionService.java @@ -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) { diff --git a/src/com/projectswg/networking/server/ServerConnectionWrapper.java b/src/com/projectswg/networking/server/ServerConnectionWrapper.java index 43b9817..7bbdf4d 100644 --- a/src/com/projectswg/networking/server/ServerConnectionWrapper.java +++ b/src/com/projectswg/networking/server/ServerConnectionWrapper.java @@ -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); } } diff --git a/src/com/projectswg/resources/ServerConnectionStatus.java b/src/com/projectswg/resources/ServerConnectionStatus.java deleted file mode 100644 index 2a72215..0000000 --- a/src/com/projectswg/resources/ServerConnectionStatus.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.projectswg.resources; - -public enum ServerConnectionStatus { - CONNECTING, - CONNECTED, - DISCONNECTED -} diff --git a/src/com/projectswg/services/PacketRecordingService.java b/src/com/projectswg/services/PacketRecordingService.java index fa99586..9349409 100644 --- a/src/com/projectswg/services/PacketRecordingService.java +++ b/src/com/projectswg/services/PacketRecordingService.java @@ -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 {