diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/awareness/ObjectAware.java b/src/main/java/com/projectswg/holocore/resources/support/objects/awareness/ObjectAware.java index 1c6af7143..e7d0b4b1b 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/awareness/ObjectAware.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/awareness/ObjectAware.java @@ -27,7 +27,6 @@ package com.projectswg.holocore.resources.support.objects.awareness; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; -import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,12 +39,10 @@ public class ObjectAware { private static final Collection EMPTY_SET = Collections.emptyList(); - private final SWGObject object; private final EnumMap> awareness; private final AtomicReference chunk; - public ObjectAware(@NotNull SWGObject obj) { - this.object = obj; + public ObjectAware() { this.awareness = new EnumMap<>(AwarenessType.class); this.chunk = new AtomicReference<>(null); for (AwarenessType type : AwarenessType.getValues()) { @@ -55,9 +52,6 @@ public class ObjectAware { public synchronized void setAware(@NotNull AwarenessType type, @NotNull Collection objects) { awareness.put(type, objects); - - if (object instanceof CreatureObject) - ((CreatureObject) object).flushObjectsAware(); } @NotNull diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/SWGObject.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/SWGObject.java index c9a721370..84d94dc46 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/SWGObject.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/SWGObject.java @@ -82,7 +82,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable slots = new ConcurrentHashMap<>(); private final Map attributes = Collections.synchronizedMap(new LinkedHashMap<>()); private final Map slotsAvailable = new ConcurrentHashMap<>(); - private final ObjectAware awareness = new ObjectAware(this); + private final ObjectAware awareness = new ObjectAware(); private final Set observers = ConcurrentHashMap.newKeySet(); private final Map dataAttributes = new EnumMap<>(ObjectDataAttribute.class); private final Map serverAttributes= new EnumMap<>(ServerAttribute.class); diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java index 427d25ca3..1aae3ce8e 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObject.java @@ -99,11 +99,18 @@ public class CreatureObject extends TangibleObject { getAwareness().setAware(AwarenessType.SELF, List.of(this)); } - public void flushObjectsAware() { + public void flushObjectCreates() { Player owner = getOwnerShallow(); if (getTerrain() == Terrain.GONE || owner == null || owner.getPlayerState() == PlayerState.DISCONNECTED) return; - awareness.flushAware(); + awareness.flushCreates(owner); + } + + public void flushObjectDestroys() { + Player owner = getOwnerShallow(); + if (getTerrain() == Terrain.GONE || owner == null || owner.getPlayerState() == PlayerState.DISCONNECTED) + return; + awareness.flushDestroys(owner); } public void resetObjectsAware() { diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectAwareness.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectAwareness.java index c7da5ce13..673c7e090 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectAwareness.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/creature/CreatureObjectAwareness.java @@ -31,7 +31,6 @@ import com.projectswg.common.network.packets.swg.zone.*; import com.projectswg.common.network.packets.swg.zone.baselines.Baseline.BaselineType; import com.projectswg.common.network.packets.swg.zone.building.UpdateCellPermissionMessage; import com.projectswg.holocore.resources.support.global.player.Player; -import com.projectswg.holocore.resources.support.global.player.PlayerState; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; import com.projectswg.holocore.resources.support.objects.swg.building.BuildingObject; import com.projectswg.holocore.resources.support.objects.swg.cell.CellObject; @@ -53,23 +52,10 @@ public class CreatureObjectAwareness { this.objectComparator = Comparator.comparingInt(CreatureObjectAwareness::getObjectDepth).thenComparingDouble(this::getDistance); } - public synchronized void flushAware() { - Player target = creature.getOwner(); - // If we're disconnected, no need to worry about awareness - if (target == null || target.getPlayerState() == PlayerState.DISCONNECTED) { - for (SWGObject obj : aware) { - obj.removeObserver(creature); - } - aware.clear(); - awareIds.clear(); - return; - } - + public synchronized void flushCreates(@NotNull Player target) { Set newAware = creature.getAware(); List create = new ArrayList<>(); - List destroy = new ArrayList<>(); List added = new ArrayList<>(); - List removed = new ArrayList<>(); // Create Deltas for (SWGObject createCandidate : newAware) { @@ -79,31 +65,6 @@ public class CreatureObjectAwareness { } getCreateList(create, added); - for (SWGObject destroyCandidate : aware) { - if (newAware.contains(destroyCandidate)) - continue; - removed.add(destroyCandidate); - } - getDestroyList(destroy, removed); - - // Remove destroyed objects so that nobody tries to send a packet to us after we send the destroy - for (Iterator it = aware.iterator(); it.hasNext(); ) { - SWGObject currentAware = it.next(); - for (SWGObject remove : destroy) { // Since the "create" is filtered, aware could also have been filtered - if (isParent(currentAware, remove)) { - it.remove(); - awareIds.remove(currentAware.getObjectId()); - remove.removeObserver(creature); - break; - } - } - } - - // Destroy the objects on the client - for (SWGObject obj : destroy) { - destroyObject(obj, target); - } - // Create the objects on the client LinkedList createStack = new LinkedList<>(); for (SWGObject obj : create) { @@ -128,6 +89,42 @@ public class CreatureObjectAwareness { assert aware.contains(creature) : "not aware of creature"; } + public synchronized void flushDestroys(@NotNull Player target) { + Set newAware = creature.getAware(); + List destroy = new ArrayList<>(); + List removed = new ArrayList<>(); + + // Create Deltas + for (SWGObject destroyCandidate : aware) { + if (newAware.contains(destroyCandidate)) + continue; + removed.add(destroyCandidate); + } + getDestroyList(destroy, removed); + + // Remove destroyed objects so that nobody tries to send a packet to us after we send the destroy + for (Iterator it = aware.iterator(); it.hasNext(); ) { + SWGObject currentAware = it.next(); + for (SWGObject remove : destroy) { // Since the "create" is filtered, aware could also have been filtered + if (isParent(currentAware, remove)) { + it.remove(); + awareIds.remove(currentAware.getObjectId()); + remove.removeObserver(creature); + break; + } + } + } + + // Destroy the objects on the client + for (SWGObject obj : destroy) { + destroyObject(obj, target); + } + + // Hope we didn't screw anything up + assert aware.contains(creature.getSlottedObject("ghost")) : "not aware of ghost " + creature; + assert aware.contains(creature) : "not aware of creature"; + } + public synchronized void resetObjectsAware() { for (SWGObject obj : aware) { obj.removeObserver(creature); @@ -144,7 +141,7 @@ public class CreatureObjectAwareness { return aware.contains(obj); } - void getCreateList(List list, List added) { + private void getCreateList(List list, List added) { added.sort(objectComparator); for (SWGObject obj : added) { SWGObject parent = obj.getParent(); @@ -163,7 +160,7 @@ public class CreatureObjectAwareness { } } - void getDestroyList(List list, List removed) { + private void getDestroyList(List list, List removed) { removed.sort(objectComparator); for (SWGObject obj : removed) { // Don't delete our own parent nor child objects if we're deleting their parent (optimization) @@ -185,7 +182,7 @@ public class CreatureObjectAwareness { } private boolean isBundledObject(SWGObject obj, SWGObject parent) { - return parent != null && (obj.getSlotArrangement() == -1 || obj.getBaselineType() == BaselineType.PLAY); + return parent != null && (obj.getSlotArrangement() == -1 || obj.getBaselineType() == BaselineType.PLAY || parent == creature); } private void createObject(@NotNull SWGObject obj, @NotNull Player target) { diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/group/GroupObject.java b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/group/GroupObject.java index a70701d07..538537f64 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/swg/group/GroupObject.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/swg/group/GroupObject.java @@ -49,7 +49,6 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.stream.Collectors; public class GroupObject extends SWGObject { @@ -249,7 +248,6 @@ public class GroupObject extends SWGObject { creature.setGroupId(0); groupMembers.remove(member); creature.setAware(AwarenessType.GROUP, List.of()); - setAware(AwarenessType.GROUP, groupMembers.stream().map(GroupMember::getCreature).collect(Collectors.toList())); } groupMembers.sendDeltaMessage(this); } diff --git a/src/main/java/com/projectswg/holocore/services/support/objects/awareness/AwarenessService.java b/src/main/java/com/projectswg/holocore/services/support/objects/awareness/AwarenessService.java index 941d16d0c..cf7272cc5 100644 --- a/src/main/java/com/projectswg/holocore/services/support/objects/awareness/AwarenessService.java +++ b/src/main/java/com/projectswg/holocore/services/support/objects/awareness/AwarenessService.java @@ -95,10 +95,12 @@ public class AwarenessService extends Service { public void update() { awareness.updateChunks(); + onlinePlayers.forEach(CreatureObject::flushObjectCreates); while (!positionUpdates.isEmpty()) { Runnable r = positionUpdates.poll(); r.run(); } + onlinePlayers.forEach(CreatureObject::flushObjectDestroys); onlinePlayers.forEach(CreatureObject::sendAndFlushAllDeltas); } @@ -115,8 +117,8 @@ public class AwarenessService extends Service { break; case PE_LOGGED_OUT: if (creature != null) { - awareness.updateObject(creature); onlinePlayers.remove(creature); + creature.resetObjectsAware(); } break; case PE_FIRST_ZONE: diff --git a/src/test/java/com/projectswg/holocore/resources/support/objects/awareness/TestObjectAwareness.java b/src/test/java/com/projectswg/holocore/resources/support/objects/awareness/TestObjectAwareness.java index ef2534a4c..6038ebb69 100644 --- a/src/test/java/com/projectswg/holocore/resources/support/objects/awareness/TestObjectAwareness.java +++ b/src/test/java/com/projectswg/holocore/resources/support/objects/awareness/TestObjectAwareness.java @@ -213,6 +213,8 @@ public class TestObjectAwareness extends TestRunnerNoIntents { private void update(CreatureObject obj) { awareness.updateObject(obj); awareness.updateChunks(); + obj.flushObjectCreates(); + obj.flushObjectDestroys(); } private void moveNoAssert(TestLocation location) {