From e793ffd0c9cd3ef1c04234a68c212741d7800482 Mon Sep 17 00:00:00 2001 From: karl2883 Date: Sun, 23 Aug 2026 20:10:05 +0200 Subject: [PATCH 1/3] add EntityContainer.ContainsEntity method to avoid allocations in hot paths --- Code/Components/EntityContainer.cs | 5 +++++ Code/Entities/Containers/HoldableContainer.cs | 6 +++--- Code/Entities/Modifiers/CollidableModifier.cs | 2 +- Code/MiscHooks.cs | 6 +++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Code/Components/EntityContainer.cs b/Code/Components/EntityContainer.cs index b0f1367..cc8acf3 100644 --- a/Code/Components/EntityContainer.cs +++ b/Code/Components/EntityContainer.cs @@ -199,6 +199,11 @@ protected virtual void RemoveContained(IEntityHandler handler) } handler.OnDetach(this); } + + public virtual bool ContainsEntity(Entity entity) + { + return entity != null && HandlersFor.ContainsKey(entity); + } protected List> ParseList(string list) { diff --git a/Code/Entities/Containers/HoldableContainer.cs b/Code/Entities/Containers/HoldableContainer.cs index b43b0b8..4a09167 100644 --- a/Code/Entities/Containers/HoldableContainer.cs +++ b/Code/Entities/Containers/HoldableContainer.cs @@ -327,7 +327,7 @@ public override void Update() foreach (SeekerBarrier barrier in Scene.Tracker.GetEntities()) { barrier.Collidable = true; - var collided = CollideCheck(barrier) && !_Container.GetEntities().Contains(barrier); + var collided = CollideCheck(barrier) && !_Container.ContainsEntity(barrier); barrier.Collidable = false; if (collided) @@ -525,12 +525,12 @@ public override void Render() public override bool IsRiding(Solid solid) { - return HasGravity && !_Container.GetEntities().Contains(solid) && base.IsRiding(solid); + return HasGravity && !_Container.ContainsEntity(solid) && base.IsRiding(solid); } public override bool IsRiding(JumpThru jumpThru) { - return HasGravity && !_Container.GetEntities().Contains(jumpThru) && base.IsRiding(jumpThru); + return HasGravity && !_Container.ContainsEntity(jumpThru) && base.IsRiding(jumpThru); } private IEnumerator DestroyRoutine() diff --git a/Code/Entities/Modifiers/CollidableModifier.cs b/Code/Entities/Modifiers/CollidableModifier.cs index 090ca80..44db73f 100644 --- a/Code/Entities/Modifiers/CollidableModifier.cs +++ b/Code/Entities/Modifiers/CollidableModifier.cs @@ -36,7 +36,7 @@ public CollidableModifier(EntityData data, Vector2 offset) : base(data.Position Add(Container = new EntityContainer(data) { - IsValid = e => !(e is Solidifier solidifier && Container.GetEntities().Contains(solidifier.Entity)) && e is not Hazard, + IsValid = e => !(e is Solidifier solidifier && Container.ContainsEntity(solidifier.Entity)) && e is not Hazard, DefaultIgnored = e => e.Get() != null, OnAttach = h => OnAttach(h.Entity), OnDetach = h => OnDetach(h.Entity) diff --git a/Code/MiscHooks.cs b/Code/MiscHooks.cs index b701a45..930da89 100644 --- a/Code/MiscHooks.cs +++ b/Code/MiscHooks.cs @@ -309,8 +309,8 @@ private static void Collide_Check_Entity_Entity(ILContext il) } // If this statement is *true*, Collide Check returns *false* private static bool CheckContainers(Entity a, Entity b) => - (a is IContainer iA && iA.Container is { } aContainer && aContainer != null && !aContainer.CollideWithContained && aContainer.GetEntities().Contains(b)) || - (b is IContainer iB && iB.Container is { } bContainer && bContainer != null && !bContainer.CollideWithContained && bContainer.GetEntities().Contains(a)) || + (a is IContainer iA && iA.Container is { } aContainer && !aContainer.CollideWithContained && aContainer.ContainsEntity(b)) || + (b is IContainer iB && iB.Container is { } bContainer && !bContainer.CollideWithContained && bContainer.ContainsEntity(a)) || (a is CollidableModifier.Solidifier aSolid && aSolid.Entity == b) || (b is CollidableModifier.Solidifier bSolid && bSolid.Entity == a); @@ -726,7 +726,7 @@ private static bool Player_IsRiding_Solid(On.Celeste.Player.orig_IsRiding_Solid { if (self.StateMachine.State == Player.StClimb) { - var data = DynamicData.For(self); + var data = DynamicData.For(self); if (data.Get("EeveeHelper_cPopDelay") is float cPopDelay && cPopDelay > 0f) { return self.CollideCheck(solid, self.Position + new Vector2((float)self.Facing, -1f)); From 744c695f7785958685be5a0dd033990cab89b13f Mon Sep 17 00:00:00 2001 From: karl2883 Date: Sun, 23 Aug 2026 20:11:37 +0200 Subject: [PATCH 2/3] lazier Collide.Check IL hook for performance --- Code/MiscHooks.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Code/MiscHooks.cs b/Code/MiscHooks.cs index 930da89..d950575 100644 --- a/Code/MiscHooks.cs +++ b/Code/MiscHooks.cs @@ -297,14 +297,22 @@ private static void HookLevelEntityLoading(ILCursor cursor, string type) private static void Collide_Check_Entity_Entity(ILContext il) { - ILCursor cursor = new ILCursor(il); - ILLabel label = null; - if(cursor.TryGotoNext(MoveType.After, i => i.MatchBeq(out label))) + ILCursor cursor = new(il); + + if (cursor.TryGotoNext(MoveType.After, i => i.MatchCallvirt("Collide"))) { + ILLabel returnResult = cursor.DefineLabel(); + cursor.Emit(OpCodes.Dup); + cursor.Emit(OpCodes.Brfalse, returnResult); + + // return/calculate !CheckContainers(a, b) only if the collision was true in the first place + cursor.Emit(OpCodes.Pop); cursor.Emit(OpCodes.Ldarg_0); cursor.Emit(OpCodes.Ldarg_1); cursor.Emit(OpCodes.Call, typeof(MiscHooks).GetMethod(nameof(CheckContainers), BindingFlags.NonPublic | BindingFlags.Static)); - cursor.Emit(OpCodes.Brtrue, label); + cursor.Emit(OpCodes.Ldc_I4_0); + cursor.Emit(OpCodes.Ceq); + cursor.MarkLabel(returnResult); } } // If this statement is *true*, Collide Check returns *false* From 5e9abb251112f301188b27adb31c74fd88fab737 Mon Sep 17 00:00:00 2001 From: karl2883 Date: Sun, 23 Aug 2026 20:25:03 +0200 Subject: [PATCH 3/3] safer ContainsEntity --- Code/Components/EntityContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Code/Components/EntityContainer.cs b/Code/Components/EntityContainer.cs index cc8acf3..5436c52 100644 --- a/Code/Components/EntityContainer.cs +++ b/Code/Components/EntityContainer.cs @@ -199,10 +199,15 @@ protected virtual void RemoveContained(IEntityHandler handler) } handler.OnDetach(this); } - + public virtual bool ContainsEntity(Entity entity) { - return entity != null && HandlersFor.ContainsKey(entity); + foreach (IEntityHandler handler in Contained) + { + if (handler.Entity == entity) + return true; + } + return false; } protected List> ParseList(string list)