/// <inheritdoc /> protected override void OnFinishedServicingEvents() { //After ALL the queued interest changes have been serviced //we can actually handle the changes and send them and such //We need to iterate the entire interest dictionary //That means we need to check the new incoming and outgoing entities //We do this because we need to build update packets for the players //so that they can become aware of them AND we can start pushing //events to them foreach (var kvp in ManagedInterestCollections) { //We want to skip any collection that doesn't have any pending changes. //No reason to send a message about it nor dequeue anything if (!kvp.Value.HasPendingChanges()) { continue; } //Even though this modifies the collections //the write lock of this type is reserved only //for adding or removing new entities. Not for //actually changing the data itself. using (LockingPolicy.ReaderLock(null, CancellationToken.None)) { //We should only build packets for players. if (kvp.Key.EntityType == EntityType.Player) { VisibilityMessageSender.Send(new EntityVisibilityChangeContext(kvp.Key, kvp.Value)); } //No matter player or NPC we should dequeue the joining/leaving //entites so that the state of the known entites reflects the diff packets sent InterestDequeueSetCommand dequeueCommand = new InterestDequeueSetCommand(kvp.Value, kvp.Value); //TODO: Should we execute right away? Or after all packets are sent? dequeueCommand.Execute(); } } #if DEBUG || DEBUG_BUILD foreach (var kvp in ManagedInterestCollections) { if (!kvp.Value.EnteringDequeueable.isEmpty) { throw new InvalidOperationException($"Failed to fully queue: {nameof(kvp.Value.EnteringDequeueable)}"); } if (!kvp.Value.LeavingDequeueable.isEmpty) { throw new InvalidOperationException($"Failed to fully queue: {nameof(kvp.Value.LeavingDequeueable)}"); } } #endif }
public void Test_DequeueCommand_Handles_Entering_Entities() { //arrange //InterestDequeueSetCommand InterestCollection interestCollection = new InterestCollection(); interestCollection.Register(new NetworkEntityGuid(1), new NetworkEntityGuid(1)); interestCollection.Register(new NetworkEntityGuid(2), new NetworkEntityGuid(2)); //act Assert.AreEqual(0, interestCollection.ContainedEntities.Count); InterestDequeueSetCommand dequeue = new InterestDequeueSetCommand(interestCollection, interestCollection); dequeue.Execute(); //assert Assert.AreEqual(2, interestCollection.ContainedEntities.Count); Assert.True(interestCollection.EnteringDequeueable.isEmpty); }
public void Test_DequeueCommand_Handles_Leaving_Entities() { //arrange //InterestDequeueSetCommand InterestCollection interestCollection = new InterestCollection(); interestCollection.Add(new NetworkEntityGuid(1)); interestCollection.Add(new NetworkEntityGuid(2)); interestCollection.Unregister(new NetworkEntityGuid(1)); interestCollection.Unregister(new NetworkEntityGuid(2)); //act Assert.AreEqual(2, interestCollection.ContainedEntities.Count, "Has no contained entites."); InterestDequeueSetCommand dequeue = new InterestDequeueSetCommand(interestCollection, interestCollection); dequeue.Execute(); //assert Assert.AreEqual(0, interestCollection.ContainedEntities.Count, "Expected the contained entities to be removed, but some remained."); Assert.True(interestCollection.LeavingDequeueable.isEmpty); }