/*********************************************************************************** * 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.Queue; import resources.SortedLinkedList; import resources.server_info.Log; 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 final Queue assembleQueue; private final SortedLinkedList sequenced; private short sendSequence; private int crc; public OutboundNetworkHandler() { assembleQueue = new LinkedList(); sequenced = new SortedLinkedList(); sendSequence = 0; crc = 0; } public synchronized void reset() { 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.listIterator(); while (it.hasNext()) { SequencedPacket sp = it.next(); if (sp.getSequence() <= sequence) it.remove(); else break; } Log.d("OutboundNetworkHandler", "Ack: %d Remaining: %d", sequence, sequenced.size()); } } public synchronized void onOutOfOrder(short sequence) { synchronized (sequenced) { Iterator it = sequenced.listIterator(); while (it.hasNext()) { SequencedPacket sp = it.next(); if (sp.getSequence() <= sequence) pushAssembledEncrypted(sp.getPacket()); else break; } Log.d("OutboundNetworkHandler", "Out Of Order: %d", sequence); } } public synchronized boolean hasAssembled() { synchronized (assembleQueue) { return !assembleQueue.isEmpty(); } } public synchronized byte [] pollAssembled() { synchronized (assembleQueue) { return assembleQueue.poll(); } } private void pushAssembledEncrypted(byte [] data) { data = Encryption.encode(data, crc); synchronized (assembleQueue) { assembleQueue.add(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(); pushSequencedPacket(f.getSequence(), encoded); pushAssembledEncrypted(encoded); } 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(); pushSequencedPacket(f.getSequence(), encoded); pushAssembledEncrypted(encoded); } sendSequence = (short) lastSeq; return count; } else { d.setSequence(sendSequence++); byte [] encoded = d.encode().array(); pushSequencedPacket(d.getSequence(), encoded); pushAssembledEncrypted(encoded); 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; public SequencedPacket(short sequence, byte [] packet) { this.sequence = sequence; this.packet = packet; } public short getSequence() { return sequence; } public byte [] getPacket() { return packet; } @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; } } }