From ab79500f13d36b32b955c80f0c6057d1fb23a45f Mon Sep 17 00:00:00 2001 From: Obique PSWG Date: Sat, 2 Jan 2016 22:17:39 -0600 Subject: [PATCH] Changed Holocore to use TCP. No big deal --- pom.xml | 11 +- .../network/CloseConnectionIntent.java | 20 +- .../network/ConnectionClosedIntent.java} | 45 +-- .../network/ConnectionOpenedIntent.java} | 38 +- .../network/ForceDisconnectIntent.java | 30 +- src/intents/network/GalacticPacketIntent.java | 14 +- src/intents/network/InboundPacketIntent.java | 13 +- src/main/ProjectSWG.java | 1 - src/network/FragmentedHandler.java | 111 ------ src/network/InboundNetworkHandler.java | 194 ---------- src/network/NetworkClient.java | 150 ++++---- src/network/NetworkProtocol.java | 154 -------- src/network/OutboundNetworkHandler.java | 311 ---------------- src/network/OutboundPacketService.java | 185 +--------- src/network/PacketSender.java | 5 +- src/network/PacketType.java | 2 +- src/network/encryption/Compression.java | 35 ++ .../soe/ClientNetworkStatusUpdate.java | 107 ------ src/network/packets/soe/DataChannelA.java | 180 --------- src/network/packets/soe/Disconnect.java | 105 ------ src/network/packets/soe/Fragmented.java | 141 ------- src/network/packets/soe/MultiPacket.java | 140 ------- .../soe/ServerNetworkStatusUpdate.java | 96 ----- src/network/packets/soe/SessionRequest.java | 76 ---- src/network/packets/soe/SessionResponse.java | 103 ------ .../commands/callbacks/QaToolCmdCallback.java | 5 +- src/resources/control/Service.java | 11 +- src/resources/network/DisconnectReason.java | 9 + src/resources/network/TCPServer.java | 247 +++++++++++++ .../objects/creature/CreatureObject.java | 9 +- src/services/CoreManager.java | 62 +--- src/services/galaxy/ConnectionService.java | 63 +--- src/services/galaxy/GameManager.java | 4 + src/services/group/GroupService.java | 1 - .../network/NetworkClientManager.java | 347 +++++++----------- .../network/NetworkListenerService.java | 199 ---------- src/services/network/NetworkManager.java | 8 +- src/services/player/LoginService.java | 21 +- src/services/player/PlayerManager.java | 86 +---- src/services/player/ZoneManager.java | 23 +- src/services/trader/TraderManager.java | 20 + .../trader/resources/ResourceService.java | 11 + src/services/trader/survey/SurveyService.java | 65 ++++ test/main/TestAll.java | 2 - test/network/encryption/TestCompression.java | 42 +++ test/network/encryption/TestEncryption.java | 14 - test/network/encryption/TestFragmented.java | 94 ----- 47 files changed, 769 insertions(+), 2841 deletions(-) rename src/{network/packets/soe/Acknowledge.java => intents/network/ConnectionClosedIntent.java} (76%) rename src/{network/packets/soe/OutOfOrder.java => intents/network/ConnectionOpenedIntent.java} (79%) delete mode 100644 src/network/FragmentedHandler.java delete mode 100644 src/network/InboundNetworkHandler.java delete mode 100644 src/network/NetworkProtocol.java delete mode 100644 src/network/OutboundNetworkHandler.java create mode 100644 src/network/encryption/Compression.java delete mode 100644 src/network/packets/soe/ClientNetworkStatusUpdate.java delete mode 100644 src/network/packets/soe/DataChannelA.java delete mode 100644 src/network/packets/soe/Disconnect.java delete mode 100644 src/network/packets/soe/Fragmented.java delete mode 100644 src/network/packets/soe/MultiPacket.java delete mode 100644 src/network/packets/soe/ServerNetworkStatusUpdate.java delete mode 100644 src/network/packets/soe/SessionRequest.java delete mode 100644 src/network/packets/soe/SessionResponse.java create mode 100644 src/resources/network/DisconnectReason.java create mode 100644 src/resources/network/TCPServer.java delete mode 100644 src/services/network/NetworkListenerService.java create mode 100644 src/services/trader/TraderManager.java create mode 100644 src/services/trader/resources/ResourceService.java create mode 100644 src/services/trader/survey/SurveyService.java create mode 100644 test/network/encryption/TestCompression.java delete mode 100644 test/network/encryption/TestFragmented.java diff --git a/pom.xml b/pom.xml index 8fed16b1f..c8f90288a 100644 --- a/pom.xml +++ b/pom.xml @@ -44,9 +44,14 @@ test - org.slf4j - slf4j-simple - 1.7.12 + org.slf4j + slf4j-simple + 1.7.12 + + + net.jpountz.lz4 + lz4 + 1.3.0 diff --git a/src/intents/network/CloseConnectionIntent.java b/src/intents/network/CloseConnectionIntent.java index 93ca222bd..3cf6d9eb4 100644 --- a/src/intents/network/CloseConnectionIntent.java +++ b/src/intents/network/CloseConnectionIntent.java @@ -27,46 +27,36 @@ ***********************************************************************************/ package intents.network; -import network.packets.soe.Disconnect.DisconnectReason; import resources.control.Intent; +import resources.network.DisconnectReason; public class CloseConnectionIntent extends Intent { public static final String TYPE = "CloseConnectionIntent"; - private int connId; private long networkId; private DisconnectReason reason; - public CloseConnectionIntent(int connId, long networkId, DisconnectReason reason) { + public CloseConnectionIntent(long networkId, DisconnectReason reason) { super(TYPE); - setConnectionId(connId); setNetworkId(networkId); - setReason(reason); - } - - public void setConnectionId(int connId) { - this.connId = connId; + setDisconnectReason(reason); } public void setNetworkId(long networkId) { this.networkId = networkId; } - public void setReason(DisconnectReason reason) { + public void setDisconnectReason(DisconnectReason reason) { this.reason = reason; } - public int getConnectionId() { - return connId; - } - public long getNetworkId() { return networkId; } - public DisconnectReason getReason() { + public DisconnectReason getDisconnectReason() { return reason; } diff --git a/src/network/packets/soe/Acknowledge.java b/src/intents/network/ConnectionClosedIntent.java similarity index 76% rename from src/network/packets/soe/Acknowledge.java rename to src/intents/network/ConnectionClosedIntent.java index bdf854010..2137904a0 100644 --- a/src/network/packets/soe/Acknowledge.java +++ b/src/intents/network/ConnectionClosedIntent.java @@ -25,44 +25,39 @@ * along with Holocore. If not, see . * * * ***********************************************************************************/ -package network.packets.soe; +package intents.network; -import java.nio.ByteBuffer; +import resources.control.Intent; +import resources.network.DisconnectReason; -import network.packets.Packet; -public class Acknowledge extends Packet { +public class ConnectionClosedIntent extends Intent { - private short sequence; + public static final String TYPE = "ConnectionClosedIntent"; - public Acknowledge() { - sequence = 0; + private long networkId; + private DisconnectReason reason; + + public ConnectionClosedIntent(long networkId, DisconnectReason reason) { + super(TYPE); + setNetworkId(networkId); + setReason(reason); } - public Acknowledge(ByteBuffer data) { - decode(data); + public void setNetworkId(long networkId) { + this.networkId = networkId; } - public Acknowledge(short sequence) { - this.sequence = sequence; + public void setReason(DisconnectReason reason) { + this.reason = reason; } - public void decode(ByteBuffer data) { - if (data.array().length < 4) - return; - data.position(2); - sequence = getNetShort(data); + public long getNetworkId() { + return networkId; } - public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(4); - addNetShort(data, 21); - addNetShort(data, sequence); - return data; + public DisconnectReason getReason() { + return reason; } - public void setSequence(short sequence) { this.sequence = sequence; } - - public short getSequence() { return sequence; } - } diff --git a/src/network/packets/soe/OutOfOrder.java b/src/intents/network/ConnectionOpenedIntent.java similarity index 79% rename from src/network/packets/soe/OutOfOrder.java rename to src/intents/network/ConnectionOpenedIntent.java index 0524c77d3..e46254cb6 100644 --- a/src/network/packets/soe/OutOfOrder.java +++ b/src/intents/network/ConnectionOpenedIntent.java @@ -25,40 +25,28 @@ * along with Holocore. If not, see . * * * ***********************************************************************************/ -package network.packets.soe; +package intents.network; -import java.nio.ByteBuffer; - -import network.packets.Packet; +import resources.control.Intent; -public class OutOfOrder extends Packet { +public class ConnectionOpenedIntent extends Intent { - private short sequence; + public static final String TYPE = "ConnectionOpenedIntent"; - public OutOfOrder() { - + private long networkId; + + public ConnectionOpenedIntent(long networkId) { + super(TYPE); + setNetworkId(networkId); } - public OutOfOrder(short sequence) { - this.sequence = sequence; + public void setNetworkId(long networkId) { + this.networkId = networkId; } - public OutOfOrder(ByteBuffer data) { - decode(data); + public long getNetworkId() { + return networkId; } - public void decode(ByteBuffer data) { - data.position(2); - sequence = getNetShort(data); - } - - public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(4); - addNetShort(data, 0x11); - addNetShort(data, sequence); - return data; - } - - public short getSequence() { return sequence; } } diff --git a/src/intents/network/ForceDisconnectIntent.java b/src/intents/network/ForceDisconnectIntent.java index 186ca85dc..3c27c3116 100644 --- a/src/intents/network/ForceDisconnectIntent.java +++ b/src/intents/network/ForceDisconnectIntent.java @@ -27,8 +27,8 @@ ***********************************************************************************/ package intents.network; -import network.packets.soe.Disconnect.DisconnectReason; import resources.control.Intent; +import resources.network.DisconnectReason; import resources.player.Player; public class ForceDisconnectIntent extends Intent { @@ -36,50 +36,46 @@ public class ForceDisconnectIntent extends Intent { public static final String TYPE = "ForceDisconnectIntent"; private Player player; - private DisconnectReason reason; private boolean disappearImmediately; + private DisconnectReason reason; public ForceDisconnectIntent(Player player) { this(player, false); } - public ForceDisconnectIntent(Player player, DisconnectReason reason) { - this(player, reason, false); - } - public ForceDisconnectIntent(Player player, boolean disappearImmediately) { - this(player, DisconnectReason.APPLICATION, disappearImmediately); + this(player, disappearImmediately, DisconnectReason.APPLICATION); } - public ForceDisconnectIntent(Player player, DisconnectReason reason, boolean disappearImmediately) { + public ForceDisconnectIntent(Player player, boolean disappearImmediately, DisconnectReason reason) { super(TYPE); setPlayer(player); - setDisconnectReason(reason); setDisappearImmediately(disappearImmediately); + setDisconnectReason(reason); } public void setPlayer(Player player) { this.player = player; } - public void setDisconnectReason(DisconnectReason reason) { - this.reason = reason; - } - public void setDisappearImmediately(boolean disappearImmediately) { this.disappearImmediately = disappearImmediately; } + public void setDisconnectReason(DisconnectReason reason) { + this.reason = reason; + } + public Player getPlayer() { return player; } - public DisconnectReason getDisconnectReason() { - return reason; - } - public boolean getDisappearImmediately() { return disappearImmediately; } + public DisconnectReason getDisconnectReason() { + return reason; + } + } diff --git a/src/intents/network/GalacticPacketIntent.java b/src/intents/network/GalacticPacketIntent.java index c9de856a1..87f4efcd3 100644 --- a/src/intents/network/GalacticPacketIntent.java +++ b/src/intents/network/GalacticPacketIntent.java @@ -29,27 +29,23 @@ package intents.network; import intents.GalacticIntent; import network.packets.Packet; -import resources.network.ServerType; public class GalacticPacketIntent extends GalacticIntent { public static final String TYPE = "GalacticPacketIntent"; private Packet packet; - private ServerType type; private long networkId; public GalacticPacketIntent(InboundPacketIntent i) { super(TYPE); setPacket(i.getPacket()); - setServerType(i.getServerType()); setNetworkId(i.getNetworkId()); } - public GalacticPacketIntent(ServerType type, Packet p, long networkId) { + public GalacticPacketIntent(Packet p, long networkId) { super(TYPE); setPacket(p); - setServerType(type); setNetworkId(networkId); } @@ -57,10 +53,6 @@ public class GalacticPacketIntent extends GalacticIntent { this.packet = p; } - public void setServerType(ServerType type) { - this.type = type; - } - public void setNetworkId(long networkId) { this.networkId = networkId; } @@ -69,10 +61,6 @@ public class GalacticPacketIntent extends GalacticIntent { return packet; } - public ServerType getServerType() { - return type; - } - public long getNetworkId() { return networkId; } diff --git a/src/intents/network/InboundPacketIntent.java b/src/intents/network/InboundPacketIntent.java index cf24bbd58..cebbd0b5e 100644 --- a/src/intents/network/InboundPacketIntent.java +++ b/src/intents/network/InboundPacketIntent.java @@ -29,20 +29,17 @@ package intents.network; import network.packets.Packet; import resources.control.Intent; -import resources.network.ServerType; public class InboundPacketIntent extends Intent { public static final String TYPE = "InboundPacketIntent"; private Packet packet; - private ServerType type; private long networkId; - public InboundPacketIntent(ServerType type, Packet p, long networkId) { + public InboundPacketIntent(Packet p, long networkId) { super(TYPE); setPacket(p); - setServerType(type); setNetworkId(networkId); } @@ -50,10 +47,6 @@ public class InboundPacketIntent extends Intent { this.packet = p; } - public void setServerType(ServerType type) { - this.type = type; - } - public void setNetworkId(long networkId) { this.networkId = networkId; } @@ -62,10 +55,6 @@ public class InboundPacketIntent extends Intent { return packet; } - public ServerType getServerType() { - return type; - } - public long getNetworkId() { return networkId; } diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index f67e35c09..8e67afd46 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -142,7 +142,6 @@ public class ProjectSWG { private void loop() { setStatus((manager.getGalaxyStatus() == GalaxyStatus.UP) ? ServerStatus.OPEN : ServerStatus.LOCKED); while (!shutdownRequested && !manager.isShutdownRequested() && manager.isOperational()) { - manager.flushPackets(); // Sends any packets that weren't sent try { Thread.sleep(50); } catch (InterruptedException e) { diff --git a/src/network/FragmentedHandler.java b/src/network/FragmentedHandler.java deleted file mode 100644 index f164215bb..000000000 --- a/src/network/FragmentedHandler.java +++ /dev/null @@ -1,111 +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 network; - -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; - -import network.packets.soe.Fragmented; - -public class FragmentedHandler { - - private List fragPackets; - private int fragSize; - - public FragmentedHandler() { - fragPackets = new ArrayList(); - fragSize = 0; - } - - public void reset() { - fragPackets.clear(); - fragSize = 0; - } - - public byte [] onReceived(Fragmented f) { - synchronized (fragPackets) { - if (insertIfNew(f) && getBufferedSize() == fragSize) { - byte [] data = new byte[fragSize]; - int offset = 0; - for (Fragmented frag : fragPackets) { - offset = spliceFragmentedIntoBuffer(frag, data, offset); - } - updateMetadata(); - return data; - } - return new byte[0]; - } - } - - private boolean insertIfNew(Fragmented f) { - synchronized (fragPackets) { - if (!fragPackets.contains(f)) { - fragPackets.add(f); - updateMetadata(); - return true; - } - } - return false; - } - - private int spliceFragmentedIntoBuffer(Fragmented f, byte [] data, int offset) { - byte [] fData = f.encode().array(); - int header = offset==0?8:4; - System.arraycopy(fData, header, data, offset, fData.length-header); - return offset + fData.length-header; - } - - private void updateMetadata() { - synchronized (fragPackets) { - if (fragPackets.isEmpty()) - fragSize = 0; - else - fragSize = fragPackets.get(0).encode().order(ByteOrder.BIG_ENDIAN).getInt(4); - } - } - - private int getBufferedSize() { - int curSize = 0; - int i = 0; - short prevSeq = (short) (fragPackets.get(0).getSequence()-1); - for (Fragmented frag : fragPackets) { - // Update previous sequence and verify all in-order - if (prevSeq+1 != frag.getSequence()) - break; - prevSeq = frag.getSequence(); - // Update current size - curSize += (i == 0) ? frag.encode().array().length-8 : frag.encode().array().length-4; - i++; - if (curSize >= fragSize) - break; - } - return curSize; - } - -} diff --git a/src/network/InboundNetworkHandler.java b/src/network/InboundNetworkHandler.java deleted file mode 100644 index c8aa72618..000000000 --- a/src/network/InboundNetworkHandler.java +++ /dev/null @@ -1,194 +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 network; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.LinkedList; -import java.util.Queue; - -import network.encryption.Encryption; -import network.packets.Packet; -import network.packets.soe.Acknowledge; -import network.packets.soe.ClientNetworkStatusUpdate; -import network.packets.soe.DataChannelA; -import network.packets.soe.Disconnect; -import network.packets.soe.Fragmented; -import network.packets.soe.MultiPacket; -import network.packets.soe.OutOfOrder; -import network.packets.soe.SessionRequest; -import network.packets.soe.SessionResponse; -import network.packets.swg.SWGPacket; -import network.packets.swg.zone.object_controller.ObjectController; - -public class InboundNetworkHandler { - - private final Queue inboundQueue; - private final FragmentedHandler fragStream; - private final InboundEventCallback eventCallback; - private short recvSequence; - private int crc; - - public InboundNetworkHandler(InboundEventCallback eventCallback) { - this.inboundQueue = new LinkedList(); - this.fragStream = new FragmentedHandler(); - this.eventCallback = eventCallback; - recvSequence = -1; - crc = 0; - } - - public synchronized void reset() { - fragStream.reset(); - inboundQueue.clear(); - recvSequence = -1; - crc = 0; - } - - public synchronized void setCrc(int crc) { - this.crc = crc; - } - - public synchronized int getCrc() { - return crc; - } - - public synchronized short getReceivedSequence() { - return recvSequence; - } - - public synchronized boolean hasInbound() { - synchronized (inboundQueue) { - return !inboundQueue.isEmpty(); - } - } - - public synchronized Packet pollInbound() { - synchronized (inboundQueue) { - return inboundQueue.poll(); - } - } - - private void pushPacket(Packet packet) { - synchronized (inboundQueue) { - inboundQueue.add(packet); - } - } - - public synchronized int onReceive(byte [] data) { - if (data.length < 2) - return 0; - if (data[1] == 1 || data[1] == 2) - return processPacket(data); - else - return processPacket(Encryption.decode(data, crc)); - } - - private int processPacket(byte [] data) { - if (data.length < 2) - return 0; - ByteBuffer bb = ByteBuffer.wrap(data); - int packets = 0; - switch (data[1]) { - case 0x00: - if (data[0] > 0) - packets += processSwgPacket(data); - break; - case 0x01: ++packets; pushPacket(new SessionRequest(bb)); break; - case 0x02: ++packets; pushPacket(new SessionResponse(bb)); break; - case 0x03: packets += processMulti(new MultiPacket(bb)); break; - case 0x05: ++packets; pushPacket(new Disconnect(bb)); break; - case 0x07: ++packets; pushPacket(new ClientNetworkStatusUpdate(bb)); break; - case 0x09: packets += processData(new DataChannelA(bb)); break; - case 0x0D: packets += processFrag(new Fragmented(bb)); break; - case 0x11: ++packets; pushPacket(new OutOfOrder(bb)); break; - case 0x15: ++packets; pushPacket(new Acknowledge(bb)); break; - default: - break; - } - return packets; - } - - private int processMulti(MultiPacket packet) { - int packets = 0; - for (Packet p : packet.getPackets()) - packets += processPacket(p.getData().array()); - return packets; - } - - private int processData(DataChannelA packet) { - if (packet.getSequence() != (short) (recvSequence+1)) { - if (packet.getSequence() > recvSequence) - eventCallback.sendOutOfOrder(packet.getSequence()); - return 0; - } - recvSequence = packet.getSequence(); - eventCallback.sendAcknowledge(recvSequence); - int packets = 0; - for (SWGPacket p : packet.getPackets()) { - processSwgPacket(p.getData().array()); - ++packets; - } - return packets; - } - - private int processFrag(Fragmented packet) { - if (packet.getSequence() != (short) (recvSequence+1)) { - if (packet.getSequence() > recvSequence) - eventCallback.sendOutOfOrder(packet.getSequence()); - return 0; - } - recvSequence = packet.getSequence(); - eventCallback.sendAcknowledge(recvSequence); - return processPacket(fragStream.onReceived(packet)); - } - - private int processSwgPacket(byte [] data) { - if (data.length < 6) - return 0; - ByteBuffer bb = ByteBuffer.wrap(data); - int crc = bb.order(ByteOrder.LITTLE_ENDIAN).getInt(2); - SWGPacket packet; - if (crc == ObjectController.CRC) - packet = ObjectController.decodeController(bb); - else { - packet = PacketType.getForCrc(crc); - if (packet != null) - packet.decode(bb); - } - if (packet == null) - return 0; - pushPacket(packet); - return 1; - } - - public interface InboundEventCallback { - public void sendAcknowledge(short sequence); - public void sendOutOfOrder(short sequence); - } - -} diff --git a/src/network/NetworkClient.java b/src/network/NetworkClient.java index 72d80590a..4629e23c2 100644 --- a/src/network/NetworkClient.java +++ b/src/network/NetworkClient.java @@ -29,103 +29,127 @@ package network; import intents.network.InboundPacketIntent; -import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; import java.util.List; import resources.control.Intent; -import resources.network.ServerType; +import network.encryption.Compression; import network.packets.Packet; +import network.packets.swg.SWGPacket; +import network.packets.swg.zone.object_controller.ObjectController; public class NetworkClient { private final Object prevPacketIntentMutex = new Object(); + private final InetSocketAddress address; private final long networkId; - private final ServerType serverType; - private final NetworkProtocol protocol; - private InetAddress address; + private final PacketSender packetSender; private Intent prevPacketIntent; - private int port; - private int connId; - public NetworkClient(ServerType type, InetAddress addr, int port, long networkId, PacketSender packetSender) { - this.serverType = type; + public NetworkClient(InetSocketAddress address, long networkId, PacketSender packetSender) { + this.address = address; this.networkId = networkId; - protocol = new NetworkProtocol(type, addr, port, packetSender); + this.packetSender = packetSender; prevPacketIntent = null; - connId = 0; - updateNetworkInfo(addr, port); } - public void updateNetworkInfo(InetAddress addr, int port) { - protocol.updateNetworkInfo(addr, port); - this.address = addr; - this.port = port; - } - - public void resetNetwork() { - protocol.resetNetwork(); - connId = 0; - } - - public void resendOldUnacknowledged() { - protocol.resendOldUnacknowledged(); - } - - public void setCrc(int crc) { - protocol.setCrc(crc); - } - - public void setConnectionId(int id) { - connId = id; - } - - public InetAddress getAddress() { + public InetSocketAddress getAddress() { return address; } - public int getPort() { - return port; - } - - public int getCrc() { - return protocol.getCrc(); - } - - public int getConnectionId() { - return connId; - } - public long getNetworkId() { return networkId; } public void sendPacket(Packet p) { - protocol.sendPacket(p); + byte [] encoded = p.encode().array(); + int decompressedLength = encoded.length; + boolean compressed = encoded.length >= 16; + if (compressed) { + byte [] compressedData = Compression.compress(encoded); + if (compressedData.length >= encoded.length) + compressed = false; + else + encoded = compressedData; + } + ByteBuffer data = ByteBuffer.allocate(encoded.length + 5).order(ByteOrder.LITTLE_ENDIAN); + byte bitmask = 0; + bitmask |= (compressed?1:0) << 0; // Compressed + bitmask |= 1 << 1; // SWG + data.put(bitmask); + data.putShort((short) encoded.length); + data.putShort((short) decompressedLength); + data.put(encoded); + packetSender.sendPacket(address, data.array()); } - public boolean processPacket(ServerType type, byte [] data) { - if (type != serverType || type == ServerType.UNKNOWN) - return false; - if (type == ServerType.PING) - return true; - List packets = protocol.process(data); + public boolean process(byte [] data) { + List packets = processPackets(ByteBuffer.wrap(data)); for (Packet p : packets) { - p.setAddress(address); - p.setPort(port); + p.setAddress(address.getAddress()); + p.setPort(address.getPort()); synchronized (prevPacketIntentMutex) { - InboundPacketIntent i = new InboundPacketIntent(type, p, networkId); - if (prevPacketIntent == null) - i.broadcast(); - else - i.broadcastAfterIntent(prevPacketIntent); + InboundPacketIntent i = new InboundPacketIntent(p, networkId); + i.broadcastAfterIntent(prevPacketIntent); prevPacketIntent = i; } } return packets.size() > 0; } + private List processPackets(ByteBuffer data) { + List packets = new ArrayList<>(); + boolean added = true; + while (added && data.remaining() > 0) { + added = processPacket(packets, data); + } + return packets; + } + + private boolean processPacket(List packets, ByteBuffer data) { + if (data.remaining() < 5) { + System.err.println("Not enough remaining data for header! Remaining: " + data.remaining()); + return false; + } + data.order(ByteOrder.LITTLE_ENDIAN); + byte bitfield = data.get(); + boolean compressed = (bitfield & (1<<0)) != 0; + boolean swg = (bitfield & (1<<1)) != 0; + int length = data.getShort(); + int decompressedLength = data.getShort(); + if (data.remaining() < length) { + System.err.println("Not enough remaining data! Remaining: " + data.remaining() + " Length: " + length); + return false; + } + byte [] pData = new byte[length]; + data.get(pData); + if (compressed) { + pData = Compression.decompress(pData, decompressedLength); + length = pData.length; + } + if (swg) { + if (length < 6) { + System.err.println("Length too small: " + length); + return false; + } + ByteBuffer pBuffer = ByteBuffer.wrap(pData).order(ByteOrder.LITTLE_ENDIAN); + int crc = pBuffer.getInt(2); + if (crc == 0x80CE5E46) + packets.add(ObjectController.decodeController(pBuffer)); + else { + SWGPacket packet = PacketType.getForCrc(crc); + packet.decode(pBuffer); + packets.add(packet); + } + } + return true; + } + public String toString() { - return "NetworkClient[ConnId=" + connId + " " + address + ":" + port + "]"; + return "NetworkClient["+address+"]"; } } diff --git a/src/network/NetworkProtocol.java b/src/network/NetworkProtocol.java deleted file mode 100644 index 4e76384e3..000000000 --- a/src/network/NetworkProtocol.java +++ /dev/null @@ -1,154 +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 network; - -import java.net.InetAddress; -import java.util.LinkedList; -import java.util.List; - -import resources.network.ServerType; -import resources.network.UDPServer.UDPPacket; -import resources.server_info.Log; -import network.InboundNetworkHandler.InboundEventCallback; -import network.packets.Packet; -import network.packets.soe.Acknowledge; -import network.packets.soe.ClientNetworkStatusUpdate; -import network.packets.soe.OutOfOrder; -import network.packets.soe.ServerNetworkStatusUpdate; - -public class NetworkProtocol implements InboundEventCallback { - - private final InboundNetworkHandler inbound; - private final OutboundNetworkHandler outbound; - private final ServerType serverType; - private final PacketSender packetSender; - - private InetAddress address; - private int port; - private int crc; - - public NetworkProtocol(ServerType type, InetAddress address, int port, PacketSender packetSender) { - this.inbound = new InboundNetworkHandler(this); - this.outbound = new OutboundNetworkHandler(); - this.serverType = type; - this.packetSender = packetSender; - crc = 0; - updateNetworkInfo(address, port); - } - - public void setCrc(int crc) { - this.crc = crc; - inbound.setCrc(crc); - outbound.setCrc(crc); - } - - public int getCrc() { - return crc; - } - - @Override - public void sendAcknowledge(short sequence) { - sendPacket(new Acknowledge(sequence)); - } - - @Override - public void sendOutOfOrder(short sequence) { - sendPacket(new OutOfOrder(sequence)); - } - - public void updateNetworkInfo(InetAddress addr, int port) { - this.address = addr; - this.port = port; - } - - public void resetNetwork() { - inbound.reset(); - outbound.reset(); - } - - public void resendOldUnacknowledged() { - outbound.resendOldUnacknowledged(); - flushAssembled(); - } - - public List process(byte [] data) { - inbound.onReceive(data); - List packets = new LinkedList(); - while (inbound.hasInbound()) { - process(packets, inbound.pollInbound()); - } - return packets; - } - - private void process(List packets, Packet packet) { - if (packet == null) - throw new NullPointerException("Inbound packet cannot be null!"); - packets.add(packet); - if (packet instanceof OutOfOrder) - outbound.onOutOfOrder(((OutOfOrder) packet).getSequence()); - else if (packet instanceof Acknowledge) - outbound.onAcknowledge(((Acknowledge) packet).getSequence()); - else if (packet instanceof ClientNetworkStatusUpdate) - processClientNetworkUpdate((ClientNetworkStatusUpdate) packet); - flushAssembled(); - } - - public void sendPacket(Packet packet) { - if (address == null) { - Log.w("NetworkProtocol", "Address is null! Cannot send packet"); - return; - } - outbound.assemble(packet); - flushAssembled(); - } - - private void send(byte [] data) { - if (data == null) - throw new NullPointerException("Outbound data cannot be null!"); - packetSender.sendPacket(serverType, new UDPPacket(address, port, data)); - } - - private void processClientNetworkUpdate(ClientNetworkStatusUpdate update) { - ServerNetworkStatusUpdate serverNet = new ServerNetworkStatusUpdate(); - serverNet.setClientTickCount((short) update.getTick()); - serverNet.setServerSyncStampLong(0); - int recv = inbound.getReceivedSequence() < 0 ? 0 : inbound.getReceivedSequence(); - int send = outbound.getSentSequence(); - serverNet.setClientPacketsSent(recv); - serverNet.setClientPacketsRecv(send); - serverNet.setServerPacketsSent(send); - serverNet.setServerPacketsRecv(recv); - sendPacket(serverNet); - } - - private void flushAssembled() { - while (outbound.hasAssembled()) - send(outbound.pollAssembled()); - } - -} diff --git a/src/network/OutboundNetworkHandler.java b/src/network/OutboundNetworkHandler.java deleted file mode 100644 index 7ab784c46..000000000 --- a/src/network/OutboundNetworkHandler.java +++ /dev/null @@ -1,311 +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 network; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.concurrent.TimeUnit; - -import network.encryption.Encryption; -import network.packets.Packet; -import network.packets.soe.DataChannelA; -import network.packets.soe.Fragmented; -import network.packets.soe.MultiPacket; -import network.packets.soe.SessionRequest; -import network.packets.soe.SessionResponse; -import network.packets.swg.SWGPacket; - -public class OutboundNetworkHandler { - - private static final long RESEND_TIMEOUT = TimeUnit.MILLISECONDS.toMillis(3000); - - private final Queue assembleQueue; - private final Queue sequenced; - private short sendSequence; - private int crc; - - public OutboundNetworkHandler() { - assembleQueue = new LinkedList(); - sequenced = new PriorityQueue(); - sendSequence = 0; - crc = 0; - } - - public synchronized void reset() { - sequenced.clear(); - assembleQueue.clear(); - sendSequence = 0; - crc = 0; - } - - public synchronized void setCrc(int crc) { - this.crc = crc; - } - - public synchronized int getCrc() { - return crc; - } - - public synchronized short getSentSequence() { - return sendSequence; - } - - public synchronized void onAcknowledge(short sequence) { - synchronized (sequenced) { - Iterator it = sequenced.iterator(); - while(it.hasNext()) { - SequencedPacket sp = it.next(); - if (sp.getSequence() <= sequence) { - it.remove(); - } else { - if (sp.isSent()) - sp.setSent(false); - else - break; - } - } - } - } - - public synchronized void onOutOfOrder(short sequence) { - synchronized (sequenced) { - Iterator it = sequenced.iterator(); - while (it.hasNext()) { - SequencedPacket sp = it.next(); - if (sp.getSequence() > sequence) { - if (sp.isSent()) - sp.setSent(false); - else - break; - } else if (!sp.isSent() || sp.getSequence() == sequence) { - pushAssembledUnencrypted(sp.getPacket()); // Pre-encrypted before putting into list - sp.setSent(true); - sp.updateSent(); - } - } - } - } - - public synchronized boolean hasAssembled() { - synchronized (assembleQueue) { - return !assembleQueue.isEmpty(); - } - } - - public synchronized byte [] pollAssembled() { - synchronized (assembleQueue) { - return assembleQueue.poll(); - } - } - - public synchronized void resendOldUnacknowledged() { - synchronized (sequenced) { - for (SequencedPacket packet : sequenced) { - if (packet.hasBeen(RESEND_TIMEOUT)) { - pushAssembledUnencrypted(packet.getPacket()); // Pre-encrypted before putting into list - packet.setSent(true); - packet.updateSent(); - } - } - } - } - - private byte [] pushAssembledEncrypted(byte [] data) { - data = Encryption.encode(data, crc); - synchronized (assembleQueue) { - assembleQueue.add(data); - } - return data; - } - - private void pushAssembledUnencrypted(byte [] data) { - synchronized (assembleQueue) { - assembleQueue.add(data); - } - } - - private void pushSequencedPacket(short sequence, byte [] packet) { - synchronized (sequenced) { - sequenced.add(new SequencedPacket(sequence, packet)); - } - } - - public synchronized int assemble(Packet packet) { - if (packet instanceof SessionRequest || packet instanceof SessionResponse) { - pushAssembledUnencrypted(packet.encode().array()); - return 1; - } else - return assembleUnencrypted(packet); - } - - private int assembleUnencrypted(Packet packet) { - if (packet instanceof SWGPacket) - return assembleSwg((SWGPacket) packet); - else - return assembleSoe(packet); - } - - private int assembleSoe(Packet packet) { - if (packet instanceof DataChannelA) - return assembleDataChannelA((DataChannelA) packet); - if (packet instanceof MultiPacket) - return assembleMultiPacket((MultiPacket) packet); - pushAssembledEncrypted(packet.encode().array()); - return 1; - } - - private int assembleSwg(SWGPacket packet) { - return assembleDataChannelA(new DataChannelA(packet)); - } - - private int assembleMultiPacket(MultiPacket m) { - int len = m.getLength(); - if (len >= 493) { - int count = getFragmentedPacketCount(len); - int lastSeq = updateSequencesMulti((short)(sendSequence+count), m); - for (Fragmented f : Fragmented.encode(m.encode(), sendSequence)) { - byte [] encoded = f.encode().array(); - byte [] encrypted = pushAssembledEncrypted(encoded); - pushSequencedPacket(f.getSequence(), encrypted); - } - sendSequence = (short) (lastSeq + 1); - return count; - } else { - sendSequence = updateSequencesMulti(sendSequence, m); - pushAssembledEncrypted(m.encode().array()); - return 1; - } - } - - private int assembleDataChannelA(DataChannelA d) { - int len = d.getLength(); - if (len >= 493) { - int count = getFragmentedPacketCount(len); - int lastSeq = updateSequenceData((short)(sendSequence+count), d); - for (Fragmented f : Fragmented.encode(d.encode(), sendSequence)) { - byte [] encoded = f.encode().array(); - byte [] encrypted = pushAssembledEncrypted(encoded); - pushSequencedPacket(f.getSequence(), encrypted); - } - sendSequence = (short) lastSeq; - return count; - } else { - d.setSequence(sendSequence++); - byte [] encoded = d.encode().array(); - byte [] encrypted = pushAssembledEncrypted(encoded); - pushSequencedPacket(d.getSequence(), encrypted); - return 1; - } - } - - private short updateSequencesMulti(short seq, MultiPacket m) { - for (Packet p : m.getPackets()) { - if (p instanceof DataChannelA) - seq = updateSequenceData(seq, (DataChannelA) p); - } - return seq; - } - - private short updateSequenceData(short seq, DataChannelA d) { - d.setSequence(seq++); - return seq; - } - - private int getFragmentedPacketCount(int length) { - return (int) Math.ceil((length+4)/489.0); - } - - private static class SequencedPacket implements Comparable { - private final short sequence; - private final byte [] packet; - private long sentTime; - private boolean sent; - - public SequencedPacket(short sequence, byte [] packet) { - this.sequence = sequence; - this.packet = packet; - this.sentTime = System.nanoTime(); - sent = false; - } - - public short getSequence() { - return sequence; - } - - public byte [] getPacket() { - return packet; - } - - public boolean isSent() { - return sent; - } - - public void setSent(boolean sent) { - this.sent = sent; - } - - public boolean hasBeen(double milliseconds) { - return hasBeen() >= milliseconds; - } - - public double hasBeen() { - return (System.nanoTime() - sentTime) / 1E6; - } - - public void updateSent() { - sentTime = System.nanoTime(); - } - - @Override - public int compareTo(SequencedPacket sp) { - if (sequence < sp.getSequence()) - return -1; - if (sequence == sp.getSequence()) - return 0; - return 1; - } - - @Override - public boolean equals(Object o) { - if (o == null) - return false; - if (o instanceof SequencedPacket) - return ((SequencedPacket) o).getSequence() == sequence; - return false; - } - - @Override - public int hashCode() { - return sequence; - } - - } - -} diff --git a/src/network/OutboundPacketService.java b/src/network/OutboundPacketService.java index c445b5501..2760acc95 100644 --- a/src/network/OutboundPacketService.java +++ b/src/network/OutboundPacketService.java @@ -1,190 +1,25 @@ -/*********************************************************************************** -* 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 network; -import intents.network.CloseConnectionIntent; import intents.network.OutboundPacketIntent; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import resources.control.Intent; -import resources.control.Service; import network.packets.Packet; -import network.packets.soe.DataChannelA; -import network.packets.soe.MultiPacket; -import network.packets.soe.SessionResponse; -import network.packets.swg.SWGPacket; public class OutboundPacketService { - private final Map outboundPackets = new HashMap(); + private final Object outboundMutex = new Object(); + private Intent previousOutboundIntent = null; - public OutboundPacketService() { - new Service() { - public void onIntentReceived(Intent i) { - if (i instanceof CloseConnectionIntent) - closeConnection(((CloseConnectionIntent)i).getNetworkId()); - } - }.registerForIntent(CloseConnectionIntent.TYPE); + public void send(long networkId, Packet ... packets) { + for (Packet packet : packets) + send(networkId, packet); } - public void sendPacket(long networkId, Packet ... packets) { - getSender(networkId).add(packets); - } - - public void sendPacket(long networkId, Packet packet) { - getSender(networkId).add(packet); - } - - public int flushPackets() { - int count = 0; - synchronized (outboundPackets) { - for (OutboundPacketSender sender : outboundPackets.values()) - count += sender.sendPackets(); + public void send(long networkId, Packet packet) { + synchronized (outboundMutex) { + Intent i = new OutboundPacketIntent(packet, networkId); + i.broadcastAfterIntent(previousOutboundIntent); + previousOutboundIntent = i; } - return count; - } - - private void closeConnection(long networkId) { - synchronized (outboundPackets) { - outboundPackets.remove(networkId); - } - } - - private OutboundPacketSender getSender(long networkId) { - synchronized (outboundPackets) { - OutboundPacketSender outbound = outboundPackets.get(networkId); - if (outbound == null) { - outbound = new OutboundPacketSender(networkId); - outboundPackets.put(networkId, outbound); - } - return outbound; - } - } - - private static class OutboundPacketSender { - - private final List outbound; - private final long networkId; - private Intent prevOutbound; - private boolean hasSoe; - private boolean hasSwg; - - public OutboundPacketSender(long networkId) { - outbound = new LinkedList(); - this.networkId = networkId; - prevOutbound = null; - hasSoe = false; - hasSwg = false; - } - - public synchronized void add(Packet ... packets) { - for (Packet p : packets) - add(p); - } - - public synchronized void add(Packet p) { - if (p instanceof SessionResponse) { - send(p); - } else { - if (p instanceof SWGPacket) - hasSwg = true; - else - hasSoe = true; - outbound.add(p); - } - } - - public synchronized int sendPackets() { - if (outbound.isEmpty()) - return 0; - int size = outbound.size(); - packageAndSendPackets(); - return size; - } - - private synchronized void send(Packet p) { - Intent out = new OutboundPacketIntent(p, networkId); - out.broadcastAfterIntent(prevOutbound); - prevOutbound = out; - } - - private synchronized void clear() { - hasSoe = false; - hasSwg = false; - outbound.clear(); - } - - private synchronized void packageAndSendPackets() { - if (hasSwg) { - if (hasSoe) - packageMix(); - else - packageSwg(); - } else { - if (hasSoe) - packageSoe(); - } - clear(); - } - - private synchronized void packageSoe() { - send(new MultiPacket(new LinkedList(outbound))); - } - - private synchronized void packageSwg() { - send(new DataChannelA(outbound.toArray(new SWGPacket[outbound.size()]))); - } - - private synchronized void packageMix() { - MultiPacket multi = new MultiPacket(); - DataChannelA data = null; - for (Packet p : outbound) { - if (p instanceof SWGPacket) { - if (data == null) - data = new DataChannelA(); - data.addPacket((SWGPacket) p); - } else { - if (data != null) { - multi.addPacket(data); - data = null; - } - multi.addPacket(p); - } - } - if (data != null) - multi.addPacket(data); - send(multi); - } - } } diff --git a/src/network/PacketSender.java b/src/network/PacketSender.java index 8fc8dac23..c69ad37b1 100644 --- a/src/network/PacketSender.java +++ b/src/network/PacketSender.java @@ -1,10 +1,9 @@ package network; -import resources.network.ServerType; -import resources.network.UDPServer.UDPPacket; +import java.net.InetSocketAddress; public interface PacketSender { - void sendPacket(ServerType type, UDPPacket packet); + void sendPacket(InetSocketAddress sock, byte [] data); } diff --git a/src/network/PacketType.java b/src/network/PacketType.java index 10ddb6f80..5c38464ce 100644 --- a/src/network/PacketType.java +++ b/src/network/PacketType.java @@ -228,7 +228,7 @@ public enum PacketType { try { return c.newInstance(); } catch (Exception e) { - System.err.println("Packet: " + c.getName()); + System.err.printf("Packet: [%08X] %s%n", crc, c.getName()); e.printStackTrace(); } return null; diff --git a/src/network/encryption/Compression.java b/src/network/encryption/Compression.java new file mode 100644 index 000000000..0f0a5f488 --- /dev/null +++ b/src/network/encryption/Compression.java @@ -0,0 +1,35 @@ +package network.encryption; + +import net.jpountz.lz4.LZ4Compressor; +import net.jpountz.lz4.LZ4Factory; +import net.jpountz.lz4.LZ4SafeDecompressor; + +public class Compression { + + private static final LZ4Compressor COMPRESSOR = LZ4Factory.safeInstance().highCompressor(); + private static final LZ4SafeDecompressor DECOMPRESSOR = LZ4Factory.safeInstance().safeDecompressor(); + + public static byte [] compress(byte [] data) { + int maxCompressedLength = COMPRESSOR.maxCompressedLength(data.length); + byte[] compressed = new byte[maxCompressedLength]; + int length = COMPRESSOR.compress(data, compressed); + byte [] ret = new byte[length]; + System.arraycopy(compressed, 0, ret, 0, length); + return ret; + } + + public static byte [] decompress(byte [] data) { + return decompress(data, data.length * 10); + } + + public static byte [] decompress(byte [] data, int bufferSize) { + byte [] restored = new byte[bufferSize]; + int length = DECOMPRESSOR.decompress(data, restored); + if (length == bufferSize) + return restored; + byte [] ret = new byte[length]; + System.arraycopy(restored, 0, ret, 0, length); + return ret; + } + +} diff --git a/src/network/packets/soe/ClientNetworkStatusUpdate.java b/src/network/packets/soe/ClientNetworkStatusUpdate.java deleted file mode 100644 index 5213b5a7b..000000000 --- a/src/network/packets/soe/ClientNetworkStatusUpdate.java +++ /dev/null @@ -1,107 +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 network.packets.soe; - -import java.nio.ByteBuffer; -import network.packets.Packet; - - -public class ClientNetworkStatusUpdate extends Packet { - - private int clientTickCount; - private int lastUpdate; - private int avgUpdate; - private int shortUpdate; - private int longUpdate; - private int lastServerUpdate; - private long packetSent; - private long packetRecv; - - public ClientNetworkStatusUpdate() { - - } - - public ClientNetworkStatusUpdate(ByteBuffer data) { - decode(data); - } - - public ClientNetworkStatusUpdate(int clientTickCount, int lastUpdate, int avgUpdate, int shortUpdate, int longUpdate, int lastServerUpdate, long packetsSent, long packetsRecv) { - this.clientTickCount = clientTickCount; - this.lastUpdate = lastUpdate; - this.avgUpdate = avgUpdate; - this.shortUpdate = shortUpdate; - this.longUpdate = longUpdate; - this.lastServerUpdate = lastServerUpdate; - this.packetSent = packetsSent; - this.packetRecv = packetsRecv; - } - - public void decode(ByteBuffer data) { - data.position(2); - clientTickCount = getNetShort(data); - lastUpdate = getNetInt(data); - avgUpdate = getNetInt(data); - shortUpdate = getNetInt(data); - longUpdate = getNetInt(data); - lastServerUpdate = getNetInt(data); - packetSent = getNetLong(data); - packetRecv = getNetLong(data); - } - - public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(40); - addNetShort(data, 7); - addNetShort(data, clientTickCount); - addNetInt( data, lastUpdate); - addNetInt( data, avgUpdate); - addNetInt( data, shortUpdate); - addNetInt( data, longUpdate); - addNetInt( data, lastServerUpdate); - addNetLong( data, packetSent); - addNetLong( data, packetRecv); - return data; - } - - public int getTick() { return clientTickCount; } - public int getLastUpdate() { return lastUpdate; } - public int getAverageUpdate() { return avgUpdate; } - public int getShortestUpdate() { return shortUpdate; } - public int getLongestUpdate() { return longUpdate; } - public int getLastServerUpdate() { return lastServerUpdate; } - public long getSent() { return packetSent; } - public long getRecv() { return packetRecv; } - - public void setTick(int tick) { this.clientTickCount = tick; } - public void setLastUpdate(int last) { this.lastUpdate = last; } - public void setAverageUpdate(int avg) { this.avgUpdate = avg; } - public void setShortestUpdate(int shortest) { this.shortUpdate = shortest; } - public void setLongestUpdate(int longest) { this.longUpdate = longest; } - public void setLastServerUpdate(int last) { this.lastServerUpdate = last; } - public void setPacketsSent(long sent) { this.packetSent = sent; } - public void setPacketsRecv(long recv) { this.packetRecv = recv; } -} diff --git a/src/network/packets/soe/DataChannelA.java b/src/network/packets/soe/DataChannelA.java deleted file mode 100644 index de968483a..000000000 --- a/src/network/packets/soe/DataChannelA.java +++ /dev/null @@ -1,180 +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 network.packets.soe; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import network.packets.Packet; -import network.packets.swg.SWGPacket; - - -public class DataChannelA extends Packet implements Comparable { - - private List content = new ArrayList(); - private short sequence = 0; - private short multiPacket = 0; - - public DataChannelA() { - - } - - public DataChannelA(ByteBuffer data) { - decode(data); - } - - public DataChannelA(SWGPacket packet) { - content.add(packet); - } - - public DataChannelA(List packets) { - content = packets; - } - - public DataChannelA(SWGPacket [] packets) { - for (SWGPacket p : packets) { - content.add(p); - } - } - - public void decode(ByteBuffer data) { - super.decode(data); - if (getOpcode() != 9) - return; - data.position(2); - sequence = getNetShort(data); - multiPacket = getNetShort(data); - if (multiPacket == 0x19) { - int length = 0; - while (data.remaining() > 1) { - length = getByte(data) & 0xFF; - if (length == 0xFF) - length = getNetShort(data); - if (length > data.remaining()) { - data.position(data.position()-1); - return; - } - ByteBuffer pData = ByteBuffer.allocate(length); - data.get(pData.array()); - SWGPacket packet = new SWGPacket(); - packet.decode(pData); - content.add(packet); - } - } else { - data.position(data.position()-2); - ByteBuffer pData = ByteBuffer.allocate(data.remaining()); - data.get(pData.array()); - SWGPacket packet = new SWGPacket(); - packet.decode(pData); - content.add(packet); - } - } - - public ByteBuffer encode() { - return encode(this.sequence); - } - - public ByteBuffer encode(int sequence) { - this.sequence = (short) sequence; - if (content.size() == 1) { - byte [] pData = content.get(0).encode().array(); - ByteBuffer data = ByteBuffer.allocate(4 + pData.length); - addNetShort(data, 9); - addNetShort(data, sequence); - data.put(pData); - return data; - } else if (content.size() > 1) { - int length = getLength(); - ByteBuffer data= ByteBuffer.allocate(length); - addNetShort(data, 9); - addNetShort(data, sequence); - addNetShort(data, 0x19); - for (SWGPacket packet : content) { - byte [] pData = packet.encode().array(); - if (pData.length >= 0xFF) { - addByte(data, 0xFF); - addNetShort(data, pData.length); - } else { - data.put((byte) pData.length); - } - data.put(pData); - } - return data; - } else { - return ByteBuffer.allocate(0); - } - } - - public void addPacket(SWGPacket packet) { - content.add(packet); - } - - public void clearPackets() { - content.clear(); - } - - public int getLength() { - if (content.size() == 1) { - return 4 + content.get(0).encode().array().length; - } else { - int length = 6; - for (SWGPacket packet : content) { - int addLength = packet.encode().array().length; - length += 1 + addLength + ((addLength >= 0xFF) ? 2 : 0); - } - return length; - } - } - - @Override - public int compareTo(DataChannelA d) { - if (sequence < d.sequence) - return -1; - if (sequence == d.sequence) - return 0; - return 1; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DataChannelA)) - return false; - return ((DataChannelA) o).sequence == sequence; - } - - @Override - public int hashCode() { - return sequence; - } - - public void setSequence(short sequence) { this.sequence = sequence; } - - public short getSequence() { return sequence; } - public List getPackets() { return content; } -} diff --git a/src/network/packets/soe/Disconnect.java b/src/network/packets/soe/Disconnect.java deleted file mode 100644 index 4f6bf9fbf..000000000 --- a/src/network/packets/soe/Disconnect.java +++ /dev/null @@ -1,105 +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 network.packets.soe; - -import java.nio.ByteBuffer; - -import network.packets.Packet; - - -public class Disconnect extends Packet { - - private int connectionId; - private DisconnectReason reason; - - public Disconnect() { - connectionId = 0; - reason = DisconnectReason.NONE; - } - - public Disconnect(int connectionId, DisconnectReason reason) { - this.connectionId = connectionId; - this.reason = reason; - } - - public Disconnect(ByteBuffer data){ - this.decode(data); - } - - public void decode(ByteBuffer data) { - data.position(2); - connectionId = getNetInt(data); - reason = getReason(getNetShort(data)); - } - - public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(8); - addNetShort(data, 5); - addNetInt(data, connectionId); - addNetShort(data, reason.getReason()); - return data; - } - - public int getConnectionID() { return connectionId; } - public DisconnectReason getReason() { return reason; } - - private DisconnectReason getReason(int reason) { - for (DisconnectReason dr : DisconnectReason.values()) - if (dr.getReason() == reason) - return dr; - return DisconnectReason.NONE; - } - - public enum DisconnectReason { - NONE (0x00), - ICMP_ERROR (0x01), - TIMEOUT (0x02), - OTHER_SIDE_TERMINATED (0x03), - MANAGER_DELETED (0x04), - CONNECT_FAIL (0x05), - APPLICATION (0x06), - UNREACHABLE_CONNECTION (0x07), - UNACKNOWLEDGED_TIMEOUT (0x08), - NEW_CONNECTION_ATTEMPT (0x09), - CONNECTION_REFUSED (0x0A), - MUTUAL_CONNETION_ERROR (0x0B), - CONNETING_TO_SELF (0x0C), - RELIABLE_OVERFLOW (0x0D), - COUNT (0x0E); - - private short reason; - - DisconnectReason(int reason) { - this.reason = (short) reason; - } - - public short getReason() { - return reason; - } - } -} diff --git a/src/network/packets/soe/Fragmented.java b/src/network/packets/soe/Fragmented.java deleted file mode 100644 index fe2e97661..000000000 --- a/src/network/packets/soe/Fragmented.java +++ /dev/null @@ -1,141 +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 network.packets.soe; - -import java.nio.ByteBuffer; - -import network.packets.Packet; - - -public class Fragmented extends Packet implements Comparable { - - private short sequence; - private int length; - private ByteBuffer packet; - private ByteBuffer data; - - public Fragmented() { - this.sequence = 0; - this.length = 0; - this.packet = null; - this.data = null; - } - - public Fragmented(ByteBuffer data) { - decode(data); - length = -1; - } - - public Fragmented(ByteBuffer data, int sequence) { - this.sequence = (short) sequence; - decode(data); - length = -1; - } - - public void setPacket(ByteBuffer packet) { - this.packet = packet; - } - - public void decode(ByteBuffer data) { - data.position(2); - sequence = getNetShort(data); - this.data = data; - } - - public ByteBuffer encode() { - return data; - } - - public Fragmented [] encode(int startSequence) { - packet.position(0); - int ord = 0; - Fragmented [] packets = new Fragmented[(int) Math.ceil((packet.remaining()+4)/489.0)]; - while (packet.remaining() > 0) { - packets[ord] = createSegment(startSequence++, ord++, packet); - } - return packets; - } - - @Override - public int compareTo(Fragmented f) { - if (sequence < f.sequence) - return -1; - if (sequence == f.sequence) - return 0; - return 1; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Fragmented) - return ((Fragmented) o).sequence == sequence; - if (o instanceof Number) - return ((Number) o).shortValue() == sequence; - return false; - } - - @Override - public int hashCode() { - return sequence; - } - - public ByteBuffer getPacketData() { return data; } - public short getSequence() { return sequence; } - public int getDatLength() { return length; } - - public static final Fragmented [] encode(ByteBuffer data, int startSequence) { - data.position(0); - int ord = 0; - Fragmented [] packets = new Fragmented[(int) Math.ceil((data.remaining()+4)/489.0)]; - while (data.remaining() > 0) { - packets[ord] = createSegment(startSequence++, ord++, data); - } - return packets; - } - - private static final Fragmented createSegment(int startSequence, int ord, ByteBuffer packet) { - int header = (ord == 0) ? 8 : 4; - ByteBuffer data = ByteBuffer.allocate(Math.min(packet.remaining()+header, 493)); - - addNetShort(data, 0x0D); - addNetShort(data, startSequence); - if (ord == 0) - addNetInt(data, packet.remaining()); - - int len = data.remaining(); - data.put(packet.array(), packet.position(), len); - packet.position(packet.position() + len); - - byte [] pData = new byte[data.array().length-header]; - System.arraycopy(data.array(), header, pData, 0, pData.length); - Fragmented f = new Fragmented(data, startSequence); - f.packet = ByteBuffer.wrap(pData); - return f; - } - -} diff --git a/src/network/packets/soe/MultiPacket.java b/src/network/packets/soe/MultiPacket.java deleted file mode 100644 index dd457acb1..000000000 --- a/src/network/packets/soe/MultiPacket.java +++ /dev/null @@ -1,140 +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 network.packets.soe; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; - -import network.packets.Packet; - -public class MultiPacket extends Packet { - - private List content = new ArrayList(); - - public MultiPacket() { - this(new ArrayList()); - } - - public MultiPacket(ByteBuffer data) { - this(new ArrayList()); - decode(data); - } - - public MultiPacket(List packets) { - this.content = packets; - } - - public void decode(ByteBuffer data) { - data.position(2); - int length = data.array().length; - int pLength = 0; - for (int i = 2; i < length; i += pLength) { - if (data.remaining() < 1) - return; - pLength = getByte(data) & 0xFF; - if (pLength == 255) { - if (data.remaining() < 2) - return; - pLength = data.order(ByteOrder.LITTLE_ENDIAN).getShort(); - } - if (pLength > data.remaining()) { - data.position(data.position()-1); - return; - } - byte [] pData = new byte[pLength]; - data.get(pData); - Packet p = new Packet(); - p.decode(ByteBuffer.wrap(pData)); - content.add(p); - } - } - - public ByteBuffer encode() { - int length = 2; - for (Packet packet : content) { - int pLength = packet.encode().array().length; - if (pLength >= 255) { - length += 3; - } else { - length += 1; - } - length += pLength; - } - ByteBuffer data = ByteBuffer.allocate(length); - data.order(ByteOrder.BIG_ENDIAN).putShort((short)3); - for (Packet packet : content) { - byte [] pData = packet.encode().array(); - if (pData.length >= 255) { - data.put((byte)255); - addShort(data, (short)pData.length); - } else { - data.put((byte)pData.length); - } - data.put(pData); - } - return data; - } - - public int getLength() { - int length = 2; - for (Packet packet : content) { - int pLength = getPacketLength(packet); - if (pLength >= 255) { - length += 3; - } else { - length += 1; - } - length += pLength; - } - return length; - } - - private int getPacketLength(Packet packet) { - if (packet instanceof DataChannelA) - return ((DataChannelA) packet).getLength(); - else if (packet instanceof Acknowledge || packet instanceof OutOfOrder) - return 4; - else if (packet instanceof Disconnect) - return 8; - return packet.encode().array().length; - } - - public void addPacket(Packet packet) { - content.add(packet); - } - - public void clearPackets() { - content.clear(); - } - - public List getPackets() { - return content; - } -} diff --git a/src/network/packets/soe/ServerNetworkStatusUpdate.java b/src/network/packets/soe/ServerNetworkStatusUpdate.java deleted file mode 100644 index 026fc4bea..000000000 --- a/src/network/packets/soe/ServerNetworkStatusUpdate.java +++ /dev/null @@ -1,96 +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 network.packets.soe; - -import java.nio.ByteBuffer; - -import network.packets.Packet; - - -public class ServerNetworkStatusUpdate extends Packet { - - private short clientTickCount = 0; - private int serverSyncStampLong = 0; - private long clientPacketsSent = 0; - private long clientPacketsRecv = 0; - private long serverPacketsSent = 0; - private long serverPacketsRecv = 0; - - public ServerNetworkStatusUpdate() { - - } - - public ServerNetworkStatusUpdate(ByteBuffer data) { - decode(data); - } - - public ServerNetworkStatusUpdate(int clientTickCount, long clientSent, long clientRecv, long serverSent, long serverRecv) { - this.clientTickCount = (short) clientTickCount; - this.serverSyncStampLong = 0; - this.clientPacketsSent = clientSent; - this.clientPacketsRecv = clientRecv; - this.serverPacketsSent = serverSent; - this.serverPacketsRecv = serverRecv; - } - - public void decode(ByteBuffer data) { - data.position(2); - clientTickCount = getNetShort(data); - serverSyncStampLong = getNetInt(data); - clientPacketsSent = getNetLong(data); - clientPacketsRecv = getNetLong(data); - serverPacketsSent = getNetLong(data); - serverPacketsRecv = getNetLong(data); - } - - public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(40); - addNetShort(data, 8); - addNetShort(data, clientTickCount); - addNetInt( data, serverSyncStampLong); - addNetLong( data, clientPacketsSent); - addNetLong( data, clientPacketsRecv); - addNetLong( data, serverPacketsSent); - addNetLong( data, serverPacketsRecv); - return data; - } - - public short getClientTickCount() { return clientTickCount; } - public int getServerSyncStampLong() { return serverSyncStampLong; } - public long getClientPacketsSent() { return clientPacketsSent; } - public long getClientPacketsRecv() { return clientPacketsRecv; } - public long getServerPacketsSent() { return serverPacketsSent; } - public long getServerPacketsRecv() { return serverPacketsRecv; } - - public void setClientTickCount(short tick) { this.clientTickCount = tick; } - public void setServerSyncStampLong(int sync) { this.serverSyncStampLong = sync; } - public void setClientPacketsSent(int sent) { this.clientPacketsSent = sent; } - public void setClientPacketsRecv(int recv) { this.clientPacketsRecv = recv; } - public void setServerPacketsSent(int sent) { this.serverPacketsSent = sent; } - public void setServerPacketsRecv(int recv) { this.serverPacketsRecv = recv; } -} diff --git a/src/network/packets/soe/SessionRequest.java b/src/network/packets/soe/SessionRequest.java deleted file mode 100644 index d4b0feba9..000000000 --- a/src/network/packets/soe/SessionRequest.java +++ /dev/null @@ -1,76 +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 network.packets.soe; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import network.packets.Packet; - - -public class SessionRequest extends Packet { - - private int crcLength; - private int connectionID; - private int udpSize; - - public SessionRequest() { - - } - - public SessionRequest(ByteBuffer data) { - decode(data); - } - - public SessionRequest(int crcLength, int connectionID, int udpSize) { - this.crcLength = crcLength; - this.connectionID = connectionID; - this.udpSize = udpSize; - } - - public void decode(ByteBuffer packet) { - super.decode(packet); - packet.position(2); - crcLength = getNetInt(packet); - connectionID = getNetInt(packet); - udpSize = getNetInt(packet); - } - - public ByteBuffer encode() { - ByteBuffer bb = ByteBuffer.allocate(14).order(ByteOrder.BIG_ENDIAN); - addNetShort(bb, 1); - addNetInt(bb, crcLength); - addNetInt(bb, connectionID); - addNetInt(bb, udpSize); - return bb; - } - - public int getCrcLength() { return crcLength; } - public int getConnectionID() { return connectionID; } - public int getUdpSize() { return udpSize; } -} diff --git a/src/network/packets/soe/SessionResponse.java b/src/network/packets/soe/SessionResponse.java deleted file mode 100644 index ba2c6c23f..000000000 --- a/src/network/packets/soe/SessionResponse.java +++ /dev/null @@ -1,103 +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 network.packets.soe; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import network.packets.Packet; - - -public class SessionResponse extends Packet { - - private int connectionID; - private int crcSeed; - private byte crcLength; - private byte encryptionFlag; - private byte xorLength; - private int udpSize; - - public SessionResponse() { - - } - - public SessionResponse(ByteBuffer data) { - decode(data); - } - - public SessionResponse(int connectionID, - int crcSeed, - byte crcLength, - byte encryptionFlag, - byte xorLength, - int udpSize) { - this.connectionID = connectionID; - this.crcSeed = crcSeed; - this.crcLength = crcLength; - this.encryptionFlag = encryptionFlag; - this.xorLength = xorLength; - this.udpSize = udpSize; - } - - public void decode(ByteBuffer data) { - super.decode(data); - data.position(2); - connectionID = getNetInt(data); - crcSeed = getNetInt(data); - crcLength = data.get(); - encryptionFlag = data.get(); - xorLength = data.get(); - udpSize = getNetInt(data); - } - - public ByteBuffer encode() { - ByteBuffer bb = ByteBuffer.allocate(17).order(ByteOrder.BIG_ENDIAN); - addNetShort(bb, 2); - addNetInt( bb, connectionID); - addNetInt( bb, crcSeed); - addByte( bb, crcLength); - addByte( bb, encryptionFlag); - addByte( bb, xorLength); - addNetInt( bb, udpSize); - return bb; - } - - public int getConnectionID() { return connectionID; } - public int getCrcSeed() { return crcSeed; } - public byte getCrcLength() { return crcLength; } - public short getEncryptionFlag() { return encryptionFlag; } - public byte getXorLength() { return xorLength; } - public int getUdpSize() { return udpSize; } - - public void setConnectionID(int id) { this.connectionID = id; } - public void setCrcSeed(int crc) { this.crcSeed = crc; } - public void setCrcLength(int length) { this.crcLength = (byte) length; } - public void setEncryptionFlag(short flag) { this.encryptionFlag = (byte) flag; } - public void setXorLength(byte xorLength) { this.xorLength = xorLength; } - public void setUdpSize(int size) { this.udpSize = size; } -} diff --git a/src/resources/commands/callbacks/QaToolCmdCallback.java b/src/resources/commands/callbacks/QaToolCmdCallback.java index 7729d9162..c676089dc 100644 --- a/src/resources/commands/callbacks/QaToolCmdCallback.java +++ b/src/resources/commands/callbacks/QaToolCmdCallback.java @@ -34,6 +34,7 @@ import intents.player.DeleteCharacterIntent; import resources.Location; import resources.Terrain; import resources.commands.ICmdCallback; +import resources.network.DisconnectReason; import resources.objects.SWGObject; import resources.objects.building.BuildingObject; import resources.objects.cell.CellObject; @@ -56,8 +57,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; -import network.packets.soe.Disconnect.DisconnectReason; - /** * Created by Waverunner on 8/19/2015 */ @@ -142,7 +141,7 @@ public class QaToolCmdCallback implements ICmdCallback { new DeleteCharacterIntent((CreatureObject) target).broadcast(); Player owner = target.getOwner(); if (owner != null) - new CloseConnectionIntent(owner.getConnectionId(), owner.getNetworkId(), DisconnectReason.APPLICATION).broadcast(); + new CloseConnectionIntent(owner.getNetworkId(), DisconnectReason.APPLICATION).broadcast(); return; } Log.i("QA", "[%s] Requested deletion of object: %s", player.getUsername(), target); diff --git a/src/resources/control/Service.java b/src/resources/control/Service.java index a7bffc096..ac4edc0ff 100644 --- a/src/resources/control/Service.java +++ b/src/resources/control/Service.java @@ -133,16 +133,7 @@ public abstract class Service implements IntentReceiver { * @param packets the packet(s) to send */ public void sendPacket(final long networkId, final Packet ... packets) { - outboundPacketService.sendPacket(networkId, packets); - } - - /** - * Sends all packets that were stored in the buffer via sendPacket() - * @return the number of packets sent (includes SWG packets inside - * multi/data packets) - */ - public int flushPackets() { - return outboundPacketService.flushPackets(); + outboundPacketService.send(networkId, packets); } /** diff --git a/src/resources/network/DisconnectReason.java b/src/resources/network/DisconnectReason.java new file mode 100644 index 000000000..dbf909e4b --- /dev/null +++ b/src/resources/network/DisconnectReason.java @@ -0,0 +1,9 @@ +package resources.network; + +public enum DisconnectReason { + TIMEOUT, + OTHER_SIDE_TERMINATED, + APPLICATION, + NEW_CONNECTION_ATTEMPT, + CONNECTION_REFUSED +} diff --git a/src/resources/network/TCPServer.java b/src/resources/network/TCPServer.java new file mode 100644 index 000000000..8f08b4101 --- /dev/null +++ b/src/resources/network/TCPServer.java @@ -0,0 +1,247 @@ +/*********************************************************************************** +* 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 resources.network; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + + +public class TCPServer { + + private final Map sockets; + private final InetAddress addr; + private final int port; + private final int bufferSize; + private ServerSocketChannel channel; + private TCPCallback callback; + private TCPListener listener; + + public TCPServer(InetAddress addr, int port, int bufferSize) { + this.sockets = new HashMap<>(); + this.addr = addr; + this.port = port; + this.bufferSize = bufferSize; + this.channel = null; + listener = new TCPListener(); + } + + public void bind() throws IOException { + channel = ServerSocketChannel.open(); + channel.socket().bind(new InetSocketAddress(addr, port)); + channel.configureBlocking(false); + listener.start(); + } + + public SocketChannel connectTo(InetAddress addr, int port) throws IOException { + return connectTo(new InetSocketAddress(addr, port)); + } + + public SocketChannel connectTo(InetSocketAddress sock) throws IOException { + synchronized (sockets) { + SocketChannel sc = sockets.get(sock); + if (sc == null || (!sc.isConnected() && !sc.isConnectionPending())) { + sc = SocketChannel.open(sock); + sc.configureBlocking(false); + sockets.put(sock, sc); + } + return sc; + } + } + + public boolean disconnect(SocketAddress sock) { + synchronized (sockets) { + SocketChannel sc = sockets.get(sock); + sockets.remove(sock); + try { + sc.close(); + if (callback != null) + callback.onConnectionDisconnect(sc.socket()); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + } + + private boolean disconnect(SocketChannel sc) { + try { + return disconnect(sc.getRemoteAddress()); + } catch (IOException e) { + return false; + } + } + + public boolean close() { + listener.stop(); + try { + if (channel != null) + channel.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + public boolean send(InetSocketAddress sock, byte [] data) { + SocketChannel sc = sockets.get(sock); + try { + if (sc != null && sc.isConnected()) { + sc.write(ByteBuffer.wrap(data)); + return true; + } + } catch (IOException e) { + e.printStackTrace(); + disconnect(sc); + } + return false; + } + + public void setCallback(TCPCallback callback) { + this.callback = callback; + } + + private void accept() { + try { + SocketChannel sc = channel.accept(); + if (sc == null) + return; + sc.configureBlocking(false); + sockets.put(sc.getRemoteAddress(), sc); + if (callback != null) + callback.onIncomingConnection(sc.socket()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void read(SocketChannel s) { + ByteBuffer data = ByteBuffer.allocate(bufferSize); + try { + int n = s.read(data); + if (n == -1) { + disconnect(s); + return; + } + if (n == 0) + return; + data.flip(); + ByteBuffer smaller = ByteBuffer.allocate(n); + smaller.put(data.array(), 0, n); + if (callback != null) + callback.onIncomingData(s.socket(), smaller.array()); + } catch (IOException e) { + e.printStackTrace(); + disconnect(s); + } + } + + public interface TCPCallback { + void onIncomingConnection(Socket s); + void onConnectionDisconnect(Socket s); + void onIncomingData(Socket s, byte [] data); + } + + private class TCPListener implements Runnable { + + private Thread thread; + private boolean running; + + public TCPListener() { + running = false; + thread = null; + } + + public void start() { + running = true; + thread = new Thread(this); + thread.start(); + } + + public void stop() { + running = false; + if (thread != null) + thread.interrupt(); + thread = null; + } + + public void run() { + while (running) { + try (Selector selector = setupSelector()) { + if (selector.select() > 0) + processSelectionKeys(selector.selectedKeys()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private Selector setupSelector() throws IOException { + Selector selector = Selector.open(); + channel.register(selector, SelectionKey.OP_ACCEPT); + synchronized (sockets) { + for (SocketChannel sc : sockets.values()) { + sc.configureBlocking(false); + sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT); + } + } + return selector; + } + + private void processSelectionKeys(Set keys) { + for (SelectionKey key : keys) { + if (key.isAcceptable()) { + accept(); + } else if (key.isReadable()) { + SelectableChannel selectable = key.channel(); + if (selectable instanceof SocketChannel) + read((SocketChannel) selectable); + } else if (key.isConnectable()) { + SelectableChannel selectable = key.channel(); + if (!selectable.isOpen() && selectable instanceof SocketChannel) { + disconnect((SocketChannel) selectable); + } + } + } + } + } + +} diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index d71419b0d..1f4ac6fde 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -57,8 +57,9 @@ public class CreatureObject extends TangibleObject { private static final long serialVersionUID = 1L; - private transient GroupInviterData inviterData = new GroupInviterData(0, null, "", 0); - private transient long lastReserveOperation = 0; + private transient GroupInviterData inviterData = new GroupInviterData(0, null, "", 0); + private transient long lastReserveOperation = 0; + private transient long groupId = 0; private Posture posture = Posture.UPRIGHT; private Race race = Race.HUMAN; @@ -95,7 +96,6 @@ public class CreatureObject extends TangibleObject { private boolean performing = false; private boolean shownOnRadar = true; private boolean beast = false; - private long groupId = 0; private byte factionRank = 0; private long ownerId = 0; private int battleFatigue = 0; @@ -127,7 +127,10 @@ public class CreatureObject extends TangibleObject { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); + // Transient Variables inviterData = new GroupInviterData(0, null, "", 0); + lastReserveOperation = 0; + groupId = 0; } public void removeEquipment(SWGObject obj) { diff --git a/src/services/CoreManager.java b/src/services/CoreManager.java index 90f4dc272..543f9526b 100644 --- a/src/services/CoreManager.java +++ b/src/services/CoreManager.java @@ -38,12 +38,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import network.packets.Packet; -import network.packets.soe.DataChannelA; -import network.packets.soe.MultiPacket; -import network.packets.swg.SWGPacket; -import network.packets.swg.zone.baselines.Baseline; -import network.packets.swg.zone.object_controller.ObjectController; import intents.network.InboundPacketIntent; import intents.network.OutboundPacketIntent; import intents.server.ServerManagementIntent; @@ -127,15 +121,7 @@ public class CoreManager extends Manager { @Override public void onIntentReceived(Intent i) { if (packetDebug) { - if (i instanceof InboundPacketIntent) { - InboundPacketIntent in = (InboundPacketIntent) i; - packetOutput.println("IN " + in.getNetworkId() + ":" + in.getServerType()); - outputPacket(1, in.getPacket()); - } else if (i instanceof OutboundPacketIntent) { - OutboundPacketIntent out = (OutboundPacketIntent) i; - packetOutput.println("OUT " + out.getNetworkId()); - outputPacket(1, out.getPacket()); - } + } if (i instanceof ServerManagementIntent) handleServerManagementIntent((ServerManagementIntent) i); @@ -180,52 +166,6 @@ public class CoreManager extends Manager { return galaxy.getStatus(); } - private void outputPacket(int indent, Packet packet) { - if (packet instanceof DataChannelA) { - for (SWGPacket p : ((DataChannelA) packet).getPackets()) { - for (int i = 0; i < indent; i++) - packetOutput.print(" "); - outputSWG(p); - } - } else if (packet instanceof MultiPacket) { - for (Packet p : ((MultiPacket) packet).getPackets()) { - for (int i = 0; i < indent; i++) - packetOutput.print(" "); - if (p instanceof SWGPacket) - outputSWG((SWGPacket) p); - if (p instanceof DataChannelA) - outputPacket(indent+1, p); - } - } else if (packet instanceof SWGPacket) { - for (int i = 0; i < indent; i++) - packetOutput.print(" "); - outputSWG((SWGPacket) packet); - } else { - for (int i = 0; i < indent; i++) - packetOutput.print(" "); - packetOutput.println(packet.getClass().getSimpleName()); - } - } - - private void outputSWG(SWGPacket p) { - if (p instanceof Baseline) - outputBaseline((Baseline) p); - else if (p instanceof ObjectController) - outputObjectController((ObjectController) p); - else - packetOutput.println(p.getClass().getSimpleName()); - } - - private void outputBaseline(Baseline b) { - packetOutput.println("Baseline [" + b.getId() + "] " + b.getType() + " " + b.getNum()); - } - - private void outputObjectController(ObjectController cont) { - int crc = cont.getControllerCrc(); - long id = cont.getObjectId(); - packetOutput.println("ObjectController [" + id + "] 0x" + Integer.toHexString(crc)); - } - /** * Returns the time in milliseconds since the server started initialization * @return the core time represented as a double diff --git a/src/services/galaxy/ConnectionService.java b/src/services/galaxy/ConnectionService.java index b65048a0a..4ad890635 100644 --- a/src/services/galaxy/ConnectionService.java +++ b/src/services/galaxy/ConnectionService.java @@ -44,11 +44,10 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import main.ProjectSWG; -import network.packets.soe.Disconnect; -import network.packets.soe.Disconnect.DisconnectReason; import network.packets.swg.zone.HeartBeat; import resources.control.Intent; import resources.control.Service; +import resources.network.DisconnectReason; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import resources.player.Player; @@ -61,13 +60,11 @@ import utilities.ThreadUtilities; public class ConnectionService extends Service { - private static final double LD_THRESHOLD = TimeUnit.MINUTES.toMillis(3); // Time since last packet - private static final double DISAPPEAR_THRESHOLD = TimeUnit.MINUTES.toMillis(2); // Time after the LD + private static final double DISAPPEAR_THRESHOLD = TimeUnit.SECONDS.toMillis(30); // Time after the LD private static final String INCREMENT_POPULATION_SQL = "UPDATE galaxies SET population = population + 1 WHERE id = ?"; private static final String DECREMENT_POPULATION_SQL = "UPDATE galaxies SET population = population - 1 WHERE id = ?"; private final ScheduledExecutorService updateService; - private final Runnable updateRunnable; private final Runnable disappearRunnable; private final Set disappearPlayers; private final Set zonedInPlayers; @@ -79,21 +76,6 @@ public class ConnectionService extends Service { updateService = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("conn-update-service")); zonedInPlayers = new LinkedHashSet(); disappearPlayers = new HashSet(); - updateRunnable = new Runnable() { - public void run() { - synchronized (zonedInPlayers) { - Iterator i = zonedInPlayers.iterator(); - while (i.hasNext()) { - Player p = i.next(); - if (p.getTimeSinceLastPacket() > LD_THRESHOLD) { - i.remove(); - logOut(p); - disconnect(p, DisconnectReason.TIMEOUT); - } - } - } - } - }; disappearRunnable = new Runnable() { public void run() { synchronized (disappearPlayers) { @@ -102,7 +84,7 @@ public class ConnectionService extends Service { DisappearPlayer p = iter.next(); if ((System.nanoTime()-p.getTime())/1E6 >= DISAPPEAR_THRESHOLD) { DebugUtilities.printPlayerCharacterDebug(ConnectionService.this, p.getPlayer(), "Disappearing"); - disappear(p.getPlayer(), DisconnectReason.TIMEOUT); + disappear(p.getPlayer(), false, DisconnectReason.APPLICATION); iter.remove(); } } @@ -125,7 +107,6 @@ public class ConnectionService extends Service { @Override public boolean start() { - updateService.scheduleAtFixedRate(updateRunnable, 10, 10, TimeUnit.SECONDS); return super.start(); } @@ -196,16 +177,6 @@ public class ConnectionService extends Service { Player p = gpi.getPlayerManager().getPlayerFromNetworkId(gpi.getNetworkId()); if (p != null) p.sendPacket(gpi.getPacket()); - } else if (gpi.getPacket() instanceof Disconnect) { - Player p = gpi.getPlayerManager().getPlayerFromNetworkId(gpi.getNetworkId()); - if (p != null) { - if (p.getPlayerState() != PlayerState.DISCONNECTED) { - logOut(p); - disconnect(p, DisconnectReason.TIMEOUT); - } else { - disconnect(p, DisconnectReason.OTHER_SIDE_TERMINATED); - } - } } } @@ -213,7 +184,7 @@ public class ConnectionService extends Service { logOut(fdi.getPlayer(), !fdi.getDisappearImmediately()); disconnect(fdi.getPlayer(), fdi.getDisconnectReason()); if (fdi.getDisappearImmediately()) - disappear(fdi.getPlayer(), fdi.getDisconnectReason()); + disappear(fdi.getPlayer(), fdi.getDisappearImmediately(), fdi.getDisconnectReason()); } private void onZonePlayerSwapIntent(ZonePlayerSwapIntent zpsi) { @@ -225,7 +196,7 @@ public class ConnectionService extends Service { Log.i("ConnectionService", "Logged out %s with character %s", before.getUsername(), before.getCharacterName()); new PlayerEventIntent(before, before.getGalaxyName(), PlayerEvent.PE_LOGGED_OUT).broadcast(); Log.i("ConnectionService", "Disconnected %s with character %s and reason: %s", before.getUsername(), before.getCharacterName(), DisconnectReason.NEW_CONNECTION_ATTEMPT); - new CloseConnectionIntent(before.getConnectionId(), before.getNetworkId(), DisconnectReason.NEW_CONNECTION_ATTEMPT).broadcast(); + new CloseConnectionIntent(before.getNetworkId(), DisconnectReason.NEW_CONNECTION_ATTEMPT).broadcast(); before.setPlayerState(PlayerState.DISCONNECTED); creature.setOwner(after); } @@ -279,10 +250,6 @@ public class ConnectionService extends Service { return player; } - private void logOut(Player p) { - logOut(p, true); - } - private void logOut(Player p, boolean addToDisappear) { System.out.println("[" + p.getUsername() +"] Logged out " + p.getCharacterName()); Log.i("ConnectionService", "Logged out %s with character %s", p.getUsername(), p.getCharacterName()); @@ -298,25 +265,21 @@ public class ConnectionService extends Service { } } - private void disappear(Player p, DisconnectReason reason) { + private void disappear(Player p, boolean newConnection, DisconnectReason reason) { System.out.println("[" + p.getUsername() +"] " + p.getCharacterName() + " disappeared"); - Log.i("ConnectionService", "Disappeared %s with character %s", p.getUsername(), p.getCharacterName()); + Log.i("ConnectionService", "Disappeared %s with character %s with reason %s", p.getUsername(), p.getCharacterName(), reason); - switch(reason) { - case NEW_CONNECTION_ATTEMPT: // The player is attempting to re-zone - removeFromDisappear(p); - break; - default: - removeFromLists(p); - break; - } + if (newConnection) // Attempting to re-zone + removeFromDisappear(p); + else + removeFromLists(p); p.setPlayerState(PlayerState.DISCONNECTED); new PlayerEventIntent(p, PlayerEvent.PE_DISAPPEAR).broadcast(); } private void disconnect(Player player, DisconnectReason reason) { - Log.i("ConnectionService", "Disconnected %s with character %s and reason: %s", player.getUsername(), player.getCharacterName(), reason); - new CloseConnectionIntent(player.getConnectionId(), player.getNetworkId(), reason).broadcast(); + Log.i("ConnectionService", "Disconnected %s with character %s with reason %s", player.getUsername(), player.getCharacterName(), reason); + new CloseConnectionIntent(player.getNetworkId(), reason).broadcast(); } private void updatePlayTime(Player p) { diff --git a/src/services/galaxy/GameManager.java b/src/services/galaxy/GameManager.java index 05109b13d..a863e3d04 100644 --- a/src/services/galaxy/GameManager.java +++ b/src/services/galaxy/GameManager.java @@ -32,9 +32,11 @@ import services.commands.CommandService; import services.faction.FactionService; import services.galaxy.terminals.TerminalService; import services.sui.SuiService; +import services.trader.TraderManager; public class GameManager extends Manager { + private final TraderManager traderManager; private final CommandService commandService; private final ConnectionService connectionService; private final SuiService suiService; @@ -44,6 +46,7 @@ public class GameManager extends Manager { // private final GroupService groupService; public GameManager() { + traderManager = new TraderManager(); commandService = new CommandService(); connectionService = new ConnectionService(); suiService = new SuiService(); @@ -52,6 +55,7 @@ public class GameManager extends Manager { factionService = new FactionService(); // groupService = new GroupService(); + addChildService(traderManager); addChildService(commandService); addChildService(connectionService); addChildService(suiService); diff --git a/src/services/group/GroupService.java b/src/services/group/GroupService.java index c288897a2..0e94283ef 100644 --- a/src/services/group/GroupService.java +++ b/src/services/group/GroupService.java @@ -44,7 +44,6 @@ import resources.encodables.StringId; import resources.objects.creature.CreatureObject; import resources.objects.group.GroupObject; import resources.player.Player; -import resources.player.PlayerEvent; import resources.server_info.Log; import services.objects.ObjectCreator; import services.player.PlayerManager; diff --git a/src/services/network/NetworkClientManager.java b/src/services/network/NetworkClientManager.java index 3b2eea426..54beb4e10 100644 --- a/src/services/network/NetworkClientManager.java +++ b/src/services/network/NetworkClientManager.java @@ -28,327 +28,236 @@ package services.network; import intents.network.CloseConnectionIntent; -import intents.network.InboundPacketIntent; +import intents.network.ConnectionClosedIntent; +import intents.network.ConnectionOpenedIntent; import intents.network.OutboundPacketIntent; +import java.io.IOException; import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.util.ArrayList; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; import java.util.HashMap; +import java.util.Hashtable; import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import network.NetworkClient; -import network.PacketReceiver; import network.PacketSender; import network.packets.Packet; -import network.packets.soe.Disconnect; -import network.packets.soe.SessionRequest; -import network.packets.soe.SessionResponse; -import network.packets.soe.Disconnect.DisconnectReason; import resources.config.ConfigFile; import resources.control.Intent; import resources.control.Manager; -import resources.network.ServerType; -import resources.network.UDPServer.UDPPacket; +import resources.network.DisconnectReason; +import resources.network.TCPServer; +import resources.network.TCPServer.TCPCallback; +import resources.server_info.Config; +import resources.server_info.Log; import utilities.ThreadUtilities; -public class NetworkClientManager extends Manager implements PacketReceiver { +public class NetworkClientManager extends Manager implements TCPCallback, PacketSender { - private final Map > clients; - private final Map networkClients; + private final Map sockets; + private final Map clients; private final Queue receivedPackets; - private final ScheduledExecutorService packetResender; private final ExecutorService packetProcessor; - private final Random crcGenerator; - private final PacketSender packetSender; private final Runnable processPacketRunnable; - private final Runnable packetResendRunnable; - private long networkId; + private final AtomicLong networkIdCounter; + private final TCPServer tcpServer; - public NetworkClientManager(PacketSender packetSender) { - this.packetSender = packetSender; - clients = new HashMap>(); - networkClients = new HashMap(); + public NetworkClientManager() { + sockets = new HashMap(); + clients = new Hashtable(); receivedPackets = new LinkedList<>(); - packetResender = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("packet-resender")); - packetProcessor = Executors.newCachedThreadPool(ThreadUtilities.newThreadFactory("packet-processor-%d")); - crcGenerator = new Random(); + networkIdCounter = new AtomicLong(1); + packetProcessor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), ThreadUtilities.newThreadFactory("packet-processor-%d")); processPacketRunnable = new Runnable() { public void run() { synchronized (receivedPackets) { ReceivedPacket recv = receivedPackets.poll(); if (recv == null) return; - handlePacket(recv.getType(), recv.getPacket()); + handlePacket(recv); } } }; - packetResendRunnable = new Runnable() { - public void run() { - resendOldUnacknowledged(); - } - }; - networkId = 0; + tcpServer = new TCPServer(getBindAddr(), getBindPort(), getBufferSize()); - registerForIntent(InboundPacketIntent.TYPE); registerForIntent(OutboundPacketIntent.TYPE); registerForIntent(CloseConnectionIntent.TYPE); } @Override - public boolean initialize() { - packetResender.scheduleAtFixedRate(packetResendRunnable, 0, 200, TimeUnit.MILLISECONDS); - return super.initialize(); + public boolean start() { + try { + tcpServer.bind(); + tcpServer.setCallback(this); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return super.start(); } @Override public boolean stop() { - for (NetworkClient client : networkClients.values()) { - client.sendPacket(new Disconnect(client.getConnectionId(), DisconnectReason.APPLICATION)); - } + tcpServer.close(); return super.stop(); } @Override public boolean terminate() { packetProcessor.shutdownNow(); - packetResender.shutdownNow(); boolean success = true; try { success = packetProcessor.awaitTermination(5, TimeUnit.SECONDS); - success = packetResender.awaitTermination(5, TimeUnit.SECONDS) && success; } catch (InterruptedException e) { e.printStackTrace(); } return super.terminate() && success; } - @Override - public void receivePacket(ServerType type, UDPPacket packet) { - synchronized (receivedPackets) { - receivedPackets.add(new ReceivedPacket(type, packet)); - } - packetProcessor.submit(processPacketRunnable); - } - @Override public void onIntentReceived(Intent i) { if (i instanceof OutboundPacketIntent) { Packet p = ((OutboundPacketIntent)i).getPacket(); if (p != null) handleOutboundPacket(((OutboundPacketIntent) i).getNetworkId(), p); - } else if (i instanceof InboundPacketIntent) { - Packet p = ((InboundPacketIntent) i).getPacket(); - if (p != null) { - if (p instanceof SessionRequest) - initializeSession((SessionRequest) p); - if (p instanceof Disconnect) - disconnectSession(((InboundPacketIntent) i).getNetworkId(), (Disconnect) p); - } } else if (i instanceof CloseConnectionIntent) { - int connId = ((CloseConnectionIntent)i).getConnectionId(); long netId = ((CloseConnectionIntent)i).getNetworkId(); - DisconnectReason reason = ((CloseConnectionIntent)i).getReason(); - removeClient(netId); - sendPacket(netId, new Disconnect(connId, reason)); + deleteSession(netId); } } - private void initializeSession(SessionRequest req) { - NetworkClient client = getClient(req.getAddress(), req.getPort()); - if (client == null) { - return; + @Override + public void onIncomingConnection(Socket s) { + SocketAddress addr = s.getRemoteSocketAddress(); + if (addr instanceof InetSocketAddress) + createSession(networkIdCounter.incrementAndGet(), (InetSocketAddress) addr); + else + Log.e(this, "Incoming connection has socket address of instance: %s", addr.getClass().getSimpleName()); + } + + @Override + public void onConnectionDisconnect(Socket s) { + SocketAddress addr = s.getRemoteSocketAddress(); + if (addr instanceof InetSocketAddress) + onSessionDisconnect((InetSocketAddress) addr); + else + Log.e(this, "Connection Disconnected. Has socket address of instance: %s", addr.getClass().getSimpleName()); + } + + @Override + public void onIncomingData(Socket s, byte [] data) { + synchronized (receivedPackets) { + SocketAddress addr = s.getRemoteSocketAddress(); + if (addr instanceof InetSocketAddress) + receivedPackets.add(new ReceivedPacket((InetSocketAddress) addr, data)); + else + Log.e(this, "Incoming data has socket address of instance: %s", addr.getClass().getSimpleName()); + packetProcessor.submit(processPacketRunnable); } - SessionResponse outPacket = new SessionResponse(); - outPacket.setConnectionID(client.getConnectionId()); - outPacket.setCrcSeed(client.getCrc()); - outPacket.setCrcLength(2); - outPacket.setEncryptionFlag((short) 1); - outPacket.setXorLength((byte) 4); - outPacket.setUdpSize(getConfig(ConfigFile.NETWORK).getInt("MAX-PACKET-SIZE", 496)); - sendPacket(client.getNetworkId(), outPacket); } - private void disconnectSession(long networkId, Disconnect d) { - disconnectSession(networkId, d.getAddress(), d.getPort(), d.getReason()); + @Override + public void sendPacket(InetSocketAddress sock, byte[] data) { + tcpServer.send(sock, data); } - private void disconnectSession(long networkId, InetAddress addr, int port, DisconnectReason reason) { - removeClient(networkId, addr, port); + private InetAddress getBindAddr() { + Config c = getConfig(ConfigFile.NETWORK); + String ip = c.getString("BIND-ADDR", "::1"); + try { + return InetAddress.getByName(ip); + } catch (UnknownHostException e) { + System.err.println("NetworkListenerService: Unknown host for IP: " + ip); + } + return null; } - private int generateCrc() { - int crc = 0; - do { - crc = crcGenerator.nextInt(); - } while (crc == 0); - return crc; + private int getBindPort() { + return getConfig(ConfigFile.NETWORK).getInt("BIND-PORT", 44463); } - private void resendOldUnacknowledged() { + private int getBufferSize() { + return getConfig(ConfigFile.NETWORK).getInt("BUFFER-SIZE", 1024); + } + + private void createSession(long networkId, InetSocketAddress address) { synchronized (clients) { - for (NetworkClient client : networkClients.values()) { - client.resendOldUnacknowledged(); - flushPackets(); + sockets.put(address, networkId); + clients.put(networkId, new NetworkClient(address, networkId, this)); + new ConnectionOpenedIntent(networkId).broadcast(); + System.out.println("Created Session: " + networkId + " / " + address); + } + } + + private void onSessionDisconnect(InetSocketAddress address) { + synchronized (clients) { + Long networkId = sockets.get(address); + if (networkId != null) { + deleteSession(networkId); + new ConnectionClosedIntent(networkId, DisconnectReason.OTHER_SIDE_TERMINATED).broadcast(); } } } + private void deleteSession(long networkId) { + synchronized (clients) { + NetworkClient client = clients.get(networkId); + if (client == null) + return; + clients.remove(networkId); + sockets.remove(client.getAddress()); + } + } + private void handleOutboundPacket(long networkId, Packet p) { synchronized (clients) { - NetworkClient client = networkClients.get(networkId); + NetworkClient client = clients.get(networkId); if (client != null) client.sendPacket(p); + else + Log.w(this, "NetworkClient does not exist for ID: %d", networkId); } } - private void handlePacket(ServerType type, UDPPacket p) { - InetAddress addr = p.getAddress(); - if (addr == null) - return; - if (p.getData().length == 14 && p.getData()[0] == 0 && p.getData()[1] == 1) { - handleSessionRequest(type, p); - return; - } - if (type == ServerType.LOGIN || type == ServerType.ZONE) - handlePacket(p.getAddress(), p.getPort(), type, p.getData()); - } - - private void handlePacket(InetAddress addr, int port, ServerType type, byte [] data) { + private void handlePacket(ReceivedPacket packet) { synchronized (clients) { - List ipList = clients.get(addr); - if (ipList != null) { - synchronized (ipList) { - for (NetworkClient c : ipList) { - if (c.processPacket(type, data)) { - c.updateNetworkInfo(addr, port); - } - } - } + Long netId = sockets.get(packet.getAddress()); + if (netId == null) { + Log.w(this, "Unknown socket address! Address: %s", packet.getAddress()); + return; } + NetworkClient client = clients.get(netId); + if (client != null) + client.process(packet.getData()); + else + Log.w(this, "Unknown connection! Network ID: %d Address: %s", netId, packet.getAddress()); } } - private void handleSessionRequest(ServerType type, UDPPacket p) { - SessionRequest req = new SessionRequest(ByteBuffer.wrap(p.getData())); - req.setAddress(p.getAddress()); - req.setPort(p.getPort()); - NetworkClient client = createSession(type, req); - if (client != null) - client.processPacket(type, p.getData()); - } - - private NetworkClient createSession(ServerType type, SessionRequest req) { - NetworkClient client = getClient(req.getAddress(), req.getPort()); - if (client != null) { - if (client.getConnectionId() == req.getConnectionID()) { - client.resetNetwork(); - client.updateNetworkInfo(req.getAddress(), req.getPort()); - return client; - } else - return null; - } - client = createClient(type, req.getAddress(), req.getPort()); - client.setCrc(generateCrc()); - client.setConnectionId(req.getConnectionID()); - return client; - } - - private NetworkClient createClient(ServerType type, InetAddress addr, int port) { - synchronized (clients) { - NetworkClient client = new NetworkClient(type, addr, port, networkId++, packetSender); - List ipList = clients.get(addr); - if (ipList == null) { - ipList = new ArrayList(); - clients.put(addr, ipList); - } - synchronized (ipList) { - ipList.add(client); - } - networkClients.put(client.getNetworkId(), client); - return client; - } - } - - private NetworkClient getClient(InetAddress addr, int port) { - synchronized (clients) { - List ipList = clients.get(addr); - if (ipList != null) { - synchronized (ipList) { - for (NetworkClient c : ipList) { - if (c.getPort() == port) - return c; - } - } - } - } - return null; - } - - private boolean removeClient(long networkId) { - synchronized (clients) { - NetworkClient client = networkClients.remove(networkId); - if (client != null) { - InetAddress addr = client.getAddress(); - int port = client.getPort(); - List ipList = clients.get(addr); - if (ipList != null) { - synchronized (ipList) { - for (NetworkClient c : ipList) { - if (c.getPort() == port) { - ipList.remove(c); - return true; - } - } - } - } - client.resetNetwork(); - } - } - return false; - } - - private boolean removeClient(long networkId, InetAddress addr, int port) { - synchronized (clients) { - networkClients.remove(networkId); - List ipList = clients.get(addr); - if (ipList != null) { - synchronized (ipList) { - for (NetworkClient c : ipList) { - if (c.getPort() == port) { - ipList.remove(c); - return true; - } - } - } - } - } - return false; - } - private static class ReceivedPacket { - private final ServerType type; - private final UDPPacket packet; + private final InetSocketAddress address; + private final byte [] data; - public ReceivedPacket(ServerType type, UDPPacket packet) { - this.type = type; - this.packet = packet; + public ReceivedPacket(InetSocketAddress address, byte [] data) { + this.address = address; + this.data = data; } - public ServerType getType() { - return type; + public InetSocketAddress getAddress() { + return address; } - public UDPPacket getPacket() { - return packet; + public byte [] getData() { + return data; } } diff --git a/src/services/network/NetworkListenerService.java b/src/services/network/NetworkListenerService.java deleted file mode 100644 index 0a8cd5c87..000000000 --- a/src/services/network/NetworkListenerService.java +++ /dev/null @@ -1,199 +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 services.network; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -import network.PacketReceiver; -import network.PacketSender; -import resources.Galaxy; -import resources.config.ConfigFile; -import resources.control.Service; -import resources.network.ServerType; -import resources.network.UDPServer; -import resources.network.UDPServer.UDPCallback; -import resources.network.UDPServer.UDPPacket; -import resources.server_info.Config; - -public class NetworkListenerService extends Service implements PacketSender { - - private Server login; - private Server zone; - private Server ping; - private Galaxy galaxy; - - public NetworkListenerService(Galaxy galaxy) { - this.galaxy = galaxy; - login = new Server(ServerType.LOGIN); - zone = new Server(ServerType.ZONE); - ping = new Server(ServerType.PING); - } - - public void setPacketReceiver(PacketReceiver packetReceiver) { - login.setPacketReceiver(packetReceiver); - zone.setPacketReceiver(packetReceiver); - ping.setPacketReceiver(packetReceiver); - } - - private InetAddress getBindAddr(Config c, String firstTry, String secondTry) { - String t = firstTry; - try { - if (c.containsKey(firstTry)) - return InetAddress.getByName(c.getString(firstTry, "127.0.0.1")); - t = secondTry; - if (c.containsKey(secondTry)) - return InetAddress.getByName(c.getString(secondTry, "127.0.0.1")); - } catch (UnknownHostException e) { - System.err.println("NetworkListenerService: Unknown host for IP: " + t); - } - return null; - } - - @Override - public boolean initialize() { - Config network = getConfig(ConfigFile.NETWORK); - int packetSize = network.getInt("MAX-PACKET-SIZE", 496); - InetAddress loginBind = getBindAddr(network, "LOGIN-BIND-ADDR", "BIND-ADDR"); - InetAddress zoneBind = getBindAddr(network, "ZONE-BIND-ADDR", "BIND-ADDR"); - InetAddress pingBind = getBindAddr(network, "PING-BIND-ADDR", "BIND-ADDR"); - boolean init = true; - init = login.initialize(loginBind, network.getInt("LOGIN-PORT", 44453), packetSize) && init; - init = zone.initialize(zoneBind, galaxy.getZonePort(), packetSize) && init; - init = ping.initialize(pingBind, galaxy.getPingPort(), packetSize) && init; - return super.initialize() && isOperational() && init; - } - - @Override - public boolean start() { - boolean start = super.start(); - login.start(); - zone.start(); - ping.start(); - System.out.println("NetworkListenerService: Login/Zone/Ping Servers are now online."); - return start; - } - - @Override - public boolean terminate() { - login.terminate(); - zone.terminate(); - ping.terminate(); - return super.terminate(); - } - - @Override - public boolean isOperational() { - if (!login.isRunning()) - return false; - if (!zone.isRunning()) - return false; - if (!ping.isRunning()) - return false; - return true; - } - - @Override - public void sendPacket(ServerType type, UDPPacket packet) { - send(type, packet.getAddress(), packet.getPort(), packet.getData()); - } - - public void send(ServerType type, InetAddress addr, int port, byte [] data) { - switch (type) { - case LOGIN: - login.send(addr, port, data); - break; - case ZONE: - zone.send(addr, port, data); - break; - case PING: // Nobody gets to send pings - case UNKNOWN: - break; - } - } - - private class Server implements UDPCallback { - - private final ServerType type; - private PacketReceiver packetReceiver; - private UDPServer server; - - public Server(ServerType type) { - this.type = type; - } - - public void setPacketReceiver(PacketReceiver packetReceiver) { - this.packetReceiver = packetReceiver; - } - - public boolean initialize(InetAddress bindAddr, int port, int maxPacket) { - try { - if (bindAddr == null) - server = new UDPServer(port, maxPacket); - else - server = new UDPServer(bindAddr, port, maxPacket); - } catch (Exception e) { - System.err.println("NetworkListener: Failed to initialize UDP server [" + type + "] on " + bindAddr + ":" + port + ". Reason: " + e.getMessage()); - return false; - } - return isRunning(); - } - - public boolean start() { - if (server == null) - return false; - server.setCallback(this); - return true; - } - - public boolean terminate() { - if (server == null) - return false; - server.close(); - return true; - } - - public boolean isRunning() { - if (server == null) - return false; - return server.isRunning(); - } - - public void send(InetAddress addr, int port, byte [] data) { - server.send(port, addr, data); - } - - public void onReceivedPacket(UDPPacket packet) { - if (type == ServerType.PING) - send(packet.getAddress(), packet.getPort(), packet.getData()); - else - packetReceiver.receivePacket(type, packet); - } - } - -} diff --git a/src/services/network/NetworkManager.java b/src/services/network/NetworkManager.java index 160a0fa41..9378a3cac 100644 --- a/src/services/network/NetworkManager.java +++ b/src/services/network/NetworkManager.java @@ -32,16 +32,12 @@ import resources.control.Manager; public class NetworkManager extends Manager { - private NetworkListenerService netListenerService; - private NetworkClientManager netClientManager; + private final NetworkClientManager netClientManager; public NetworkManager(Galaxy galaxy) { - netListenerService = new NetworkListenerService(galaxy); - netClientManager = new NetworkClientManager(netListenerService); - netListenerService.setPacketReceiver(netClientManager); + netClientManager = new NetworkClientManager(); addChildService(netClientManager); - addChildService(netListenerService); } } diff --git a/src/services/player/LoginService.java b/src/services/player/LoginService.java index e2c59d5d3..fa8c23d20 100644 --- a/src/services/player/LoginService.java +++ b/src/services/player/LoginService.java @@ -30,6 +30,7 @@ package services.player; import intents.GalacticIntent; import intents.LoginEventIntent; import intents.LoginEventIntent.LoginEvent; +import intents.network.GalacticPacketIntent; import intents.player.DeleteCharacterIntent; import java.sql.PreparedStatement; @@ -42,7 +43,6 @@ import java.util.Random; import main.ProjectSWG; import network.packets.Packet; -import network.packets.soe.SessionRequest; import network.packets.swg.ErrorMessage; import network.packets.swg.ServerUnixEpochTime; import network.packets.swg.login.CharacterCreationDisabled; @@ -56,8 +56,6 @@ import network.packets.swg.login.LoginClusterStatus; import network.packets.swg.login.LoginEnumCluster; import network.packets.swg.login.LoginIncorrectClientId; import network.packets.swg.login.OfflineServersMessage; -import network.packets.swg.login.ServerId; -import network.packets.swg.login.ServerString; import network.packets.swg.login.StationIdHasJediSlot; import resources.Galaxy; import resources.Race; @@ -98,6 +96,7 @@ public class LoginService extends Service { random = new Random(); registerForIntent(DeleteCharacterIntent.TYPE); + registerForIntent(GalacticPacketIntent.TYPE); } @Override @@ -115,15 +114,13 @@ public class LoginService extends Service { public void onIntentReceived(Intent i) { if (i instanceof DeleteCharacterIntent) { deleteCharacter(((DeleteCharacterIntent) i).getCreature().getObjectId()); + } else if (i instanceof GalacticPacketIntent) { + GalacticPacketIntent gpi = (GalacticPacketIntent) i; + handlePacket(gpi, gpi.getPlayerManager().getPlayerFromNetworkId(gpi.getNetworkId()), gpi.getPacket()); } } public void handlePacket(GalacticIntent intent, Player player, Packet p) { - if (p instanceof SessionRequest) { - player.setConnectionId(((SessionRequest)p).getConnectionID()); - player.setPlayerState(PlayerState.DISCONNECTED); - sendServerInfo(player); - } if (p instanceof LoginClientId) handleLogin(player, (LoginClientId) p); if (p instanceof DeleteCharacterRequest) @@ -137,14 +134,6 @@ public class LoginService extends Service { return name + ":" + id; } - private void sendServerInfo(Player player) { - Config c = getConfig(ConfigFile.NETWORK); - String name = c.getString("LOGIN-SERVER-NAME", "LoginServer"); - int id = c.getInt("LOGIN-SERVER-ID", 1); - sendPacket(player.getNetworkId(), new ServerString(name + ":" + id)); - sendPacket(player.getNetworkId(), new ServerId(id)); - } - private void handleCharDeletion(GalacticIntent intent, Player player, DeleteCharacterRequest request) { SWGObject obj = intent.getObjectManager().destroyObject(request.getPlayerId()); if (obj != null && obj instanceof CreatureObject) { diff --git a/src/services/player/PlayerManager.java b/src/services/player/PlayerManager.java index 269c8bcdc..51cf38c0d 100644 --- a/src/services/player/PlayerManager.java +++ b/src/services/player/PlayerManager.java @@ -30,28 +30,22 @@ package services.player; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import network.packets.Packet; -import network.packets.soe.SessionRequest; -import network.packets.swg.login.ClientIdMsg; -import network.packets.swg.zone.insertion.SelectCharacter; import intents.NotifyPlayersPacketIntent; import intents.PlayerEventIntent; +import intents.network.ConnectionOpenedIntent; import intents.network.GalacticPacketIntent; import intents.network.InboundPacketIntent; -import intents.player.ZonePlayerSwapIntent; import resources.Terrain; import resources.control.Intent; import resources.control.Manager; -import resources.network.ServerType; import resources.player.Player; import resources.player.PlayerEvent; import resources.player.PlayerState; -import resources.server_info.Log; public class PlayerManager extends Manager { @@ -72,6 +66,7 @@ public class PlayerManager extends Manager { registerForIntent(GalacticPacketIntent.TYPE); registerForIntent(PlayerEventIntent.TYPE); registerForIntent(NotifyPlayersPacketIntent.TYPE); + registerForIntent(ConnectionOpenedIntent.TYPE); } @Override @@ -83,12 +78,12 @@ public class PlayerManager extends Manager { public void onIntentReceived(Intent i) { if (i instanceof InboundPacketIntent) onInboundPacketIntent((InboundPacketIntent) i); - else if (i instanceof GalacticPacketIntent) - onGalacticPacketIntent((GalacticPacketIntent) i); else if (i instanceof PlayerEventIntent) onPlayerEventIntent((PlayerEventIntent) i); else if (i instanceof NotifyPlayersPacketIntent) onNotifyPlayersPacketIntent((NotifyPlayersPacketIntent) i); + else if (i instanceof ConnectionOpenedIntent) + onConnectionOpenedIntent((ConnectionOpenedIntent) i); } public boolean playerExists(String name) { @@ -241,45 +236,6 @@ public class PlayerManager extends Manager { } } - private void removeDuplicatePlayers(Player player, long charId) { - synchronized (players) { - Iterator it = players.values().iterator(); - while (it.hasNext()) { - Player p = it.next(); - if (p != player && p.getCreatureObject() != null && p.getCreatureObject().getObjectId() == charId) { - new ZonePlayerSwapIntent(p, player, p.getCreatureObject()).broadcast(); - it.remove(); - } - } - } - } - - private Player transitionLoginToZone(long networkId, int galaxyId, String galaxyName, ClientIdMsg clientId) { - final byte [] nToken = clientId.getSessionToken(); - synchronized (players) { - for (Player p : players.values()) { - byte [] pToken = p.getSessionToken(); - if (pToken.length != nToken.length) - continue; - boolean match = true; - for (int t = 0; t < pToken.length && match; t++) { - if (pToken[t] != nToken[t]) - match = false; - } - if (match) { - players.remove(p.getNetworkId()); - p.setNetworkId(networkId); - p.setGalaxyId(galaxyId); - p.setGalaxyName(galaxyName); - players.put(networkId, p); - Log.i("PlayerManager", "Transitioned %s from login to zone", p.getUsername()); - return p; - } - } - } - return null; - } - private void onPlayerEventIntent(PlayerEventIntent pei) { synchronized (players) { if (pei.getEvent() == PlayerEvent.PE_DISAPPEAR) { @@ -297,34 +253,6 @@ public class PlayerManager extends Manager { player.updateLastPacketTimestamp(); } - private void onGalacticPacketIntent(GalacticPacketIntent gpi) { - Packet packet = gpi.getPacket(); - ServerType type = gpi.getServerType(); - long networkId = gpi.getNetworkId(); - Player player = null; - if (type == ServerType.ZONE && packet instanceof ClientIdMsg) { - String galaxyName = gpi.getGalaxy().getName(); - int galaxyId = gpi.getGalaxy().getId(); - player = transitionLoginToZone(networkId, galaxyId, galaxyName, (ClientIdMsg) packet); - } else - player = getPlayerFromNetworkId(networkId); - if (player != null && type == ServerType.ZONE && packet instanceof SelectCharacter) - removeDuplicatePlayers(player, ((SelectCharacter)packet).getCharacterId()); - if (type == ServerType.LOGIN && player == null) { - player = new Player(this, networkId); - synchronized (players) { - players.put(networkId, player); - } - } - if (player != null) { - if (type == ServerType.LOGIN) - loginService.handlePacket(gpi, player, packet); - else if (type == ServerType.ZONE) - zoneService.handlePacket(gpi, player, networkId, packet); - } else if (type == ServerType.ZONE && packet instanceof SessionRequest) - zoneService.handlePacket(gpi, player, networkId, packet); - } - private void onNotifyPlayersPacketIntent(NotifyPlayersPacketIntent nppi) { if (nppi.getNetworkIds() != null) { if (nppi.getTerrain() != null) notifyPlayersAtPlanet(nppi.getNetworkIds(), nppi.getCondition(), nppi.getTerrain(), nppi.getPacket()); @@ -334,4 +262,10 @@ public class PlayerManager extends Manager { else notifyPlayers(nppi.getCondition(), nppi.getPacket()); } } + + private void onConnectionOpenedIntent(ConnectionOpenedIntent coi) { + synchronized (players) { + players.put(coi.getNetworkId(), new Player(this, coi.getNetworkId())); + } + } } diff --git a/src/services/player/ZoneManager.java b/src/services/player/ZoneManager.java index e0f7c5e1d..cfa5a7eae 100644 --- a/src/services/player/ZoneManager.java +++ b/src/services/player/ZoneManager.java @@ -31,14 +31,12 @@ import intents.GalacticIntent; import intents.PlayerEventIntent; import intents.RequestZoneInIntent; import intents.chat.ChatBroadcastIntent; +import intents.network.GalacticPacketIntent; import main.ProjectSWG; import network.packets.Packet; -import network.packets.soe.SessionRequest; import network.packets.swg.login.AccountFeatureBits; import network.packets.swg.login.ClientIdMsg; import network.packets.swg.login.ClientPermissionsMessage; -import network.packets.swg.login.ServerId; -import network.packets.swg.login.ServerString; import network.packets.swg.zone.CmdSceneReady; import network.packets.swg.zone.GalaxyLoopTimesResponse; import network.packets.swg.zone.HeartBeat; @@ -52,12 +50,13 @@ import network.packets.swg.zone.chat.ChatSystemMessage; import network.packets.swg.zone.chat.VoiceChatStatus; import network.packets.swg.zone.insertion.ChatServerStatus; import network.packets.swg.zone.insertion.CmdStartScene; + import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; -import resources.Galaxy; + import resources.Location; import resources.Race; import resources.config.ConfigFile; @@ -72,7 +71,6 @@ import resources.player.Player; import resources.player.PlayerEvent; import resources.player.PlayerFlags; import resources.player.PlayerState; -import resources.server_info.Config; import resources.server_info.Log; import java.io.File; @@ -91,6 +89,7 @@ public class ZoneManager extends Manager { addChildService(characterCreationService); registerForIntent(RequestZoneInIntent.TYPE); + registerForIntent(GalacticPacketIntent.TYPE); } @Override @@ -104,13 +103,14 @@ public class ZoneManager extends Manager { if (i instanceof RequestZoneInIntent) { RequestZoneInIntent zii = (RequestZoneInIntent) i; zoneInPlayer(zii.getPlayer(), zii.getCreature(), zii.getGalaxy()); + } else if (i instanceof GalacticPacketIntent) { + GalacticPacketIntent gpi = (GalacticPacketIntent) i; + handlePacket(gpi, gpi.getPlayerManager().getPlayerFromNetworkId(gpi.getNetworkId()), gpi.getNetworkId(), gpi.getPacket()); } } public void handlePacket(GalacticIntent intent, Player player, long networkId, Packet p) { characterCreationService.handlePacket(intent, player, networkId, p); - if (p instanceof SessionRequest) - sendServerInfo(intent.getGalaxy(), networkId); if (p instanceof ClientIdMsg) handleClientIdMsg(player, (ClientIdMsg) p); if (p instanceof RequestGalaxyLoopTimes) @@ -194,7 +194,6 @@ public class ZoneManager extends Manager { sendPacket(player, new ParametersMessage()); sendPacket(player, new ChatOnConnectAvatar()); sendPacket(player, new CmdStartScene(false, objId, race, l, ProjectSWG.getGalacticTime(), (int)(System.currentTimeMillis()/1E3))); - flushPackets(); } private void initPlayerBeforeZoneIn(Player player, CreatureObject creatureObj, PlayerObject playerObj) { @@ -231,14 +230,6 @@ public class ZoneManager extends Manager { ghost.updateWaypoint(waypoint); } - - private void sendServerInfo(Galaxy galaxy, long networkId) { - Config c = getConfig(ConfigFile.NETWORK); - String name = c.getString("ZONE-SERVER-NAME", galaxy.getName()); - int id = c.getInt("ZONE-SERVER-ID", galaxy.getId()); - sendPacket(networkId, new ServerString(name + ":" + id)); - sendPacket(networkId, new ServerId(id)); - } private void handleCmdSceneReady(Player player, CmdSceneReady p) { player.setPlayerState(PlayerState.ZONED_IN); diff --git a/src/services/trader/TraderManager.java b/src/services/trader/TraderManager.java new file mode 100644 index 000000000..3c25ff53e --- /dev/null +++ b/src/services/trader/TraderManager.java @@ -0,0 +1,20 @@ +package services.trader; + +import resources.control.Manager; +import services.trader.resources.ResourceService; +import services.trader.survey.SurveyService; + +public class TraderManager extends Manager { + + private final ResourceService resourceService; + private final SurveyService surveyService; + + public TraderManager() { + resourceService = new ResourceService(); + surveyService = new SurveyService(); + + addChildService(resourceService); + addChildService(surveyService); + } + +} diff --git a/src/services/trader/resources/ResourceService.java b/src/services/trader/resources/ResourceService.java new file mode 100644 index 000000000..f147e8dab --- /dev/null +++ b/src/services/trader/resources/ResourceService.java @@ -0,0 +1,11 @@ +package services.trader.resources; + +import resources.control.Service; + +public class ResourceService extends Service { + + public ResourceService() { + // Equation: 100 / (1 + e^(-8(x-0.5))) + 2 + } + +} diff --git a/src/services/trader/survey/SurveyService.java b/src/services/trader/survey/SurveyService.java new file mode 100644 index 000000000..66257a4e0 --- /dev/null +++ b/src/services/trader/survey/SurveyService.java @@ -0,0 +1,65 @@ +package services.trader.survey; + +import intents.radial.RadialRegisterIntent; +import intents.radial.RadialRequestIntent; +import intents.radial.RadialResponseIntent; +import intents.radial.RadialSelectionIntent; + +import java.util.HashSet; +import java.util.Set; + +import resources.control.Intent; +import resources.control.Service; +import resources.server_info.Log; + +public class SurveyService extends Service { + + public SurveyService() { + registerForIntent(RadialRequestIntent.TYPE); + registerForIntent(RadialSelectionIntent.TYPE); + } + + @Override + public boolean start() { + Set templates = new HashSet<>(); + templates.add("object/tangible/survey_tool/shared_survey_tool_moisture.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_inorganic.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_gas.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_all_s01.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_geo_thermal.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_lumber.iff"); + templates.add("object/tangible/survey_tool/base/shared_survey_tool_base.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_all.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_mineral.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_wind.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_organic.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_solar.iff"); + templates.add("object/tangible/survey_tool/shared_survey_tool_liquid.iff"); + new RadialRegisterIntent(templates, true).broadcast(); + return super.start(); + } + + @Override + public void onIntentReceived(Intent i) { + switch (i.getType()) { + case RadialRequestIntent.TYPE: + if (i instanceof RadialRequestIntent) + onRadialRequest((RadialRequestIntent) i); + break; + case RadialSelectionIntent.TYPE: + if (i instanceof RadialSelectionIntent) + onRadialSelected((RadialSelectionIntent) i); + break; + } + } + + private void onRadialRequest(RadialRequestIntent rri) { + Log.i("SurveyService", "Requested: %s", rri.getTarget().getTemplate()); + new RadialResponseIntent(rri.getPlayer(), rri.getTarget(), rri.getRequest().getOptions(), rri.getRequest().getCounter()).broadcast(); + } + + private void onRadialSelected(RadialSelectionIntent rsi) { + Log.i("SurveyService", "Selected: %s from %s", rsi.getSelection(), rsi.getTarget().getTemplate()); + } + +} diff --git a/test/main/TestAll.java b/test/main/TestAll.java index 9e313af68..a44c70065 100644 --- a/test/main/TestAll.java +++ b/test/main/TestAll.java @@ -28,7 +28,6 @@ package main; import network.encryption.TestEncryption; -import network.encryption.TestFragmented; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -39,7 +38,6 @@ import resources.TestResources; @RunWith(Suite.class) @SuiteClasses({ TestEncryption.class, - TestFragmented.class, TestResources.class }) public class TestAll { diff --git a/test/network/encryption/TestCompression.java b/test/network/encryption/TestCompression.java new file mode 100644 index 000000000..65760e46b --- /dev/null +++ b/test/network/encryption/TestCompression.java @@ -0,0 +1,42 @@ +package network.encryption; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class TestCompression { + + @Test + public void testSmall() { + byte [] data = new byte[101]; + for (int i = 0; i < data.length; i++) + data[i] = (byte) (i % 100); + byte [] compressed = Compression.compress(data); + byte [] decompressed = Compression.decompress(compressed); + Assert.assertArrayEquals(data, decompressed); + } + + @Test + public void testMedium() { + byte [] data = new byte[256]; + for (int i = 0; i < data.length; i++) + data[i] = (byte) (i % 100); + byte [] compressed = Compression.compress(data); + byte [] decompressed = Compression.decompress(compressed); + Assert.assertArrayEquals(data, decompressed); + } + + @Test + public void testLarge() { + byte [] data = new byte[1024]; + for (int i = 0; i < data.length; i++) + data[i] = (byte) (i % 100); + byte [] compressed = Compression.compress(data); + Assert.assertTrue("Compressed should be less than actual. Compressed: "+compressed.length+" Data: "+data.length, compressed.length < data.length); + byte [] decompressed = Compression.decompress(compressed); + Assert.assertArrayEquals(data, decompressed); + } + +} diff --git a/test/network/encryption/TestEncryption.java b/test/network/encryption/TestEncryption.java index 50c284fed..ed43a242c 100644 --- a/test/network/encryption/TestEncryption.java +++ b/test/network/encryption/TestEncryption.java @@ -29,8 +29,6 @@ package network.encryption; import java.util.Random; -import network.packets.soe.Acknowledge; - import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -99,16 +97,4 @@ public class TestEncryption { // } // } - @Test - public void testAcknowledge() { - Random r = new Random(); - int crc = r.nextInt(); - System.out.println(Integer.toHexString(crc)); - Acknowledge a = new Acknowledge((short) 5); - byte [] data = a.encode().array(); - byte [] encoded = Encryption.encode(data, crc); - byte [] decoded = Encryption.decode(encoded, crc); - Assert.assertArrayEquals(data, decoded); - } - } diff --git a/test/network/encryption/TestFragmented.java b/test/network/encryption/TestFragmented.java deleted file mode 100644 index 780348688..000000000 --- a/test/network/encryption/TestFragmented.java +++ /dev/null @@ -1,94 +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 network.encryption; - -import java.nio.ByteBuffer; -import java.util.Random; - -import network.packets.soe.Fragmented; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class TestFragmented { - - @Test - public void testFragmented() { - Random r = new Random(); - byte [] randomData = new byte[1024]; - r.nextBytes(randomData); - test(randomData); - } - - @Test - public void testFragmentedSmall() { - Random r = new Random(); - byte [] randomData = new byte[100]; - r.nextBytes(randomData); - test(randomData); - } - - @Test - public void testFragmentedEverySize() { - for (int i = 0; i < 496*4; i++) { - byte [] junkData = new byte[i]; - Fragmented main = new Fragmented(); - main.setPacket(ByteBuffer.wrap(junkData)); - try { - main.encode(5); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail("Failed to encode at fragmented size " + i); - } - } - } - - private void test(byte [] data) { - FragmentedChannelA fragCore2 = new FragmentedChannelA(); - FragmentedChannelA [] fragsCore2 = fragCore2.create(data); - - Fragmented fragCore3 = new Fragmented(); - fragCore3.setPacket(ByteBuffer.wrap(data)); - Fragmented [] fragsCore3 = fragCore3.encode(5); - - if (fragsCore3.length < fragsCore2.length) - return; - - Assert.assertEquals(fragsCore2.length, fragsCore3.length); - for (int i = 0; i < fragsCore2.length; i++) { - fragsCore2[i].setSequence((short) (i + 5)); - byte [] c2 = fragsCore2[i].serialize().array(); - byte [] c3 = fragsCore3[i].encode().array(); - Assert.assertArrayEquals(c2, c3); - } - } - -}