diff --git a/src/network/packets/Packet.java b/src/network/packets/Packet.java index b1cf7c78f..e6bda3a4a 100644 --- a/src/network/packets/Packet.java +++ b/src/network/packets/Packet.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package network.packets; +import resources.common.CRC; import resources.network.BaselineBuilder; import java.net.InetAddress; @@ -84,7 +85,7 @@ public class Packet { } public static void addBoolean(ByteBuffer bb, boolean b) { - bb.put(b ? (byte)1 : (byte)0); + bb.put(b ? (byte) 1 : (byte) 0); } public static void addAscii(ByteBuffer bb, String s) { @@ -112,7 +113,7 @@ public class Packet { } public static void addShort(ByteBuffer bb, int i) { - bb.order(ByteOrder.LITTLE_ENDIAN).putShort((short)i); + bb.order(ByteOrder.LITTLE_ENDIAN).putShort((short) i); } public static void addNetLong(ByteBuffer bb, long l) { @@ -124,11 +125,11 @@ public class Packet { } public static void addNetShort(ByteBuffer bb, int i) { - bb.order(ByteOrder.BIG_ENDIAN).putShort((short)i); + bb.order(ByteOrder.BIG_ENDIAN).putShort((short) i); } public static void addByte(ByteBuffer bb, int b) { - bb.put((byte)b); + bb.put((byte) b); } public static void addData(ByteBuffer bb, byte[] data) { @@ -140,6 +141,10 @@ public class Packet { bb.put(b); } + public static void addCrc(ByteBuffer bb, String crcString) { + addInt(bb, CRC.getCrc(crcString)); + } + public static boolean getBoolean(ByteBuffer bb) { return getByte(bb) == 1; } diff --git a/src/resources/encodables/OutOfBandData.java b/src/resources/encodables/OutOfBandData.java new file mode 100644 index 000000000..7ede67dc7 --- /dev/null +++ b/src/resources/encodables/OutOfBandData.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.encodables; + +import resources.network.BaselineBuilder; + +import java.nio.ByteBuffer; + +/** + * @author Waverunner + */ +public interface OutOfBandData extends BaselineBuilder.Encodable{ + int decodeOutOfBandData(ByteBuffer data); // TODO: Remove with Encodable refactor + OutOfBandPackage.Type getOobType(); + int getOobPosition(); +} diff --git a/src/resources/encodables/OutOfBandPackage.java b/src/resources/encodables/OutOfBandPackage.java index 83962a2a5..0b73ef130 100644 --- a/src/resources/encodables/OutOfBandPackage.java +++ b/src/resources/encodables/OutOfBandPackage.java @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import network.packets.Packet; import resources.network.BaselineBuilder.Encodable; import resources.objects.waypoint.WaypointObject; @@ -52,8 +53,8 @@ public class OutOfBandPackage implements Encodable { packages.add(outOfBandData); } - public static OutOfBandPackage createWithProse(Object stf, String key, Object prose) { - return new OutOfBandPackage(new ProsePackage(stf, key, prose)); + public List getPackages() { + return packages; } @Override @@ -65,7 +66,7 @@ public class OutOfBandPackage implements Encodable { data = new LinkedList<>(); for (OutOfBandData outOfBandData : packages) { - byte[] bytes = outOfBandData.encodeOutOfBandData(); + byte[] bytes = packOutOfBandData(outOfBandData); dataSize += bytes.length; data.add(bytes); } @@ -75,9 +76,7 @@ public class OutOfBandPackage implements Encodable { bb.putInt(dataSize / 2); - for (byte[] bytes : data) { - bb.put(bytes); - } + data.forEach(bb::put); return bb.array(); } @@ -86,34 +85,71 @@ public class OutOfBandPackage implements Encodable { int size = data.getInt() * 2; for (int read = 0; read < size; read+= 3) { - boolean addedByte = data.getShort() > 0; // ?? Seen as 1 + int padding = data.getShort(); + read += unpackOutOfBandData(data, Type.valueOf(data.get())); - Type type = Type.valueOf(data.get()); - switch(type) { - case PROSE_PACKAGE: - ProsePackage prose = new ProsePackage(); - read += prose.decodeOutOfBandData(data); - packages.add(prose); - break; - case WAYPOINT: - WaypointObject waypoint = new WaypointObject(-1); - read += waypoint.decodeOutOfBandData(data); - packages.add(waypoint); - break; - default: - System.err.println("Tried to decode an unsupported OutOfBandData Type: " + type); - break; - } - - if (addedByte) { - data.get(); - read+= 1; - } + data.position(data.position() + padding); + read += padding; } } + private int unpackOutOfBandData(ByteBuffer data, Type type) { + // Position doesn't seem to be reflective of it's spot in the package list, not sure if this can be automated + // as the client will send -3 for multiple waypoints in a mail, so could be static for each OutOfBandData + // If that's the case, then we can move the position variable to the Type enum instead of a method return statement +/* int position =*/ data.getInt(); + int read = 4; + switch(type) { + case PROSE_PACKAGE: + ProsePackage prose = new ProsePackage(); + read += prose.decodeOutOfBandData(data); + packages.add(prose); + break; + case WAYPOINT: + WaypointObject waypoint = new WaypointObject(-1); + read += waypoint.decodeOutOfBandData(data); + packages.add(waypoint); + break; + case STRING_ID: + StringId stringId = new StringId(); + read += stringId.decodeOutOfBandData(data); + packages.add(stringId); + break; + default: + System.err.println("Tried to decode an unsupported OutOfBandData Type: " + type); + break; + } + return read; + } + + private byte[] packOutOfBandData(OutOfBandData data) { + byte[] base = data.encode(); + + // Type and position is included in the padding size + int paddingSize = (base.length + 5) % 2; + + ByteBuffer bb = ByteBuffer.allocate(7 + paddingSize + base.length); + + Packet.addShort(bb, paddingSize); // Number of bytes for decoding to skip over when reading + Packet.addByte(bb, data.getOobType().getType()); + Packet.addInt(bb, data.getOobPosition()); + + bb.put(base); + + for (int i = 0; i < paddingSize; i++) { + Packet.addByte(bb, 0); + } + + return bb.array(); + } + + @Override + public String toString() { + return "OutOfBandPackage[packages=" + packages + "]"; + } + public enum Type { - NONE(-1), + UNDEFINED(Byte.MIN_VALUE), OBJECT(0), PROSE_PACKAGE(1), UNKNOWN(2), @@ -138,12 +174,7 @@ public class OutOfBandPackage implements Encodable { if (type.getType() == typeByte) return type; } - return Type.NONE; + return Type.UNDEFINED; } } - - public interface OutOfBandData extends Encodable { - byte[] encodeOutOfBandData(); - int decodeOutOfBandData(ByteBuffer data); - } } diff --git a/src/resources/encodables/ProsePackage.java b/src/resources/encodables/ProsePackage.java index 898d5b873..8e7369366 100644 --- a/src/resources/encodables/ProsePackage.java +++ b/src/resources/encodables/ProsePackage.java @@ -34,10 +34,10 @@ import java.nio.charset.Charset; import resources.network.BaselineBuilder.Encodable; -public class ProsePackage implements OutOfBandPackage.OutOfBandData { +public class ProsePackage implements OutOfBandData { private static final long serialVersionUID = 1L; - private Stf base = new Stf("", ""); + private StringId base = new StringId("", ""); private Prose actor = new Prose(); private Prose target = new Prose(); @@ -114,25 +114,25 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { else { System.err.println("DF can only be a Float!"); } break; - default: return; + default: break; } } public void setSTF(Object prose) { - if (prose instanceof Stf) { base = (Stf) prose; } + if (prose instanceof StringId) { base = (StringId) prose; } else if (prose instanceof String) { - if (((String) prose).startsWith("@")) { base = new Stf((String) prose); } + if (((String) prose).startsWith("@")) { base = new StringId((String) prose); } else { System.err.println("The base STF cannot be a custom string!"); } } else { System.err.println("The base STF must be either a Stf or a String! Received class: " + prose.getClass().getName()); } } public void setTU(Object prose) { - if (prose instanceof Stf) - actor.setStf((Stf) prose); + if (prose instanceof StringId) + actor.setStringId((StringId) prose); else if (prose instanceof String) { if (((String) prose).startsWith("@")) - actor.setStf(new Stf((String) prose)); + actor.setStringId(new StringId((String) prose)); else actor.setText((String) prose); } else if (prose instanceof Long) @@ -144,11 +144,11 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { } public void setTT(Object prose) { - if (prose instanceof Stf) - target.setStf((Stf) prose); + if (prose instanceof StringId) + target.setStringId((StringId) prose); else if (prose instanceof String) { if (((String) prose).startsWith("@")) - target.setStf(new Stf((String) prose)); + target.setStringId(new StringId((String) prose)); else target.setText((String) prose); } else if (prose instanceof Long) @@ -160,11 +160,11 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { } public void setTO(Object prose) { - if (prose instanceof Stf) - other.setStf((Stf) prose); + if (prose instanceof StringId) + other.setStringId((StringId) prose); else if (prose instanceof String) { if (((String) prose).startsWith("@")) - other.setStf(new Stf((String) prose)); + other.setStringId(new StringId((String) prose)); else other.setText((String) prose); } else if (prose instanceof Long) @@ -182,7 +182,11 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { public void setDF(Float prose) { df = prose; } - + + public void setGrammarFlag(boolean useGrammar) { + grammarFlag = useGrammar; + } + @Override public byte[] encode() { if (base == null) // There must be a base stf always @@ -210,39 +214,45 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { return bb.array(); } - - @Override - public byte[] encodeOutOfBandData() { - return encode(); - } - @Override public int decodeOutOfBandData(ByteBuffer data) { return 0; } + @Override + public OutOfBandPackage.Type getOobType() { + return OutOfBandPackage.Type.PROSE_PACKAGE; + } + + @Override + public int getOobPosition() { + return -1; + } + @Override public String toString() { - return "[ProsePackage] " + base.toString() + " Actor=" + actor.toString() - + " Target=" + target.toString() + " Other=" + other.toString(); + return "ProsePackage[base=" + base + ", grammarFlag=" + grammarFlag + + ", actor=" + actor + ", target=" + target + ", other=" + other + + ", di=" + di + ", df=" + df + + "]"; } private class Prose implements Encodable { private long objectId; - private Stf stf; + private StringId stringId; private String text; public Prose() { - stf = new Stf("", ""); + stringId = new StringId("", ""); } public void setObjectId(long objectId) { this.objectId = objectId; } - public void setStf(Stf stf) { - this.stf = stf; + public void setStringId(StringId stringId) { + this.stringId = stringId; } public void setText(String text) { @@ -251,7 +261,7 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { @Override public byte[] encode() { - byte[] stfData = stf.encode(); + byte[] stfData = stringId.encode(); ByteBuffer bb = ByteBuffer.allocate(12 + stfData.length + (text != null ? (4 + (text.length() * 2)) : 0)).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(objectId); @@ -268,7 +278,7 @@ public class ProsePackage implements OutOfBandPackage.OutOfBandData { @Override public String toString() { - return stf.toString() + " " + objectId + " -- " + text; + return "Prose[objectId=" + objectId + ", stringId=" + stringId + ", text='" + text + "']"; } } } diff --git a/src/resources/encodables/Stf.java b/src/resources/encodables/StringId.java similarity index 85% rename from src/resources/encodables/Stf.java rename to src/resources/encodables/StringId.java index 29634336b..719bb9522 100644 --- a/src/resources/encodables/Stf.java +++ b/src/resources/encodables/StringId.java @@ -28,23 +28,24 @@ package resources.encodables; import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.Charset; +import network.packets.Packet; import resources.network.BaselineBuilder.Encodable; -public class Stf implements Encodable { +public class StringId implements Encodable, OutOfBandData { private static final long serialVersionUID = 1L; private String key = ""; private String file = ""; - - public Stf(String file, String key) { + + public StringId() {} + + public StringId(String file, String key) { this.file = file; this.key = key; } - public Stf(String stf) { + public StringId(String stf) { if (!stf.contains(":")) { System.err.println("Stf: Invalid stf format! Expected a semi-colon for " + stf); return; @@ -61,17 +62,30 @@ public class Stf implements Encodable { @Override public byte[] encode() { - ByteBuffer buffer = ByteBuffer.allocate(8 + key.length() + file.length()).order(ByteOrder.LITTLE_ENDIAN); - - buffer.putShort((short) file.length()); - buffer.put(file.getBytes(Charset.forName("UTF-8"))); - buffer.putInt(0); - buffer.putShort((short) key.length()); - buffer.put(key.getBytes(Charset.forName("UTF-8"))); + ByteBuffer buffer = ByteBuffer.allocate(8 + key.length() + file.length()); + + Packet.addAscii(buffer, file); + Packet.addInt(buffer, 0); + Packet.addAscii(buffer, key); return buffer.array(); } + @Override + public int decodeOutOfBandData(ByteBuffer data) { + return 0; + } + + @Override + public OutOfBandPackage.Type getOobType() { + return OutOfBandPackage.Type.STRING_ID; + } + + @Override + public int getOobPosition() { + return -1; + } + public String getKey() { return key; } public void setKey(String key) { this.key = key; } diff --git a/src/resources/network/BaselineBuilder.java b/src/resources/network/BaselineBuilder.java index 2caa8f95f..53fc48f98 100644 --- a/src/resources/network/BaselineBuilder.java +++ b/src/resources/network/BaselineBuilder.java @@ -164,8 +164,10 @@ public class BaselineBuilder { public interface Encodable extends Serializable { byte [] encode(); + // TODO: int decode(ByteBuffer data); } + // TODO: move into Encodable public interface Decodable { void decode(ByteBuffer data); } diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java index e222086cd..08f66c4c3 100644 --- a/src/resources/objects/SWGObject.java +++ b/src/resources/objects/SWGObject.java @@ -47,7 +47,7 @@ import resources.common.CRC; import resources.containers.ContainerPermissions; import resources.containers.ContainerResult; import resources.containers.DefaultPermissions; -import resources.encodables.Stf; +import resources.encodables.StringId; import resources.network.BaselineBuilder; import resources.network.DeltaBuilder; import resources.player.Player; @@ -72,8 +72,8 @@ public abstract class SWGObject implements Serializable, Comparable { private Player owner = null; private SWGObject parent = null; - private Stf stf = new Stf("", ""); - private Stf detailStf = new Stf("", ""); + private StringId stringId = new StringId("", ""); + private StringId detailStringId = new StringId("", ""); private String template = ""; private int crc = 0; private String objectName = ""; @@ -108,7 +108,7 @@ public abstract class SWGObject implements Serializable, Comparable { /** * Adds the specified object to this object and places it in the appropriate slot if needed - * @param object + * @param object Object to add to this container, which will either be put into the appropriate slot(s) or become a contained object */ public boolean addObject(SWGObject object) { // If the arrangement is -1, then this object will be a contained object @@ -358,19 +358,19 @@ public abstract class SWGObject implements Serializable, Comparable { } public void setStf(String stfFile, String stfKey) { - this.stf = new Stf(stfFile, stfKey); + this.stringId = new StringId(stfFile, stfKey); } - public void setStf(String stf) { - this.stf = new Stf(stf); + public void setStringId(String stringId) { + this.stringId = new StringId(stringId); } public void setDetailStf(String stfFile, String stfKey) { - this.detailStf = new Stf(stfFile, stfKey); + this.detailStringId = new StringId(stfFile, stfKey); } - public void setDetailStf(String stf) { - this.detailStf = new Stf(stf); + public void setDetailStringId(String stf) { + this.detailStringId = new StringId(stf); } public void setTemplate(String template) { @@ -404,12 +404,12 @@ public abstract class SWGObject implements Serializable, Comparable { return parent; } - public Stf getStf() { - return stf; + public StringId getStringId() { + return stringId; } - public Stf getDetailStf() { - return detailStf; + public StringId getDetailStringId() { + return detailStringId; } public String getTemplate() { @@ -849,7 +849,7 @@ public abstract class SWGObject implements Serializable, Comparable { public void createBaseline3(Player target, BaselineBuilder bb) { bb.addFloat(complexity); // 0 - bb.addObject(stf); // 1 + bb.addObject(stringId); // 1 bb.addUnicode(objectName); // custom name -- 2 bb.addInt(volume); // 3 @@ -862,7 +862,7 @@ public abstract class SWGObject implements Serializable, Comparable { public void createBaseline6(Player target, BaselineBuilder bb) { bb.addInt(target.getGalaxyId()); // 0 - bb.addObject(detailStf); // 1 + bb.addObject(detailStringId); // 1 bb.incrementOperandCount(2); } diff --git a/src/resources/objects/waypoint/WaypointObject.java b/src/resources/objects/waypoint/WaypointObject.java index 3afd881cc..b42db8f67 100644 --- a/src/resources/objects/waypoint/WaypointObject.java +++ b/src/resources/objects/waypoint/WaypointObject.java @@ -28,20 +28,17 @@ package resources.objects.waypoint; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import network.packets.Packet; import network.packets.swg.zone.baselines.Baseline.BaselineType; import resources.Location; import resources.Terrain; -import resources.common.CRC; +import resources.encodables.OutOfBandData; import resources.encodables.OutOfBandPackage; -import resources.network.BaselineBuilder.Encodable; import resources.objects.intangible.IntangibleObject; import resources.player.Player; -import utilities.Encoder; -public class WaypointObject extends IntangibleObject implements OutOfBandPackage.OutOfBandData { +public class WaypointObject extends IntangibleObject implements OutOfBandData { private static final long serialVersionUID = 1L; @@ -93,44 +90,42 @@ public class WaypointObject extends IntangibleObject implements OutOfBandPackage @Override public byte[] encode() { Location loc = getLocation(); - ByteBuffer bb = ByteBuffer.allocate(42 + name.length() * 2).order(ByteOrder.LITTLE_ENDIAN); - bb.putInt(0); - bb.putFloat((float) loc.getX()); - bb.putFloat((float) loc.getY()); - bb.putFloat((float) loc.getZ()); - bb.putLong(cellId); - bb.putInt(CRC.getCrc(loc.getTerrain().getName())); - bb.put(Encoder.encodeUnicode(name)); - bb.putLong(getObjectId()); - bb.put((byte) color.getValue()); - bb.put((byte) (active ? 1 : 0)); - return bb.array(); - } - - @Override - public byte[] encodeOutOfBandData() { - byte[] encoded = encode(); - ByteBuffer bb = ByteBuffer.allocate(encoded.length + 8).order(ByteOrder.LITTLE_ENDIAN); - bb.putShort((short) 1); // ?? - bb.put(OutOfBandPackage.Type.WAYPOINT.getType()); - bb.putInt(-3); // ?? - bb.put(encoded); - bb.put((byte) 0); + ByteBuffer bb = ByteBuffer.allocate(42 + name.length() * 2); + Packet.addInt(bb, 0); + Packet.addFloat(bb, (float) loc.getX()); + Packet.addFloat(bb, (float) loc.getY()); + Packet.addFloat(bb, (float) loc.getZ()); + Packet.addLong(bb, cellId); + Packet.addCrc(bb, loc.getTerrain().getName()); + Packet.addUnicode(bb, name); + Packet.addLong(bb, getObjectId()); + Packet.addByte(bb, color.getValue()); + Packet.addBoolean(bb, active); return bb.array(); } @Override public int decodeOutOfBandData(ByteBuffer data) { - data.getInt(); // -3 data.getInt(); setLocation(data.getFloat(), data.getFloat(), data.getFloat()); - cellId = data.getLong(); + cellId = data.getLong(); getLocation().setTerrain(Terrain.getTerrainFromCrc(data.getInt())); - name = Packet.getUnicode(data); - data.getLong(); - color = WaypointColor.valueOf(data.get()); - active = Packet.getBoolean(data); - return 46 + name.length() * 2; + System.out.println(getLocation()); + name = Packet.getUnicode(data); + Packet.getLong(data); // objectId + color = WaypointColor.valueOf(data.get()); + active = Packet.getBoolean(data); + return 42 + name.length() * 2; + } + + @Override + public OutOfBandPackage.Type getOobType() { + return OutOfBandPackage.Type.WAYPOINT; + } + + @Override + public int getOobPosition() { + return -3; } @Override @@ -151,7 +146,9 @@ public class WaypointObject extends IntangibleObject implements OutOfBandPackage @Override public String toString() { - return "[WaypointObject] " + getLocation() + " Name: " + name + " Color: " + color + " Active: " + active; + return "WaypointObject[" + + "cellId=" + cellId + ", name='" + name + '\'' + ", color=" + color + ", active=" + active + + ", location=" + getLocation() + "]"; } public enum WaypointColor{ diff --git a/src/services/objects/ObjectCreator.java b/src/services/objects/ObjectCreator.java index bf7f1ddff..024763e6d 100644 --- a/src/services/objects/ObjectCreator.java +++ b/src/services/objects/ObjectCreator.java @@ -51,7 +51,7 @@ import resources.objects.weapon.WeaponObject; public final class ObjectCreator { - public static final SWGObject createObjectFromTemplate(long objectId, String template) { + public static SWGObject createObjectFromTemplate(long objectId, String template) { if (!template.startsWith("object/")) return null; if (!template.endsWith(".iff")) @@ -102,8 +102,8 @@ public final class ObjectCreator { private static void setObjectAttribute(String key, String value, SWGObject object) { switch(key) { - case ObjectData.OBJ_STF: object.setStf(value); break; - case ObjectData.DETAIL_STF: object.setDetailStf(value); break; + case ObjectData.OBJ_STF: object.setStringId(value); break; + case ObjectData.DETAIL_STF: object.setDetailStringId(value); break; case ObjectData.VOLUME_LIMIT: object.setVolume(Integer.valueOf(value)); break; case ObjectData.CONTAINER_TYPE: object.setContainerType(Integer.valueOf(value)); break; default: break;