From 820ddfe4a29a409f56dcedfb727e54fb9b301ea0 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Sun, 21 May 2017 11:27:55 -0500 Subject: [PATCH] Basic updates to existing classes and added EnumLookup --- .../projectswg/common/concurrency/Delay.java | 34 ++++ .../common/control/IntentChain.java | 10 + src/com/projectswg/common/data/CRC.java | 11 +- .../projectswg/common/data/EnumLookup.java | 36 ++++ src/com/projectswg/common/data/RGB.java | 14 +- .../common/data/location/Location.java | 186 ++++++++++------- .../common/data/location/Point3D.java | 19 +- .../common/data/location/Quaternion.java | 19 +- .../common/data/location/Terrain.java | 64 +++--- src/com/projectswg/common/debug/Assert.java | 2 +- .../projectswg/common/encoding/Encodable.java | 5 +- .../projectswg/common/network/NetBuffer.java | 190 ++++++++++++++---- .../common/network/NetBufferStream.java | 10 +- .../projectswg/common/network/TCPServer.java | 36 ++-- 14 files changed, 434 insertions(+), 202 deletions(-) create mode 100644 src/com/projectswg/common/data/EnumLookup.java diff --git a/src/com/projectswg/common/concurrency/Delay.java b/src/com/projectswg/common/concurrency/Delay.java index 23ff005..509e93e 100644 --- a/src/com/projectswg/common/concurrency/Delay.java +++ b/src/com/projectswg/common/concurrency/Delay.java @@ -32,31 +32,65 @@ import java.util.concurrent.locks.LockSupport; public class Delay { + /** + * Sleeps for the specified number of nanoseconds + * @param nanos the number of nanoseconds to sleep + * @return TRUE if this operation has been interrupted + */ public static boolean sleepNano(long nanos) { LockSupport.parkNanos(nanos); return isInterrupted(); } + /** + * Sleeps for the specified number of microseconds + * @param micro the number of microseconds to sleep + * @return TRUE if this operation has been interrupted + */ public static boolean sleepMicro(long micro) { return sleepNano(micro * 1000); } + /** + * Sleeps for the specified number of milliseconds + * @param milli the number of milliseconds to sleep + * @return TRUE if this operation has been interrupted + */ public static boolean sleepMilli(long milli) { return sleepNano(milli * 1000000); } + /** + * Sleeps for the specified number of seconds + * @param sec the number of seconds to sleep + * @return TRUE if this operation has been interrupted + */ public static boolean sleepSeconds(long sec) { return sleepNano(sec * 1000000000); } + /** + * Sleeps for the specified amount of time + * @param time the amount of time to sleep + * @param unit the unit of time + * @return TRUE if this operation has been interrupted + */ public static boolean sleep(long time, TimeUnit unit) { return sleepNano(unit.toNanos(time)); } + /** + * Returns whether or not this thread has been interrupted + * @return TRUE if interrupted, FALSE otherwise + */ public static boolean isInterrupted() { return Thread.currentThread().isInterrupted(); } + /** + * Clears the interrupted flag so future calls to isInterrupted will return + * FALSE + */ public static void clearInterrupted() { Thread.interrupted(); } diff --git a/src/com/projectswg/common/control/IntentChain.java b/src/com/projectswg/common/control/IntentChain.java index 0f92222..459725f 100644 --- a/src/com/projectswg/common/control/IntentChain.java +++ b/src/com/projectswg/common/control/IntentChain.java @@ -27,6 +27,8 @@ ***********************************************************************************/ package com.projectswg.common.control; +import com.projectswg.common.debug.Assert; + public class IntentChain { private final IntentManager intentManager; @@ -64,4 +66,12 @@ public class IntentChain { } } + public static void broadcastChain(Intent ... intents) { + Assert.test(intents.length > 0, "Intent length must be greater than 0!"); + for (int i = 1; i < intents.length; i++) { + intents[i].broadcastAfterIntent(intents[i-1]); + } + intents[0].broadcast(); + } + } diff --git a/src/com/projectswg/common/data/CRC.java b/src/com/projectswg/common/data/CRC.java index b265ccb..d42c231 100644 --- a/src/com/projectswg/common/data/CRC.java +++ b/src/com/projectswg/common/data/CRC.java @@ -27,8 +27,6 @@ ***********************************************************************************/ package com.projectswg.common.data; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import com.projectswg.common.encoding.Encodable; @@ -110,11 +108,16 @@ public class CRC implements Encodable, Persistable { } @Override - public void decode(ByteBuffer data) { - this.crc = data.order(ByteOrder.LITTLE_ENDIAN).getInt(); + public void decode(NetBuffer data) { + this.crc = data.getInt(); this.str = getString(crc); } + @Override + public int getLength() { + return 4; + } + @Override public void save(NetBufferStream stream) { stream.addInt(crc); diff --git a/src/com/projectswg/common/data/EnumLookup.java b/src/com/projectswg/common/data/EnumLookup.java new file mode 100644 index 0000000..cb7d462 --- /dev/null +++ b/src/com/projectswg/common/data/EnumLookup.java @@ -0,0 +1,36 @@ +package com.projectswg.common.data; + +import java.util.HashMap; +import java.util.Map; + +public class EnumLookup> { + + private final Map lookup; + + public EnumLookup(Class c, CustomLookupAdder adder) { + lookup = new HashMap<>(); + for (T t : c.getEnumConstants()) { + lookup.put(adder.getKey(t), t); + } + } + + public T getEnum(K k, T def) { + T t = lookup.get(k); + if (t == null) + return def; + return t; + } + + public boolean containsEnum(K k) { + return lookup.containsKey(k); + } + + public int size() { + return lookup.size(); + } + + public interface CustomLookupAdder { + K getKey(T t); + } + +} diff --git a/src/com/projectswg/common/data/RGB.java b/src/com/projectswg/common/data/RGB.java index 5e0865d..44b395a 100644 --- a/src/com/projectswg/common/data/RGB.java +++ b/src/com/projectswg/common/data/RGB.java @@ -28,7 +28,6 @@ package com.projectswg.common.data; import java.awt.Color; -import java.nio.ByteBuffer; import com.projectswg.common.encoding.Encodable; import com.projectswg.common.network.NetBuffer; @@ -63,10 +62,15 @@ public class RGB implements Encodable { } @Override - public void decode(ByteBuffer data) { - r = data.get(); - g = data.get(); - b = data.get(); + public void decode(NetBuffer data) { + r = data.getByte(); + g = data.getByte(); + b = data.getByte(); + } + + @Override + public int getLength() { + return 3; } public byte getR() { diff --git a/src/com/projectswg/common/data/location/Location.java b/src/com/projectswg/common/data/location/Location.java index ea1b43a..479563d 100644 --- a/src/com/projectswg/common/data/location/Location.java +++ b/src/com/projectswg/common/data/location/Location.java @@ -27,8 +27,6 @@ ***********************************************************************************/ package com.projectswg.common.data.location; -import java.nio.ByteBuffer; - import com.projectswg.common.encoding.Encodable; import com.projectswg.common.network.NetBuffer; import com.projectswg.common.network.NetBufferStream; @@ -44,7 +42,7 @@ public class Location implements Encodable, Persistable { this(Double.NaN, Double.NaN, Double.NaN, null); } - public Location(Location l) { + public Location(Location l) { this(l.getX(), l.getY(), l.getZ(), l.terrain); orientation.set(l.orientation); } @@ -55,19 +53,44 @@ public class Location implements Encodable, Persistable { this.terrain = terrain; } - public void setTerrain(Terrain terrain) { this.terrain = terrain; } - public void setX(double x) { point.setX(x); } - public void setY(double y) { point.setY(y); } - public void setZ(double z) { point.setZ(z); } - public void setOrientationX(double oX) { orientation.setX(oX); } - public void setOrientationY(double oY) { orientation.setY(oY); } - public void setOrientationZ(double oZ) { orientation.setZ(oZ); } - public void setOrientationW(double oW) { orientation.setW(oW); } + public void setTerrain(Terrain terrain) { + this.terrain = terrain; + } + + public void setX(double x) { + point.setX(x); + } + + public void setY(double y) { + point.setY(y); + } + + public void setZ(double z) { + point.setZ(z); + } + + public void setOrientationX(double oX) { + orientation.setX(oX); + } + + public void setOrientationY(double oY) { + orientation.setY(oY); + } + + public void setOrientationZ(double oZ) { + orientation.setZ(oZ); + } + + public void setOrientationW(double oW) { + orientation.setW(oW); + } + public void setPosition(double x, double y, double z) { setX(x); setY(y); setZ(z); } + public void setOrientation(double oX, double oY, double oZ, double oW) { setOrientationX(oX); setOrientationY(oY); @@ -75,24 +98,56 @@ public class Location implements Encodable, Persistable { setOrientationW(oW); } - public Terrain getTerrain() { return terrain; } - public double getX() { return point.getX(); } - public double getY() { return point.getY(); } - public double getZ() { return point.getZ(); } - public Point3D getPosition() { return new Point3D(point); } - public double getOrientationX() { return orientation.getX(); } - public double getOrientationY() { return orientation.getY(); } - public double getOrientationZ() { return orientation.getZ(); } - public double getOrientationW() { return orientation.getW(); } - public Quaternion getOrientation() { return new Quaternion(orientation); } + public Terrain getTerrain() { + return terrain; + } + + public double getX() { + return point.getX(); + } + + public double getY() { + return point.getY(); + } + + public double getZ() { + return point.getZ(); + } + + public Point3D getPosition() { + return new Point3D(point); + } + + public double getOrientationX() { + return orientation.getX(); + } + + public double getOrientationY() { + return orientation.getY(); + } + + public double getOrientationZ() { + return orientation.getZ(); + } + + public double getOrientationW() { + return orientation.getW(); + } + + public Quaternion getOrientation() { + return new Quaternion(orientation); + } public boolean isWithinDistance(Location l, double x, double y, double z) { if (getTerrain() != l.getTerrain()) return false; - double xD = Math.abs(getX() - l.getX()); - double yD = Math.abs(getY() - l.getY()); - double zD = Math.abs(getZ() - l.getZ()); - return xD <= x && yD <= y && zD <= z; + if (Math.abs(getX() - l.getX()) > x) + return false; + if (Math.abs(getY() - l.getY()) > y) + return false; + if (Math.abs(getZ() - l.getZ()) > z) + return false; + return true; } public boolean isWithinDistance(Location l, double radius) { @@ -102,14 +157,14 @@ public class Location implements Encodable, Persistable { public boolean isWithinDistance(Terrain t, double x, double y, double z, double radius) { if (getTerrain() != t) return false; - return square(getX()-x) + square(getY()-y) + square(getZ()-z) <= square(radius); + return square(getX() - x) + square(getY() - y) + square(getZ() - z) <= square(radius); } public boolean isWithinFlatDistance(Location l, double radius) { return isWithinFlatDistance(l.point, radius); } - public boolean isWithinFlatDistance(Point3D target, double radius){ + public boolean isWithinFlatDistance(Point3D target, double radius) { return square(getX() - target.getX()) + square(getZ() - target.getZ()) <= square(radius); } @@ -120,7 +175,7 @@ public class Location implements Encodable, Persistable { } public void translateLocation(Location l) { - point.rotateAround(l.getX(), l.getY(), l.getZ(), l.orientation); + point.rotateAround(l.getX(), l.getY(), l.getZ(), l.orientation); orientation.rotateByQuaternion(l.orientation); } @@ -132,6 +187,7 @@ public class Location implements Encodable, Persistable { /** * Sets the orientation to be facing the specified heading + * * @param heading the heading to face, in degrees */ public void setHeading(double heading) { @@ -140,6 +196,7 @@ public class Location implements Encodable, Persistable { /** * Rotates the orientation by the specified angle along the Y-axis + * * @param angle the angle to rotate by in degrees */ public void rotateHeading(double angle) { @@ -148,6 +205,7 @@ public class Location implements Encodable, Persistable { /** * Rotates the orientation by the specified angle along the specified axises + * * @param angle the angle to rotate by in degrees * @param axisX the amount of rotation about the x-axis * @param axisY the amount of rotation about the x-axis @@ -158,34 +216,13 @@ public class Location implements Encodable, Persistable { } public void mergeWith(Location l) { - if (terrain == null || terrain != l.getTerrain()) - terrain = l.getTerrain(); - mergeLocation(l.getX(), l.getY(), l.getZ()); - mergeOrientation(l); - } - - public void mergeLocation(double lX, double lY, double lZ) { - if (!isEqual(getX(), lX)) - point.setX(lX); - if (!isEqual(getY(), lY)) - point.setY(lY); - if (!isEqual(getZ(), lZ)) - point.setZ(lZ); - } - - private void mergeOrientation(Location l) { - if (!isEqual(getOrientationX(), l.getOrientationX())) - orientation.setX(l.getOrientationX()); - if (!isEqual(getOrientationY(), l.getOrientationY())) - orientation.setY(l.getOrientationY()); - if (!isEqual(getOrientationZ(), l.getOrientationZ())) - orientation.setZ(l.getOrientationZ()); - if (!isEqual(getOrientationW(), l.getOrientationW())) - orientation.setW(l.getOrientationW()); + this.terrain = l.getTerrain(); + this.point.set(l.getX(), l.getY(), l.getZ()); + this.orientation.set(l.getOrientationX(), l.getOrientationY(), l.getOrientationZ(), l.getOrientationW()); } public double getSpeed(Location l, double deltaTime) { - double dist = Math.sqrt(square(getX()-l.getX()) + square(getY()-l.getY()) + square(getZ()-l.getZ())); + double dist = Math.sqrt(square(getX() - l.getX()) + square(getY() - l.getY()) + square(getZ() - l.getZ())); return dist / deltaTime; } @@ -194,9 +231,10 @@ public class Location implements Encodable, Persistable { } private double square(double x) { - return x*x; + return x * x; } + @Override public boolean equals(Object o) { if (!(o instanceof Location)) return false; @@ -223,13 +261,14 @@ public class Location implements Encodable, Persistable { return true; } + @Override public int hashCode() { - return hash(getX())*13 + hash(getY())*17 + hash(getZ())*19 + hash(getOrientationX())*23 + hash(getOrientationY())*29 + hash(getOrientationZ())*31 + hash(getOrientationW())*37; + return hash(getX()) * 13 + hash(getY()) * 17 + hash(getZ()) * 19 + hash(getOrientationX()) * 23 + hash(getOrientationY()) * 29 + hash(getOrientationZ()) * 31 + hash(getOrientationW()) * 37; } private int hash(double x) { long v = Double.doubleToLongBits(x); - return (int)(v^(v>>>32)); + return (int) (v ^ (v >>> 32)); } private boolean isEqual(double x, double y) { @@ -239,26 +278,31 @@ public class Location implements Encodable, Persistable { return false; return Math.abs(x - y) <= 1E-7; } - + @Override public byte[] encode() { NetBuffer buf = NetBuffer.allocate(28); - buf.addFloat(safeEncodeDouble(orientation.getX())); - buf.addFloat(safeEncodeDouble(orientation.getY())); - buf.addFloat(safeEncodeDouble(orientation.getZ())); - buf.addFloat(safeEncodeDouble(orientation.getW())); - buf.addFloat(safeEncodeDouble(point.getX())); - buf.addFloat(safeEncodeDouble(point.getY())); - buf.addFloat(safeEncodeDouble(point.getZ())); + buf.addFloat((float) orientation.getX()); + buf.addFloat((float) orientation.getY()); + buf.addFloat((float) orientation.getZ()); + buf.addFloat((float) orientation.getW()); + buf.addFloat((float) point.getX()); + buf.addFloat((float) point.getY()); + buf.addFloat((float) point.getZ()); return buf.array(); } - + @Override - public void decode(ByteBuffer data) { + public void decode(NetBuffer data) { orientation.decode(data); point.decode(data); } + @Override + public int getLength() { + return 28; + } + @Override public void save(NetBufferStream stream) { stream.addByte(0); @@ -277,7 +321,7 @@ public class Location implements Encodable, Persistable { if (stream.getBoolean()) terrain = Terrain.valueOf(stream.getAscii()); } - + @Override public String toString() { return String.format("Location[TRN=%s, %s %s]", terrain, point, orientation); @@ -295,8 +339,12 @@ public class Location implements Encodable, Persistable { return Math.sqrt(square(dstX - getX()) + square(dstY - getY()) + square(dstZ - getZ())); } - private float safeEncodeDouble(double d) { - return (float) (Double.isNaN(d) ? 0 : d); + public double flatDistanceTo(Location destination) { + return flatDistanceTo(destination.getX(), destination.getZ()); + } + + public double flatDistanceTo(double dstX, double dstZ) { + return Math.sqrt(square(dstX - getX()) + square(dstZ - getZ())); } } diff --git a/src/com/projectswg/common/data/location/Point3D.java b/src/com/projectswg/common/data/location/Point3D.java index 851fa75..f26a28f 100644 --- a/src/com/projectswg/common/data/location/Point3D.java +++ b/src/com/projectswg/common/data/location/Point3D.java @@ -27,8 +27,6 @@ ***********************************************************************************/ package com.projectswg.common.data.location; -import java.nio.ByteBuffer; - import com.projectswg.common.encoding.Encodable; import com.projectswg.common.network.NetBuffer; import com.projectswg.common.network.NetBufferStream; @@ -107,11 +105,15 @@ public class Point3D implements Encodable, Persistable { } @Override - public void decode(ByteBuffer data) { - NetBuffer buf = NetBuffer.wrap(data); - x = buf.getFloat(); - y = buf.getFloat(); - z = buf.getFloat(); + public void decode(NetBuffer data) { + x = data.getFloat(); + y = data.getFloat(); + z = data.getFloat(); + } + + @Override + public int getLength() { + return 12; } @Override @@ -128,6 +130,7 @@ public class Point3D implements Encodable, Persistable { z = stream.getFloat(); } + @Override public boolean equals(Object o) { if (!(o instanceof Point3D)) return false; @@ -140,10 +143,12 @@ public class Point3D implements Encodable, Persistable { return true; } + @Override public int hashCode() { return Double.hashCode(getX()) ^ Double.hashCode(getY()) ^ Double.hashCode(getZ()); } + @Override public String toString() { return String.format("Point3D[%.2f, %.2f, %.2f]", x, y, z); } diff --git a/src/com/projectswg/common/data/location/Quaternion.java b/src/com/projectswg/common/data/location/Quaternion.java index d5f8c03..a5efb58 100644 --- a/src/com/projectswg/common/data/location/Quaternion.java +++ b/src/com/projectswg/common/data/location/Quaternion.java @@ -27,8 +27,6 @@ ***********************************************************************************/ package com.projectswg.common.data.location; -import java.nio.ByteBuffer; - import com.projectswg.common.encoding.Encodable; import com.projectswg.common.network.NetBuffer; import com.projectswg.common.network.NetBufferStream; @@ -162,15 +160,19 @@ public class Quaternion implements Encodable, Persistable { } @Override - public void decode(ByteBuffer data) { - NetBuffer buf = NetBuffer.wrap(data); - x = buf.getFloat(); - y = buf.getFloat(); - z = buf.getFloat(); - w = buf.getFloat(); + public void decode(NetBuffer data) { + x = data.getFloat(); + y = data.getFloat(); + z = data.getFloat(); + w = data.getFloat(); updateRotationMatrix(); } + @Override + public int getLength() { + return 16; + } + @Override public void save(NetBufferStream stream) { stream.addFloat((float) x); @@ -188,6 +190,7 @@ public class Quaternion implements Encodable, Persistable { updateRotationMatrix(); } + @Override public String toString() { return String.format("Quaternion[%.3f, %.3f, %.3f, %.3f]", x, y, z, w); } diff --git a/src/com/projectswg/common/data/location/Terrain.java b/src/com/projectswg/common/data/location/Terrain.java index 0e9b747..fe7b9c7 100644 --- a/src/com/projectswg/common/data/location/Terrain.java +++ b/src/com/projectswg/common/data/location/Terrain.java @@ -28,11 +28,10 @@ package com.projectswg.common.data.location; import java.util.Locale; -import java.util.Map; import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; import com.projectswg.common.data.CRC; +import com.projectswg.common.data.EnumLookup; public enum Terrain { ADVENTURE1 ("terrain/adventure1.trn"), @@ -103,35 +102,34 @@ public enum Terrain { WATERTABLETEST ("terrain/watertabletest.trn"), YAVIN4 ("terrain/yavin4.trn"); - private static final Map CRC_TO_NAME = new ConcurrentHashMap<>(); - private static final Map NAME_TO_CRC = new ConcurrentHashMap<>(); - private static final Map NAME_TO_TERRAIN = new ConcurrentHashMap<>(); - private static final Map CRC_TO_TERRAIN = new ConcurrentHashMap<>(); + private static final EnumLookup CRC_LOOKUP = new EnumLookup<>(Terrain.class, t -> t.getCrc()); + private static final EnumLookup NAME_LOOKUP = new EnumLookup<>(Terrain.class, t -> t.getName()); private String file; private String name; private int crc; - static { - for (Terrain p : values()) { - CRC_TO_TERRAIN.put(p.getCrc(), p); - CRC_TO_NAME.put(p.getCrc(), p.name()); - NAME_TO_CRC.put(p.name().toLowerCase(Locale.US), p.getCrc()); - NAME_TO_CRC.put(p.name().toLowerCase(Locale.US), p.getCrc()); - NAME_TO_TERRAIN.put(p.name().toLowerCase(Locale.US), p); - } - } - Terrain(String file) { this.file = file; this.name = file.substring(8, file.length() - 4); this.crc = CRC.getCrc(name); } - public String getFile() { return file; } - public String getName() { return name; } - public String getNameCapitalized() { return Character.toUpperCase(name.charAt(0)) + name.substring(1); } - public int getCrc() { return crc; } + public String getFile() { + return file; + } + + public String getName() { + return name; + } + + public String getNameCapitalized() { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + public int getCrc() { + return crc; + } public Location getStartLocation() { Location l = new Location(); @@ -149,45 +147,35 @@ public enum Terrain { } public static int getTerrainCount() { - return CRC_TO_TERRAIN.size(); + return CRC_LOOKUP.size(); } public static String getNameFromCrc(int crc) { - String name = CRC_TO_NAME.get(crc); - if (name == null) - return ""; - return name; + Terrain t = CRC_LOOKUP.getEnum(crc, null); + return (t == null) ? "" : t.getName(); } public static int getCrcFromName(String name) { - Integer crc = NAME_TO_CRC.get(name.toLowerCase(Locale.ENGLISH)); - if (crc == null) - return 0; - return crc; + Terrain t = NAME_LOOKUP.getEnum(name.toLowerCase(Locale.ENGLISH), null); + return (t == null) ? 0 : t.getCrc(); } /** * Note: Defaults to TATOOINE */ public static Terrain getTerrainFromCrc(int crc) { - Terrain p = CRC_TO_TERRAIN.get(crc); - if (p == null) - return TATOOINE; - return p; + return CRC_LOOKUP.getEnum(crc, null); } /** * Note: Defaults to TATOOINE */ public static Terrain getTerrainFromName(String name) { - Terrain p = NAME_TO_TERRAIN.get(name.toLowerCase(Locale.ENGLISH)); - if (p == null) - return TATOOINE; - return p; + return NAME_LOOKUP.getEnum(name.toLowerCase(Locale.ENGLISH), null); } public static boolean doesTerrainExistForName(String name) { - return NAME_TO_TERRAIN.containsKey(name.toLowerCase(Locale.ENGLISH)); + return NAME_LOOKUP.containsEnum(name.toLowerCase(Locale.ENGLISH)); } } diff --git a/src/com/projectswg/common/debug/Assert.java b/src/com/projectswg/common/debug/Assert.java index d9e0ae7..091e24f 100644 --- a/src/com/projectswg/common/debug/Assert.java +++ b/src/com/projectswg/common/debug/Assert.java @@ -92,7 +92,7 @@ public class Assert { Log.e(e); } - private static class AssertionException extends RuntimeException { + public static class AssertionException extends RuntimeException { private static final long serialVersionUID = 1L; diff --git a/src/com/projectswg/common/encoding/Encodable.java b/src/com/projectswg/common/encoding/Encodable.java index bbc5b80..77affff 100644 --- a/src/com/projectswg/common/encoding/Encodable.java +++ b/src/com/projectswg/common/encoding/Encodable.java @@ -26,9 +26,10 @@ ******************************************************************************/ package com.projectswg.common.encoding; -import java.nio.ByteBuffer; +import com.projectswg.common.network.NetBuffer; public interface Encodable { + void decode(NetBuffer data); byte [] encode(); - void decode(ByteBuffer data); + int getLength(); } diff --git a/src/com/projectswg/common/network/NetBuffer.java b/src/com/projectswg/common/network/NetBuffer.java index e7ec30b..3bb1e00 100644 --- a/src/com/projectswg/common/network/NetBuffer.java +++ b/src/com/projectswg/common/network/NetBuffer.java @@ -29,8 +29,10 @@ package com.projectswg.common.network; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import com.projectswg.common.debug.Log; import com.projectswg.common.encoding.Encodable; @@ -39,8 +41,8 @@ import com.projectswg.common.encoding.StringType; public class NetBuffer { - public static final Charset ASCII = Charset.forName("UTF-8"); - public static final Charset UNICODE = Charset.forName("UTF-16LE"); + public final Charset ASCII = Charset.forName("UTF-8"); + public final Charset UNICODE = Charset.forName("UTF-16LE"); private final ByteBuffer data; private final int size; @@ -74,6 +76,14 @@ public class NetBuffer { return data.position(); } + public int limit() { + return data.limit(); + } + + public int capacity() { + return data.capacity(); + } + public void position(int position) { data.position(position); } @@ -82,32 +92,32 @@ public class NetBuffer { data.position(data.position()+relative); } + public void flip() { + data.flip(); + } + public ByteBuffer getBuffer() { return data; } + public void add(NetBuffer buffer) { + add(buffer.getBuffer()); + } + + public void add(ByteBuffer buffer) { + data.put(buffer); + } + public void addBoolean(boolean b) { data.put(b ? (byte)1 : (byte)0); } public void addAscii(String s) { - data.order(ByteOrder.LITTLE_ENDIAN); - data.putShort((short)s.length()); - data.put(s.getBytes(ASCII)); - } - - public void addAscii(char [] s) { - data.order(ByteOrder.LITTLE_ENDIAN); - data.putShort((short)s.length); - ByteBuffer bb = ASCII.encode(CharBuffer.wrap(s)); - byte [] bData = new byte[bb.limit()]; - bb.get(bData); - data.put(bData); + addArray(s.getBytes(ASCII)); } public void addUnicode(String s) { - data.order(ByteOrder.LITTLE_ENDIAN); - data.putInt(s.length()); + addInt(s.length()); data.put(s.getBytes(UNICODE)); } @@ -120,7 +130,7 @@ public class NetBuffer { } public void addFloat(float f) { - data.putFloat(f); + data.order(ByteOrder.LITTLE_ENDIAN).putFloat(f); } public void addShort(int i) { @@ -148,10 +158,47 @@ public class NetBuffer { data.put(b); } + public void addArrayLarge(byte [] b) { + addInt(b.length); + data.put(b); + } + public void addRawArray(byte [] b) { data.put(b); } + public void addList(Collection list) { + if (list == null) { + addInt(0); + return; + } + + addInt(list.size()); + for (Encodable encodable : list) { + addEncodable(encodable); + } + } + + public void addList(List list, StringType type) { + addInt(list.size()); + + switch (type) { + case ASCII: + for (String s : list) { + addAscii(s); + } + break; + case UNICODE: + for (String s : list) { + addUnicode(s); + } + break; + default: + Log.e("Cannot encode StringType " + type); + break; + } + } + public void addEncodable(Encodable e) { data.put(e.encode()); } @@ -161,23 +208,11 @@ public class NetBuffer { } public String getAscii() { - data.order(ByteOrder.LITTLE_ENDIAN); - short length = data.getShort(); - if (length > data.remaining()) - return ""; - byte [] str = new byte[length]; - data.get(str); - return new String(str, ASCII); + return new String(getArray(), ASCII); } public String getUnicode() { - data.order(ByteOrder.LITTLE_ENDIAN); - int length = data.getInt() * 2; - if (length > data.remaining()) - return ""; - byte [] str = new byte[length]; - data.get(str); - return new String(str, UNICODE); + return new String(getArray(getInt() * 2), UNICODE); } public String getString(StringType type) { @@ -221,9 +256,11 @@ public class NetBuffer { } public byte [] getArray() { - byte [] bData = new byte[getShort()]; - data.get(bData); - return bData; + return getArray(getShort()); + } + + public byte [] getArrayLarge() { + return getArray(getInt()); } public byte [] getArray(int size) { @@ -232,12 +269,33 @@ public class NetBuffer { return bData; } + public int [] getIntArray() { + int [] ints = new int[getInt()]; + for (int i = 0; i < ints.length; i++) + ints[i] = getInt(); + return ints; + } + + public int [] getIntArray(int size) { + int [] ints = new int[size]; + for (int i = 0; i < ints.length; i++) + ints[i] = getInt(); + return ints; + } + + public boolean[] getBooleanArray() { + boolean[] booleans = new boolean[getInt()]; + for(int i = 0; i < booleans.length; i++) + booleans[i] = getBoolean(); + return booleans; + } + public Object getGeneric(Class type) { if (Encodable.class.isAssignableFrom(type)) { T instance = null; try { instance = type.newInstance(); - ((Encodable) instance).decode(data); + ((Encodable) instance).decode(this); } catch (InstantiationException | IllegalAccessException e) { Log.e(e); } @@ -256,11 +314,69 @@ public class NetBuffer { return null; } + public List getList(Class type) { + int size = getInt(); + + if (size < 0) { + Log.e("Read list with size less than zero!"); + return null; + } else if (size == 0) { + return new ArrayList<>(); + } + + List list = new ArrayList<>(); + + try { + for (int i = 0; i < size; i++) { + T instance = type.newInstance(); + instance.decode(this); + list.add(instance); + } + } catch (InstantiationException | IllegalAccessException e) { + Log.e(e); + } + + if (size != list.size()) + Log.e("Expected list size %d but only have %d elements in the list", size, list.size()); + return list; + } + + public List getList(StringType type) { + int size = getInt(); + + if (size < 0) { + Log.e("Read list with size less than zero!"); + return null; + } else if (size == 0) { + return new ArrayList<>(); + } + + List list = new ArrayList<>(); + + switch (type) { + case ASCII: + for (int i = 0; i < size; i++) { + list.add(getAscii()); + } + break; + case UNICODE: + for (int i = 0; i < size; i++) { + list.add(getUnicode()); + } + break; + default: + Log.e("Do not know how to read list of StringType " + type); + break; + } + + return list; + } + public T getEncodable(Class type) { T instance = null; try { instance = type.newInstance(); - instance.decode(data); + instance.decode(this); } catch (InstantiationException | IllegalAccessException e) { Log.e(e); } diff --git a/src/com/projectswg/common/network/NetBufferStream.java b/src/com/projectswg/common/network/NetBufferStream.java index e8e6438..1168d6e 100644 --- a/src/com/projectswg/common/network/NetBufferStream.java +++ b/src/com/projectswg/common/network/NetBufferStream.java @@ -110,10 +110,12 @@ public class NetBufferStream extends OutputStream { } } + @Override public void write(byte [] data) { write(data, 0, data.length); } + @Override public void write(byte [] data, int offset, int length) { ensureCapacity(size + length); synchronized (bufferMutex) { @@ -281,14 +283,6 @@ public class NetBufferStream extends OutputStream { } } - public void addAscii(char[] s) { - ensureCapacity(size+2+s.length); - synchronized (bufferMutex) { - buffer.addAscii(s); - size += 2 + s.length; - } - } - public void addUnicode(String s) { ensureCapacity(size+4+s.length()*2); synchronized (bufferMutex) { diff --git a/src/com/projectswg/common/network/TCPServer.java b/src/com/projectswg/common/network/TCPServer.java index 6b01076..f42efbb 100644 --- a/src/com/projectswg/common/network/TCPServer.java +++ b/src/com/projectswg/common/network/TCPServer.java @@ -30,7 +30,6 @@ package com.projectswg.common.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.ClosedByInterruptException; @@ -95,12 +94,12 @@ public class TCPServer { Assert.notNull(sock); synchronized (sockets) { SocketChannel sc = sockets.get(sock); - Assert.notNull(sc); + if (sc == null) + return true; sockets.remove(sock); try { - Socket s = sc.socket(); sc.close(); - callbackManager.callOnEach((callback) -> callback.onConnectionDisconnect(s, sock)); + callbackManager.callOnEach((callback) -> callback.onConnectionDisconnect(sc, sock)); return true; } catch (IOException e) { Log.e(e); @@ -133,22 +132,10 @@ public class TCPServer { return false; } - public boolean send(InetSocketAddress sock, ByteBuffer data) { + public SocketChannel getChannel(SocketAddress sock) { synchronized (sockets) { - SocketChannel sc = sockets.get(sock); - Assert.notNull(sc); - Assert.test(sc.isConnected()); - Assert.test(data.hasRemaining()); - try { - while (data.hasRemaining()) - sc.write(data); - return true; - } catch (IOException e) { - Log.e("TCPServer", "Terminated connection with %s. Error: %s", sock.toString(), e.getMessage()); - disconnect(sc); - } + return sockets.get(sock); } - return false; } public void setCallback(TCPCallback callback) { @@ -156,9 +143,9 @@ public class TCPServer { } public interface TCPCallback { - void onIncomingConnection(Socket s); - void onConnectionDisconnect(Socket s, SocketAddress addr); - void onIncomingData(Socket s, byte [] data); + void onIncomingConnection(SocketChannel s, SocketAddress addr); + void onConnectionDisconnect(SocketChannel s, SocketAddress addr); + void onIncomingData(SocketChannel s, SocketAddress addr, byte [] data); } private class TCPServerListener implements Runnable { @@ -186,6 +173,7 @@ public class TCPServer { thread = null; } + @Override public void run() { try (Selector selector = setupSelector()) { while (running) { @@ -241,7 +229,8 @@ public class TCPServer { sockets.put(sc.getRemoteAddress(), sc); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); - callbackManager.callOnEach((callback) -> callback.onIncomingConnection(sc.socket())); + SocketAddress addr = sc.getRemoteAddress(); + callbackManager.callOnEach((callback) -> callback.onIncomingConnection(sc, addr)); } } catch (IOException e) { Log.e(e); @@ -260,7 +249,8 @@ public class TCPServer { } else if (n > 0) { ByteBuffer smaller = ByteBuffer.allocate(n); smaller.put(buffer); - callbackManager.callOnEach((callback) -> callback.onIncomingData(s.socket(), smaller.array())); + SocketAddress addr = s.getRemoteAddress(); + callbackManager.callOnEach((callback) -> callback.onIncomingData(s, addr, smaller.array())); return true; } } catch (IOException e) {