diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index 1a3f27a6a..5026dfa79 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -262,7 +262,7 @@ public class ProjectSWG { private void loop() { setStatus((manager.getGalaxyStatus() == GalaxyStatus.UP) ? ServerStatus.OPEN : ServerStatus.LOCKED); while (!shutdownRequested && !manager.isShutdownRequested() && manager.isOperational()) { - if (Delay.sleepMicro(50)) + if (Delay.sleepMilli(50)) throw new CoreException("Main Thread Interrupted"); } } diff --git a/src/resources/objects/SWGObject.java b/src/resources/objects/SWGObject.java index cd22a8b33..b8271503c 100644 --- a/src/resources/objects/SWGObject.java +++ b/src/resources/objects/SWGObject.java @@ -29,17 +29,19 @@ package resources.objects; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import com.projectswg.common.concurrency.SynchronizedMap; -import com.projectswg.common.concurrency.SynchronizedSet; import com.projectswg.common.data.CRC; import com.projectswg.common.data.location.Location; import com.projectswg.common.data.location.Terrain; @@ -79,11 +81,11 @@ public abstract class SWGObject extends BaselineObject implements Comparable containedObjects= new SynchronizedSet<>(); + private final Set containedObjects= new CopyOnWriteArraySet<>(); private final Map slots = new SynchronizedMap<>(); private final Map attributes = new SynchronizedMap<>(new LinkedHashMap<>()); private final ObjectAware awareness = new ObjectAware(this); - private final Map dataAttributes = new SynchronizedMap<>(); + private final Map dataAttributes = new ConcurrentHashMap<>(); private final AtomicInteger updateCounter = new AtomicInteger(1); private ObjectClassification classification = ObjectClassification.GENERATED; @@ -121,12 +123,10 @@ public abstract class SWGObject extends BaselineObject implements Comparable observer.sendPacket(update)); AwarenessUtilities.callForNewObserver(oldObservers, newObservers, (observer) -> createObject(observer)); AwarenessUtilities.callForOldObserver(oldObservers, newObservers, (observer) -> destroyObject(observer)); - new ContainerTransferIntent(this, container).broadcast(); + if (parent != container) + new ContainerTransferIntent(this, container).broadcast(); return ContainerResult.SUCCESS; } @@ -275,9 +274,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable getContainedObjects() { - synchronized (containedObjects) { - return new ArrayList<>(containedObjects); - } + return Collections.unmodifiableSet(containedObjects); } public boolean hasSlot(String slotName) { - synchronized (slots) { - return slots.containsKey(slotName); - } + return slots.containsKey(slotName); } public void setSlot(String name, SWGObject value) { - synchronized (slots) { - slots.put(name, value); - } + slots.put(name, value); } public Map getSlots() { @@ -358,16 +349,12 @@ public abstract class SWGObject extends BaselineObject implements Comparable bubble) - bubble = dist; - } + for (SWGObject contained : containedObjects) { + double x = contained.getX(); + double z = contained.getZ(); + double dist = Math.sqrt(x*x+z*z) + contained.getLoadRange(); + if (dist > bubble) + bubble = dist; } return bubble; } @@ -628,10 +613,8 @@ public abstract class SWGObject extends BaselineObject implements Comparable contained; - synchronized (containedObjects) { - contained = new HashSet<>(containedObjects); - } - synchronized (slots) { - contained.addAll(slots.values()); - contained.remove(null); - } + Set contained = new HashSet<>(containedObjects); + contained.addAll(slots.values()); + contained.remove(null); stream.addList(contained, (c) -> SWGObjectFactory.save(c, stream)); } diff --git a/src/resources/objects/awareness/AwarenessHandler.java b/src/resources/objects/awareness/AwarenessHandler.java index 7b005620a..d763a74cd 100644 --- a/src/resources/objects/awareness/AwarenessHandler.java +++ b/src/resources/objects/awareness/AwarenessHandler.java @@ -73,7 +73,7 @@ public class AwarenessHandler implements AutoCloseable { public void moveObject(SWGObject obj, Location requestedLocation) { // Remove from previous awareness - if (obj.getTerrain() != requestedLocation.getTerrain()) { + if (obj.getTerrain() != requestedLocation.getTerrain() && obj.getTerrain() != null) { TerrainMap oldTerrainMap = getTerrainMap(obj.getTerrain()); if (oldTerrainMap != null) oldTerrainMap.removeWithoutUpdate(obj); @@ -109,6 +109,32 @@ public class AwarenessHandler implements AutoCloseable { obj.resetAwareness(); } + public void transferContainers(SWGObject obj, SWGObject newContainer) { + // Remove from previous awareness + TerrainMap oldMap = getTerrainMap(obj.getTerrain()); + if (oldMap != null) + oldMap.removeWithoutUpdate(obj); + // Update location + if (obj.getParent() != newContainer) { + obj.moveToContainer(newContainer); + obj.onObjectMoved(); + // Update awareness + if (newContainer == null) { + Location loc = obj.getLocation(); + if (obj.getTerrain() != Terrain.GONE) { + TerrainMap map = getTerrainMap(loc.getTerrain()); + if (map != null) { + map.moveWithinMap(obj); + } else { + Log.e("Unknown terrain: %s", loc.getTerrain()); + } + } + } else { + obj.resetAwareness(); + } + } + } + public void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) { if (obj.getTerrain() != Terrain.GONE) { TerrainMap map = getTerrainMap(obj); diff --git a/src/resources/objects/awareness/TerrainMapChunk.java b/src/resources/objects/awareness/TerrainMapChunk.java index b2e608e56..828a73134 100644 --- a/src/resources/objects/awareness/TerrainMapChunk.java +++ b/src/resources/objects/awareness/TerrainMapChunk.java @@ -31,8 +31,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; - -import com.projectswg.common.concurrency.SynchronizedSet; +import java.util.concurrent.CopyOnWriteArraySet; import resources.objects.SWGObject; import resources.objects.creature.CreatureObject; @@ -46,7 +45,7 @@ class TerrainMapChunk { private final double maxZ; public TerrainMapChunk(double minX, double minZ, double maxX, double maxZ) { - objects = new SynchronizedSet<>(); // There will be some expanding and shrinking + objects = new CopyOnWriteArraySet<>(); // There will be some expanding and shrinking this.minX = minX; this.minZ = minZ; this.maxX = maxX; @@ -79,11 +78,9 @@ class TerrainMapChunk { public void getWithinAwareness(SWGObject obj, Collection withinRange) { double loadRange = obj.getLoadRange(); - synchronized (objects) { - for (SWGObject test : objects) { - if (isValidWithinRange(obj, test, loadRange)) - withinRange.add(test); - } + for (SWGObject test : objects) { + if (isValidWithinRange(obj, test, loadRange)) + withinRange.add(test); } } diff --git a/src/resources/server_info/ConfigWatcher.java b/src/resources/server_info/ConfigWatcher.java index b4d6a0d76..ffee61e98 100644 --- a/src/resources/server_info/ConfigWatcher.java +++ b/src/resources/server_info/ConfigWatcher.java @@ -69,7 +69,6 @@ public final class ConfigWatcher { } catch (IOException e) { this.watcher = null; } - watcherThread.setInterruptOnStop(true); } public void start() { @@ -79,7 +78,7 @@ public final class ConfigWatcher { public void stop() { running.set(false); - watcherThread.stop(); + watcherThread.stop(true); watcherThread.awaitTermination(1000); } diff --git a/src/services/galaxy/travel/TravelHelper.java b/src/services/galaxy/travel/TravelHelper.java index 7feadfddd..62200d28c 100644 --- a/src/services/galaxy/travel/TravelHelper.java +++ b/src/services/galaxy/travel/TravelHelper.java @@ -84,7 +84,7 @@ class TravelHelper { } public void stop() { - travelExecutor.stop(); + travelExecutor.stop(false); } public void addTravelPoint(TravelPoint point) { diff --git a/src/services/objects/ObjectAwareness.java b/src/services/objects/ObjectAwareness.java index 8ef73726f..1be3ac367 100644 --- a/src/services/objects/ObjectAwareness.java +++ b/src/services/objects/ObjectAwareness.java @@ -188,7 +188,7 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { SWGObject obj = i.getObject(); Assert.notNull(obj); Assert.notNull(obj.getTerrain()); - moveObject(obj, i.getContainer(), obj.getLocation()); + transferContainers(obj, i.getContainer()); } private void handleZoneIn(CreatureObject creature, Player player, Location loc, SWGObject parent) { @@ -303,16 +303,18 @@ public class ObjectAwareness extends Service implements TerrainMapCallback { awarenessHandler.moveObject(obj, parent, requestedLocation); } + private void transferContainers(SWGObject obj, SWGObject newContainer) { + awarenessHandler.transferContainers(obj, newContainer); + } + private void moveObjectWithTransform(SWGObject obj, SWGObject parent, Location requestedLocation, double speed, int update) { - SWGObject oldParent = obj.getParent(); - Location oldLocation = obj.getLocation(); moveObject(obj, parent, requestedLocation); if (parent == null) dataTransformHandler.handleMove(obj, speed, update); else dataTransformHandler.handleMove(obj, parent, speed, update); if (obj instanceof CreatureObject && ((CreatureObject) obj).isLoggedInPlayer()) - new PlayerTransformedIntent((CreatureObject) obj, oldParent, parent, oldLocation, requestedLocation).broadcast(); + new PlayerTransformedIntent((CreatureObject) obj, obj.getParent(), parent, obj.getLocation(), requestedLocation).broadcast(); } private void disappearObject(SWGObject obj, boolean disappearObjects, boolean disappearCustom) { diff --git a/src/services/objects/ObjectManager.java b/src/services/objects/ObjectManager.java index 6906d5434..3058b3be7 100644 --- a/src/services/objects/ObjectManager.java +++ b/src/services/objects/ObjectManager.java @@ -27,8 +27,8 @@ ***********************************************************************************/ package services.objects; -import java.util.Hashtable; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import com.projectswg.common.control.Manager; @@ -76,7 +76,7 @@ public class ObjectManager extends Manager { staticItemService = new StaticItemService(); database = new CachedObjectDatabase<>("odb/objects.db", SWGObjectFactory::create, SWGObjectFactory::save); - objectMap = new Hashtable<>(16*1024); + objectMap = new ConcurrentHashMap<>(128*1024); started = new AtomicBoolean(false); addChildService(objectAwareness); @@ -94,17 +94,13 @@ public class ObjectManager extends Manager { @Override public boolean initialize() { - synchronized (objectMap) { - objectMap.putAll(clientBuildoutService.loadClientObjects()); - } + objectMap.putAll(clientBuildoutService.loadClientObjects()); if (!loadObjects()) return false; synchronized (database) { database.traverse((obj) -> loadObject(obj)); } - synchronized (objectMap) { - objectMap.forEach((id, obj) -> new ObjectCreatedIntent(obj).broadcast()); - } + objectMap.forEach((id, obj) -> new ObjectCreatedIntent(obj).broadcast()); return super.initialize(); } @@ -150,24 +146,20 @@ public class ObjectManager extends Manager { @Override public boolean start() { - synchronized (objectMap) { - for (SWGObject obj : objectMap.values()) { - if (obj instanceof AIObject) - ((AIObject) obj).aiStart(); - } - started.set(true); + for (SWGObject obj : objectMap.values()) { + if (obj instanceof AIObject) + ((AIObject) obj).aiStart(); } + started.set(true); return super.start(); } @Override public boolean stop() { - synchronized (objectMap) { - started.set(false); - for (SWGObject obj : objectMap.values()) { - if (obj instanceof AIObject) - ((AIObject) obj).aiStop(); - } + started.set(false); + for (SWGObject obj : objectMap.values()) { + if (obj instanceof AIObject) + ((AIObject) obj).aiStop(); } return super.stop(); } @@ -191,20 +183,16 @@ public class ObjectManager extends Manager { } if (!(obj instanceof AIObject)) return; - synchronized (objectMap) { - if (started.get()) - ((AIObject) obj).aiStart(); - } + if (started.get()) + ((AIObject) obj).aiStart(); } private void processDestroyObjectIntent(DestroyObjectIntent doi) { destroyObject(doi.getObject()); if (!(doi.getObject() instanceof AIObject)) return; - synchronized (objectMap) { - if (started.get()) - ((AIObject) doi.getObject()).aiStop(); - } + if (started.get()) + ((AIObject) doi.getObject()).aiStop(); } private void processGalacticPacketIntent(GalacticPacketIntent gpi) { @@ -220,17 +208,13 @@ public class ObjectManager extends Manager { } public SWGObject getObjectById(long objectId) { - synchronized (objectMap) { - return objectMap.get(objectId); - } + return objectMap.get(objectId); } private void putObject(SWGObject object) { - synchronized (objectMap) { - SWGObject replaced = objectMap.put(object.getObjectId(), object); - if (replaced != null && replaced != object) - Log.e("Replaced object in object map! Old: %s New: %s", replaced, object); - } + SWGObject replaced = objectMap.put(object.getObjectId(), object); + if (replaced != null && replaced != object) + Log.e("Replaced object in object map! Old: %s New: %s", replaced, object); } private SWGObject destroyObject(SWGObject object) { @@ -247,10 +231,8 @@ public class ObjectManager extends Manager { if (database.remove(object)) database.save(); } - synchronized (objectMap) { - objectMap.remove(object.getObjectId()); - } - + objectMap.remove(object.getObjectId()); + return object; } diff --git a/test/resources/objects/awareness/TestAwarenessHandler.java b/test/resources/objects/awareness/TestAwarenessHandler.java index b342130dc..4e259141e 100644 --- a/test/resources/objects/awareness/TestAwarenessHandler.java +++ b/test/resources/objects/awareness/TestAwarenessHandler.java @@ -142,7 +142,8 @@ public class TestAwarenessHandler { awaitCallbacks(awareness); callback.testAssert(0, 0, 1, 0); callback.set(0, 0, 0, 0); - awareness.moveObject(creature, starport.getCellByNumber(1), new Location(0, 0, 0, Terrain.TATOOINE)); + creature.setPosition(Terrain.TATOOINE, 0, 0, 0); + awareness.transferContainers(creature, starport.getCellByNumber(1)); awaitCallbacks(awareness); callback.testAssert(0, 0, 0, 0); }