diff --git a/src/resources/Point3D.java b/src/resources/Point3D.java index ba886aac1..879ff4c6d 100644 --- a/src/resources/Point3D.java +++ b/src/resources/Point3D.java @@ -82,21 +82,20 @@ public class Point3D implements Encodable, Persistable { setZ(z); } + public void translate(Point3D p) { + translate(p.getX(), p.getY(), p.getZ()); + } + + public void translate(double x, double y, double z) { + this.x += x; + this.y += y; + this.z += z; + } + public void rotateAround(double x, double y, double z, Quaternion rot) { - double oX = rot.getX(); - double oY = rot.getY(); - double oZ = rot.getZ(); - double oW = rot.getW(); - double oX2 = oX * oX; - double oY2 = oY * oY; - double oZ2 = oZ * oZ; - double oW2 = oW * oW; - double nX = x + oW2*getX() + 2*oY*oW*getZ() - 2*oZ*oW*getY() + oX2*getX() + 2*oY*oX*getY() + 2*oZ*oX*getZ() - oZ2*getX() - oY2*getX(); - double nY = y + 2*oX*oY*getX() + oY2*getY() + 2*oZ*oY*getZ() + 2*oW*oZ*getX() - oZ2*getY() + oW2*getY() - 2*oX*oW*getZ() - oX2*getY(); - double nZ = z + 2*oX*oZ*getX() + 2*oY*oZ*getY() + oZ2*getZ() - 2*oW*oY*getX() - oY2*getZ() + 2*oW*oX*getY() - oX2*getZ() + oW2*getZ(); - set(nX, nY, nZ); + rot.rotatePoint(this); + translate(x, y, z); } - @Override public byte[] encode() { diff --git a/src/resources/Quaternion.java b/src/resources/Quaternion.java index d54e8999d..50f0bc91f 100644 --- a/src/resources/Quaternion.java +++ b/src/resources/Quaternion.java @@ -36,6 +36,7 @@ import java.nio.ByteBuffer; public class Quaternion implements Encodable, Persistable { + private final double [][] rotationMatrix; private double x; private double y; private double z; @@ -50,6 +51,8 @@ public class Quaternion implements Encodable, Persistable { this.y = y; this.z = z; this.w = w; + this.rotationMatrix = new double[3][3]; + updateRotationMatrix(); } public double getX() { @@ -74,25 +77,30 @@ public class Quaternion implements Encodable, Persistable { public void setX(double x) { this.x = x; + updateRotationMatrix(); } public void setY(double y) { this.y = y; + updateRotationMatrix(); } public void setZ(double z) { this.z = z; + updateRotationMatrix(); } public void setW(double w) { this.w = w; + updateRotationMatrix(); } public void set(double x, double y, double z, double w) { - setX(x); - setY(y); - setZ(z); - setW(w); + this.x = x; + this.y = y; + this.z = z; + this.w = w; + updateRotationMatrix(); } public void set(Quaternion q) { @@ -127,12 +135,20 @@ public class Quaternion implements Encodable, Persistable { normalize(); } + public void rotatePoint(Point3D p) { + double nX = rotationMatrix[0][0]*p.getX() + rotationMatrix[0][1]*p.getY() + rotationMatrix[0][2]*p.getZ(); + double nY = rotationMatrix[1][0]*p.getX() + rotationMatrix[1][1]*p.getY() + rotationMatrix[1][2]*p.getZ(); + double nZ = rotationMatrix[2][0]*p.getX() + rotationMatrix[2][1]*p.getY() + rotationMatrix[2][2]*p.getZ(); + p.set(nX, nY, nZ); + } + public void normalize() { double mag = Math.sqrt(x * x + y * y + z * z + w * w); x /= mag; y /= mag; z /= mag; w /= mag; + updateRotationMatrix(); } @Override @@ -151,6 +167,7 @@ public class Quaternion implements Encodable, Persistable { y = Packet.getFloat(data); z = Packet.getFloat(data); w = Packet.getFloat(data); + updateRotationMatrix(); } @Override @@ -167,9 +184,38 @@ public class Quaternion implements Encodable, Persistable { y = stream.getFloat(); z = stream.getFloat(); w = stream.getFloat(); + updateRotationMatrix(); } public String toString() { return String.format("Quaternion[%.3f, %.3f, %.3f, %.3f]", x, y, z, w); } + + private void updateRotationMatrix() { + double x2 = x * x; + double y2 = y * y; + double z2 = z * z; + double w2 = w * w; + updateRotationMatrixX(x2, y2, z2, w2); + updateRotationMatrixY(x2, y2, z2, w2); + updateRotationMatrixZ(x2, y2, z2, w2); + } + + private void updateRotationMatrixX(double x2, double y2, double z2, double w2) { + rotationMatrix[0][0] = x2 + w2 - y2 - z2; + rotationMatrix[0][1] = 2*y*x - 2*z*w; + rotationMatrix[0][2] = 2*y*w + 2*z*x; + } + + private void updateRotationMatrixY(double x2, double y2, double z2, double w2) { + rotationMatrix[1][0] = 2*x*y + 2*w*z; + rotationMatrix[1][1] = y2 - z2 + w2 - x2; + rotationMatrix[1][2] = 2*z*y - 2*x*w; + } + + private void updateRotationMatrixZ(double x2, double y2, double z2, double w2) { + rotationMatrix[2][0] = 2*x*z - 2*w*y; + rotationMatrix[2][1] = 2*y*z + 2*w*x; + rotationMatrix[2][2] = z2 + w2 - x2 - y2; + } } diff --git a/src/resources/callback/CallbackManager.java b/src/resources/callback/CallbackManager.java index b8fc99266..cb2960cec 100644 --- a/src/resources/callback/CallbackManager.java +++ b/src/resources/callback/CallbackManager.java @@ -29,10 +29,12 @@ package resources.callback; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; public class CallbackManager extends BaseCallbackManager { private final List callbacks; + private final AtomicInteger running; public CallbackManager(String name) { this(name, 1); @@ -41,6 +43,7 @@ public class CallbackManager extends BaseCallbackManager { public CallbackManager(String name, int threadCount) { super(name, threadCount); this.callbacks = new ArrayList<>(); + this.running = new AtomicInteger(0); } public void addCallback(T callback) { @@ -68,8 +71,13 @@ public class CallbackManager extends BaseCallbackManager { } } + public boolean isQueueEmpty() { + return running.get() == 0; + } + public boolean callOnEach(CallCallback call) { synchronized (callbacks) { + running.incrementAndGet(); return call(() -> { for (T callback : callbacks) { try { @@ -78,6 +86,7 @@ public class CallbackManager extends BaseCallbackManager { e.printStackTrace(); } } + running.decrementAndGet(); }); } } diff --git a/src/resources/objects/awareness/AwarenessHandler.java b/src/resources/objects/awareness/AwarenessHandler.java index f7a15305b..7a51926c2 100644 --- a/src/resources/objects/awareness/AwarenessHandler.java +++ b/src/resources/objects/awareness/AwarenessHandler.java @@ -57,6 +57,14 @@ public class AwarenessHandler { } } + public boolean isCallbacksDone() { + for (TerrainMap map : terrains.values()) { + if (!map.isCallbacksDone()) + return false; + } + return true; + } + private void loadTerrainMaps(TerrainMapCallback callback) { for (Terrain t : Terrain.values()) { TerrainMap map = new TerrainMap(t); @@ -113,9 +121,13 @@ public class AwarenessHandler { } public void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) { - moveObject(obj, GONE_LOCATION); - if (disappearObjects) - obj.clearObjectsAware(); + TerrainMap map = getTerrainMap(obj); + if (map != null) { + if (disappearObjects) + map.removeFromMap(obj); + else + map.removeWithoutUpdate(obj); + } if (disappearCustom) obj.clearCustomAware(true); } diff --git a/src/resources/objects/awareness/TerrainMap.java b/src/resources/objects/awareness/TerrainMap.java index 90e299e49..31f09f903 100644 --- a/src/resources/objects/awareness/TerrainMap.java +++ b/src/resources/objects/awareness/TerrainMap.java @@ -78,6 +78,10 @@ public class TerrainMap { callbackManager.setCallback(callback); } + public boolean isCallbacksDone() { + return callbackManager.isQueueEmpty(); + } + public void moveWithinMap(SWGObject obj, Location loc) { obj.setLocation(loc); if (isInAwareness(obj)) { @@ -94,12 +98,16 @@ public class TerrainMap { } public void removeWithoutUpdate(SWGObject obj) { - remove(obj); + if (isInAwareness(obj)) { + remove(obj); + } } public void removeFromMap(SWGObject obj) { - remove(obj); - update(obj); + if (isInAwareness(obj)) { + remove(obj); + update(obj); + } } private void move(SWGObject obj) { diff --git a/test/resources/TestResources.java b/test/resources/TestResources.java index d4f5de53b..ebaa9b365 100644 --- a/test/resources/TestResources.java +++ b/test/resources/TestResources.java @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; -import resources.collections.SWGListTest; +import resources.collections.TestSWGList; import resources.collections.TestSWGBitSet; import resources.collections.TestSWGFlag; import resources.network.TestLittleEndianDataOutputStream; @@ -42,7 +42,7 @@ import resources.services.TestConfig; @RunWith(Suite.class) @SuiteClasses({ - SWGListTest.class, + TestSWGList.class, TestSWGFlag.class, TestSWGBitSet.class, TestWeatherType.class, diff --git a/test/resources/collections/SWGListTest.java b/test/resources/collections/TestSWGList.java similarity index 83% rename from test/resources/collections/SWGListTest.java rename to test/resources/collections/TestSWGList.java index 98916e918..160aeef2a 100644 --- a/test/resources/collections/SWGListTest.java +++ b/test/resources/collections/TestSWGList.java @@ -38,7 +38,7 @@ import java.util.List; /** * Created by Waverunner on 6/7/2015 */ -public class SWGListTest { +public class TestSWGList { @Test public void testAdd() throws Exception { @@ -49,20 +49,14 @@ public class SWGListTest { } SWGList swgList = new SWGList<>(3, 6, Encoder.StringType.ASCII); - long start = System.nanoTime(); Collections.addAll(swgList, strings); - long end = System.nanoTime(); Assert.assertArrayEquals(strings, swgList.toArray()); - System.out.println("SWGList: Took "+ (end-start)/1E6 + "ms to add " + size + " elements"); List list = new ArrayList<>(); - long startList = System.nanoTime(); Collections.addAll(list, strings); - long endList = System.nanoTime(); Assert.assertArrayEquals(swgList.toArray(), list.toArray()); - System.out.println("ArrayList: Took "+ (endList-startList)/1E6 + "ms to add " + size + " elements"); } @Test @@ -85,12 +79,7 @@ public class SWGListTest { SWGList swgList = new SWGList<>(3, 6, Encoder.StringType.ASCII); Collections.addAll(swgList, strings); - - long start = System.nanoTime(); swgList.encode(); - long end = System.nanoTime(); - - System.out.println("SWGList: Took " + (end-start)/1E6 + "ms to encode data"); } } \ No newline at end of file diff --git a/test/resources/objects/awareness/MapCallback.java b/test/resources/objects/awareness/MapCallback.java index 623ac9e2b..4e21e1378 100644 --- a/test/resources/objects/awareness/MapCallback.java +++ b/test/resources/objects/awareness/MapCallback.java @@ -33,32 +33,34 @@ import org.junit.Assert; import resources.objects.SWGObject; import resources.objects.awareness.TerrainMap.TerrainMapCallback; +import resources.server_info.Log; class MapCallback implements TerrainMapCallback { - private final BlockingCounter withinRange = new BlockingCounter(); - private final BlockingCounter outOfRange = new BlockingCounter(); - private final BlockingCounter successfulMove = new BlockingCounter(); - private final BlockingCounter failedMove = new BlockingCounter(); + private final AtomicInteger withinRange = new AtomicInteger(0); + private final AtomicInteger outOfRange = new AtomicInteger(0); + private final AtomicInteger successfulMove = new AtomicInteger(0); + private final AtomicInteger failedMove = new AtomicInteger(0); @Override public void onWithinRange(SWGObject obj, SWGObject inRange) { - withinRange.increment(); + withinRange.incrementAndGet(); } @Override public void onOutOfRange(SWGObject obj, SWGObject outRange) { - outOfRange.increment(); + outOfRange.incrementAndGet(); } @Override public void onMoveSuccess(SWGObject obj) { - successfulMove.increment(); + successfulMove.incrementAndGet(); } @Override public void onMoveFailure(SWGObject obj) { - failedMove.increment(); + failedMove.incrementAndGet(); + Log.e(this, "Move failed!"); } public void set(int withinRange, int outOfRange, int successful, int failed) { @@ -84,18 +86,18 @@ class MapCallback implements TerrainMapCallback { return failedMove.get(); } - public void waitAndTest(int withinRange, int outOfRange, int successful, int failed, long timeout) { - waitFor(withinRange, outOfRange, successful, failed, timeout); - testAssert(withinRange, outOfRange, successful, failed); - } +// public void waitAndTest(int withinRange, int outOfRange, int successful, int failed, long timeout) { +// waitFor(withinRange, outOfRange, successful, failed, timeout); +// testAssert(withinRange, outOfRange, successful, failed); +// } - public void waitFor(int withinRange, int outOfRange, int successful, int failed, long timeout) { - long start = System.nanoTime(); - this.withinRange.waitUntil(withinRange, timeout); - this.outOfRange.waitUntil(outOfRange, timeout-(long)((System.nanoTime()-start)/1E6)); - this.successfulMove.waitUntil(successful, timeout-(long)((System.nanoTime()-start)/1E6)); - this.failedMove.waitUntil(failed, timeout-(long)((System.nanoTime()-start)/1E6)); - } +// public void waitFor(int withinRange, int outOfRange, int successful, int failed, long timeout) { +// long start = System.nanoTime(); +// this.withinRange.waitUntil(withinRange, timeout); +// this.outOfRange.waitUntil(outOfRange, timeout-(long)((System.nanoTime()-start)/1E6)); +// this.successfulMove.waitUntil(successful, timeout-(long)((System.nanoTime()-start)/1E6)); +// this.failedMove.waitUntil(failed, timeout-(long)((System.nanoTime()-start)/1E6)); +// } public void testAssert(int withinRange, int outOfRange, int successful, int failed) { Assert.assertEquals("TEST-WITHIN-RANGE", withinRange, this.withinRange.get()); @@ -104,57 +106,4 @@ class MapCallback implements TerrainMapCallback { Assert.assertEquals("TEST-FAILED-MOVE", failed, this.failedMove.get()); } - private static class BlockingCounter { - - private final Object mutex; - private final AtomicInteger integer; - - public BlockingCounter() { - mutex = new Object(); - integer = new AtomicInteger(0); - } - - public boolean waitUntil(int value, long timeout) { - long start = System.nanoTime(); - while (timeout > 0 && integer.get() < value && waitForChange(timeout)) { - timeout -= (long) ((System.nanoTime()-start)/1E6); - start = System.nanoTime(); - if (integer.get() >= value) - return true; - } - return false; - } - - public boolean waitForChange(long timeout) { - synchronized (mutex) { - int initial = integer.get(); - try { - mutex.wait(timeout); - } catch (InterruptedException e) { - return false; - } - return initial != integer.get(); - } - } - - public void increment() { - integer.incrementAndGet(); - synchronized (mutex) { - mutex.notifyAll(); - } - } - - public int get() { - return integer.get(); - } - - public void set(int i) { - integer.set(i); - synchronized (mutex) { - mutex.notifyAll(); - } - } - - } - } diff --git a/test/resources/objects/awareness/TestAwarenessHandler.java b/test/resources/objects/awareness/TestAwarenessHandler.java index 88afa6a61..59a3decdf 100644 --- a/test/resources/objects/awareness/TestAwarenessHandler.java +++ b/test/resources/objects/awareness/TestAwarenessHandler.java @@ -95,10 +95,12 @@ public class TestAwarenessHandler { GenericCreatureObject creature = new GenericCreatureObject(1); initAwareness(awareness, callback); awareness.moveObject(creature, CREATURE_LOCATION); - callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(WITHIN_RANGE.size(), 0, 1, 0); callback.set(0, 0, 0, 0); awareness.moveObject(creature, CREATURE_LOCATION); - callback.waitAndTest(0, 0, 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(0, 0, 1, 0); } @Test @@ -108,13 +110,16 @@ public class TestAwarenessHandler { initAwareness(awareness, callback); GenericCreatureObject creature = new GenericCreatureObject(1); awareness.moveObject(creature, CREATURE_LOCATION); - callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(WITHIN_RANGE.size(), 0, 1, 0); callback.set(0, 0, 0, 0); awareness.moveObject(creature, new Location(0, 0, 0, Terrain.TATOOINE)); - callback.waitAndTest(0, WITHIN_RANGE.size(), 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(0, WITHIN_RANGE.size(), 1, 0); callback.set(0, 0, 0, 0); awareness.moveObject(creature, CREATURE_LOCATION); - callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(WITHIN_RANGE.size(), 0, 1, 0); } @Test @@ -130,7 +135,8 @@ public class TestAwarenessHandler { Assert.assertNotNull("Starport is null!", starport); initAwareness(awareness, callback); awareness.moveObject(creature, CREATURE_LOCATION); - callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000); + awaitCallbacks(awareness, 1000); + callback.testAssert(WITHIN_RANGE.size(), 0, 1, 0); callback.set(0, 0, 0, 0); awareness.moveObject(creature, starport.getCellByNumber(1), new Location(0, 0, 0, Terrain.TATOOINE)); awareness.moveObject(CREATURE2, CREATURE2_LOCATION); @@ -142,10 +148,21 @@ public class TestAwarenessHandler { for (SWGObject obj : EISLEY_OBJECTS) { awareness.moveObject(obj, obj.getLocation()); } - callback.waitFor(0, 0, EISLEY_OBJECTS.size(), 0, 2000); + awaitCallbacks(awareness, 1000); callback.set(0, 0, 0, 0); } + private void awaitCallbacks(AwarenessHandler awareness, long timeout) { + try { + while (!awareness.isCallbacksDone() && timeout > 0) { + Thread.sleep(1); + timeout--; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private static class MapCallbackRealistic extends MapCallback { @Override diff --git a/test/resources/objects/awareness/TestTerrainMap.java b/test/resources/objects/awareness/TestTerrainMap.java index 02821cc8e..b50670824 100644 --- a/test/resources/objects/awareness/TestTerrainMap.java +++ b/test/resources/objects/awareness/TestTerrainMap.java @@ -60,7 +60,8 @@ public class TestTerrainMap { GenericCreatureObject objB = new GenericCreatureObject(2); map.moveWithinMap(objA, new Location(0, 0, 0, Terrain.TATOOINE)); // 0 - Within Range, 1 - Successful Move map.moveWithinMap(objB, new Location(5, 0, 5, Terrain.TATOOINE)); // 1 - Within Range, 1 - Successful Move - callback.waitAndTest(1, 0, 2, 0, 1000); + awaitCallbacks(map, 1000); + callback.testAssert(1, 0, 2, 0); } finally { map.stop(); } @@ -81,7 +82,8 @@ public class TestTerrainMap { objB.setLocation(new Location(5, 0, 5, Terrain.TATOOINE)); objB.moveToContainer(cell); map.moveToParent(objB, cell); // 0 - Within Range, 0 - Successful Move - callback.waitAndTest(1, 0, 2, 0, 1000); + awaitCallbacks(map, 1000); + callback.testAssert(1, 0, 2, 0); } finally { map.stop(); } @@ -96,9 +98,12 @@ public class TestTerrainMap { map.start(); GenericCreatureObject objA = new GenericCreatureObject(1); GenericCreatureObject objB = new GenericCreatureObject(2); + objA.setPrefLoadRange(200); + objB.setPrefLoadRange(200); map.moveWithinMap(objA, new Location(5, 0, 5, Terrain.TATOOINE)); // 0 - Within Range, 1 - Successful Move map.moveWithinMap(objB, new Location(-5, 0, -5, Terrain.TATOOINE)); // 1 - Within Range, 1 - Successful Move - callback.waitAndTest(1, 0, 2, 0, 1000); + awaitCallbacks(map, 1000); + callback.testAssert(1, 0, 2, 0); } finally { map.stop(); } @@ -135,7 +140,7 @@ public class TestTerrainMap { map.moveWithinMap(obj, obj.getLocation()); } map.moveWithinMap(creature, creatureLocation); - callback.waitFor(0, 0, tatObjects.size()+1, 0, 1000); + awaitCallbacks(map, 1000); Assert.assertEquals(0, withinRange.size()); Assert.assertTrue("TEST-ONLY-WITHIN-RANGE", onlyWithinRange.get()); } finally { @@ -153,4 +158,15 @@ public class TestTerrainMap { return true; } + private void awaitCallbacks(TerrainMap map, long timeout) { + try { + while (!map.isCallbacksDone() && timeout > 0) { + Thread.sleep(1); + timeout--; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }