From e6e98d3aa2c1c310beaef92133d4a1f3fba193e4 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Fri, 6 Nov 2015 00:58:15 +0000 Subject: [PATCH] Cache self.ToLuaValue in ScriptTriggers. When making an Lua function call, any LuaCustomClrObject must be introspected via reflection in order to determine what to expose in Lua code. In OpenRA, we use these for any types that implement IScriptBindable, such as Actor. Previously, we would need to pay the cost of this reflection for every individual Lua call an Actor used in its ScriptTriggers trait where it passed `self` as a parameter. This would be repeated every time. For performance, we now cache self.ToLuaValue in the trait and use that for all calls so we only pay the reflection cost once on trait construction. This removes a significant overhead in the Lua bridging code. --- .../Properties/ProductionProperties.cs | 4 +- .../Scripting/ScriptTriggers.cs | 192 ++++++++++-------- 2 files changed, 107 insertions(+), 89 deletions(-) diff --git a/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs index 52c5ab3670..5f3fa10b81 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs @@ -101,7 +101,7 @@ namespace OpenRA.Mods.Common.Scripting "only contain alive actors.")] public bool Build(string[] actorTypes, LuaFunction actionFunc = null) { - if (triggers.Triggers[Trigger.OnProduction].Any()) + if (triggers.HasAnyCallbacksFor(Trigger.OnProduction)) return false; var queue = queues.Where(q => actorTypes.All(t => GetBuildableInfo(t).Queue.Contains(q.Info.Type))) @@ -150,7 +150,7 @@ namespace OpenRA.Mods.Common.Scripting "Note: it does not check whether this particular type of actor is being produced.")] public bool IsProducing(string actorType) { - if (triggers.Triggers[Trigger.OnProduction].Any()) + if (triggers.HasAnyCallbacksFor(Trigger.OnProduction)) return true; return queues.Where(q => GetBuildableInfo(actorType).Queue.Contains(q.Info.Type)) diff --git a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs index a7c8481354..6c0f945aa8 100644 --- a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs +++ b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs @@ -28,13 +28,14 @@ namespace OpenRA.Mods.Common.Scripting [Desc("Allows map scripts to attach triggers to this actor via the Triggers global.")] public class ScriptTriggersInfo : ITraitInfo { - public object Create(ActorInitializer init) { return new ScriptTriggers(init.World); } + public object Create(ActorInitializer init) { return new ScriptTriggers(init.World, init.Self); } } public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyOtherProduction, - INotifyObjectivesUpdated, INotifyCapture, INotifyInfiltrated, INotifyAddedToWorld, INotifyRemovedFromWorld, IDisposable, INotifyDiscovered + INotifyObjectivesUpdated, INotifyCapture, INotifyInfiltrated, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyDiscovered, INotifyActorDisposing { readonly World world; + readonly Actor self; public event Action OnKilledInternal = _ => { }; public event Action OnCapturedInternal = _ => { }; @@ -42,19 +43,44 @@ namespace OpenRA.Mods.Common.Scripting public event Action OnProducedInternal = (a, b) => { }; public event Action OnOtherProducedInternal = (a, b) => { }; - public Dictionary>> Triggers = new Dictionary>>(); + readonly Dictionary> triggers = new Dictionary>(); - public ScriptTriggers(World world) + struct Triggerable : IDisposable + { + public readonly LuaFunction Function; + public readonly ScriptContext Context; + public readonly LuaValue Self; + public Triggerable(LuaFunction function, ScriptContext context, Actor self) + { + Function = (LuaFunction)function.CopyReference(); + Context = context; + Self = self.ToLuaValue(Context); + } + + public void Dispose() + { + Function.Dispose(); + Self.Dispose(); + } + } + + public ScriptTriggers(World world, Actor self) { this.world = world; + this.self = self; foreach (Trigger t in Enum.GetValues(typeof(Trigger))) - Triggers.Add(t, new List>()); + triggers.Add(t, new List()); } public void RegisterCallback(Trigger trigger, LuaFunction func, ScriptContext context) { - Triggers[trigger].Add(Pair.New((LuaFunction)func.CopyReference(), context)); + triggers[trigger].Add(new Triggerable(func, context, self)); + } + + public bool HasAnyCallbacksFor(Trigger trigger) + { + return triggers[trigger].Count > 0; } public void TickIdle(Actor self) @@ -62,16 +88,15 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnIdle]) + foreach (var f in triggers[Trigger.OnIdle]) { try { - using (var a = self.ToLuaValue(f.Second)) - f.First.Call(a).Dispose(); + f.Function.Call(f.Self).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -82,17 +107,16 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnDamaged]) + foreach (var f in triggers[Trigger.OnDamaged]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = e.Attacker.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var b = e.Attacker.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -104,17 +128,16 @@ namespace OpenRA.Mods.Common.Scripting return; // Run Lua callbacks - foreach (var f in Triggers[Trigger.OnKilled]) + foreach (var f in triggers[Trigger.OnKilled]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = e.Attacker.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var b = e.Attacker.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -129,17 +152,16 @@ namespace OpenRA.Mods.Common.Scripting return; // Run Lua callbacks - foreach (var f in Triggers[Trigger.OnProduction]) + foreach (var f in triggers[Trigger.OnProduction]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = other.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var b = other.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -153,16 +175,16 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnPlayerWon]) + foreach (var f in triggers[Trigger.OnPlayerWon]) { try { - using (var a = player.ToLuaValue(f.Second)) - f.First.Call(a).Dispose(); + using (var a = player.ToLuaValue(f.Context)) + f.Function.Call(a).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -173,16 +195,16 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnPlayerLost]) + foreach (var f in triggers[Trigger.OnPlayerLost]) { try { - using (var a = player.ToLuaValue(f.Second)) - f.First.Call(a).Dispose(); + using (var a = player.ToLuaValue(f.Context)) + f.Function.Call(a).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -193,17 +215,17 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnObjectiveAdded]) + foreach (var f in triggers[Trigger.OnObjectiveAdded]) { try { - using (var a = player.ToLuaValue(f.Second)) - using (var b = id.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var a = player.ToLuaValue(f.Context)) + using (var b = id.ToLuaValue(f.Context)) + f.Function.Call(a, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -214,17 +236,17 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnObjectiveCompleted]) + foreach (var f in triggers[Trigger.OnObjectiveCompleted]) { try { - using (var a = player.ToLuaValue(f.Second)) - using (var b = id.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var a = player.ToLuaValue(f.Context)) + using (var b = id.ToLuaValue(f.Context)) + f.Function.Call(a, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -235,17 +257,17 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnObjectiveFailed]) + foreach (var f in triggers[Trigger.OnObjectiveFailed]) { try { - using (var a = player.ToLuaValue(f.Second)) - using (var b = id.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var a = player.ToLuaValue(f.Context)) + using (var b = id.ToLuaValue(f.Context)) + f.Function.Call(a, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -256,19 +278,18 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnCapture]) + foreach (var f in triggers[Trigger.OnCapture]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = captor.ToLuaValue(f.Second)) - using (var c = oldOwner.ToLuaValue(f.Second)) - using (var d = newOwner.ToLuaValue(f.Second)) - f.First.Call(a, b, c, d).Dispose(); + using (var b = captor.ToLuaValue(f.Context)) + using (var c = oldOwner.ToLuaValue(f.Context)) + using (var d = newOwner.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b, c, d).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -282,17 +303,16 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnInfiltrated]) + foreach (var f in triggers[Trigger.OnInfiltrated]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = infiltrator.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var b = infiltrator.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -303,16 +323,15 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnAddedToWorld]) + foreach (var f in triggers[Trigger.OnAddedToWorld]) { try { - using (var a = self.ToLuaValue(f.Second)) - f.First.Call(a).Dispose(); + f.Function.Call(f.Self).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -324,16 +343,15 @@ namespace OpenRA.Mods.Common.Scripting return; // Run Lua callbacks - foreach (var f in Triggers[Trigger.OnRemovedFromWorld]) + foreach (var f in triggers[Trigger.OnRemovedFromWorld]) { try { - using (var a = self.ToLuaValue(f.Second)) - f.First.Call(a).Dispose(); + f.Function.Call(f.Self).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -348,17 +366,17 @@ namespace OpenRA.Mods.Common.Scripting return; // Run Lua callbacks - foreach (var f in Triggers[Trigger.OnOtherProduction]) + foreach (var f in triggers[Trigger.OnOtherProduction]) { try { - using (var a = producee.ToLuaValue(f.Second)) - using (var b = produced.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var a = producee.ToLuaValue(f.Context)) + using (var b = produced.ToLuaValue(f.Context)) + f.Function.Call(a, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -372,33 +390,31 @@ namespace OpenRA.Mods.Common.Scripting if (world.Disposing) return; - foreach (var f in Triggers[Trigger.OnDiscovered]) + foreach (var f in triggers[Trigger.OnDiscovered]) { try { - using (var a = self.ToLuaValue(f.Second)) - using (var b = discoverer.ToLuaValue(f.Second)) - f.First.Call(a, b).Dispose(); + using (var b = discoverer.ToLuaValue(f.Context)) + f.Function.Call(f.Self, b).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } - foreach (var f in Triggers[Trigger.OnPlayerDiscovered]) + foreach (var f in triggers[Trigger.OnPlayerDiscovered]) { try { - using (var a = self.Owner.ToLuaValue(f.Second)) - using (var b = discoverer.ToLuaValue(f.Second)) - using (var c = self.ToLuaValue(f.Second)) - f.First.Call(a, b, c).Dispose(); + using (var a = self.Owner.ToLuaValue(f.Context)) + using (var b = discoverer.ToLuaValue(f.Context)) + f.Function.Call(a, b, f.Self).Dispose(); } catch (Exception ex) { - f.Second.FatalError(ex.Message); + f.Context.FatalError(ex.Message); return; } } @@ -408,8 +424,10 @@ namespace OpenRA.Mods.Common.Scripting { world.AddFrameEndTask(w => { - Triggers[trigger].Select(p => p.First).Do(f => f.Dispose()); - Triggers[trigger].Clear(); + var triggerables = triggers[trigger]; + foreach (var f in triggerables) + f.Dispose(); + triggerables.Clear(); }); } @@ -419,7 +437,7 @@ namespace OpenRA.Mods.Common.Scripting Clear(t); } - public void Dispose() + public void Disposing(Actor self) { ClearAll(); }