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.
This commit is contained in:
RoosterDragon
2015-11-06 00:58:15 +00:00
parent f52bbd1b0b
commit e6e98d3aa2
2 changed files with 107 additions and 89 deletions

View File

@@ -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))

View File

@@ -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<Actor> OnKilledInternal = _ => { };
public event Action<Actor> OnCapturedInternal = _ => { };
@@ -42,19 +43,44 @@ namespace OpenRA.Mods.Common.Scripting
public event Action<Actor, Actor> OnProducedInternal = (a, b) => { };
public event Action<Actor, Actor> OnOtherProducedInternal = (a, b) => { };
public Dictionary<Trigger, List<Pair<LuaFunction, ScriptContext>>> Triggers = new Dictionary<Trigger, List<Pair<LuaFunction, ScriptContext>>>();
readonly Dictionary<Trigger, List<Triggerable>> triggers = new Dictionary<Trigger, List<Triggerable>>();
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<Pair<LuaFunction, ScriptContext>>());
triggers.Add(t, new List<Triggerable>());
}
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();
}