diff --git a/src/network/packets/swg/zone/object_controller/DataTransformWithParent.java b/src/network/packets/swg/zone/object_controller/DataTransformWithParent.java
index 0e11db38..6dacd981 100644
--- a/src/network/packets/swg/zone/object_controller/DataTransformWithParent.java
+++ b/src/network/packets/swg/zone/object_controller/DataTransformWithParent.java
@@ -99,7 +99,7 @@ public class DataTransformWithParent extends ObjectController {
this.useLookAtYaw = useLookAtYaw;
}
- public int getCounter() {
+ public int getUpdateCounter() {
return counter;
}
diff --git a/src/resources/Location.java b/src/resources/Location.java
index b08120bb..011712d8 100644
--- a/src/resources/Location.java
+++ b/src/resources/Location.java
@@ -107,6 +107,10 @@ public class Location implements Encodable, Persistable {
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){
return square(getX() - target.getX()) + square(getZ() - target.getZ()) <= square(radius);
}
diff --git a/src/resources/Point3D.java b/src/resources/Point3D.java
index 6455446e..ba886aac 100644
--- a/src/resources/Point3D.java
+++ b/src/resources/Point3D.java
@@ -87,9 +87,13 @@ public class Point3D implements Encodable, Persistable {
double oY = rot.getY();
double oZ = rot.getZ();
double oW = rot.getW();
- double nX = x + oW*oW*getX() + 2*oY*oW*getZ() - 2*oZ*oW*getY() + oX*oX*getX() + 2*oY*oX*getY() + 2*oZ*oX*getZ() - oZ*oZ*getX() - oY*oY*getX();
- double nY = y + 2*oX*oY*getX() + oY*oY*getY() + 2*oZ*oY*getZ() + 2*oW*oZ*getX() - oZ*oZ*getY() + oW*oW*getY() - 2*oX*oW*getZ() - oX*oX*getY();
- double nZ = z + 2*oX*oZ*getX() + 2*oY*oZ*getY() + oZ*oZ*getZ() - 2*oW*oY*getX() - oY*oY*getZ() + 2*oW*oX*getY() - oX*oX*getZ() + oW*oW*getZ();
+ 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);
}
diff --git a/src/resources/Terrain.java b/src/resources/Terrain.java
index 9dbdefd1..82637d7d 100644
--- a/src/resources/Terrain.java
+++ b/src/resources/Terrain.java
@@ -148,6 +148,10 @@ public enum Terrain {
return l;
}
+ public static int getTerrainCount() {
+ return CRC_TO_TERRAIN.size();
+ }
+
public static String getNameFromCrc(int crc) {
String name = CRC_TO_NAME.get(crc);
if (name == null)
diff --git a/src/resources/buildout/BuildoutAreaGrid.java b/src/resources/buildout/BuildoutAreaGrid.java
index 5501d237..60c9f097 100644
--- a/src/resources/buildout/BuildoutAreaGrid.java
+++ b/src/resources/buildout/BuildoutAreaGrid.java
@@ -78,7 +78,11 @@ public class BuildoutAreaGrid {
public BuildoutArea getBuildoutArea(Terrain t, double x, double z) {
BuildoutAreaNode node;
synchronized (grid) {
- node = grid[getNodeIndex(z)][getNodeIndex(x)];
+ int zInd = getNodeIndex(z);
+ int xInd = getNodeIndex(x);
+ if (xInd < 0 || xInd >= grid.length || zInd < 0 || zInd >= grid.length)
+ return null;
+ node = grid[zInd][xInd];
if (node == null)
return null;
}
diff --git a/src/resources/callback/BaseCallbackManager.java b/src/resources/callback/BaseCallbackManager.java
new file mode 100644
index 00000000..66303187
--- /dev/null
+++ b/src/resources/callback/BaseCallbackManager.java
@@ -0,0 +1,106 @@
+/***********************************************************************************
+* 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.callback;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import utilities.ThreadUtilities;
+
+class BaseCallbackManager {
+
+ private final Object executorMutex;
+ private final String name;
+ private final int threadCount;
+ private final AtomicBoolean shutdown;
+ private ExecutorService executor;
+
+ public BaseCallbackManager(String name) {
+ this(name, 1);
+ }
+
+ public BaseCallbackManager(String name, int threadCount) {
+ this.executorMutex = new Object();
+ this.name = name;
+ this.threadCount = threadCount;
+ this.shutdown = new AtomicBoolean(false);
+ this.executor = null;
+ }
+
+ public void start() {
+ synchronized (executorMutex) {
+ if (executor != null && !shutdown.get())
+ executor.shutdown();
+ shutdown.set(false);
+ if (threadCount <= 1)
+ executor = Executors.newSingleThreadExecutor(ThreadUtilities.newThreadFactory(name+"-callback-manager"));
+ else
+ executor = Executors.newFixedThreadPool(threadCount, ThreadUtilities.newThreadFactory(name+"-callback-manager-%d"));
+ }
+ }
+
+ public void stop() {
+ synchronized (executorMutex) {
+ executor.shutdown();
+ shutdown.set(true);
+ }
+ }
+
+ public boolean awaitTermination(long timeout, TimeUnit unit) {
+ synchronized (executorMutex) {
+ if (executor == null)
+ return true;
+ if (!shutdown.get())
+ throw new IllegalStateException("Cannot wait for termination when never stopped!");
+ try {
+ return executor.awaitTermination(timeout, unit);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ }
+
+ protected boolean call(Runnable r) {
+ synchronized (executorMutex) {
+ if (executor == null)
+ throw new IllegalStateException("Manager has not been started!");
+ if (shutdown.get())
+ return false;
+ try {
+ executor.execute(r);
+ return true;
+ } catch (RejectedExecutionException e) {
+ return false;
+ }
+ }
+ }
+
+}
diff --git a/src/resources/callback/CallbackManager.java b/src/resources/callback/CallbackManager.java
new file mode 100644
index 00000000..b8fc9926
--- /dev/null
+++ b/src/resources/callback/CallbackManager.java
@@ -0,0 +1,89 @@
+/***********************************************************************************
+* 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.callback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CallbackManager extends BaseCallbackManager {
+
+ private final List callbacks;
+
+ public CallbackManager(String name) {
+ this(name, 1);
+ }
+
+ public CallbackManager(String name, int threadCount) {
+ super(name, threadCount);
+ this.callbacks = new ArrayList<>();
+ }
+
+ public void addCallback(T callback) {
+ synchronized (callbacks) {
+ callbacks.add(callback);
+ }
+ }
+
+ public void removeCallback(T callback) {
+ synchronized (callbacks) {
+ callbacks.remove(callback);
+ }
+ }
+
+ public void setCallback(T callback) {
+ synchronized (callbacks) {
+ callbacks.clear();
+ callbacks.add(callback);
+ }
+ }
+
+ public void clearCallbacks() {
+ synchronized (callbacks) {
+ callbacks.clear();
+ }
+ }
+
+ public boolean callOnEach(CallCallback call) {
+ synchronized (callbacks) {
+ return call(() -> {
+ for (T callback : callbacks) {
+ try {
+ call.run(callback);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ }
+
+
+ public interface CallCallback {
+ void run(T callback);
+ }
+}
diff --git a/src/resources/commands/callbacks/SocialInternalCmdCallback.java b/src/resources/commands/callbacks/SocialInternalCmdCallback.java
index 67308aa1..f52657c1 100644
--- a/src/resources/commands/callbacks/SocialInternalCmdCallback.java
+++ b/src/resources/commands/callbacks/SocialInternalCmdCallback.java
@@ -47,9 +47,8 @@ public class SocialInternalCmdCallback implements ICmdCallback {
PlayerEmote emote = new PlayerEmote(objectId, objectId, ((target == null) ? 0 : target.getObjectId()), Short.valueOf(cmd[1]));
player.sendPacket(emote);
- for (SWGObject aware : player.getCreatureObject().getObservers()) {
- if (aware.getOwner() != null)
- aware.getOwner().sendPacket(new PlayerEmote(aware.getObjectId(), emote));
+ for (Player aware : player.getCreatureObject().getObservers()) {
+ aware.sendPacket(new PlayerEmote(aware.getCreatureObject().getObjectId(), emote));
}
}
}
diff --git a/src/resources/commands/callbacks/TransferItemCallback.java b/src/resources/commands/callbacks/TransferItemCallback.java
index 9fe8a192..0f426550 100644
--- a/src/resources/commands/callbacks/TransferItemCallback.java
+++ b/src/resources/commands/callbacks/TransferItemCallback.java
@@ -94,8 +94,8 @@ public class TransferItemCallback implements ICmdCallback {
return;
}
- // A wearable container cannot be the child of a wearable container
- if (newContainer.getContainerType() == 2 && target.getContainerType() == 2) {
+ // A container can only be the child of another container if the other container has a larger volume
+ if (newContainer.getContainerType() == 2 && target.getContainerType() == 2 && target.getMaxContainerSize()>= newContainer.getMaxContainerSize()) {
new ChatBroadcastIntent(player, "@container_error_message:container12").broadcast();
return;
}
diff --git a/src/resources/control/Service.java b/src/resources/control/Service.java
index a9c4052e..b14cec4c 100644
--- a/src/resources/control/Service.java
+++ b/src/resources/control/Service.java
@@ -27,11 +27,7 @@
***********************************************************************************/
package resources.control;
-import network.OutboundPacketService;
-import network.packets.Packet;
import resources.config.ConfigFile;
-import resources.player.Player;
-import resources.player.PlayerState;
import resources.server_info.Config;
import resources.server_info.DataManager;
import resources.server_info.Log;
@@ -43,8 +39,6 @@ import resources.server_info.RelationalDatabase;
*/
public abstract class Service implements IntentReceiver {
- private static final OutboundPacketService outboundPacketService = new OutboundPacketService();
-
public Service() {
IntentManager.getInstance().initialize();
}
@@ -120,28 +114,6 @@ public abstract class Service implements IntentReceiver {
Log.w(this, "Warning: " + getClass().getSimpleName() + " did not override onIntentReceived");
}
- /**
- * Stores the packet(s) in a buffer until flushPackets() is called.
- * flushPackets() is called every couple milliseconds.
- * @param player the player to send the packet(s) to
- * @param packets the packet(s) to send
- */
- public void sendPacket(Player player, Packet ... packets) {
- if (player.getPlayerState() == PlayerState.DISCONNECTED)
- return;
- sendPacket(player.getNetworkId(), packets);
- }
-
- /**
- * Stores the packet(s) in a buffer until flushPackets() is called.
- * flushPackets() is called every couple milliseconds if you forget.
- * @param networkId the network id to send the packet(s) to
- * @param packets the packet(s) to send
- */
- public void sendPacket(final long networkId, final Packet ... packets) {
- outboundPacketService.send(networkId, packets);
- }
-
/**
* Gets the config object associated with a certain file, or NULL if the
* file failed to load on startup
diff --git a/src/resources/network/DeltaBuilder.java b/src/resources/network/DeltaBuilder.java
index 6d02fe19..9c17ec31 100644
--- a/src/resources/network/DeltaBuilder.java
+++ b/src/resources/network/DeltaBuilder.java
@@ -30,7 +30,6 @@ package resources.network;
import network.packets.swg.zone.baselines.Baseline.BaselineType;
import network.packets.swg.zone.deltas.DeltasMessage;
import resources.objects.SWGObject;
-import resources.objects.creature.CreatureObject;
import resources.player.Player;
import resources.player.PlayerState;
import utilities.Encoder;
@@ -65,8 +64,9 @@ public class DeltaBuilder {
public void send() {
DeltasMessage message = getBuiltMessage();
- boolean sendSelf = true;
- if (object instanceof CreatureObject && ((CreatureObject) object).isLoggedInPlayer() && object.getOwner().getPlayerState() != PlayerState.ZONED_IN)
+ Player owner = object.getOwner();
+ boolean sendSelf = owner != null;
+ if (owner == null || owner.getPlayerState() != PlayerState.ZONED_IN)
sendSelf = false;
switch(num) {
case 3:
diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java
index 93439d5e..79536120 100644
--- a/src/resources/objects/SWGObject.java
+++ b/src/resources/objects/SWGObject.java
@@ -32,11 +32,7 @@ import network.packets.swg.zone.SceneCreateObjectByCrc;
import network.packets.swg.zone.SceneDestroyObject;
import network.packets.swg.zone.SceneEndBaselines;
import network.packets.swg.zone.UpdateContainmentMessage;
-import network.packets.swg.zone.UpdateTransformMessage;
-import network.packets.swg.zone.UpdateTransformWithParentMessage;
import network.packets.swg.zone.baselines.Baseline.BaselineType;
-import network.packets.swg.zone.object_controller.DataTransform;
-import network.packets.swg.zone.object_controller.DataTransformWithParent;
import resources.Location;
import resources.Terrain;
import resources.buildout.BuildoutArea;
@@ -49,15 +45,16 @@ import resources.network.BaselineBuilder;
import resources.network.BaselineObject;
import resources.network.NetBuffer;
import resources.network.NetBufferStream;
+import resources.objects.awareness.ObjectAware;
import resources.objects.building.BuildingObject;
import resources.objects.creature.CreatureObject;
import resources.persistable.Persistable;
import resources.persistable.SWGObjectFactory;
import resources.player.Player;
-import resources.player.PlayerState;
import resources.server_info.Log;
import services.CoreManager;
import services.objects.ObjectCreator;
+import utilities.AwarenessUtilities;
import java.util.ArrayList;
import java.util.Collection;
@@ -74,12 +71,11 @@ public abstract class SWGObject extends BaselineObject implements Comparable containedObjects= new HashSet<>();
- private final Map attributes = new LinkedHashMap<>();
- private final Set objectsAware = new HashSet<>();
- private final Set customAware = new HashSet<>();
private final HashMap slots = new HashMap<>(); // HashMap used for null value support
- private final Map dataAttributes = new HashMap<>();
- private final AtomicInteger updateCounter = new AtomicInteger(1);
+ private final transient Map attributes = new LinkedHashMap<>();
+ private final transient ObjectAware awareness = new ObjectAware(this);
+ private final transient Map dataAttributes = new HashMap<>();
+ private final transient AtomicInteger updateCounter = new AtomicInteger(1);
private ObjectClassification classification = ObjectClassification.GENERATED;
private GameObjectType gameObjectType = GameObjectType.GOT_NONE;
@@ -97,7 +93,8 @@ public abstract class SWGObject extends BaselineObject implements Comparable oldAware = object.getObjectsAware();
+ for (SWGObject obj : oldAware)
+ object.getAwareness().addObjectAware(obj.getAwareness());
+ object.getAwareness().addObjectAware(object.getSuperParent().getAwareness());
+ // Remove as parent
object.parent = null;
+ object.getAwareness().setParent(null);
object.slotArrangement = -1;
+ updateChildRadius(null);
}
/**
@@ -164,7 +171,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable oldObservers = getObserversAndParent();
+ Set oldObservers = getObserversAndParent();
SWGObject parent = this.parent;
if (parent != null)
parent.removeObject(this);
@@ -174,18 +181,11 @@ public abstract class SWGObject extends BaselineObject implements Comparable Parent
- updateObjectAwareness(container.getObjectsAware(), false, true); // Create/Destroy
- updateObjectAwareness(new HashSet<>(), true, false); // Clear Aware
- }
- } else if (parent != null) { // Parent -> World
- updateObjectAwareness(parent.getObjectsAware(), true, true); // Create/Destroy & Aware
}
- oldObservers.retainAll(getObserversAndParent());
long newId = (container != null) ? container.getObjectId() : 0;
- for (SWGObject update : oldObservers)
- update.getOwner().sendPacket(new UpdateContainmentMessage(objectId, newId, slotArrangement));
+ UpdateContainmentMessage update = new UpdateContainmentMessage(getObjectId(), newId, getSlotArrangement());
+ AwarenessUtilities.callForNewObserver(oldObservers, getObserversAndParent(), (observer) -> observer.sendPacket(update));
return ContainerResult.SUCCESS;
}
@@ -288,6 +288,8 @@ public abstract class SWGObject extends BaselineObject implements Comparable> getArrangement() {
return arrangement;
}
@@ -492,6 +504,10 @@ public abstract class SWGObject extends BaselineObject implements Comparable bubble)
+ bubble = dist;
+ }
+ }
+ return bubble;
}
- public void setLoadRange(double range) {
- this.loadRange = range;
+ public double getChildRadius() {
+ return childRadius;
+ }
+
+ public double getPrefLoadRange() {
+ return prefLoadRange;
+ }
+
+ public void setPrefLoadRange(double range) {
+ this.prefLoadRange = range;
}
/**
@@ -570,10 +604,28 @@ public abstract class SWGObject extends BaselineObject implements Comparable maxDistance)
+ maxDistance = dist;
+ }
+ }
+ childRadius = maxDistance;
+ } else {
+ double maxDistance = childRadius;
+ double dist = location.distanceTo(updated.location);
+ if (dist > maxDistance)
+ maxDistance = dist;
+ childRadius = maxDistance;
+ }
+ }
+
private final void sendSceneCreateObject(Player target) {
- if (target == null)
- return;
SceneCreateObjectByCrc create = new SceneCreateObjectByCrc();
create.setObjectId(objectId);
create.setLocation(buildoutArea == null ? location : buildoutArea.adjustLocation(location));
@@ -584,164 +636,134 @@ public abstract class SWGObject extends BaselineObject implements Comparable observers = target.getAwareness().getChildObservers();
+ if (target.getOwner() != null)
+ observers.add(target.getOwner());
+ for (Player observer : observers) {
+ createObject(observer, ignoreSnapshotChecks);
+ }
+ }
+
+ public void createObject(Player target, boolean ignoreSnapshotChecks) {
+ boolean send = !isSnapshot() || ignoreSnapshotChecks;
+ if (send) {
sendSceneCreateObject(target);
sendBaselines(target);
}
createChildrenObjects(target, ignoreSnapshotChecks);
- sendFinalBaselinePackets(target);
- if (!isSnapshot() || ignoreSnapshotChecks)
+ if (send) {
+ sendFinalBaselinePackets(target);
target.sendPacket(new SceneEndBaselines(getObjectId()));
+ }
}
- private void destroyObject(Player target) {
- if (!isSnapshot())
- sendSceneDestroyObject(target);
+ public void destroyObject(SWGObject target) {
+ if (isSnapshot() || target == null)
+ return;
+ Set observers = target.getAwareness().getChildObservers();
+ if (target.getOwner() != null)
+ observers.add(target.getOwner());
+ for (Player observer : observers) {
+ sendSceneDestroyObject(observer);
+ }
}
- public void clearAware() {
- updateObjectAwareness(new HashSet<>());
+ public void addObjectAware(SWGObject aware) {
+ if (awareness.addObjectAware(aware.getAwareness())) {
+ createObject(aware);
+ aware.createObject(this);
+ }
+ }
+
+ public void removeObjectAware(SWGObject aware) {
+ if (awareness.removeObjectAware(aware.getAwareness())) {
+ destroyObject(aware);
+ aware.destroyObject(this);
+ }
+ }
+
+ public boolean isObjectAware(SWGObject aware) {
+ return awareness.isObjectAware(aware);
+ }
+
+ public void clearObjectsAware() {
+ Set aware = awareness.getObjectsAware();
+ awareness.clearObjectsAware();
+ for (SWGObject obj : aware) {
+ destroyObject(obj);
+ obj.destroyObject(this);
+ }
}
public void resetAwareness() {
- synchronized (objectsAware) {
- objectsAware.clear();
- }
+ awareness.clearObjectsAware();
}
-
+
public Set getObjectsAware() {
- Set aware;
- synchronized (objectsAware) {
- aware = new HashSet<>(objectsAware);
- }
- synchronized (customAware) {
- aware.addAll(customAware);
- }
- if (parent != null) {
- aware.addAll(parent.getObjectsAware());
- aware.add(getSuperParent());
- }
- return aware;
+ return awareness.getObjectsAware();
}
public void addCustomAware(SWGObject aware) {
- internalAddCustomAware(aware, true);
- aware.internalAddCustomAware(this, true);
+ if (awareness.addCustomAware(aware.getAwareness())) {
+ createObject(aware);
+ aware.createObject(this);
+ }
}
public void removeCustomAware(SWGObject aware) {
- internalRemoveCustomAware(aware, true);
- aware.internalRemoveCustomAware(this, true);
+ if (awareness.removeCustomAware(aware.getAwareness())) {
+ destroyObject(aware);
+ aware.destroyObject(this);
+ }
}
- private void internalAddCustomAware(SWGObject aware, boolean sendUpdates) {
- boolean changed = false;
- synchronized (customAware) {
- changed = customAware.add(aware);
- }
- if (changed && sendUpdates && aware.getOwner() != null)
- createObject(aware.getOwner());
- }
-
- private void internalRemoveCustomAware(SWGObject aware, boolean sendUpdates) {
- boolean changed = false;
- synchronized (customAware) {
- changed = customAware.remove(aware);
- }
- if (changed && sendUpdates && aware.getOwner() != null)
- destroyObject(aware.getOwner());
+ public boolean isCustomAware(SWGObject aware) {
+ return awareness.isCustomAware(aware);
}
public void clearCustomAware(boolean sendUpdates) {
- Set copy;
- synchronized (customAware) {
- copy = new HashSet<>(customAware);
- }
- for (SWGObject aware : copy) {
- internalRemoveCustomAware(aware, sendUpdates);
- aware.internalRemoveCustomAware(this, sendUpdates);
+ Set aware = sendUpdates ? awareness.getCustomAware() : null;
+ awareness.clearCustomAware();
+ if (!sendUpdates)
+ return;
+ for (SWGObject obj : aware) {
+ destroyObject(obj);
+ obj.destroyObject(this);
}
}
- public Set getObserversAndParent() {
- Set observers = getObservers();
+ public Set getObserversAndParent() {
+ Set observers = getObservers();
Player parentOwner = (getParent() != null) ? getParent().getOwner() : null;
if (parentOwner != null)
- observers.add(parentOwner.getCreatureObject());
+ observers.add(parentOwner);
return observers;
}
- public Set getObservers() {
- Player owner = getOwner();
- SWGObject parent = getParent();
- if (parent == null)
- return getObservers(owner, this, true);
- while (parent.getParent() != null)
- parent = parent.getParent();
- return parent.getObservers(owner, this, true);
+ public Set getObservers() {
+ return getObservers(true);
}
- private Set getObservers(boolean useAware) {
- return getObservers(getOwner(), this, useAware);
- }
-
- /**
- * Gets all observers within the tree. Called from the head node of the tree to search. In
- * addition to the contained objects, it also searches aware objects if useAware is true
- *
- * @param owner the original owner that should be ignored from the search
- * @param original the original object that should be ignored from the search
- * @param useAware TRUE if aware objects should be searched
- * @return a set with unique SWGObjects that have unique player owners
- */
- private Set getObservers(Player owner, SWGObject original, boolean useAware) {
- Set observers = new HashSet<>();
- synchronized (containedObjects) {
- addObserversToSet(containedObjects, observers, owner, original);
- }
- if (useAware) {
- addObserversToSet(getObjectsAware(), observers, owner, original);
- }
- return observers;
- }
-
- private void addObserversToSet(Collection nearby, Set observers, Player owner, SWGObject original) {
- for (SWGObject aware : nearby) {
- if (!aware.isVisible(original))
- continue;
- if (checkAwareIsObserver(aware, owner, original))
- observers.add(aware);
- else
- observers.addAll(aware.getObservers(owner, original, false));
- }
- }
-
- private boolean checkAwareIsObserver(SWGObject aware, Player owner, SWGObject original) {
- if (!(aware instanceof CreatureObject))
- return false;
- Player awareOwner = aware.getOwner();
- if (awareOwner == null || awareOwner.equals(owner))
- return false;
- if (awareOwner.getPlayerState() != PlayerState.ZONED_IN)
- return false;
- return ((CreatureObject) aware).isLoggedInPlayer();
+ private Set getObservers(boolean useAware) {
+ if (!useAware)
+ return awareness.getChildObservers();
+ return awareness.getObservers();
}
public void sendObserversAndSelf(Packet ... packets) {
@@ -750,9 +772,8 @@ public abstract class SWGObject extends BaselineObject implements Comparable observers = getObservers();
- for (SWGObject observer : observers) {
- observer.getOwner().sendPacket(packets);
+ for (Player observer : getObservers()) {
+ observer.sendPacket(packets);
}
}
@@ -772,115 +793,21 @@ public abstract class SWGObject extends BaselineObject implements Comparable withinRange) {
- updateObjectAwareness(withinRange, true, true);
- }
-
- public void updateObjectAwareness(Set withinRange, boolean updateAware, boolean sendUpdate) {
- Set oldAware;
- synchronized (objectsAware) {
- oldAware = new HashSet<>(objectsAware);
- }
- for (SWGObject obj : withinRange) {
- if (!oldAware.contains(obj))
- obj.awarenessCreate(this, updateAware, sendUpdate);
- }
- for (SWGObject obj : oldAware) {
- if (!withinRange.contains(obj))
- obj.awarenessDestroy(this, updateAware, sendUpdate);
- }
- }
-
- private void awarenessCreate(SWGObject obj, boolean updateAware, boolean sendUpdate) {
- internalAwarenessCreate(obj, updateAware, sendUpdate);
- obj.internalAwarenessCreate(this, updateAware, sendUpdate);
- }
-
- private void awarenessDestroy(SWGObject obj, boolean updateAware, boolean sendUpdate) {
- internalAwarenessDestroy(obj, updateAware, sendUpdate);
- obj.internalAwarenessDestroy(this, updateAware, sendUpdate);
- }
-
- private void internalAwarenessCreate(SWGObject obj, boolean updateAware, boolean sendUpdate) {
- if (updateAware) {
- synchronized (objectsAware) {
- if (!objectsAware.add(obj))
- return;
- }
- }
- if (!sendUpdate)
- return;
- Player owner = getOwner();
- if (owner != null && !owner.equals(obj.getOwner())) {
- // Don't resend character baselines to the player every time they reenter awareness!
- obj.createObject(owner);
- }
-
- for (SWGObject create : getObservers(false))
- obj.createObject(create.getOwner());
- }
-
- private void internalAwarenessDestroy(SWGObject obj, boolean updateAware, boolean sendUpdate) {
- if (updateAware) {
- synchronized (objectsAware) {
- if (!objectsAware.remove(obj))
- return;
- }
- }
- if (!sendUpdate)
- return;
- Player owner = getOwner();
- if (owner != null && !owner.equals(obj.getOwner())) {
- // Don't destroy the character of a player that's just left a building
- obj.destroyObject(getOwner());
- }
- for (SWGObject destroy : getObservers(false))
- obj.destroyObject(destroy.getOwner());
- }
-
- public void sendDataTransforms(DataTransform dt) {
- UpdateTransformMessage transform = new UpdateTransformMessage();
- transform.setObjectId(getObjectId());
- transform.setX((short) (dt.getLocation().getX() * 4 + 0.5));
- transform.setY((short) (dt.getLocation().getY() * 4 + 0.5));
- transform.setZ((short) (dt.getLocation().getZ() * 4 + 0.5));
- transform.setUpdateCounter(updateCounter.incrementAndGet());
- transform.setDirection(dt.getMovementAngle());
- transform.setSpeed((byte) (dt.getSpeed()+0.5));
- transform.setLookAtYaw((byte) (dt.getLookAtYaw() * 16));
- transform.setUseLookAtYaw(dt.isUseLookAtYaw());
- sendObservers(transform);
- }
-
- public void sendParentDataTransforms(DataTransformWithParent ptm) {
- UpdateTransformWithParentMessage transform = new UpdateTransformWithParentMessage(ptm.getCellId(), getObjectId());
- transform.setLocation(ptm.getLocation());
- transform.setUpdateCounter(updateCounter.incrementAndGet());
- transform.setDirection(ptm.getMovementAngle());
- transform.setSpeed((byte) ptm.getSpeed());
- transform.setLookDirection((byte) (ptm.getLookAtYaw() * 16));
- transform.setUseLookDirection(ptm.isUseLookAtYaw());
- sendObservers(transform);
- }
-
protected void createChildrenObjects(Player target) {
createChildrenObjects(target, false);
}
- protected void createChildrenObjects(Player target, boolean ignoreSnapshotChecks) {
+ private void createChildrenObjects(Player target, boolean ignoreSnapshotChecks) {
synchronized (slots) {
if (slots.size() == 0 && containedObjects.size() == 0)
return;
}
- List sentObjects = new ArrayList<>();
-
// First create the objects in the slots
synchronized (slots) {
for (SWGObject slotObject : slots.values()) {
- if (slotObject != null && !sentObjects.contains(slotObject)) {
+ if (slotObject != null) {
slotObject.createObject(target, ignoreSnapshotChecks);
- sentObjects.add(slotObject);
}
}
}
@@ -888,7 +815,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable {
stream.addAscii(e.getKey());
@@ -1027,7 +954,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable attributes.put(stream.getAscii(), stream.getAscii()));
stream.getList((i) -> SWGObjectFactory.create(stream).moveToContainer(this));
}
@@ -1042,7 +969,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable attributes.put(stream.getAscii(), stream.getAscii()));
stream.getList((i) -> SWGObjectFactory.create(stream).moveToContainer(this));
}
diff --git a/src/resources/objects/awareness/Aware.java b/src/resources/objects/awareness/Aware.java
new file mode 100644
index 00000000..1f6fd3f6
--- /dev/null
+++ b/src/resources/objects/awareness/Aware.java
@@ -0,0 +1,178 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
+import resources.player.Player;
+import resources.player.PlayerState;
+
+class Aware {
+
+ private final SWGObject object;
+ private final List awareness;
+ private final Object parentMutex;
+ private Aware parent;
+
+ public Aware(SWGObject obj) {
+ if (obj == null)
+ throw new NullPointerException("Object cannot be null!");
+ this.object = obj;
+ this.awareness = new ArrayList<>();
+ this.parentMutex = new Object();
+ }
+
+ public SWGObject getObject() {
+ return object;
+ }
+
+ public void setParent(Aware parent) {
+ this.parent = parent;
+ }
+
+ public boolean add(Aware a) {
+ boolean success = internalAdd(a);
+ return a.internalAdd(this) && success;
+ }
+
+ public boolean remove(Aware a) {
+ boolean success = internalRemove(a);
+ return a.internalRemove(this) && success;
+ }
+
+ public boolean contains(SWGObject obj) {
+ synchronized (awareness) {
+ for (Aware a : awareness) {
+ if (a.getObject().equals(obj))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public void clear() {
+ List aware;
+ synchronized (awareness) {
+ aware = new ArrayList<>(awareness);
+ }
+ for (Aware a : aware)
+ remove(a);
+ }
+
+ public Set getAware() {
+ Set aware = new HashSet<>(awareness.size());
+ synchronized (awareness) {
+ for (Aware a : awareness) {
+ aware.add(a.getObject());
+ }
+ }
+ Aware parent;
+ synchronized (parentMutex) {
+ parent = this.parent;
+ }
+ if (parent != null)
+ aware.addAll(parent.getAware());
+ return aware;
+ }
+
+ public Set getObservers() {
+ Player owner = object.getOwner();
+ synchronized (parentMutex) {
+ if (parent == null)
+ return getObservers(owner, getObject(), true);
+ return getSuperParent().getObservers(owner, getObject(), true);
+ }
+ }
+
+ private boolean internalAdd(Aware binding) {
+ synchronized (awareness) {
+ return awareness.add(binding);
+ }
+ }
+
+ private boolean internalRemove(Aware binding) {
+ synchronized (awareness) {
+ return awareness.remove(binding);
+ }
+ }
+
+ private Aware getParent() {
+ synchronized (parentMutex) {
+ return parent;
+ }
+ }
+
+ private Aware getSuperParent() {
+ Aware tmpParent = getParent();
+ Aware ret = tmpParent;
+ if (tmpParent == null)
+ return ret;
+ while ((tmpParent = tmpParent.getParent()) != null) {
+ ret = tmpParent;
+ }
+ return ret;
+ }
+
+ private Set getObservers(Player owner, SWGObject original, boolean useAware) {
+ Set observers = new HashSet<>();
+ addObserversToSet(getObject().getContainedObjects(), observers, owner, original);
+ if (useAware) {
+ addObserversToSet(getAware(), observers, owner, original);
+ }
+ return observers;
+ }
+
+ public static void addObserversToSet(Collection nearby, Collection observers, Player owner, SWGObject original) {
+ for (SWGObject aware : nearby) {
+ if (!aware.isVisible(original))
+ continue;
+ if (isObserver(aware, owner, original))
+ observers.add(aware.getOwner());
+ else
+ addObserversToSet(aware.getContainedObjects(), observers, owner, original);
+ }
+ }
+
+ public static boolean isObserver(SWGObject aware, Player owner, SWGObject original) {
+ if (!(aware instanceof CreatureObject))
+ return false;
+ Player awareOwner = aware.getOwner();
+ if (awareOwner == null || awareOwner.equals(owner))
+ return false;
+ if (awareOwner.getPlayerState() != PlayerState.ZONED_IN)
+ return false;
+ return ((CreatureObject) aware).isLoggedInPlayer();
+ }
+
+}
diff --git a/src/resources/objects/awareness/AwarenessHandler.java b/src/resources/objects/awareness/AwarenessHandler.java
new file mode 100644
index 00000000..f7a15305
--- /dev/null
+++ b/src/resources/objects/awareness/AwarenessHandler.java
@@ -0,0 +1,134 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import resources.Location;
+import resources.Terrain;
+import resources.buildout.BuildoutArea;
+import resources.objects.SWGObject;
+import resources.objects.awareness.TerrainMap.TerrainMapCallback;
+import resources.player.Player;
+import resources.server_info.Log;
+import utilities.AwarenessUtilities;
+
+public class AwarenessHandler {
+
+ private static final Location GONE_LOCATION = new Location(0, 0, 0, null);
+
+ private final Map terrains;
+
+ public AwarenessHandler(TerrainMapCallback callback) {
+ terrains = new HashMap<>(Terrain.getTerrainCount());
+ loadTerrainMaps(callback);
+ }
+
+ public void close() {
+ for (TerrainMap map : terrains.values()) {
+ map.stop();
+ }
+ }
+
+ private void loadTerrainMaps(TerrainMapCallback callback) {
+ for (Terrain t : Terrain.values()) {
+ TerrainMap map = new TerrainMap(t);
+ map.start();
+ map.setCallback(callback);
+ terrains.put(t, map);
+ }
+ }
+
+ public void moveObject(SWGObject obj, Location requestedLocation) {
+ if (obj.getParent() != null)
+ obj.moveToContainer(null);
+ // Adjust to server coordinates
+ BuildoutArea area = obj.getBuildoutArea();
+ if (area != null)
+ requestedLocation = area.adjustLocation(requestedLocation);
+ // Remove from previous awareness
+ Terrain oldTerrain = obj.getTerrain();
+ Terrain newTerrain = requestedLocation.getTerrain();
+ if (oldTerrain != newTerrain && oldTerrain != null) {
+ obj.clearObjectsAware(); // Moving to GONE
+ TerrainMap oldTerrainMap = getTerrainMap(oldTerrain);
+ if (oldTerrainMap != null)
+ oldTerrainMap.removeFromMap(obj);
+ }
+ // Add to new awareness
+ TerrainMap map = getTerrainMap(newTerrain);
+ if (map != null) {
+ map.moveWithinMap(obj, requestedLocation);
+ } else if (!requestedLocation.equals(GONE_LOCATION)) {
+ Log.e(this, "Unknown terrain: %s", newTerrain);
+ }
+ }
+
+ public void moveObject(SWGObject obj, SWGObject parent, Location requestedLocation) {
+ Set oldAware = obj.getObjectsAware();
+ Set oldObservers = obj.getObservers();
+ if (obj.getParent() != parent)
+ obj.moveToContainer(parent);
+ // Remove from previous awareness
+ TerrainMap oldMap = getTerrainMap(requestedLocation.getTerrain());
+ if (oldMap != null)
+ oldMap.removeWithoutUpdate(obj);
+ // Update location
+ obj.setLocation(requestedLocation);
+ // Update awareness
+ TerrainMap map = getTerrainMap(parent);
+ if (map != null) {
+ map.moveToParent(obj, parent);
+ AwarenessUtilities.handleUpdateAwarenessManual(obj, oldAware, oldObservers, obj.getObjectsAware(), obj.getObservers());
+ } else if (!requestedLocation.equals(GONE_LOCATION)) {
+ Log.e(this, "Unknown terrain: %s", requestedLocation.getTerrain());
+ }
+ }
+
+ public void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) {
+ moveObject(obj, GONE_LOCATION);
+ if (disappearObjects)
+ obj.clearObjectsAware();
+ if (disappearCustom)
+ obj.clearCustomAware(true);
+ }
+
+ private TerrainMap getTerrainMap(SWGObject object) {
+ Terrain t = object.getTerrain();
+ if (t == null)
+ return null;
+ return getTerrainMap(t);
+ }
+
+ private TerrainMap getTerrainMap(Terrain t) {
+ return terrains.get(t);
+ }
+
+}
diff --git a/src/resources/objects/awareness/DataTransformHandler.java b/src/resources/objects/awareness/DataTransformHandler.java
new file mode 100644
index 00000000..76664fe9
--- /dev/null
+++ b/src/resources/objects/awareness/DataTransformHandler.java
@@ -0,0 +1,104 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import resources.Location;
+import resources.buildout.BuildoutArea;
+import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
+import network.packets.swg.zone.UpdateTransformMessage;
+import network.packets.swg.zone.UpdateTransformWithParentMessage;
+
+public class DataTransformHandler {
+
+ private final SpeedCheckHandler speedCheckHandler;
+
+ public DataTransformHandler() {
+ speedCheckHandler = new SpeedCheckHandler();
+ }
+
+ public boolean handleMove(SWGObject obj, Location requestedLocation, double speed, int update) {
+ if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer())
+ speedCheckHandler.moveObjectSpeedChecks((CreatureObject) obj, requestedLocation);
+ BuildoutArea area = obj.getBuildoutArea();
+ if (area != null)
+ requestedLocation = area.adjustLocation(requestedLocation);
+ obj.sendObservers(createTransform(obj, requestedLocation, speed, update));
+ return true;
+ }
+
+ public boolean handleMove(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) {
+ if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer())
+ speedCheckHandler.moveObjectSpeedChecks((CreatureObject) obj, parent, requestedLocation);
+ obj.sendObservers(createTransform(obj, parent.getObjectId(), requestedLocation, speed, update));
+ return true;
+ }
+
+ private UpdateTransformMessage createTransform(SWGObject obj, Location requestedLocation, double speed, int update) {
+ UpdateTransformMessage transform = new UpdateTransformMessage();
+ transform.setObjectId(obj.getObjectId());
+ transform.setX((short) (requestedLocation.getX() * 4 + 0.5));
+ transform.setY((short) (requestedLocation.getY() * 4 + 0.5));
+ transform.setZ((short) (requestedLocation.getZ() * 4 + 0.5));
+ transform.setUpdateCounter(obj.getNextUpdateCount());
+ transform.setDirection(getMovementAngle(requestedLocation));
+ transform.setSpeed((byte) (speed+0.5));
+ transform.setLookAtYaw((byte) 0);
+ transform.setUseLookAtYaw(false);
+ return transform;
+ }
+
+ private UpdateTransformWithParentMessage createTransform(SWGObject obj, long cellId, Location requestedLocation, double speed, int update) {
+ UpdateTransformWithParentMessage transform = new UpdateTransformWithParentMessage(cellId, obj.getObjectId());
+ transform.setLocation(requestedLocation);
+ transform.setUpdateCounter(obj.getNextUpdateCount());
+ transform.setDirection(getMovementAngle(requestedLocation));
+ transform.setSpeed((byte) (speed + 0.5));
+ transform.setLookDirection((byte) 0); // lookAtYaw * 16
+ transform.setUseLookDirection(false);
+ return transform;
+ }
+
+ private byte getMovementAngle(Location requestedLocation) {
+ byte movementAngle = (byte) 0.0f;
+ double wOrient = requestedLocation.getOrientationW();
+ double yOrient = requestedLocation.getOrientationY();
+ double sq = Math.sqrt(1 - (wOrient*wOrient));
+
+ if (sq != 0) {
+ if (requestedLocation.getOrientationW() > 0 && requestedLocation.getOrientationY() < 0) {
+ wOrient *= -1;
+ yOrient *= -1;
+ }
+ movementAngle = (byte) ((yOrient / sq) * (2 * Math.acos(wOrient) / 0.06283f));
+ }
+
+ return movementAngle;
+ }
+
+}
diff --git a/src/resources/objects/awareness/ObjectAware.java b/src/resources/objects/awareness/ObjectAware.java
new file mode 100644
index 00000000..64fc9d6e
--- /dev/null
+++ b/src/resources/objects/awareness/ObjectAware.java
@@ -0,0 +1,132 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import resources.objects.SWGObject;
+import resources.player.Player;
+
+public class ObjectAware {
+
+ private final SWGObject object;
+ private final Aware objectsAware;
+ private final Aware customAware;
+
+ public ObjectAware(SWGObject obj) {
+ this.object = obj;
+ this.objectsAware = new Aware(obj);
+ this.customAware = new Aware(obj);
+ }
+
+ public void setParent(ObjectAware parent) {
+ if (parent == null) {
+ objectsAware.setParent(null);
+ customAware.setParent(null);
+ } else {
+ objectsAware.setParent(parent.getRawObjectsAware());
+ customAware.setParent(parent.getRawCustomAware());
+ }
+ }
+
+ public boolean addObjectAware(ObjectAware aware) {
+ return objectsAware.add(aware.getRawObjectsAware());
+ }
+
+ public boolean removeObjectAware(ObjectAware aware) {
+ return objectsAware.remove(aware.getRawObjectsAware());
+ }
+
+ public boolean addCustomAware(ObjectAware aware) {
+ return customAware.add(aware.getRawCustomAware());
+ }
+
+ public boolean removeCustomAware(ObjectAware aware) {
+ return customAware.remove(aware.getRawCustomAware());
+ }
+
+ public boolean isObjectAware(SWGObject aware) {
+ return objectsAware.contains(aware);
+ }
+
+ public boolean isCustomAware(SWGObject aware) {
+ return customAware.contains(aware);
+ }
+
+ public void clearObjectsAware() {
+ objectsAware.clear();
+ }
+
+ public void clearCustomAware() {
+ customAware.clear();
+ }
+
+ public Set getAware() {
+ Set aware = objectsAware.getAware();
+ aware.addAll(customAware.getAware());
+ return aware;
+ }
+
+ public Set getObservers() {
+ Set observers = objectsAware.getObservers();
+ observers.addAll(customAware.getObservers());
+ return observers;
+ }
+
+ public Set getChildObservers() {
+ Set observers = new HashSet<>();
+ Aware.addObserversToSet(object.getContainedObjects(), observers, object.getOwner(), object);
+ return observers;
+ }
+
+ public Set getObjectsAware() {
+ return objectsAware.getAware();
+ }
+
+ public Set getCustomAware() {
+ return customAware.getAware();
+ }
+
+ public Set getObjectObservers() {
+ return objectsAware.getObservers();
+ }
+
+ public Set getCustomObservers() {
+ return customAware.getObservers();
+ }
+
+ private Aware getRawObjectsAware() {
+ return objectsAware;
+ }
+
+ private Aware getRawCustomAware() {
+ return customAware;
+ }
+
+}
diff --git a/src/resources/objects/awareness/SpeedCheckHandler.java b/src/resources/objects/awareness/SpeedCheckHandler.java
new file mode 100644
index 00000000..d25b9e53
--- /dev/null
+++ b/src/resources/objects/awareness/SpeedCheckHandler.java
@@ -0,0 +1,84 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import resources.Location;
+import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
+
+class SpeedCheckHandler {
+
+ public SpeedCheckHandler() {
+
+ }
+
+ public void moveObjectSpeedChecks(CreatureObject obj, Location requestedLocation) {
+ double time = ((CreatureObject) obj).getTimeSinceLastTransform() / 1000;
+ obj.updateLastTransformTime();
+ Location l = obj.getWorldLocation();
+ if (isSpeeding(obj, l, requestedLocation, time)) {
+ double angle = getMovementAngle(l, requestedLocation);
+ requestedLocation.setX(l.getX()+obj.getMovementScale()*7.3*time*Math.cos(angle));
+ requestedLocation.setZ(l.getZ()+obj.getMovementScale()*7.3*time*Math.sin(angle));
+ }
+ }
+
+ public void moveObjectSpeedChecks(CreatureObject obj, SWGObject parent, Location requestedLocation) {
+ double time = ((CreatureObject) obj).getTimeSinceLastTransform() / 1000;
+ obj.updateLastTransformTime();
+ Location l = obj.getWorldLocation();
+ Location requestedWorld = new Location(requestedLocation.getX(), 0, requestedLocation.getZ(), parent.getTerrain());
+ requestedWorld.translateLocation(parent.getWorldLocation());
+ if (isSpeeding(obj, l, requestedWorld, time)) {
+ double angle = getMovementAngle(l, requestedWorld);
+ requestedLocation.setX(requestedLocation.getX()+obj.getMovementScale()*7.3*time*invertNormalizedValue(Math.cos(angle)));
+ requestedLocation.setZ(requestedLocation.getZ()+obj.getMovementScale()*7.3*time*invertNormalizedValue(Math.sin(angle)));
+ }
+ }
+
+ private boolean isSpeeding(CreatureObject obj, Location nWorld, Location newLocation, double time) {
+ return Math.sqrt(square(nWorld.getX()-nWorld.getX()) + square(nWorld.getZ()-newLocation.getZ())) / time > obj.getMovementScale()*7.3;
+ }
+
+ private double getMovementAngle(Location oldLocation, Location newLocation) {
+ if (newLocation.getX() == oldLocation.getX())
+ return Math.PI;
+ return Math.atan2(newLocation.getZ()-oldLocation.getZ(), newLocation.getX()-oldLocation.getX()) + Math.PI;
+ }
+
+ private double invertNormalizedValue(double x) {
+ if (x < 0)
+ return -1 - x;
+ return 1-x;
+ }
+
+ private double square(double x) {
+ return x * x;
+ }
+
+}
diff --git a/src/resources/objects/awareness/TerrainMap.java b/src/resources/objects/awareness/TerrainMap.java
new file mode 100644
index 00000000..9f52b4dc
--- /dev/null
+++ b/src/resources/objects/awareness/TerrainMap.java
@@ -0,0 +1,175 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import resources.Location;
+import resources.Terrain;
+import resources.callback.CallbackManager;
+import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
+import resources.server_info.Log;
+import utilities.AwarenessUtilities;
+
+public class TerrainMap {
+
+ private static final int CHUNK_COUNT_ACROSS = 16;
+ private static final double MIN_X = -8192;
+ private static final double MIN_Z = -8192;
+ private static final double MAP_WIDTH = 16384;
+ private static final double CHUNK_WIDTH = MAP_WIDTH / CHUNK_COUNT_ACROSS;
+
+ private final CallbackManager callbackManager;
+ private final TerrainMapChunk [][] chunks;
+ private final Map objectChunk;
+
+ public TerrainMap(Terrain t) {
+ callbackManager = new CallbackManager<>("terrain-map-"+t.name(), 1);
+ chunks = new TerrainMapChunk[CHUNK_COUNT_ACROSS][CHUNK_COUNT_ACROSS];
+ objectChunk = new HashMap<>();
+ for (int z = 0; z < chunks.length; z++) {
+ for (int x = 0; x < chunks[z].length; x++) {
+ double chunkStartX = MIN_X+x*CHUNK_WIDTH;
+ double chunkStartZ = MIN_Z+z*CHUNK_WIDTH;
+ chunks[z][x] = new TerrainMapChunk(chunkStartX, chunkStartZ, chunkStartX+CHUNK_WIDTH, chunkStartZ+CHUNK_WIDTH);
+ }
+ }
+ }
+
+ public void start() {
+ callbackManager.start();
+ }
+
+ public void stop() {
+ callbackManager.stop();
+ }
+
+ public void setCallback(TerrainMapCallback callback) {
+ callbackManager.setCallback(callback);
+ }
+
+ public void moveWithinMap(SWGObject obj, Location loc) {
+ obj.setLocation(loc);
+ if (isInAwareness(obj)) {
+ move(obj);
+ update(obj);
+ callbackManager.callOnEach((call) -> call.onMoveSuccess(obj));
+ } else {
+ callbackManager.callOnEach((call) -> call.onMoveFailure(obj));
+ }
+ }
+
+ public void moveToParent(SWGObject obj, SWGObject parent) {
+ obj.resetAwareness();
+ }
+
+ public void removeWithoutUpdate(SWGObject obj) {
+ remove(obj);
+ }
+
+ public void removeFromMap(SWGObject obj) {
+ remove(obj);
+ update(obj);
+ }
+
+ private void move(SWGObject obj) {
+ TerrainMapChunk chunk = objectChunk.get(obj.getObjectId());
+ if (chunk != null) {
+ if (!chunk.isWithinBounds(obj))
+ chunk.removeObject(obj);
+ else
+ return;
+ }
+ chunk = getChunk(obj.getX(), obj.getZ());
+ if (chunk == null) {
+ Log.e("TerrainMap", "Null Chunk! Location: (%.3f, %.3f) Object: %s", obj.getX(), obj.getZ(), obj);
+ return;
+ }
+ chunk.addObject(obj);
+ objectChunk.put(obj.getObjectId(), chunk);
+ }
+
+ private void remove(SWGObject obj) {
+ TerrainMapChunk chunk = objectChunk.remove(obj.getObjectId());
+ if (chunk != null)
+ chunk.removeObject(obj);
+ }
+
+ private void update(SWGObject obj) {
+ Set prevAware = obj.getObjectsAware();
+ Set aware = getNearbyAware(obj);
+ AwarenessUtilities.callForNewAware(prevAware, aware, (inRange) -> callbackManager.callOnEach((call) -> call.onWithinRange(obj, inRange)));
+ AwarenessUtilities.callForOldAware(prevAware, aware, (outRange) -> callbackManager.callOnEach((call) -> call.onOutOfRange(obj, outRange)));
+ }
+
+ private Set getNearbyAware(SWGObject obj) {
+ Set aware = new HashSet<>();
+ int startX = Math.max(calculateIndex(obj.getX()) - 1, 0);
+ int startZ = Math.max(calculateIndex(obj.getZ()) - 1, 0);
+ int endX = Math.min(startX+3, CHUNK_COUNT_ACROSS);
+ int endZ = Math.min(startZ+3, CHUNK_COUNT_ACROSS);
+ for (int z = startZ; z < endZ; z++) {
+ for (int x = startX; x < endX; x++) {
+ aware.addAll(chunks[z][x].getWithinAwareness(obj));
+ }
+ }
+ return aware;
+ }
+
+ private boolean isInAwareness(SWGObject obj) {
+ if (obj.getParent() != null)
+ return false;
+ if (!(obj instanceof CreatureObject))
+ return true;
+ return ((CreatureObject) obj).isLoggedInPlayer() || !((CreatureObject) obj).isPlayer();
+ }
+
+ private TerrainMapChunk getChunk(double x, double z) {
+ int xInd = calculateIndex(x);
+ int zInd = calculateIndex(z);
+ if (xInd < 0 || zInd < 0 || xInd >= CHUNK_COUNT_ACROSS || zInd >= CHUNK_COUNT_ACROSS)
+ return null;
+ return chunks[zInd][xInd];
+ }
+
+ private int calculateIndex(double x) {
+ return (int) ((x+8192)/16384*CHUNK_COUNT_ACROSS);
+ }
+
+ public interface TerrainMapCallback {
+ void onWithinRange(SWGObject obj, SWGObject inRange);
+ void onOutOfRange(SWGObject obj, SWGObject outRange);
+ void onMoveSuccess(SWGObject obj);
+ void onMoveFailure(SWGObject obj);
+ }
+
+}
diff --git a/src/resources/objects/awareness/TerrainMapChunk.java b/src/resources/objects/awareness/TerrainMapChunk.java
new file mode 100644
index 00000000..494b9361
--- /dev/null
+++ b/src/resources/objects/awareness/TerrainMapChunk.java
@@ -0,0 +1,102 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import resources.Location;
+import resources.objects.SWGObject;
+import resources.objects.creature.CreatureObject;
+
+class TerrainMapChunk {
+
+ private final Set objects;
+ private final double minX;
+ private final double minZ;
+ private final double maxX;
+ private final double maxZ;
+
+ public TerrainMapChunk(double minX, double minZ, double maxX, double maxZ) {
+ objects = new HashSet<>(); // There will be some expanding and shrinking
+ this.minX = minX;
+ this.minZ = minZ;
+ this.maxX = maxX;
+ this.maxZ = maxZ;
+ }
+
+ public void addObject(SWGObject obj) {
+ synchronized (objects) {
+ objects.add(obj);
+ }
+ }
+
+ public void removeObject(SWGObject obj) {
+ synchronized (objects) {
+ objects.remove(obj);
+ }
+ }
+
+ public boolean containsObject(SWGObject obj) {
+ synchronized (objects) {
+ return objects.contains(obj);
+ }
+ }
+
+ public boolean isWithinBounds(SWGObject obj) {
+ double x = obj.getX();
+ double z = obj.getZ();
+ return minX <= x && minZ <= z && maxX >= x && maxZ >= z;
+ }
+
+ public List getWithinAwareness(SWGObject obj) {
+ double loadRange = obj.getLoadRange();
+ synchronized (objects) {
+ List withinRange = new LinkedList<>();
+ Location objLocation = obj.getWorldLocation();
+ for (SWGObject test : objects) {
+ if (isValidWithinRange(obj, test, objLocation, loadRange))
+ withinRange.add(test);
+ }
+ return withinRange;
+ }
+ }
+
+ private boolean isValidWithinRange(SWGObject obj, SWGObject inRange, Location objLocation, double range) {
+ if (obj.equals(inRange))
+ return false;
+ if (inRange instanceof CreatureObject && ((CreatureObject) inRange).isLoggedOutPlayer())
+ return false;
+ if (!inRange.getWorldLocation().isWithinFlatDistance(objLocation, Math.max(range, inRange.getLoadRange())))
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/resources/objects/buildouts/TerrainBuildoutLoader.java b/src/resources/objects/buildouts/TerrainBuildoutLoader.java
index a2ba07e3..383d06bb 100644
--- a/src/resources/objects/buildouts/TerrainBuildoutLoader.java
+++ b/src/resources/objects/buildouts/TerrainBuildoutLoader.java
@@ -91,7 +91,7 @@ class TerrainBuildoutLoader {
buildoutRow.load(areaTable.getRow(row), crcTable);
SWGObject object = createObject(buildoutRow);
object.setClassification(ObjectClassification.BUILDOUT);
- object.setLoadRange(buildoutRow.getRadius());
+ object.setPrefLoadRange(buildoutRow.getRadius());
object.setBuildoutAreaId(area.getIndex());
setCellInformation(object, buildoutRow.getCellIndex());
addObject(area.getName(), object, buildoutRow.getContainerId());
diff --git a/src/resources/objects/buildouts/TerrainSnapshotLoader.java b/src/resources/objects/buildouts/TerrainSnapshotLoader.java
index d02252f6..ddb920ed 100644
--- a/src/resources/objects/buildouts/TerrainSnapshotLoader.java
+++ b/src/resources/objects/buildouts/TerrainSnapshotLoader.java
@@ -79,7 +79,7 @@ public class TerrainSnapshotLoader {
SWGObject object = createObject(templates, node);
object.setClassification(ObjectClassification.SNAPSHOT);
object.setBuildoutAreaId(-1);
- object.setLoadRange(node.getRadius());
+ object.setPrefLoadRange(node.getRadius());
setCellInformation(object, node.getCellIndex());
addObject(object, node.getContainerId());
diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java
index a7961ed9..79c925f9 100644
--- a/src/resources/objects/creature/CreatureObject.java
+++ b/src/resources/objects/creature/CreatureObject.java
@@ -29,6 +29,7 @@ package resources.objects.creature;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
import java.util.Locale;
import java.util.Map;
@@ -42,7 +43,6 @@ import network.packets.swg.zone.baselines.Baseline.BaselineType;
import network.packets.swg.zone.object_controller.PostureUpdate;
import resources.HologramColour;
import resources.Posture;
-import resources.PvpFaction;
import resources.PvpFlag;
import resources.PvpStatus;
import resources.Race;
@@ -87,6 +87,7 @@ public class CreatureObject extends TangibleObject {
public CreatureObject(long objectId) {
super(objectId, BaselineType.CREO);
initBaseAttributes();
+ setPrefLoadRange(200);
}
@Override
@@ -132,7 +133,7 @@ public class CreatureObject extends TangibleObject {
public boolean addSkill(String skillName) {
synchronized(skills) {
- if(skills.add(skillName)) {
+ if (skills.add(skillName)) {
skills.sendDeltaMessage(this);
return true;
}
@@ -145,8 +146,8 @@ public class CreatureObject extends TangibleObject {
return skills.contains(skillName);
}
- public SWGSet getSkills() {
- return skills;
+ public Set getSkills() {
+ return Collections.unmodifiableSet(skills);
}
public int getCashBalance() {
diff --git a/src/resources/objects/group/GroupObject.java b/src/resources/objects/group/GroupObject.java
index 2614c4fc..3faf71e1 100644
--- a/src/resources/objects/group/GroupObject.java
+++ b/src/resources/objects/group/GroupObject.java
@@ -37,8 +37,6 @@ import resources.objects.creature.CreatureObject;
import resources.player.Player;
import utilities.Encoder;
-import java.io.IOException;
-import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Collections;
diff --git a/src/resources/objects/group/LootRule.java b/src/resources/objects/group/LootRule.java
index 6cea28bb..33a10d36 100644
--- a/src/resources/objects/group/LootRule.java
+++ b/src/resources/objects/group/LootRule.java
@@ -27,9 +27,6 @@
***********************************************************************************/
package resources.objects.group;
-import java.util.HashMap;
-import java.util.Map;
-
/**
*
* @author skylerlehan
diff --git a/src/resources/objects/tangible/TangibleObject.java b/src/resources/objects/tangible/TangibleObject.java
index fc1d6ae7..60edb7bf 100644
--- a/src/resources/objects/tangible/TangibleObject.java
+++ b/src/resources/objects/tangible/TangibleObject.java
@@ -355,7 +355,7 @@ public class TangibleObject extends SWGObject {
protected void sendBaselines(Player target) {
super.sendBaselines(target);
- new FactionIntent(this, FactionIntentType.FLAGUPDATE).broadcast();
+// new FactionIntent(this, FactionIntentType.FLAGUPDATE).broadcast();
}
@Override
diff --git a/src/resources/player/Player.java b/src/resources/player/Player.java
index a69f2531..5bf1276b 100644
--- a/src/resources/player/Player.java
+++ b/src/resources/player/Player.java
@@ -27,16 +27,18 @@
***********************************************************************************/
package resources.player;
+import intents.network.OutboundPacketIntent;
import network.packets.Packet;
import resources.control.Service;
import resources.objects.SWGObject;
import resources.objects.creature.CreatureObject;
import resources.objects.player.PlayerObject;
-import resources.server_info.Log;
import services.player.PlayerManager;
+import utilities.IntentChain;
public class Player implements Comparable {
+ private final IntentChain packetChain;
private Service playerManager;
private long networkId;
@@ -54,10 +56,11 @@ public class Player implements Comparable {
private long lastInboundMessage = 0;
public Player() {
- this.playerManager = null;
+ this(null, 0);
}
public Player(Service playerManager, long networkId) {
+ this.packetChain = new IntentChain();
this.playerManager = playerManager;
setNetworkId(networkId);
}
@@ -110,6 +113,8 @@ public class Player implements Comparable {
this.creatureObject = obj;
if (obj != null && obj.getOwner() != this)
obj.setOwner(this);
+ if (obj == null)
+ packetChain.reset();
}
public void updateLastPacketTimestamp() {
@@ -176,17 +181,16 @@ public class Player implements Comparable {
}
public void sendPacket(Packet ... packets) {
- if (playerManager != null)
- playerManager.sendPacket(this, packets);
- else Log.e("Player", "Couldn't send packet due to playerManager being null.");
+ for (Packet p : packets) {
+ packetChain.broadcastAfter(new OutboundPacketIntent(p, networkId));
+ }
}
@Override
public String toString() {
String str = "Player[";
- str += "ID=" + userId + " / " + getCreatureObject().getObjectId();
- str += " NAME=" + username + " / " + getCreatureObject().getName();
- str += " LEVEL=" + accessLevel;
+ str += "ID=" + userId + " / " + (creatureObject==null?"null":creatureObject.getObjectId());
+ str += " NAME=" + username + " / " + (creatureObject==null?"null":creatureObject.getName());
str += " STATE=" + state;
return str + "]";
}
diff --git a/src/resources/radial/Radials.java b/src/resources/radial/Radials.java
index 821173a6..7f92adff 100644
--- a/src/resources/radial/Radials.java
+++ b/src/resources/radial/Radials.java
@@ -30,9 +30,6 @@ package resources.radial;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
import resources.objects.SWGObject;
import resources.player.Player;
import resources.server_info.Log;
diff --git a/src/services/chat/ChatManager.java b/src/services/chat/ChatManager.java
index 2432e996..1ac5c2cb 100644
--- a/src/services/chat/ChatManager.java
+++ b/src/services/chat/ChatManager.java
@@ -321,14 +321,10 @@ public class ChatManager extends Manager {
String senderName = ChatAvatar.getFromPlayer(sender).getName();
// Notify observers of the chat message
- for (SWGObject aware : actor.getObservers()) {
- Player owner = aware.getOwner();
- if (owner == null)
- continue;
-
+ for (Player owner : actor.getObservers()) {
PlayerObject awareGhost = owner.getPlayerObject();
if (!awareGhost.isIgnored(senderName))
- aware.getOwner().sendPacket(new SpatialChat(aware.getObjectId(), message));
+ owner.sendPacket(new SpatialChat(owner.getCreatureObject().getObjectId(), message));
}
}
diff --git a/src/services/collections/CollectionBadgeManager.java b/src/services/collections/CollectionBadgeManager.java
index e827daa1..d11125ac 100644
--- a/src/services/collections/CollectionBadgeManager.java
+++ b/src/services/collections/CollectionBadgeManager.java
@@ -275,10 +275,10 @@ public class CollectionBadgeManager extends Manager {
if (hidden) {
sendSystemMessage(thisplayer, "@collection:player_hidden_slot_added", "TO", "@collection_n:" + collectionName);
- sendPacket(thisplayer, new PlayMusicMessage(0, "sound/utinni.snd", 1, false));
+ thisplayer.sendPacket(new PlayMusicMessage(0, "sound/utinni.snd", 1, false));
} else {
sendSystemMessage(thisplayer, "@collection:player_slot_added", "TU", "@collection_n:" + slotName, "TO", "@collection_n:" + collectionName);
- sendPacket(thisplayer, new PlayMusicMessage(0, "sound/utinni.snd", 1, false));
+ thisplayer.sendPacket(new PlayMusicMessage(0, "sound/utinni.snd", 1, false));
}
if (collectionComplete) {
sendSystemMessage(thisplayer, "@collection:player_collection_complete", "TO", "@collection_n:" + collectionName);
diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java
index b4c185d1..6e47dc15 100644
--- a/src/services/combat/CombatManager.java
+++ b/src/services/combat/CombatManager.java
@@ -484,6 +484,8 @@ public class CombatManager extends Manager {
case DEAD:
case INCAPACITATED:
return CombatStatus.INVALID_TARGET;
+ default:
+ break;
}
}
diff --git a/src/services/combat/CorpseService.java b/src/services/combat/CorpseService.java
index e417d68e..551bbaec 100644
--- a/src/services/combat/CorpseService.java
+++ b/src/services/combat/CorpseService.java
@@ -235,6 +235,8 @@ public final class CorpseService extends Service {
}
break;
}
+ default:
+ break;
}
}
diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java
index 65294450..b909a8c1 100644
--- a/src/services/commands/BuffService.java
+++ b/src/services/commands/BuffService.java
@@ -181,6 +181,8 @@ public class BuffService extends Service {
switch(pei.getEvent()) {
case PE_FIRST_ZONE: handleFirstZone(creature); break;
case PE_DISAPPEAR: handleDisappear(creature); break;
+ default:
+ break;
}
}
diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java
index ac9e7c6f..fda2dc39 100644
--- a/src/services/commands/CommandService.java
+++ b/src/services/commands/CommandService.java
@@ -148,6 +148,8 @@ public class CommandService extends Service {
combatQueueMap.remove(i.getPlayer());
}
break;
+ default:
+ break;
}
}
diff --git a/src/services/commands/EntertainmentService.java b/src/services/commands/EntertainmentService.java
index dac93223..0216ef7c 100644
--- a/src/services/commands/EntertainmentService.java
+++ b/src/services/commands/EntertainmentService.java
@@ -202,7 +202,8 @@ public class EntertainmentService extends Service {
performerMap.get(creature.getObjectId()).clearSpectators();
performerMap.remove(creature.getObjectId());
}
-
+ break;
+ default:
break;
}
}
diff --git a/src/services/experience/SkillManager.java b/src/services/experience/SkillManager.java
index 0e800f08..8f2c056e 100644
--- a/src/services/experience/SkillManager.java
+++ b/src/services/experience/SkillManager.java
@@ -30,11 +30,13 @@ package services.experience;
import intents.SkillModIntent;
import intents.experience.GrantSkillIntent;
import intents.network.GalacticPacketIntent;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+
import java.util.stream.Collectors;
import network.packets.Packet;
import network.packets.swg.zone.object_controller.ChangeRoleIconChoice;
diff --git a/src/services/experience/SkillTemplateService.java b/src/services/experience/SkillTemplateService.java
index 41834a80..ec65d7d8 100644
--- a/src/services/experience/SkillTemplateService.java
+++ b/src/services/experience/SkillTemplateService.java
@@ -132,11 +132,11 @@ public final class SkillTemplateService extends Service {
if (skillUp) {
creatureObject.sendObserversAndSelf(new PlayClientEffectObjectMessage("clienteffect/skill_granted.cef", "", objectId));
- sendPacket(player, new ShowFlyText(objectId, new StringId("cbt_spam", "skill_up"), Scale.LARGEST, new RGB(Color.GREEN)));
- sendPacket(player, new PlayMusicMessage(0, "sound/music_acq_bountyhunter.snd", 1, false));
+ player.sendPacket(new ShowFlyText(objectId, new StringId("cbt_spam", "skill_up"), Scale.LARGEST, new RGB(Color.GREEN)));
+ player.sendPacket(new PlayMusicMessage(0, "sound/music_acq_bountyhunter.snd", 1, false));
} else {
creatureObject.sendObserversAndSelf(new PlayClientEffectObjectMessage("clienteffect/level_granted.cef", "", objectId));
- sendPacket(player, new ShowFlyText(objectId, new StringId("cbt_spam", "level_up"), Scale.LARGEST, new RGB(Color.BLUE)));
+ player.sendPacket(new ShowFlyText(objectId, new StringId("cbt_spam", "level_up"), Scale.LARGEST, new RGB(Color.BLUE)));
}
}
diff --git a/src/services/faction/FactionService.java b/src/services/faction/FactionService.java
index 9dfa54b5..348be685 100644
--- a/src/services/faction/FactionService.java
+++ b/src/services/faction/FactionService.java
@@ -45,7 +45,6 @@ import resources.PvpFlag;
import resources.PvpStatus;
import resources.control.Intent;
import resources.control.Service;
-import resources.objects.SWGObject;
import resources.objects.creature.CreatureObject;
import resources.objects.tangible.TangibleObject;
import resources.player.Player;
@@ -207,20 +206,15 @@ public final class FactionService extends Service {
private void handleFlagChange(TangibleObject target) {
Player objOwner = target.getOwner();
- for (SWGObject o : target.getObservers()) {
- if (!(o instanceof TangibleObject))
- continue;
- TangibleObject observer = (TangibleObject) o;
- Player obsOwner = observer.getOwner();
+ for (Player observerOwner : target.getObservers()) {
+ TangibleObject observer = observerOwner.getCreatureObject();
int pvpBitmask = getPvpBitmask(target, observer);
- if (objOwner != null)
- // Send the PvP information about this observer to the owner
+ if (objOwner != null) // Send the PvP information about this observer to the owner
objOwner.sendPacket(createPvpStatusMessage(observer, observer.getPvpFlags() | pvpBitmask));
- if (obsOwner != null)
- // Send the pvp information about the owner to this observer
- obsOwner.sendPacket(createPvpStatusMessage(target, target.getPvpFlags() | pvpBitmask));
+ // Send the pvp information about the owner to this observer
+ observerOwner.sendPacket(createPvpStatusMessage(target, target.getPvpFlags() | pvpBitmask));
}
}
diff --git a/src/services/galaxy/travel/TravelService.java b/src/services/galaxy/travel/TravelService.java
index 8aeea47c..8d3f2184 100644
--- a/src/services/galaxy/travel/TravelService.java
+++ b/src/services/galaxy/travel/TravelService.java
@@ -377,7 +377,6 @@ public class TravelService extends Service {
grantTickets(purchaser, nearestPoint, destinationPoint, roundTrip);
handlePurchaseFinish(purchaserOwner, purchaser, ticketPrice, newCashBalance, newBankBalance);
-
}
private void handlePurchaseFinish(Player purchaserOwner, CreatureObject purchaser, int ticketPrice, long newCashBalance, long newBankBalance) {
diff --git a/src/services/group/GroupService.java b/src/services/group/GroupService.java
index 0ba7df2b..4d037529 100644
--- a/src/services/group/GroupService.java
+++ b/src/services/group/GroupService.java
@@ -45,11 +45,8 @@ import services.player.PlayerManager;
import utilities.IntentFactory;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
@@ -65,7 +62,7 @@ public class GroupService extends Service {
private final ScheduledExecutorService logoutService;
private final Map groups;
- private Map logoffTimers;
+ private Map> logoffTimers;
public GroupService() {
logoutService = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("group-logout-timer"));
@@ -181,7 +178,7 @@ public class GroupService extends Service {
// Create a timer with the GroupMember player owns as the key
// and a timer set to fire and remove that member as the value
CreatureObject playerCreo = player.getCreatureObject();
- Future future = logoutService.schedule(new LogOffTask(this, playerCreo), 4, TimeUnit.MINUTES);
+ Future> future = logoutService.schedule(new LogOffTask(this, playerCreo), 4, TimeUnit.MINUTES);
synchronized (this.logoffTimers) {
this.logoffTimers.put(playerCreo, future);
@@ -309,8 +306,6 @@ public class GroupService extends Service {
}
private void handleGroupUninvite(Player player, CreatureObject target) {
- CreatureObject playerCreo = player.getCreatureObject();
-
if (target == null) {
sendSystemMessage(player, "uninvite_no_target_self");
return;
@@ -319,9 +314,6 @@ public class GroupService extends Service {
Player targetOwner = target.getOwner();
String targetName = targetOwner.getCharacterName();
- if (targetOwner == null)
- return;
-
if (target.getInviterData() == null) {
sendSystemMessage(player, "uninvite_not_invited", "TT", targetName);
return;
diff --git a/src/services/objects/ClientBuildoutService.java b/src/services/objects/ClientBuildoutService.java
index ba9fff32..e4ef47d4 100644
--- a/src/services/objects/ClientBuildoutService.java
+++ b/src/services/objects/ClientBuildoutService.java
@@ -65,6 +65,12 @@ public class ClientBuildoutService extends Service {
+ "objects.radius, objects.cell_index "
+ "FROM objects "
+ "ORDER BY buildout_id ASC";
+ private static final String GET_CLIENT_OBJECTS_BY_AREA_SQL = "SELECT objects.id, objects.snapshot, objects.area_id, objects.template_crc, objects.container_id, "
+ + "objects.x, objects.y, objects.z, objects.orientation_x, objects.orientation_y, objects.orientation_z, objects.orientation_w, "
+ + "objects.radius, objects.cell_index "
+ + "FROM objects "
+ + "WHERE objects.area_id = ? "
+ + "ORDER BY buildout_id ASC";
private static final String GET_ADDITIONAL_OBJECTS_SQL = "SELECT terrain, template, x, y, z, heading, cell_id, radius, building_name "
+ "FROM additional_buildouts WHERE active = 1";
private static final String GET_BUILDING_INFO_SQL = "SELECT object_id FROM buildings WHERE building_id = ?";
@@ -108,7 +114,6 @@ public class ClientBuildoutService extends Service {
objects = new HashSet<>();
} catch (SQLException e) {
objects = new HashSet<>();
- e.printStackTrace();
Log.e(this, e);
}
double loadTime = (System.nanoTime() - startLoad) / 1E6;
@@ -116,27 +121,23 @@ public class ClientBuildoutService extends Service {
return objects;
}
+ public Collection loadClientObjectsByArea(int areaId) {
+ try {
+ if (areasById.isEmpty())
+ loadAreas(new ArrayList<>());
+ return loadObjects(areaId);
+ } catch (SQLException e) {
+ Log.e(this, e);
+ return new ArrayList<>();
+ }
+ }
+
private Collection loadObjects() throws SQLException {
Map objects = new Hashtable<>();
try (CrcDatabase strings = new CrcDatabase()) {
try (RelationalServerData data = RelationalServerFactory.getServerData("buildout/buildouts.db", "objects")) {
try (ResultSet set = data.executeQuery(GET_CLIENT_OBJECTS_SQL)) {
- set.setFetchSize(4*1024);
- BuildoutArea area = null;
- ObjectInformation info = new ObjectInformation(set);
- int prevAreaId = Integer.MAX_VALUE;
- int areaId = 0;
- while (set.next()) {
- areaId = info.getAreaIdNoLoad();
- if (prevAreaId != areaId) {
- area = areasById.get(areaId);
- prevAreaId = areaId;
- }
- if (area == null)
- continue;
- info.load(strings);
- createObject(objects, area, info);
- }
+ readAllObjects(objects, strings, set);
}
}
}
@@ -145,6 +146,39 @@ public class ClientBuildoutService extends Service {
return ret;
}
+ private Collection loadObjects(int areaId) throws SQLException {
+ Map objects = new Hashtable<>();
+ try (CrcDatabase strings = new CrcDatabase()) {
+ try (RelationalServerData data = RelationalServerFactory.getServerData("buildout/buildouts.db", "objects")) {
+ PreparedStatement ps = data.prepareStatement(GET_CLIENT_OBJECTS_BY_AREA_SQL);
+ ps.setInt(1, areaId);
+ try (ResultSet set = ps.executeQuery()) {
+ readAllObjects(objects, strings, set);
+ }
+ }
+ }
+ return new ArrayList<>(objects.values());
+ }
+
+ private void readAllObjects(Map objects, CrcDatabase strings, ResultSet set) throws SQLException {
+ set.setFetchSize(4*1024);
+ BuildoutArea area = null;
+ ObjectInformation info = new ObjectInformation(set);
+ int prevAreaId = Integer.MAX_VALUE;
+ int areaId = 0;
+ while (set.next()) {
+ areaId = info.getAreaIdNoLoad();
+ if (prevAreaId != areaId) {
+ area = areasById.get(areaId);
+ prevAreaId = areaId;
+ }
+ if (area == null)
+ continue;
+ info.load(strings);
+ createObject(objects, area, info);
+ }
+ }
+
private Collection getAdditionalObjects(Map buildouts) throws SQLException {
Map objects = new Hashtable<>();
try (CrcDatabase strings = new CrcDatabase()) {
@@ -166,7 +200,7 @@ public class ClientBuildoutService extends Service {
l.setTerrain(area.getTerrain());
obj.setLocation(l);
obj.setClassification(info.isSnapshot() ? ObjectClassification.SNAPSHOT : ObjectClassification.BUILDOUT);
- obj.setLoadRange(info.getRadius());
+ obj.setPrefLoadRange(info.getRadius());
checkCell(obj, info.getCell());
checkChild(objects, obj, info.getContainer());
objects.put(obj.getObjectId(), obj);
@@ -183,7 +217,7 @@ public class ClientBuildoutService extends Service {
l.setHeading(set.getFloat("heading"));
obj.setLocation(l);
obj.setClassification(ObjectClassification.BUILDOUT);
- obj.setLoadRange(set.getFloat("radius"));
+ obj.setPrefLoadRange(set.getFloat("radius"));
checkParent(buildouts, obj, set.getString("building_name"), set.getInt("cell_id"));
objects.put(obj.getObjectId(), obj);
} catch (NullPointerException e) {
@@ -200,9 +234,12 @@ public class ClientBuildoutService extends Service {
Log.e(this, "Unknown building name: %s", buildingName);
return;
}
- SWGObject buildingUncasted = objects.get(set.getLong("object_id"));
+ long buildingId = set.getLong("object_id");
+ if (buildingId == 0)
+ return;
+ SWGObject buildingUncasted = objects.get(buildingId);
if (buildingUncasted == null) {
- Log.e(this, "Building not found in map: %s / %d", buildingName, set.getLong("object_id"));
+ Log.e(this, "Building not found in map: %s / %d", buildingName, buildingId);
return;
}
if (!(buildingUncasted instanceof BuildingObject)) {
diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java
index 06174744..2763875d 100644
--- a/src/services/objects/ObjectAwareness.java
+++ b/src/services/objects/ObjectAwareness.java
@@ -35,50 +35,46 @@ import intents.object.MoveObjectIntent;
import intents.object.ObjectCreatedIntent;
import intents.object.ObjectTeleportIntent;
import intents.player.PlayerTransformedIntent;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import main.ProjectSWG;
import network.packets.Packet;
import network.packets.swg.zone.CmdSceneReady;
+import network.packets.swg.zone.insertion.CmdStartScene;
import network.packets.swg.zone.object_controller.DataTransform;
import network.packets.swg.zone.object_controller.DataTransformWithParent;
import resources.Location;
+import resources.Race;
import resources.Terrain;
-import resources.buildout.BuildoutArea;
import resources.control.Intent;
import resources.control.Service;
import resources.objects.SWGObject;
-import resources.objects.SWGObject.ObjectClassification;
+import resources.objects.awareness.AwarenessHandler;
+import resources.objects.awareness.DataTransformHandler;
+import resources.objects.awareness.TerrainMap.TerrainMapCallback;
import resources.objects.creature.CreatureObject;
-import resources.objects.quadtree.QuadTree;
-import resources.objects.tangible.TangibleObject;
import resources.player.Player;
import resources.server_info.Log;
-public class ObjectAwareness extends Service {
+public class ObjectAwareness extends Service implements TerrainMapCallback {
- private static final double AWARE_RANGE = 1024;
- private static final int DEFAULT_LOAD_RANGE = (int) square(200); // Squared for speed
- private static final Location GONE_LOCATION = new Location(0, 0, 0, null);
-
- private final Map > quadTree;
- private final Map locations;
+ private final AwarenessHandler awarenessHandler;
+ private final DataTransformHandler dataTransformHandler;
public ObjectAwareness() {
- quadTree = new HashMap<>();
- locations = new HashMap<>();
+ awarenessHandler = new AwarenessHandler(this);
+ dataTransformHandler = new DataTransformHandler();
+
registerForIntent(PlayerEventIntent.TYPE);
registerForIntent(ObjectCreatedIntent.TYPE);
registerForIntent(DestroyObjectIntent.TYPE);
registerForIntent(ObjectTeleportIntent.TYPE);
registerForIntent(GalacticPacketIntent.TYPE);
registerForIntent(MoveObjectIntent.TYPE);
- loadQuadTree();
+ }
+
+ @Override
+ public boolean terminate() {
+ awarenessHandler.close();
+ return super.terminate();
}
@Override
@@ -113,6 +109,27 @@ public class ObjectAwareness extends Service {
}
}
+ @Override
+ public void onWithinRange(SWGObject obj, SWGObject inRange) {
+ obj.addObjectAware(inRange);
+ }
+
+ @Override
+ public void onOutOfRange(SWGObject obj, SWGObject outRange) {
+ obj.removeObjectAware(outRange);
+ }
+
+ @Override
+ public void onMoveSuccess(SWGObject obj) {
+
+ }
+
+ @Override
+ public void onMoveFailure(SWGObject obj) {
+ Log.e(this, "Move failure! %s", obj);
+ obj.clearObjectsAware();
+ }
+
private void handlePlayerEventIntent(PlayerEventIntent pei) {
Player p = pei.getPlayer();
CreatureObject creature = p.getCreatureObject();
@@ -120,14 +137,15 @@ public class ObjectAwareness extends Service {
return;
switch (pei.getEvent()) {
case PE_DISAPPEAR:
- moveToLocation(creature, creature.getParent(), null);
+ disappearObject(creature, true, true);
break;
case PE_DESTROYED:
creature.setOwner(null);
break;
+ case PE_ZONE_IN_CLIENT:
+ startScene(creature);
+ break;
case PE_ZONE_IN_SERVER:
- creature.resetAwareness();
- moveToLocation(creature, creature.getParent(), creature.getLocation());
p.sendPacket(new CmdSceneReady());
break;
default:
@@ -135,35 +153,24 @@ public class ObjectAwareness extends Service {
}
}
- private void loadQuadTree() {
- for (Terrain t : Terrain.values()) {
- quadTree.put(t, new QuadTree(16, -8192, -8192, 8192, 8192));
- }
- }
-
private void handleObjectCreatedIntent(ObjectCreatedIntent oci) {
SWGObject object = oci.getObject();
- if (isInAwareness(object))
- moveToLocation(object, object.getLocation());
+ if (object.getParent() == null)
+ moveObject(object, object.getLocation());
}
private void handleDestroyObjectIntent(DestroyObjectIntent doi) {
SWGObject obj = doi.getObject();
- obj.setLocation(GONE_LOCATION);
+ disappearObject(obj, true, true);
obj.moveToContainer(null);
- obj.clearAware();
- obj.clearCustomAware(true);
}
private void processObjectTeleportIntent(ObjectTeleportIntent oti) {
SWGObject object = oti.getObject();
Player owner = object.getOwner();
object.setLocation(oti.getNewLocation());
- if (oti.getParent() != null) {
- moveToLocation(object, oti.getParent(), null);
- } else {
- moveToLocation(object, null);
- }
+ if (oti.getParent() != object.getParent())
+ object.moveToContainer(oti.getParent());
if (object instanceof CreatureObject && ((CreatureObject) object).isLoggedInPlayer())
new RequestZoneInIntent(owner, (CreatureObject) object, false).broadcast();
}
@@ -173,228 +180,87 @@ public class ObjectAwareness extends Service {
if (packet instanceof DataTransform) {
DataTransform trans = (DataTransform) packet;
SWGObject obj = i.getObjectManager().getObjectById(trans.getObjectId());
- if (obj instanceof CreatureObject)
- moveObject((CreatureObject) obj, trans);
+ if (obj instanceof CreatureObject) {
+ Location requestedLocation = new Location(trans.getLocation());
+ requestedLocation.setTerrain(obj.getTerrain());
+ moveObjectWithTransform(obj, requestedLocation, trans.getSpeed(), trans.getUpdateCounter());
+ }
} else if (packet instanceof DataTransformWithParent) {
- DataTransformWithParent transformWithParent = (DataTransformWithParent) packet;
- SWGObject object = i.getObjectManager().getObjectById(transformWithParent.getObjectId());
- SWGObject parent = i.getObjectManager().getObjectById(transformWithParent.getCellId());
- if (object instanceof CreatureObject)
- moveObject((CreatureObject) object, parent, transformWithParent);
+ DataTransformWithParent trans = (DataTransformWithParent) packet;
+ SWGObject obj = i.getObjectManager().getObjectById(trans.getObjectId());
+ SWGObject parent = i.getObjectManager().getObjectById(trans.getCellId());
+ if (obj instanceof CreatureObject) {
+ Location requestedLocation = new Location(trans.getLocation());
+ requestedLocation.setTerrain(obj.getTerrain());
+ moveObjectWithTransform(obj, parent, requestedLocation, trans.getSpeed(), trans.getUpdateCounter());
+ }
}
}
private void processMoveObjectIntent(MoveObjectIntent i) {
- if (i.getParent() != null)
- processMoveObjectIntentParent(i);
+ if (i.getParent() == null)
+ moveObjectWithTransform(i.getObject(), i.getNewLocation(), i.getSpeed(), i.getUpdateCounter());
else
- processMoveObjectIntentNoParent(i);
+ moveObjectWithTransform(i.getObject(), i.getParent(), i.getNewLocation(), i.getSpeed(), i.getUpdateCounter());
}
- private void processMoveObjectIntentNoParent(MoveObjectIntent i) {
- SWGObject obj = i.getObject();
- Location newLocation = i.getNewLocation();
- BuildoutArea area = obj.getBuildoutArea();
- if (area != null)
- newLocation = area.adjustLocation(newLocation);
- moveToLocation(obj, newLocation);
- if (area != null)
- newLocation = area.readjustLocation(newLocation);
- DataTransform transform = new DataTransform(obj.getObjectId());
- transform.setTimestamp((int) ProjectSWG.getGalacticTime());
- transform.setLocation(newLocation);
- transform.setLookAtYaw(0);
- transform.setUseLookAtYaw(false);
- transform.setSpeed((float) i.getSpeed());
- transform.setUpdateCounter(i.getUpdateCounter());
- obj.sendDataTransforms(transform);
+ private void startScene(CreatureObject creature) {
+ Location loc = creature.getWorldLocation();
+ long time = ProjectSWG.getGalacticTime();
+ Race race = ((CreatureObject) creature).getRace();
+ boolean ignoreSnapshots = loc.getTerrain() == Terrain.DEV_AREA;
+ Player owner = creature.getOwner();
+ creature.resetAwareness();
+ owner.sendPacket(new CmdStartScene(ignoreSnapshots, creature.getObjectId(), race, loc, time, (int)(System.currentTimeMillis()/1E3)));
+ recursiveCreateObject(creature, owner);
+ if (creature.getParent() != null) {
+ for (SWGObject obj : creature.getSuperParent().getObjectsAware()) {
+ obj.createObject(creature);
+ creature.createObject(obj);
+ }
+ }
}
- private void processMoveObjectIntentParent(MoveObjectIntent i) {
- SWGObject obj = i.getObject();
- moveToLocation(obj, i.getParent(), i.getNewLocation());
- DataTransformWithParent transform = new DataTransformWithParent(obj.getObjectId());
- transform.setTimestamp((int) ProjectSWG.getGalacticTime());
- transform.setLocation(i.getNewLocation());
- transform.setLookAtYaw(0);
- transform.setUseLookAtYaw(false);
- transform.setSpeed((float) i.getSpeed());
- transform.setUpdateCounter(i.getUpdateCounter());
- obj.sendParentDataTransforms(transform);
+ private void recursiveCreateObject(SWGObject obj, Player owner) {
+ SWGObject parent = obj.getParent();
+ if (parent != null)
+ recursiveCreateObject(parent, owner);
+ else
+ obj.createObject(owner, true);
}
- private void moveObject(CreatureObject obj, DataTransform transform) {
- transform = new DataTransform(transform);
- Location newLocation = transform.getLocation();
- newLocation.setTerrain(obj.getTerrain());
- moveObjectSpeedChecks(obj, newLocation);
- BuildoutArea area = obj.getBuildoutArea();
- if (area != null)
- newLocation = area.adjustLocation(newLocation);
- new PlayerTransformedIntent(obj, obj.getParent(), null, obj.getLocation(), newLocation).broadcast();
- moveToLocation(obj, newLocation);
- if (area != null)
- newLocation = area.readjustLocation(newLocation);
- obj.sendDataTransforms(transform);
+ private void moveObject(SWGObject obj, Location requestedLocation) {
+ if (requestedLocation == null)
+ awarenessHandler.disappearObject(obj, true, true);
+ else
+ awarenessHandler.moveObject(obj, requestedLocation);
}
- private void moveObject(CreatureObject obj, SWGObject parent, DataTransformWithParent transformWithParent) {
- Location newLocation = transformWithParent.getLocation();
- newLocation.setTerrain(obj.getTerrain());
- if (parent == null) {
- Log.e("ObjectManager", "Could not find parent for transform! Cell: %d Object: %s", transformWithParent.getCellId(), obj);
+ private void moveObject(SWGObject obj, SWGObject parent, Location requestedLocation) {
+ if (requestedLocation == null)
+ awarenessHandler.disappearObject(obj, true, true);
+ else
+ awarenessHandler.moveObject(obj, parent, requestedLocation);
+ }
+
+ private void moveObjectWithTransform(SWGObject obj, Location requestedLocation, double speed, int update) {
+ if (!dataTransformHandler.handleMove(obj, requestedLocation, speed, update))
return;
- }
- moveObjectSpeedChecks(obj, parent, newLocation);
- new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), parent, obj.getLocation(), newLocation).broadcast();
- moveToLocation(obj, parent, newLocation);
- obj.sendParentDataTransforms(transformWithParent);
+ if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer())
+ new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), null, obj.getLocation(), requestedLocation).broadcast();
+ moveObject(obj, requestedLocation);
}
- private void moveObjectSpeedChecks(CreatureObject obj, Location newLocation) {
- double time = ((CreatureObject) obj).getTimeSinceLastTransform() / 1000;
- obj.updateLastTransformTime();
- Location l = obj.getWorldLocation();
- double speed = Math.sqrt(square(l.getX()-newLocation.getX()) + square(l.getZ()-newLocation.getZ())) / time;
- if (speed > obj.getMovementScale()*7.3) {
- double angle = (newLocation.getX() == l.getX() ? 0 : Math.atan2(newLocation.getZ()-l.getZ(), newLocation.getX()-l.getX()));
- newLocation.setX(l.getX()+obj.getMovementScale()*7.3*time*Math.cos(angle));
- newLocation.setZ(l.getZ()+obj.getMovementScale()*7.3*time*Math.sin(angle));
- }
- }
-
- private void moveObjectSpeedChecks(CreatureObject obj, SWGObject parent, Location newLocation) {
- double time = ((CreatureObject) obj).getTimeSinceLastTransform() / 1000;
- obj.updateLastTransformTime();
- Location l = obj.getWorldLocation();
- Location nWorld = new Location(newLocation.getX(), 0, newLocation.getZ(), parent.getTerrain());
- nWorld.translateLocation(parent.getWorldLocation());
- double speed = Math.sqrt(square(l.getX()-nWorld.getX()) + square(l.getZ()-nWorld.getZ())) / time;
- if (speed > obj.getMovementScale()*7.3) {
- double angle = (nWorld.getX() == l.getX() ? 0 : Math.atan2(nWorld.getZ()-l.getZ(), nWorld.getX()-l.getX())) + Math.PI;
- newLocation.setX(newLocation.getX()+obj.getMovementScale()*7.3*time*invertNormalizedValue(Math.cos(angle)));
- newLocation.setZ(newLocation.getZ()+obj.getMovementScale()*7.3*time*invertNormalizedValue(Math.sin(angle)));
- }
- }
-
- private void moveToLocation(SWGObject object, Location l) {
- moveToLocation(object, null, l);
- }
-
- private void moveToLocation(SWGObject object, SWGObject parent, Location l) {
- remove(object);
- if (parent != object.getParent())
- object.moveToContainer(parent);
-
- if (l != null) {
- object.setLocation(l);
- if (isInAwareness(object)) {
- add(object);
- update(object);
- }
- } else {
- object.clearAware();
- }
- }
-
- /**
- * Adds the specified object to the awareness quadtree
- * @param object the object to add
- */
- private void add(SWGObject object) {
- Location l = object.getLocation();
- if (isInMap(object)) {
- synchronized (locations) {
- locations.put(object.getObjectId(), l);
- }
- }
- QuadTree tree = getTree(l);
- synchronized (tree) {
- tree.put(l.getX(), l.getZ(), object);
- }
- }
-
- /**
- * Removes the specified object from the awareness quadtree
- * @param object the object to remove
- */
- private boolean remove(SWGObject object) {
- Location l;
- if (isInMap(object)) {
- synchronized (locations) {
- l = locations.remove(object.getObjectId());
- }
- } else {
- l = object.getLocation();
- }
- if (l == null)
- return false;
- QuadTree tree = getTree(l);
- synchronized (tree) {
- return tree.remove(l.getX(), l.getZ(), object);
- }
- }
-
- /**
- * Updates the specified object after it has been moved, or to verify that
- * the awareness is up to date
- * @param obj the object to update
- */
- private void update(SWGObject obj) {
- if (!obj.isGenerated())
+ private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) {
+ if (!dataTransformHandler.handleMove(obj, parent, requestedLocation, speed, update))
return;
- Location l = obj.getWorldLocation();
- Set objectAware = new HashSet();
- QuadTree tree = getTree(l);
- List range;
- synchronized (tree) {
- range = tree.getWithinRange(l.getX(), l.getZ(), AWARE_RANGE);
- }
- for (SWGObject inRange : range) {
- if (isValidInRange(obj, inRange, l))
- objectAware.add(inRange);
- }
- obj.updateObjectAwareness(objectAware);
+ if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer())
+ new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), parent, obj.getLocation(), requestedLocation).broadcast();
+ moveObject(obj, parent, requestedLocation);
}
- private QuadTree getTree(Location l) {
- return quadTree.get(l.getTerrain());
- }
-
- private boolean isValidInRange(SWGObject obj, SWGObject inRange, Location objLoc) {
- if (inRange.getObjectId() == obj.getObjectId())
- return false;
- if (inRange instanceof CreatureObject && ((CreatureObject) inRange).isLoggedOutPlayer())
- return false;
- int distSquared = distanceSquared(objLoc, inRange.getWorldLocation());
- int loadSquared = (int) (square(inRange.getLoadRange()) + 0.5);
- return (loadSquared != 0 || distSquared <= DEFAULT_LOAD_RANGE) && (loadSquared == 0 || distSquared <= loadSquared);
- }
-
- private boolean isInAwareness(SWGObject object) {
- if (object instanceof CreatureObject && ((CreatureObject) object).isLoggedOutPlayer())
- return false;
- if (object.getTerrain() == null)
- return false;
- return object.getParent() == null && object instanceof TangibleObject;
- }
-
- private boolean isInMap(SWGObject object) {
- return object.getClassification() == ObjectClassification.GENERATED;
- }
-
- private double invertNormalizedValue(double x) {
- if (x < 0)
- return -1 - x;
- return 1-x;
- }
-
- private int distanceSquared(Location l1, Location l2) {
- return (int) (square(l1.getX()-l2.getX()) + square(l1.getY()-l2.getY()) + square(l1.getZ()-l2.getZ()) + 0.5);
- }
-
- private static double square(double x) {
- return x * x;
+ private void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) {
+ awarenessHandler.disappearObject(obj, disappearObjects, disappearCustom);
}
}
\ No newline at end of file
diff --git a/src/services/objects/ObjectManager.java b/src/services/objects/ObjectManager.java
index ee18ec11..95b1262b 100644
--- a/src/services/objects/ObjectManager.java
+++ b/src/services/objects/ObjectManager.java
@@ -57,7 +57,6 @@ import services.map.MapManager;
import services.player.PlayerManager;
import services.spawn.SpawnerService;
import services.spawn.StaticService;
-import utilities.Scripts;
public class ObjectManager extends Manager {
@@ -180,7 +179,6 @@ public class ObjectManager extends Manager {
@Override
public boolean terminate() {
synchronized (database) {
- database.traverse((obj) -> obj.setOwner(null));
database.close();
}
return super.terminate();
diff --git a/src/services/objects/StaticItemService.java b/src/services/objects/StaticItemService.java
index ce0705a0..8c1b879f 100644
--- a/src/services/objects/StaticItemService.java
+++ b/src/services/objects/StaticItemService.java
@@ -200,6 +200,8 @@ public final class StaticItemService extends Service {
case CONTAINER_FULL:
new ChatBroadcastIntent(requesterOwner, "@system_msg:give_item_failure").broadcast();
break;
+ default:
+ break;
}
new ObjectCreatedIntent(object).broadcast();
@@ -235,7 +237,6 @@ public final class StaticItemService extends Service {
private boolean unique;
private String conditionString;
private int volume;
- private String requiredLevel;
// TODO bio-link
private final String itemName;
private final String iffTemplate;
@@ -262,7 +263,6 @@ public final class StaticItemService extends Service {
int hitPoints = resultSet.getInt("hit_points");
conditionString = String.format("%d/%d", hitPoints, hitPoints);
volume = resultSet.getInt("volume");
- requiredLevel = String.valueOf(resultSet.getShort("required_level"));
// load type-specific attributes
return loadTypeAttributes(resultSet);
@@ -293,8 +293,7 @@ public final class StaticItemService extends Service {
object.addAttribute("unique", "1");
object.addAttribute("condition", conditionString);
object.addAttribute("volume", String.valueOf(volume));
- object.addAttribute("required_combat_level", requiredLevel);
-
+
// apply type-specific attributes
applyTypeAttributes(object);
}
@@ -319,6 +318,7 @@ public final class StaticItemService extends Service {
private Map mods; // skillmods/statmods
private String requiredProfession;
+ private String requiredLevel;
private String requiredFaction;
private String buffName;
// TODO species restriction
@@ -331,6 +331,7 @@ public final class StaticItemService extends Service {
@Override
protected boolean loadTypeAttributes(ResultSet resultSet) throws SQLException {
+ requiredLevel = String.valueOf(resultSet.getShort("required_level"));
requiredProfession = resultSet.getString("required_profession");
if (requiredProfession.equals("-")) {
// Ziggy: This value is not defined in any String Table File.
@@ -338,7 +339,6 @@ public final class StaticItemService extends Service {
} else {
requiredProfession = "@ui_roadmap:title_" + requiredProfession;
}
-
requiredFaction = resultSet.getString("required_faction");
if (requiredFaction.equals("-")) {
@@ -366,6 +366,7 @@ public final class StaticItemService extends Service {
@Override
protected void applyTypeAttributes(SWGObject object) {
object.addAttribute("class_required", requiredProfession);
+ object.addAttribute("required_combat_level", requiredLevel);
object.addAttribute("faction_restriction", requiredFaction);
// Apply the mods!
@@ -380,6 +381,7 @@ public final class StaticItemService extends Service {
private static final class ArmorAttributes extends WearableAttributes {
+ private String requiredLevel;
private String armorCategory;
private String kinetic, energy, elementals;
private float protectionWeight;
@@ -395,7 +397,7 @@ public final class StaticItemService extends Service {
if (!wearableAttributesLoaded) {
return false;
}
-
+ requiredLevel = String.valueOf(resultSet.getShort("required_level"));
String armorType = resultSet.getString("armor_category");
protectionWeight = resultSet.getFloat("protection");
@@ -427,6 +429,7 @@ public final class StaticItemService extends Service {
@Override
protected void applyTypeAttributes(SWGObject object) {
super.applyTypeAttributes(object);
+ object.addAttribute("required_combat_level", requiredLevel);
object.addAttribute("armor_category", armorCategory);
object.addAttribute("cat_armor_standard_protection.kinetic", kinetic);
object.addAttribute("cat_armor_standard_protection.energy", energy);
@@ -444,6 +447,7 @@ public final class StaticItemService extends Service {
private static final class WeaponAttributes extends WearableAttributes {
+ private String requiredLevel;
private WeaponType category;
private DamageType damageTypeEnum;
private DamageType elementalTypeEnum;
@@ -510,6 +514,7 @@ public final class StaticItemService extends Service {
return false;
}
+ requiredLevel = String.valueOf(resultSet.getShort("required_level"));
weaponCategory = "@obj_attr_n:wpn_category_" + String.valueOf(category.getNum());
damageType = resultSet.getString("damage_type");
damageTypeEnum = getDamageTypeForName(damageType);
diff --git a/src/services/player/CharacterCreationService.java b/src/services/player/CharacterCreationService.java
index 9b916c74..87814fc2 100644
--- a/src/services/player/CharacterCreationService.java
+++ b/src/services/player/CharacterCreationService.java
@@ -175,7 +175,7 @@ public class CharacterCreationService extends Service {
}
}
response.setRandomName(randomName);
- sendPacket(player.getNetworkId(), response);
+ player.sendPacket(response);
}
private void handleApproveNameRequest(PlayerManager playerMgr, Player player, ClientVerifyAndLockNameRequest request) {
@@ -191,7 +191,7 @@ public class CharacterCreationService extends Service {
err = ErrorMessage.NAME_DECLINED_IN_USE;
}
}
- sendPacket(player.getNetworkId(), new ClientVerifyAndLockNameResponse(name, err));
+ player.sendPacket(new ClientVerifyAndLockNameResponse(name, err));
}
private void handleCharCreation(ObjectManager objManager, Player player, ClientCreateCharacter create) {
@@ -217,7 +217,7 @@ public class CharacterCreationService extends Service {
} else if (createCharacterInDb(creature, create.getName(), player)) {
creationRestriction.createdCharacter(player);
Log.i("ZoneService", "%s created character %s from %s:%d", player.getUsername(), create.getName(), create.getAddress(), create.getPort());
- sendPacket(player, new CreateCharacterSuccess(creature.getObjectId()));
+ player.sendPacket(new CreateCharacterSuccess(creature.getObjectId()));
new PlayerEventIntent(player, PlayerEvent.PE_CREATE_CHARACTER).broadcast();
return ErrorMessage.NAME_APPROVED;
} else {
@@ -246,7 +246,7 @@ public class CharacterCreationService extends Service {
break;
}
Log.e("ZoneService", "Failed to create character %s for user %s with error %s and reason %s from %s:%d", create.getName(), player.getUsername(), err, reason, create.getAddress(), create.getPort());
- sendPacket(player, new CreateCharacterFailure(reason));
+ player.sendPacket(new CreateCharacterFailure(reason));
}
private boolean createCharacterInDb(CreatureObject creature, String name, Player player) {
@@ -319,7 +319,6 @@ public class CharacterCreationService extends Service {
playerObj.setAdminTag(player.getAccessLevel());
new ObjectCreatedIntent(creatureObj).broadcast();
- new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, create.getStartingPhase(), creatureObj, true).broadcast();
return creatureObj;
}
@@ -399,8 +398,9 @@ public class CharacterCreationService extends Service {
creatureObj.setHeight(create.getHeight());
creatureObj.setName(create.getName());
creatureObj.setPvpFlags(PvpFlag.PLAYER);
- creatureObj.getSkills().add("species_" + creatureObj.getRace().getSpecies());
creatureObj.setVolume(0x000F4240);
+ new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, create.getStartingPhase(), creatureObj, true).broadcast();
+ new GrantSkillIntent(GrantSkillIntent.IntentType.GRANT, "species_" + creatureObj.getRace().getSpecies(), creatureObj, true).broadcast();
WeaponObject defWeapon = (WeaponObject) createInventoryObject(objManager, creatureObj, "object/weapon/melee/unarmed/shared_unarmed_default_player.iff");
defWeapon.setMaxRange(5);
@@ -415,9 +415,6 @@ public class CharacterCreationService extends Service {
createInventoryObject(objManager, creatureObj, "object/tangible/inventory/shared_appearance_inventory.iff");
createInventoryObject(objManager, creatureObj, "object/tangible/bank/shared_character_bank.iff");
createInventoryObject(objManager, creatureObj, "object/tangible/mission_bag/shared_mission_bag.iff");
-
- // Any character can perform the basic dance.
- creatureObj.addAbility("startDance+basic");
}
private void setPlayerObjectValues(PlayerObject playerObj, ClientCreateCharacter create) {
diff --git a/src/services/player/LoginService.java b/src/services/player/LoginService.java
index 59d61bef..565a73cb 100644
--- a/src/services/player/LoginService.java
+++ b/src/services/player/LoginService.java
@@ -138,7 +138,7 @@ public class LoginService extends Service {
Log.i("LoginService", "Deleted character %s for user %s", ((CreatureObject)obj).getName(), player.getUsername());
} else
Log.w("LoginService", "Could not delete character! Character: ID: " + request.getPlayerId() + " / " + obj);
- sendPacket(player, new DeleteCharacterResponse(success));
+ player.sendPacket(new DeleteCharacterResponse(success));
}
private void handleLogin(Player player, LoginClientId id) {
@@ -177,7 +177,7 @@ public class LoginService extends Service {
Log.i("LoginService", "%s cannot login due to invalid version code: %s, expected %s from %s:%d", player.getUsername(), id.getVersion(), REQUIRED_VERSION, id.getAddress(), id.getPort());
String type = "Login Failed!";
String message = "Invalid Client Version Code: " + id.getVersion();
- sendPacket(player.getNetworkId(), new ErrorMessage(type, message, false));
+ player.sendPacket(new ErrorMessage(type, message, false));
player.setPlayerState(PlayerState.DISCONNECTED);
new LoginEventIntent(player.getNetworkId(), LoginEvent.LOGIN_FAIL_INVALID_VERSION_CODE).broadcast();
}
@@ -206,7 +206,7 @@ public class LoginService extends Service {
private void onLoginBanned(Player player, LoginClientId id) {
String type = "Login Failed!";
String message = "Sorry, you're banned!";
- sendPacket(player.getNetworkId(), new ErrorMessage(type, message, false));
+ player.sendPacket(new ErrorMessage(type, message, false));
Log.i("LoginService", "%s cannot login due to a ban, from %s:%d", player.getUsername(), id.getAddress(), id.getPort());
player.setPlayerState(PlayerState.DISCONNECTED);
new LoginEventIntent(player.getNetworkId(), LoginEvent.LOGIN_FAIL_BANNED).broadcast();
@@ -215,8 +215,8 @@ public class LoginService extends Service {
private void onInvalidUserPass(Player player, LoginClientId id, ResultSet set) throws SQLException {
String type = "Login Failed!";
String message = getUserPassError(set, id.getUsername(), id.getPassword());
- sendPacket(player, new ErrorMessage(type, message, false));
- sendPacket(player, new LoginIncorrectClientId(getServerString(), REQUIRED_VERSION));
+ player.sendPacket(new ErrorMessage(type, message, false));
+ player.sendPacket(new LoginIncorrectClientId(getServerString(), REQUIRED_VERSION));
Log.i("LoginService", "%s cannot login due to invalid user/pass from %s:%d", id.getUsername(), id.getAddress(), id.getPort());
player.setPlayerState(PlayerState.DISCONNECTED);
new LoginEventIntent(player.getNetworkId(), LoginEvent.LOGIN_FAIL_INVALID_USER_PASS).broadcast();
@@ -225,7 +225,7 @@ public class LoginService extends Service {
private void onLoginServerError(Player player, LoginClientId id) {
String type = "Login Failed!";
String message = "Server Error.";
- sendPacket(player.getNetworkId(), new ErrorMessage(type, message, false));
+ player.sendPacket(new ErrorMessage(type, message, false));
player.setPlayerState(PlayerState.DISCONNECTED);
Log.e("LoginService", "%s cannot login due to server error, from %s:%d", id.getUsername(), id.getAddress(), id.getPort());
new LoginEventIntent(player.getNetworkId(), LoginEvent.LOGIN_FAIL_SERVER_ERROR).broadcast();
@@ -242,12 +242,12 @@ public class LoginService extends Service {
clusterStatus.addGalaxy(g);
}
cluster.setMaxCharacters(getConfig(ConfigFile.PRIMARY).getInt("GALAXY-MAX-CHARACTERS", 2));
- sendPacket(p.getNetworkId(), new ServerNowEpochTime((int)(System.currentTimeMillis()/1E3)));
- sendPacket(p.getNetworkId(), token);
- sendPacket(p.getNetworkId(), cluster);
- sendPacket(p.getNetworkId(), new CharacterCreationDisabled());
- sendPacket(p.getNetworkId(), new EnumerateCharacterId(characters));
- sendPacket(p.getNetworkId(), clusterStatus);
+ p.sendPacket(new ServerNowEpochTime((int)(System.currentTimeMillis()/1E3)));
+ p.sendPacket(token);
+ p.sendPacket(cluster);
+ p.sendPacket(new CharacterCreationDisabled());
+ p.sendPacket(new EnumerateCharacterId(characters));
+ p.sendPacket(clusterStatus);
p.setPlayerState(PlayerState.LOGGED_IN);
}
diff --git a/src/services/player/ZoneManager.java b/src/services/player/ZoneManager.java
index b938e421..e0f93123 100644
--- a/src/services/player/ZoneManager.java
+++ b/src/services/player/ZoneManager.java
@@ -32,7 +32,6 @@ import intents.PlayerEventIntent;
import intents.RequestZoneInIntent;
import intents.chat.ChatBroadcastIntent;
import intents.network.GalacticPacketIntent;
-import main.ProjectSWG;
import network.packets.Packet;
import network.packets.swg.login.AccountFeatureBits;
import network.packets.swg.login.ClientIdMsg;
@@ -49,21 +48,15 @@ import network.packets.swg.zone.chat.ChatOnConnectAvatar;
import network.packets.swg.zone.chat.ChatSystemMessage;
import network.packets.swg.zone.chat.VoiceChatStatus;
import network.packets.swg.zone.insertion.ChatServerStatus;
-import network.packets.swg.zone.insertion.CmdStartScene;
-
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import resources.Location;
-import resources.Race;
-import resources.Terrain;
import resources.config.ConfigFile;
import resources.control.Intent;
import resources.control.Manager;
-import resources.objects.SWGObject;
import resources.objects.creature.CreatureMood;
import resources.objects.creature.CreatureObject;
import resources.objects.player.PlayerObject;
@@ -140,38 +133,19 @@ public class ZoneManager extends Manager {
PlayerObject playerObj = creature.getPlayerObject();
player.setPlayerState(PlayerState.ZONING_IN);
creature.setOwner(player);
-
- if (firstZone)
- sendZonePackets(player, creature);
- startScene(creature, creature.getWorldLocation());
- if (firstZone)
- playerObj.setStartPlayTime((int) System.currentTimeMillis());
initPlayerBeforeZoneIn(player, creature, playerObj);
Log.i("ObjectManager", "Zoning in %s with character %s", player.getUsername(), player.getCharacterName());
+
if (firstZone) {
- new PlayerEventIntent(player, PlayerEvent.PE_FIRST_ZONE).broadcast();
+ sendZonePackets(player, creature);
+ playerObj.setStartPlayTime((int) System.currentTimeMillis());
sendCommitHistory(player);
sendMessageOfTheDay(player);
+ new PlayerEventIntent(player, PlayerEvent.PE_FIRST_ZONE).broadcast();
}
new PlayerEventIntent(player, PlayerEvent.PE_ZONE_IN_CLIENT).broadcast();
}
- private void startScene(CreatureObject object, Location newLocation) {
- long time = ProjectSWG.getGalacticTime();
- Race race = ((CreatureObject)object).getRace();
- boolean ignoreSnapshots = newLocation.getTerrain() == Terrain.DEV_AREA;
- sendPacket(object.getOwner(), new CmdStartScene(ignoreSnapshots, object.getObjectId(), race, newLocation, time, (int)(System.currentTimeMillis()/1E3)));
- recursiveCreateObject(object, object.getOwner());
- }
-
- private void recursiveCreateObject(SWGObject obj, Player p) {
- SWGObject parent = obj.getParent();
- if (parent != null)
- recursiveCreateObject(parent, p);
- else
- obj.createObject(p, true);
- }
-
private void loadCommitHistory() {
File repoDir = new File("./" + Constants.DOT_GIT);
int commitCount = 3;
@@ -212,11 +186,11 @@ public class ZoneManager extends Manager {
}
private void sendZonePackets(Player player, CreatureObject creature) {
- sendPacket(player, new HeartBeat());
- sendPacket(player, new ChatServerStatus(true));
- sendPacket(player, new VoiceChatStatus());
- sendPacket(player, new ParametersMessage());
- sendPacket(player, new ChatOnConnectAvatar());
+ player.sendPacket(new HeartBeat());
+ player.sendPacket(new ChatServerStatus(true));
+ player.sendPacket(new VoiceChatStatus());
+ player.sendPacket(new ParametersMessage());
+ player.sendPacket(new ChatOnConnectAvatar());
}
private void initPlayerBeforeZoneIn(Player player, CreatureObject creatureObj, PlayerObject playerObj) {
@@ -263,9 +237,9 @@ public class ZoneManager extends Manager {
private void handleClientIdMsg(Player player, ClientIdMsg clientId) {
Log.i("ZoneService", "%s connected to the zone server from %s:%d", player.getUsername(), clientId.getAddress(), clientId.getPort());
player.setPlayerServer(PlayerServer.ZONE);
- sendPacket(player.getNetworkId(), new HeartBeat());
- sendPacket(player.getNetworkId(), new AccountFeatureBits());
- sendPacket(player.getNetworkId(), new ClientPermissionsMessage());
+ player.sendPacket(new HeartBeat());
+ player.sendPacket(new AccountFeatureBits());
+ player.sendPacket(new ClientPermissionsMessage());
}
}
diff --git a/src/services/sui/SuiService.java b/src/services/sui/SuiService.java
index 4a90ca42..06e8f41a 100644
--- a/src/services/sui/SuiService.java
+++ b/src/services/sui/SuiService.java
@@ -50,8 +50,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
public class SuiService extends Service {
diff --git a/src/utilities/AwarenessUtilities.java b/src/utilities/AwarenessUtilities.java
new file mode 100644
index 00000000..b9dee113
--- /dev/null
+++ b/src/utilities/AwarenessUtilities.java
@@ -0,0 +1,95 @@
+/************************************************************************************
+ * 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 utilities;
+
+import java.util.Set;
+
+import resources.objects.SWGObject;
+import resources.player.Player;
+
+public class AwarenessUtilities {
+
+ public static void handleUpdateAwarenessManual(SWGObject obj, Set oldAware, Set oldObservers, Set newAware, Set newObservers) {
+ callForNewObserver(oldObservers, newObservers, (observer) -> obj.createObject(observer.getCreatureObject()));
+ callForOldObserver(oldObservers, newObservers, (observer) -> obj.destroyObject(observer.getCreatureObject()));
+
+ callForNewAware(oldAware, newAware, (aware) -> aware.createObject(obj));
+ callForOldAware(oldAware, newAware, (aware) -> aware.destroyObject(obj));
+ }
+
+ public static void callForNewObserver(Set oldObservers, Set newObservers, ObserverBasedRunnable r) {
+ for (Player newObserver : newObservers) {
+ if (!oldObservers.contains(newObserver))
+ r.run(newObserver);
+ }
+ }
+
+ public static void callForOldObserver(Set oldObservers, Set newObservers, ObserverBasedRunnable r) {
+ for (Player oldObserver : oldObservers) {
+ if (!newObservers.contains(oldObserver))
+ r.run(oldObserver);
+ }
+ }
+
+ public static void callForSameObserver(Set oldObservers, Set newObservers, ObserverBasedRunnable r) {
+ for (Player newObserver : newObservers) {
+ if (oldObservers.contains(newObserver))
+ r.run(newObserver);
+ }
+ }
+
+ public static void callForNewAware(Set oldAware, Set newAware, AwareBasedRunnable r) {
+ for (SWGObject aware : newAware) {
+ if (!oldAware.contains(aware))
+ r.run(aware);
+ }
+ }
+
+ public static void callForOldAware(Set oldAware, Set newAware, AwareBasedRunnable r) {
+ for (SWGObject aware : oldAware) {
+ if (!newAware.contains(aware))
+ r.run(aware);
+ }
+ }
+
+ public static void callForSameAware(Set oldAware, Set newAware, AwareBasedRunnable r) {
+ for (SWGObject aware : newAware) {
+ if (oldAware.contains(aware))
+ r.run(aware);
+ }
+ }
+
+ public interface AwareBasedRunnable {
+ void run(SWGObject aware);
+ }
+
+ public interface ObserverBasedRunnable {
+ void run(Player owner);
+ }
+
+}
diff --git a/test/resources/TestResources.java b/test/resources/TestResources.java
index 8037a580..d4f5de53 100644
--- a/test/resources/TestResources.java
+++ b/test/resources/TestResources.java
@@ -35,6 +35,7 @@ import resources.collections.SWGListTest;
import resources.collections.TestSWGBitSet;
import resources.collections.TestSWGFlag;
import resources.network.TestLittleEndianDataOutputStream;
+import resources.objects.awareness.TestAwareness;
import resources.objects.quadtree.TestQuadTree;
import resources.persistable.TestPersistable;
import resources.services.TestConfig;
@@ -49,7 +50,8 @@ import resources.services.TestConfig;
TestPersistable.class,
TestConfig.class,
TestQuaternion.class,
- TestLittleEndianDataOutputStream.class
+ TestLittleEndianDataOutputStream.class,
+ TestAwareness.class
})
public class TestResources {
diff --git a/test/resources/objects/awareness/GenericCreatureObject.java b/test/resources/objects/awareness/GenericCreatureObject.java
new file mode 100644
index 00000000..96973b46
--- /dev/null
+++ b/test/resources/objects/awareness/GenericCreatureObject.java
@@ -0,0 +1,60 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import network.packets.Packet;
+import resources.objects.creature.CreatureObject;
+import resources.objects.player.PlayerObject;
+import resources.player.Player;
+import resources.player.PlayerState;
+
+class GenericCreatureObject extends CreatureObject {
+
+ private Player player;
+
+ public GenericCreatureObject(long objectId) {
+ super(objectId);
+ player = new Player() {
+ public void sendPacket(Packet ... packets) {
+ // Nah
+ }
+ };
+ player.setPlayerState(PlayerState.ZONED_IN);
+ setHasOwner(true);
+ setSlot("ghost", new PlayerObject(-objectId));
+ }
+
+ public void setHasOwner(boolean hasOwner) {
+ if (hasOwner) {
+ player.setCreatureObject(this);
+ } else {
+ player.setCreatureObject(null);
+ }
+ }
+
+}
diff --git a/test/resources/objects/awareness/MapCallback.java b/test/resources/objects/awareness/MapCallback.java
new file mode 100644
index 00000000..623ac9e2
--- /dev/null
+++ b/test/resources/objects/awareness/MapCallback.java
@@ -0,0 +1,160 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+
+import resources.objects.SWGObject;
+import resources.objects.awareness.TerrainMap.TerrainMapCallback;
+
+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();
+
+ @Override
+ public void onWithinRange(SWGObject obj, SWGObject inRange) {
+ withinRange.increment();
+ }
+
+ @Override
+ public void onOutOfRange(SWGObject obj, SWGObject outRange) {
+ outOfRange.increment();
+ }
+
+ @Override
+ public void onMoveSuccess(SWGObject obj) {
+ successfulMove.increment();
+ }
+
+ @Override
+ public void onMoveFailure(SWGObject obj) {
+ failedMove.increment();
+ }
+
+ public void set(int withinRange, int outOfRange, int successful, int failed) {
+ this.withinRange.set(withinRange);
+ this.outOfRange.set(outOfRange);
+ this.successfulMove.set(successful);
+ this.failedMove.set(failed);
+ }
+
+ public int getWithinRange() {
+ return withinRange.get();
+ }
+
+ public int getOutOfRange() {
+ return outOfRange.get();
+ }
+
+ public int getSuccess() {
+ return successfulMove.get();
+ }
+
+ public int getFailed() {
+ 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 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());
+ Assert.assertEquals("TEST-OUT-OF-RANGE", outOfRange, this.outOfRange.get());
+ Assert.assertEquals("TEST-SUCCESSFUL-MOVE", successful, this.successfulMove.get());
+ 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/TestAware.java b/test/resources/objects/awareness/TestAware.java
new file mode 100644
index 00000000..f4dacab7
--- /dev/null
+++ b/test/resources/objects/awareness/TestAware.java
@@ -0,0 +1,131 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import resources.objects.SWGObject;
+import resources.objects.cell.CellObject;
+import resources.player.Player;
+
+@RunWith(JUnit4.class)
+public class TestAware {
+
+ @Test
+ public void testAdd() {
+ GenericCreatureObject creatureA = new GenericCreatureObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ Aware a = new Aware(creatureA);
+ Aware b = new Aware(creatureB);
+ Assert.assertTrue(a.add(b));
+ Assert.assertTrue(a.contains(creatureB));
+ Assert.assertTrue(b.contains(creatureA));
+ }
+
+ @Test
+ public void testRemove() {
+ GenericCreatureObject creatureA = new GenericCreatureObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ Aware a = new Aware(creatureA);
+ Aware b = new Aware(creatureB);
+ Assert.assertTrue(a.add(b));
+ Assert.assertTrue(a.contains(creatureB));
+ Assert.assertTrue(b.contains(creatureA));
+ Assert.assertTrue(a.remove(b));
+ Assert.assertFalse(a.contains(creatureB));
+ Assert.assertFalse(b.contains(creatureA));
+ }
+
+ @Test
+ public void testAwareNoParent() {
+ GenericCreatureObject creatureA = new GenericCreatureObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ Aware a = new Aware(creatureA);
+ Aware b = new Aware(creatureB);
+ Assert.assertTrue(a.add(b));
+ Set aware = a.getAware();
+ Assert.assertEquals(1, aware.size());
+ Assert.assertTrue(aware.contains(creatureB));
+ }
+
+ @Test
+ public void testObserversNoParent() {
+ GenericCreatureObject creatureA = new GenericCreatureObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ Aware a = new Aware(creatureA);
+ Aware b = new Aware(creatureB);
+ Assert.assertTrue(a.add(b));
+ Assert.assertTrue(a.contains(creatureB));
+ Assert.assertTrue(b.contains(creatureA));
+ Set observers = a.getObservers();
+ Assert.assertEquals(1, observers.size());
+ Assert.assertTrue(observers.contains(creatureB.getOwner()));
+ }
+
+ @Test
+ public void testAwareParent() {
+ GenericCreatureObject creatureA = new GenericCreatureObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ GenericCreatureObject creatureC = new GenericCreatureObject(3);
+ Aware a = new Aware(creatureA);
+ Aware b = new Aware(creatureB);
+ Aware c = new Aware(creatureC);
+ c.setParent(a);
+ Assert.assertTrue(a.add(b));
+ Set aware = c.getAware();
+ Assert.assertEquals(1, aware.size());
+ Assert.assertTrue(aware.contains(creatureB));
+ }
+
+ @Test
+ public void testObserversParent() {
+ CellObject cellA = new CellObject(1);
+ GenericCreatureObject creatureB = new GenericCreatureObject(2);
+ GenericCreatureObject creatureC = new GenericCreatureObject(3);
+ Aware a = new Aware(cellA);
+ Aware b = new Aware(creatureB);
+ Aware c = new Aware(creatureC);
+ cellA.addObject(creatureC);
+ c.setParent(a);
+ Assert.assertTrue(a.add(b));
+ Assert.assertTrue(a.contains(creatureB));
+ Assert.assertTrue(b.contains(cellA));
+ Set observers = c.getObservers();
+ Assert.assertEquals(1, observers.size());
+ Assert.assertTrue(observers.contains(creatureB.getOwner()));
+ observers = b.getObservers();
+ Assert.assertEquals(1, observers.size());
+ Assert.assertTrue(observers.contains(creatureC.getOwner()));
+ }
+
+}
diff --git a/src/network/OutboundPacketService.java b/test/resources/objects/awareness/TestAwareness.java
similarity index 82%
rename from src/network/OutboundPacketService.java
rename to test/resources/objects/awareness/TestAwareness.java
index bab4fce0..b04785fc 100644
--- a/src/network/OutboundPacketService.java
+++ b/test/resources/objects/awareness/TestAwareness.java
@@ -25,23 +25,27 @@
* along with Holocore. If not, see . *
* *
***********************************************************************************/
-package network;
+package resources.objects.awareness;
-import intents.network.OutboundPacketIntent;
-import utilities.IntentChain;
-import network.packets.Packet;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
-public class OutboundPacketService {
+import utilities.CrcDatabaseGenerator;
+
+@RunWith(Suite.class)
+@SuiteClasses({
+ TestAware.class,
+ TestTerrainMapChunk.class,
+ TestTerrainMap.class,
+ TestAwarenessHandler.class
+})
+public class TestAwareness {
- private final IntentChain chain = new IntentChain();
-
- public void send(long networkId, Packet ... packets) {
- for (Packet packet : packets)
- send(networkId, packet);
- }
-
- public void send(long networkId, Packet packet) {
- chain.broadcastAfter(new OutboundPacketIntent(packet, networkId));
+ @BeforeClass
+ public static void setupCrcDatabase() {
+ CrcDatabaseGenerator.generate(false);
}
}
diff --git a/test/resources/objects/awareness/TestAwarenessHandler.java b/test/resources/objects/awareness/TestAwarenessHandler.java
new file mode 100644
index 00000000..61742307
--- /dev/null
+++ b/test/resources/objects/awareness/TestAwarenessHandler.java
@@ -0,0 +1,165 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import resources.Location;
+import resources.Terrain;
+import resources.objects.SWGObject;
+import resources.objects.building.BuildingObject;
+import resources.objects.creature.CreatureObject;
+import services.objects.ClientBuildoutService;
+
+@RunWith(JUnit4.class)
+public class TestAwarenessHandler {
+
+ private static final Location CREATURE_LOCATION = new Location(3618, 5, -4801, Terrain.TATOOINE);
+ private static final Location CREATURE2_LOCATION = new Location(3650, 5, -4800, Terrain.TATOOINE);
+
+ private static final List EISLEY_OBJECTS = new ArrayList<>(2524);
+ private static final List WITHIN_RANGE = new ArrayList<>(537);
+
+ private static final GenericCreatureObject CREATURE2 = new GenericCreatureObject(2);
+ private static final GenericCreatureObject CREATURE3 = new GenericCreatureObject(3);
+
+ @BeforeClass
+ public static void initTatooine() {
+ ClientBuildoutService buildoutService = new ClientBuildoutService();
+ double loadDistance = 1024*1.414; // 1024 * sqrt(2)
+ for (SWGObject obj : buildoutService.loadClientObjectsByArea(843)) { // mos eisley's area id
+ initObject(obj, loadDistance);
+ }
+ for (SWGObject obj : buildoutService.loadClientObjectsByArea(-59)) { // general tatooine's area id
+ initObject(obj, loadDistance);
+ }
+ CREATURE2.setLocation(CREATURE2_LOCATION);
+ initObject(CREATURE2, loadDistance);
+ CREATURE3.setLocation(CREATURE2_LOCATION);
+ CREATURE3.setHasOwner(false);
+ initObject(CREATURE3, loadDistance);
+ }
+
+ private static void initObject(SWGObject obj, double loadDistance) {
+ if (obj.getParent() == null && CREATURE_LOCATION.isWithinFlatDistance(obj.getLocation(), loadDistance)) {
+ EISLEY_OBJECTS.add(obj);
+ if (isValidWithinRange(obj, CREATURE_LOCATION, Math.max(obj.getLoadRange(), 200)))
+ WITHIN_RANGE.add(obj);
+ }
+ }
+
+ private static boolean isValidWithinRange(SWGObject inRange, Location objLocation, double range) {
+ if (inRange instanceof CreatureObject && ((CreatureObject) inRange).isLoggedOutPlayer())
+ return false;
+ if (!inRange.getWorldLocation().isWithinFlatDistance(objLocation, Math.max(range, inRange.getLoadRange())))
+ return false;
+ return true;
+ }
+
+ @Test
+ public void testMoveSameAwareness() throws InterruptedException {
+ MapCallbackRealistic callback = new MapCallbackRealistic();
+ AwarenessHandler awareness = new AwarenessHandler(callback);
+ GenericCreatureObject creature = new GenericCreatureObject(1);
+ initAwareness(awareness, callback);
+ awareness.moveObject(creature, CREATURE_LOCATION);
+ callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000);
+ callback.set(0, 0, 0, 0);
+ awareness.moveObject(creature, CREATURE_LOCATION);
+ callback.waitAndTest(0, 0, 1, 0, 1000);
+ }
+
+ @Test
+ public void testMoveAwayBack() throws InterruptedException {
+ MapCallbackRealistic callback = new MapCallbackRealistic();
+ AwarenessHandler awareness = new AwarenessHandler(callback);
+ initAwareness(awareness, callback);
+ GenericCreatureObject creature = new GenericCreatureObject(1);
+ awareness.moveObject(creature, CREATURE_LOCATION);
+ callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000);
+ 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);
+ callback.set(0, 0, 0, 0);
+ awareness.moveObject(creature, CREATURE_LOCATION);
+ callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000);
+ }
+
+ @Test
+ public void testMoveIntoStarport() throws InterruptedException {
+ MapCallbackRealistic callback = new MapCallbackRealistic();
+ AwarenessHandler awareness = new AwarenessHandler(callback);
+ GenericCreatureObject creature = new GenericCreatureObject(1);
+ BuildingObject starport = null;
+ for (SWGObject obj : WITHIN_RANGE) {
+ if (obj instanceof BuildingObject && obj.getTemplate().contains("starport"))
+ starport = (BuildingObject) obj;
+ }
+ Assert.assertNotNull("Starport is null!", starport);
+ initAwareness(awareness, callback);
+ awareness.moveObject(creature, CREATURE_LOCATION);
+ callback.waitAndTest(WITHIN_RANGE.size(), 0, 1, 0, 1000);
+ callback.set(0, 0, 0, 0);
+ awareness.moveObject(creature, starport.getCellByNumber(1), new Location(0, 0, 0, Terrain.TATOOINE));
+ awareness.moveObject(CREATURE2, CREATURE2_LOCATION);
+ callback.waitAndTest(0, 0, 1, 0, 1000);
+ }
+
+ private void initAwareness(AwarenessHandler awareness, MapCallback callback) {
+ callback.set(0, 0, 0, 0);
+ for (SWGObject obj : EISLEY_OBJECTS) {
+ awareness.moveObject(obj, obj.getLocation());
+ }
+ callback.waitFor(0, 0, EISLEY_OBJECTS.size(), 0, 2000);
+ callback.set(0, 0, 0, 0);
+ }
+
+ private static class MapCallbackRealistic extends MapCallback {
+
+ @Override
+ public void onWithinRange(SWGObject obj, SWGObject inRange) {
+ super.onWithinRange(obj, inRange);
+ obj.addObjectAware(inRange);
+ }
+
+ @Override
+ public void onOutOfRange(SWGObject obj, SWGObject outOfRange) {
+ super.onOutOfRange(obj, outOfRange);
+ obj.removeObjectAware(outOfRange);
+ }
+
+ }
+
+}
diff --git a/test/resources/objects/awareness/TestTerrainMap.java b/test/resources/objects/awareness/TestTerrainMap.java
new file mode 100644
index 00000000..02821cc8
--- /dev/null
+++ b/test/resources/objects/awareness/TestTerrainMap.java
@@ -0,0 +1,156 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import resources.Location;
+import resources.Terrain;
+import resources.objects.SWGObject;
+import resources.objects.cell.CellObject;
+import resources.objects.creature.CreatureObject;
+import resources.server_info.Log;
+import services.objects.ClientBuildoutService;
+
+@RunWith(JUnit4.class)
+public class TestTerrainMap {
+
+ @Test
+ public void testWithoutParent() {
+ TerrainMap map = new TerrainMap(Terrain.TATOOINE);
+ MapCallback callback = new MapCallback();
+ map.setCallback(callback);
+ try {
+ map.start();
+ GenericCreatureObject objA = new GenericCreatureObject(1);
+ 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);
+ } finally {
+ map.stop();
+ }
+ }
+
+ @Test
+ public void testWithParent() {
+ TerrainMap map = new TerrainMap(Terrain.TATOOINE);
+ MapCallback callback = new MapCallback();
+ map.setCallback(callback);
+ try {
+ map.start();
+ GenericCreatureObject objA = new GenericCreatureObject(1);
+ GenericCreatureObject objB = new GenericCreatureObject(2);
+ CellObject cell = new CellObject(3);
+ map.moveWithinMap(cell, new Location(5, 0, 5, Terrain.TATOOINE)); // 0 - Within Range, 1 - Successful Move
+ map.moveWithinMap(objA, new Location(0, 0, 0, Terrain.TATOOINE)); // 1 - Within Range, 1 - Successful Move
+ 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);
+ } finally {
+ map.stop();
+ }
+ }
+
+ @Test
+ public void testMultiChunk() {
+ TerrainMap map = new TerrainMap(Terrain.TATOOINE);
+ MapCallback callback = new MapCallback();
+ map.setCallback(callback);
+ try {
+ map.start();
+ GenericCreatureObject objA = new GenericCreatureObject(1);
+ GenericCreatureObject objB = new GenericCreatureObject(2);
+ 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);
+ } finally {
+ map.stop();
+ }
+ }
+
+ @Test
+ public void testTatooine() {
+ ClientBuildoutService buildoutService = new ClientBuildoutService();
+ Collection allObjects = buildoutService.loadClientObjectsByArea(843); // mos eisley's area id
+ List tatObjects = allObjects.stream().filter((obj) -> {
+ return obj.getTerrain() == Terrain.TATOOINE && obj.getParent() == null;
+ }).collect(Collectors.toList());
+ TerrainMap map = new TerrainMap(Terrain.TATOOINE);
+ List withinRange = new Vector<>();
+ AtomicBoolean onlyWithinRange = new AtomicBoolean(true);
+ GenericCreatureObject creature = new GenericCreatureObject(1);
+ MapCallback callback = new MapCallback() {
+ public void onWithinRange(SWGObject obj, SWGObject inRange) {
+ if (obj.equals(creature) && !withinRange.remove(inRange)) {
+ Log.e(this, "Not in list: %s", inRange);
+ onlyWithinRange.set(false);
+ }
+ }
+ };
+ map.setCallback(callback);
+ try {
+ map.start();
+ creature.setName("testTatooine");
+ Location creatureLocation = new Location(3500, 5, -4800, Terrain.TATOOINE);
+ for (SWGObject obj : tatObjects) {
+ double range = Math.min(1024*Math.sqrt(2), Math.max(obj.getLoadRange(), creature.getLoadRange()));
+ if (isValidWithinRange(creature, obj, creatureLocation, range))
+ withinRange.add(obj);
+ map.moveWithinMap(obj, obj.getLocation());
+ }
+ map.moveWithinMap(creature, creatureLocation);
+ callback.waitFor(0, 0, tatObjects.size()+1, 0, 1000);
+ Assert.assertEquals(0, withinRange.size());
+ Assert.assertTrue("TEST-ONLY-WITHIN-RANGE", onlyWithinRange.get());
+ } finally {
+ map.stop();
+ }
+ }
+
+ private boolean isValidWithinRange(SWGObject obj, SWGObject inRange, Location objLocation, double range) {
+ if (obj.equals(inRange))
+ return false;
+ if (inRange instanceof CreatureObject && ((CreatureObject) inRange).isLoggedOutPlayer())
+ return false;
+ if (!inRange.getWorldLocation().isWithinFlatDistance(objLocation, Math.max(range, inRange.getLoadRange())))
+ return false;
+ return true;
+ }
+
+}
diff --git a/test/resources/objects/awareness/TestTerrainMapChunk.java b/test/resources/objects/awareness/TestTerrainMapChunk.java
new file mode 100644
index 00000000..12ec8a2f
--- /dev/null
+++ b/test/resources/objects/awareness/TestTerrainMapChunk.java
@@ -0,0 +1,286 @@
+/************************************************************************************
+ * 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.objects.awareness;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runners.JUnit4;
+import org.junit.runner.RunWith;
+
+import resources.Location;
+import resources.Terrain;
+import resources.objects.SWGObject;
+import resources.objects.cell.CellObject;
+
+@RunWith(JUnit4.class)
+public class TestTerrainMapChunk {
+
+ /*
+ * Test Cases:
+ * A: Within A and B's load range in same chunk
+ * B: Within A's load range but not B's in same chunk
+ * C: Within A and B's load range in different chunks
+ * D: Within A's load range but not B's in different chunks
+ * E: Not within A's load range but within B's in same chunk
+ * F: Not within A's load range and not within B's in same chunk
+ * G: Not within A's load range but within B's in different chunks
+ * H: Not within A's load range and not within B's in different chunks
+ */
+
+ @Test
+ public void testContains() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = new GenericCreatureObject(1);
+ GenericCreatureObject objB = new GenericCreatureObject(2);
+ objA.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ objB.setLocation(new Location(100, 0, 0, Terrain.TATOOINE)); // 100m away
+ chunk.addObject(objA);
+ chunk.addObject(objB);
+ Assert.assertTrue("CONTAINS-OBJA", chunk.containsObject(objA));
+ Assert.assertTrue("CONTAINS-OBJB", chunk.containsObject(objB));
+ }
+
+ @Test
+ public void testRemove() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = new GenericCreatureObject(1);
+ GenericCreatureObject objB = new GenericCreatureObject(2);
+ objA.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ objB.setLocation(new Location(100, 0, 0, Terrain.TATOOINE)); // 100m away
+ chunk.addObject(objA);
+ Assert.assertTrue("CONTAINS-OBJA", chunk.containsObject(objA));
+ chunk.addObject(objB);
+ Assert.assertTrue("CONTAINS-OBJB", chunk.containsObject(objB));
+ chunk.removeObject(objA);
+ Assert.assertFalse("CONTAINS-OBJA", chunk.containsObject(objA));
+ chunk.removeObject(objB);
+ Assert.assertFalse("CONTAINS-OBJB", chunk.containsObject(objB));
+ }
+
+ @Test
+ public void testCaseA() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunk, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 50);
+ testContains(chunk, objA, objB);
+ }
+
+ @Test
+ public void testCaseB() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunk, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 25);
+ testContains(chunk, objA, objB);
+ }
+
+ @Test
+ public void testCaseC() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunkA, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 50);
+ testContains(chunkB, objA, objB);
+ }
+
+ @Test
+ public void testCaseD() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunkA, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 25);
+ testContains(chunkB, objA, objB);
+ }
+
+ @Test
+ public void testCaseE() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunk, 1, 0, 0, 0, 25);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 50);
+ testContains(chunk, objA, objB);
+ }
+
+ @Test
+ public void testCaseF() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunk, 1, 0, 0, 0, 25);
+ createCreature(chunk, 2, 50, 0, 0, 25);
+ testContains(chunk, objA);
+ }
+
+ @Test
+ public void testCaseG() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunkA, 1, 0, 0, 0, 25);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 50);
+ testContains(chunkB, objA, objB);
+ }
+
+ @Test
+ public void testCaseH() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ GenericCreatureObject objA = createCreature(chunkA, 1, 0, 0, 0, 25);
+ createCreature(chunkB, 2, 50, 0, 0, 25);
+ testContains(chunkB, objA);
+ }
+
+ @Test
+ public void testCaseCellA() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunk.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 50);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 100, cell.getLoadRange(), 1E-7);
+ testContains(chunk, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellB() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunk.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 25);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 100, cell.getLoadRange(), 1E-7);
+ testContains(chunk, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellC() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunkA.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 50);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 100, cell.getLoadRange(), 1E-7);
+ testContains(chunkB, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellD() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunkA.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 100);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 25);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 100, cell.getLoadRange(), 1E-7);
+ testContains(chunkB, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellE() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 25);
+ GenericCreatureObject objB = createCreature(chunk, 2, 50, 0, 0, 50);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 25, cell.getLoadRange(), 1E-7);
+ testContains(chunk, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellF() {
+ TerrainMapChunk chunk = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 25);
+ createCreature(chunk, 2, 50, 0, 0, 25);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 25, cell.getLoadRange(), 1E-7);
+ testContains(chunk, cell);
+ }
+
+ @Test
+ public void testCaseCellG() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunkA.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 25);
+ GenericCreatureObject objB = createCreature(chunkB, 2, 50, 0, 0, 50);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 25, cell.getLoadRange(), 1E-7);
+ testContains(chunkB, cell, objB);
+ }
+
+ @Test
+ public void testCaseCellH() {
+ TerrainMapChunk chunkA = new TerrainMapChunk(0, 0, 0, 0);
+ TerrainMapChunk chunkB = new TerrainMapChunk(0, 0, 0, 0);
+ CellObject cell = new CellObject(3);
+ cell.setPrefLoadRange(0);
+ chunkA.addObject(cell);
+ cell.setLocation(new Location(0, 0, 0, Terrain.TATOOINE));
+ GenericCreatureObject objA = createCreature(null, 1, 0, 0, 0, 25);
+ createCreature(chunkB, 2, 50, 0, 0, 25);
+ cell.addObject(objA);
+ Assert.assertEquals("CELL-LOAD-RANGE", 25, cell.getLoadRange(), 1E-7);
+ testContains(chunkB, cell);
+ }
+
+ private GenericCreatureObject createCreature(TerrainMapChunk chunk, long id, double x, double y, double z, double loadRange) {
+ GenericCreatureObject creature = new GenericCreatureObject(id);
+ creature.setLocation(new Location(x, y, z, Terrain.TATOOINE));
+ creature.setPrefLoadRange(loadRange);
+ if (chunk != null)
+ chunk.addObject(creature);
+ return creature;
+ }
+
+ private void testContains(TerrainMapChunk chunk, SWGObject src, SWGObject ... expected) {
+ List aware = chunk.getWithinAwareness(src);
+ Assert.assertEquals("TEST-SIZE", expected.length, aware.size());
+ for (SWGObject e : expected) {
+ Assert.assertTrue("TEST-CONTAIN-"+e, aware.contains(e));
+ }
+ }
+
+}