From af6dde84fde1d3d9ec2246ec47becfcefb91207b Mon Sep 17 00:00:00 2001 From: Syntriax Date: Mon, 26 Jan 2026 23:23:49 +0300 Subject: [PATCH] fix: universe entrance issues where entering behaviours adding more behaviours causing duplicate/missing calls --- .../Systems/UniverseEntranceManager.cs | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/Engine.Core/Systems/UniverseEntranceManager.cs b/Engine.Core/Systems/UniverseEntranceManager.cs index e2919e1..db40ce7 100644 --- a/Engine.Core/Systems/UniverseEntranceManager.cs +++ b/Engine.Core/Systems/UniverseEntranceManager.cs @@ -11,14 +11,29 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent private readonly ActiveBehaviourCollectorOrdered enterUniverses = new(GetPriority(), SortByAscendingPriority()); private readonly ActiveBehaviourCollectorOrdered exitUniverses = new(GetPriority(), SortByAscendingPriority()); + private bool isInitialCollectionDone = false; + private readonly FastListOrdered toCallEnterUniverses = new(GetPriority(), SortByAscendingPriority()); + protected override void OnEnteredUniverse(IUniverse universe) { - // FIXME: This causes an issue when the UniverseEntranceManager is already attached to a UniverseObject then registered into a Universe, - // the enter/exit universe collectors call OnUniverseObjectRegistered internally on Assign, but since the Universe calls the OnUniverseObjectRegistered - // event it tries to call OnUniverseObjectRegistered again on the same object, causing a duplicate entry error. - Debug.Assert.AssertTrue(BehaviourController.Count == 1, $"{nameof(UniverseEntranceManager)} must be in it's own {nameof(IUniverseObject)} with no other {nameof(IBehaviour)}s attached at the moment. Failing to do so might cause instantiation or serialization issues."); - enterUniverses.Assign(universe); exitUniverses.Assign(universe); + + // FIXME: the isInitialCollectionDone is for the sole reason of some behaviours + // adding more behaviours during entrance calls and the internal workings of + // behaviour collector not being able to tell which behaviour was already called + // (because it just runs a for loop with the behaviour count, and priority ordering doesn't help as well) + // so it sometimes double processes or misses behaviours. A more elegant way of + // handling this would be nice but for now it works good enough. + // + // SIDE NOTE: This same issue has the potential to occur on exitUniverses as well, but I've yet to run + // into an instance of it actually happening so... I'm not gonna touch it until the edge case happens. + isInitialCollectionDone = false; + enterUniverses.Assign(universe); + isInitialCollectionDone = true; + + for (int i = toCallEnterUniverses.Count - 1; i >= 0; i--) + toCallEnterUniverses[i].EnterUniverse(universe); + toCallEnterUniverses.Clear(); } protected override void OnExitedUniverse(IUniverse universe) @@ -26,8 +41,17 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent enterUniverses.Unassign(); exitUniverses.Unassign(); } + private void OnEnterUniverseCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) - => args.BehaviourCollected.EnterUniverse(Universe); + { + if (!isInitialCollectionDone) + { + toCallEnterUniverses.Add(args.BehaviourCollected); + return; + } + + args.BehaviourCollected.EnterUniverse(Universe); + } private void OnExitUniverseRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) => args.BehaviourRemoved.ExitUniverse(Universe);