mirror of
https://github.com/ProjectSWGCore/Holocore.git
synced 2026-10-20 10:03:02 -04:00
351 lines
9.7 KiB
Java
351 lines
9.7 KiB
Java
/***********************************************************************************
|
|
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
|
|
* *
|
|
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
|
|
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
|
|
* Our goal is to create an emulator which will provide a server for players to *
|
|
* continue playing a game similar to the one they used to play. We are basing *
|
|
* it on the final publish of the game prior to end-game events. *
|
|
* *
|
|
* This file is part of Holocore. *
|
|
* *
|
|
* -------------------------------------------------------------------------------- *
|
|
* *
|
|
* Holocore is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Affero General Public License as *
|
|
* published by the Free Software Foundation, either version 3 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* Holocore is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Affero General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Affero General Public License *
|
|
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
***********************************************************************************/
|
|
package network.packets;
|
|
|
|
import resources.common.CRC;
|
|
import resources.encodables.Encodable;
|
|
import utilities.Encoder;
|
|
|
|
import java.net.InetAddress;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.IntBuffer;
|
|
import java.nio.charset.Charset;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
|
|
|
|
public class Packet {
|
|
public static final Charset ascii = Charset.forName("UTF-8");
|
|
public static final Charset unicode = Charset.forName("UTF-16LE");
|
|
private InetAddress address;
|
|
private ByteBuffer data;
|
|
private int port = 0;
|
|
private int opcode;
|
|
|
|
public Packet() {
|
|
data = ByteBuffer.allocate(2);
|
|
}
|
|
|
|
public Packet(ByteBuffer data) {
|
|
decode(data);
|
|
}
|
|
|
|
public void setAddress(InetAddress address) {
|
|
this.address = address;
|
|
}
|
|
|
|
public InetAddress getAddress() {
|
|
return address;
|
|
}
|
|
|
|
public void setPort(int port) {
|
|
this.port = port;
|
|
}
|
|
|
|
public int getPort() {
|
|
return port;
|
|
}
|
|
|
|
public void setOpcode(int opcode) {
|
|
this.opcode = opcode;
|
|
}
|
|
|
|
public int getOpcode() {
|
|
return opcode;
|
|
}
|
|
|
|
public static void addList(ByteBuffer bb, Collection<? extends Encodable> list) {
|
|
if (list == null) {
|
|
addInt(bb, 0);
|
|
return;
|
|
}
|
|
|
|
addInt(bb, list.size());
|
|
for (Encodable encodable : list) {
|
|
addData(bb, encodable.encode());
|
|
}
|
|
}
|
|
|
|
public static void addList(ByteBuffer bb, List<byte[]> list) {
|
|
addInt(bb, list.size());
|
|
for (byte[] bytes : list) {
|
|
addData(bb, bytes);
|
|
}
|
|
}
|
|
|
|
public static void addList(ByteBuffer bb, List<String> list, Encoder.StringType type) {
|
|
addInt(bb, list.size());
|
|
|
|
switch(type) {
|
|
case ASCII: for (String s : list) { addAscii(bb, s); }
|
|
break;
|
|
case UNICODE: for (String s : list) { addUnicode(bb, s); }
|
|
break;
|
|
default: System.err.println("Cannot encode StringType " + type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static void addBoolean(ByteBuffer bb, boolean b) {
|
|
bb.put(b ? (byte) 1 : (byte) 0);
|
|
}
|
|
|
|
public static void addAscii(ByteBuffer bb, String s) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN);
|
|
bb.putShort((short) s.length());
|
|
bb.put(s.getBytes(ascii));
|
|
}
|
|
|
|
public static void addUnicode(ByteBuffer bb, String s) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN);
|
|
bb.putInt(s.length());
|
|
bb.put(s.getBytes(unicode));
|
|
}
|
|
|
|
public static void addLong(ByteBuffer bb, long l) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN).putLong(l);
|
|
}
|
|
|
|
public static void addInt(ByteBuffer bb, int i) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN).putInt(i);
|
|
}
|
|
|
|
public static void addFloat(ByteBuffer bb, float f) {
|
|
bb.putFloat(f);
|
|
}
|
|
|
|
public static void addShort(ByteBuffer bb, int i) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN).putShort((short) i);
|
|
}
|
|
|
|
public static void addNetLong(ByteBuffer bb, long l) {
|
|
bb.order(ByteOrder.BIG_ENDIAN).putLong(l);
|
|
}
|
|
|
|
public static void addNetInt(ByteBuffer bb, int i) {
|
|
bb.order(ByteOrder.BIG_ENDIAN).putInt(i);
|
|
}
|
|
|
|
public static void addNetShort(ByteBuffer bb, int i) {
|
|
bb.order(ByteOrder.BIG_ENDIAN).putShort((short) i);
|
|
}
|
|
|
|
public static void addByte(ByteBuffer bb, int b) {
|
|
bb.put((byte) b);
|
|
}
|
|
|
|
public static void addData(ByteBuffer bb, byte[] data) {
|
|
bb.put(data);
|
|
}
|
|
|
|
public static void addArrayList(ByteBuffer bb, byte[] b) {
|
|
addShort(bb, b.length);
|
|
bb.put(b);
|
|
}
|
|
|
|
public static void addEncodable(ByteBuffer data, Encodable encodable) {
|
|
data.put(encodable.encode());
|
|
}
|
|
|
|
public static void addCrc(ByteBuffer bb, String crcString) {
|
|
addInt(bb, CRC.getCrc(crcString));
|
|
}
|
|
|
|
public static boolean getBoolean(ByteBuffer bb) {
|
|
return getByte(bb) == 1;
|
|
}
|
|
|
|
public static String getAscii(ByteBuffer bb) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN);
|
|
short length = bb.getShort();
|
|
if (length > bb.remaining())
|
|
return "";
|
|
byte [] str = new byte[length];
|
|
bb.get(str);
|
|
return new String(str, ascii);
|
|
}
|
|
|
|
public static String getUnicode(ByteBuffer bb) {
|
|
bb.order(ByteOrder.LITTLE_ENDIAN);
|
|
int length = bb.getInt() * 2;
|
|
if (length > bb.remaining())
|
|
return "";
|
|
byte [] str = new byte[length];
|
|
bb.get(str);
|
|
return new String(str, unicode);
|
|
}
|
|
|
|
public static byte getByte(ByteBuffer bb) {
|
|
return bb.get();
|
|
}
|
|
|
|
public static short getShort(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.LITTLE_ENDIAN).getShort();
|
|
}
|
|
|
|
public static int getInt(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.LITTLE_ENDIAN).getInt();
|
|
}
|
|
|
|
public static float getFloat(ByteBuffer bb) {
|
|
return bb.getFloat();
|
|
}
|
|
|
|
public static long getLong(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.LITTLE_ENDIAN).getLong();
|
|
}
|
|
|
|
public static short getNetShort(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.BIG_ENDIAN).getShort();
|
|
}
|
|
|
|
public static int getNetInt(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.BIG_ENDIAN).getInt();
|
|
}
|
|
|
|
public static long getNetLong(ByteBuffer bb) {
|
|
return bb.order(ByteOrder.BIG_ENDIAN).getLong();
|
|
}
|
|
|
|
public static byte [] getArray(ByteBuffer bb) {
|
|
byte [] data = new byte[getShort(bb)];
|
|
bb.get(data);
|
|
return data;
|
|
}
|
|
|
|
public static byte [] getArray(ByteBuffer bb, int length) {
|
|
byte [] data = new byte[length];
|
|
bb.get(data);
|
|
return data;
|
|
}
|
|
|
|
public static int [] getIntArray(ByteBuffer bb) {
|
|
IntBuffer ib = bb.asIntBuffer();
|
|
int size = ib.get();
|
|
int [] ints = new int[size];
|
|
ib.get(ints);
|
|
return ints;
|
|
}
|
|
|
|
public static int [] getIntArray(ByteBuffer bb, int size) {
|
|
IntBuffer ib = bb.asIntBuffer();
|
|
int [] ints = new int[size];
|
|
ib.get(ints);
|
|
return ints;
|
|
}
|
|
|
|
/**
|
|
* Decodes the ByteBuffer stream into the passed List using the given listType instance for creating elements
|
|
* for the list from the buffer.
|
|
* @param bb ByteBuffer
|
|
* @param type Top-most class for the elements that are to be added to the list
|
|
* @param <T> Class type that implements {@link Encodable}
|
|
* @return Amount of bytes that are read
|
|
*/
|
|
public static <T extends Encodable> List<T> getList(ByteBuffer bb, Class<T> type) {
|
|
int size = getInt(bb);
|
|
|
|
if (size < 0) {
|
|
System.err.println("Read list with size less than zero!");
|
|
return null;
|
|
} else if (size == 0) {
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
List<T> list = new ArrayList<>();
|
|
|
|
try {
|
|
for (int i = 0; i < size; i++) {
|
|
T instance = type.newInstance();
|
|
instance.decode(bb);
|
|
list.add(instance);
|
|
}
|
|
} catch (InstantiationException | IllegalAccessException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
if (size != list.size())
|
|
System.err.println("Expected list size " + size + " but only have " + list.size() + " elements in the list");
|
|
return list;
|
|
}
|
|
|
|
public static <T extends Encodable> T getEncodable(ByteBuffer bb, Class<T> type) {
|
|
T instance = null;
|
|
try {
|
|
instance = type.newInstance();
|
|
instance.decode(bb);
|
|
} catch (InstantiationException | IllegalAccessException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
public static List<String> getList(ByteBuffer bb, Encoder.StringType type) {
|
|
int size = getInt(bb);
|
|
|
|
if (size < 0) {
|
|
System.err.println("Read list with size less than zero!");
|
|
return null;
|
|
} else if (size == 0) {
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
List<String> list = new ArrayList<>();
|
|
|
|
switch(type) {
|
|
case ASCII: for (int i = 0; i < size; i++) { list.add(getAscii(bb)); }
|
|
break;
|
|
case UNICODE: for (int i = 0; i < size; i++) { list.add(getUnicode(bb)); }
|
|
break;
|
|
default: System.err.println("Do not know how to read list of StringType " + type);
|
|
break;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public void decode(ByteBuffer data) {
|
|
data.position(0);
|
|
this.data = data;
|
|
opcode = getNetShort(data);
|
|
data.position(0);
|
|
}
|
|
|
|
public ByteBuffer getData() {
|
|
return data;
|
|
}
|
|
|
|
public ByteBuffer encode() {
|
|
return data;
|
|
}
|
|
|
|
}
|