Don’t crash if trigger callbacks throw exceptions.

This commit is contained in:
Paul Chote
2014-10-18 11:19:25 +13:00
parent 97e4c08dec
commit dbd4b0931b
3 changed files with 214 additions and 58 deletions

View File

@@ -173,12 +173,12 @@ namespace OpenRA.Scripting
} }
} }
bool error; public bool FatalErrorOccurred { get; private set; }
public void FatalError(string message) public void FatalError(string message)
{ {
Console.WriteLine("Fatal Lua Error: {0}", message); Console.WriteLine("Fatal Lua Error: {0}", message);
Game.AddChatLine(Color.White, "Fatal Lua Error", message); Game.AddChatLine(Color.White, "Fatal Lua Error", message);
error = true; FatalErrorOccurred = true;
} }
public void RegisterMapActor(string name, Actor a) public void RegisterMapActor(string name, Actor a)
@@ -195,7 +195,7 @@ namespace OpenRA.Scripting
public void WorldLoaded() public void WorldLoaded()
{ {
if (error) if (FatalErrorOccurred)
return; return;
using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"]) using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"])
@@ -204,7 +204,7 @@ namespace OpenRA.Scripting
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (error || disposed) if (FatalErrorOccurred || disposed)
return; return;
using (new PerfSample("tick_lua")) using (new PerfSample("tick_lua"))
@@ -215,6 +215,7 @@ namespace OpenRA.Scripting
{ {
if (disposed) if (disposed)
return; return;
disposed = true; disposed = true;
if (runtime != null) if (runtime != null)
runtime.Dispose(); runtime.Dispose();

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA.Scripting
using (f) using (f)
f.Call(); f.Call();
} }
catch (LuaException e) catch (Exception e)
{ {
context.FatalError(e.Message); context.FatalError(e.Message);
} }
@@ -80,6 +80,8 @@ namespace OpenRA.Mods.RA.Scripting
var group = actors.ToList(); var group = actors.ToList();
var copy = (LuaFunction)func.CopyReference(); var copy = (LuaFunction)func.CopyReference();
Action<Actor> OnMemberKilled = m => Action<Actor> OnMemberKilled = m =>
{
try
{ {
group.Remove(m); group.Remove(m);
if (!group.Any()) if (!group.Any())
@@ -87,6 +89,11 @@ namespace OpenRA.Mods.RA.Scripting
copy.Call(); copy.Call();
copy.Dispose(); copy.Dispose();
} }
}
catch (Exception e)
{
context.FatalError(e.Message);
}
}; };
foreach (var a in group) foreach (var a in group)
@@ -100,6 +107,8 @@ namespace OpenRA.Mods.RA.Scripting
var called = false; var called = false;
var copy = (LuaFunction)func.CopyReference(); var copy = (LuaFunction)func.CopyReference();
Action<Actor> OnMemberKilled = m => Action<Actor> OnMemberKilled = m =>
{
try
{ {
if (called) if (called)
return; return;
@@ -109,6 +118,11 @@ namespace OpenRA.Mods.RA.Scripting
copy.Dispose(); copy.Dispose();
called = true; called = true;
}
catch (Exception e)
{
context.FatalError(e.Message);
}
}; };
foreach (var a in actors) foreach (var a in actors)
@@ -179,6 +193,8 @@ namespace OpenRA.Mods.RA.Scripting
var copy = (LuaFunction)func.CopyReference(); var copy = (LuaFunction)func.CopyReference();
Action<Actor> OnMemberRemoved = m => Action<Actor> OnMemberRemoved = m =>
{
try
{ {
group.Remove(m); group.Remove(m);
if (!group.Any()) if (!group.Any())
@@ -186,6 +202,11 @@ namespace OpenRA.Mods.RA.Scripting
copy.Call().Dispose(); copy.Call().Dispose();
copy.Dispose(); copy.Dispose();
} }
}
catch (Exception e)
{
context.FatalError(e.Message);
}
}; };
foreach (var a in group) foreach (var a in group)
@@ -207,10 +228,17 @@ namespace OpenRA.Mods.RA.Scripting
var triggerId = 0; var triggerId = 0;
var onEntry = (LuaFunction)func.CopyReference(); var onEntry = (LuaFunction)func.CopyReference();
Action<Actor> invokeEntry = a => Action<Actor> invokeEntry = a =>
{
try
{ {
using (var luaActor = a.ToLuaValue(context)) using (var luaActor = a.ToLuaValue(context))
using (var id = triggerId.ToLuaValue(context)) using (var id = triggerId.ToLuaValue(context))
onEntry.Call(luaActor, id).Dispose(); onEntry.Call(luaActor, id).Dispose();
}
catch (Exception e)
{
context.FatalError(e.Message);
}
}; };
triggerId = context.World.ActorMap.AddCellTrigger(cells, invokeEntry, null); triggerId = context.World.ActorMap.AddCellTrigger(cells, invokeEntry, null);
@@ -226,10 +254,17 @@ namespace OpenRA.Mods.RA.Scripting
var triggerId = 0; var triggerId = 0;
var onExit = (LuaFunction)func.CopyReference(); var onExit = (LuaFunction)func.CopyReference();
Action<Actor> invokeExit = a => Action<Actor> invokeExit = a =>
{
try
{ {
using (var luaActor = a.ToLuaValue(context)) using (var luaActor = a.ToLuaValue(context))
using (var id = triggerId.ToLuaValue(context)) using (var id = triggerId.ToLuaValue(context))
onExit.Call(luaActor, id).Dispose(); onExit.Call(luaActor, id).Dispose();
}
catch (Exception e)
{
context.FatalError(e.Message);
}
}; };
triggerId = context.World.ActorMap.AddCellTrigger(cells, null, invokeExit); triggerId = context.World.ActorMap.AddCellTrigger(cells, null, invokeExit);

View File

@@ -45,25 +45,55 @@ namespace OpenRA.Mods.RA.Scripting
public void TickIdle(Actor self) public void TickIdle(Actor self)
{ {
foreach (var f in Triggers[Trigger.OnIdle]) foreach (var f in Triggers[Trigger.OnIdle])
{
try
{
using (var a = self.ToLuaValue(f.Second)) using (var a = self.ToLuaValue(f.Second))
f.First.Call(a).Dispose(); f.First.Call(a).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void Damaged(Actor self, AttackInfo e) public void Damaged(Actor self, AttackInfo e)
{ {
foreach (var f in Triggers[Trigger.OnDamaged]) foreach (var f in Triggers[Trigger.OnDamaged])
{
try
{
using (var a = self.ToLuaValue(f.Second)) using (var a = self.ToLuaValue(f.Second))
using (var b = e.Attacker.ToLuaValue(f.Second)) using (var b = e.Attacker.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void Killed(Actor self, AttackInfo e) public void Killed(Actor self, AttackInfo e)
{ {
// Run Lua callbacks // 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 a = self.ToLuaValue(f.Second))
using (var b = e.Attacker.ToLuaValue(f.Second)) using (var b = e.Attacker.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
}
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
// Run any internally bound callbacks // Run any internally bound callbacks
OnKilledInternal(self); OnKilledInternal(self);
@@ -72,72 +102,162 @@ namespace OpenRA.Mods.RA.Scripting
public void UnitProduced(Actor self, Actor other, CPos exit) public void UnitProduced(Actor self, Actor other, CPos exit)
{ {
foreach (var f in Triggers[Trigger.OnProduction]) foreach (var f in Triggers[Trigger.OnProduction])
{
try
{
using (var a = self.ToLuaValue(f.Second)) using (var a = self.ToLuaValue(f.Second))
using (var b = other.ToLuaValue(f.Second)) using (var b = other.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnPlayerWon(Player player) public void OnPlayerWon(Player player)
{ {
foreach (var f in Triggers[Trigger.OnPlayerWon]) foreach (var f in Triggers[Trigger.OnPlayerWon])
{
try
{
using (var a = player.ToLuaValue(f.Second)) using (var a = player.ToLuaValue(f.Second))
f.First.Call(a).Dispose(); f.First.Call(a).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnPlayerLost(Player player) public void OnPlayerLost(Player player)
{ {
foreach (var f in Triggers[Trigger.OnPlayerLost]) foreach (var f in Triggers[Trigger.OnPlayerLost])
{
try
{
using (var a = player.ToLuaValue(f.Second)) using (var a = player.ToLuaValue(f.Second))
f.First.Call(a).Dispose(); f.First.Call(a).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnObjectiveAdded(Player player, int id) public void OnObjectiveAdded(Player player, int id)
{ {
foreach (var f in Triggers[Trigger.OnObjectiveAdded]) foreach (var f in Triggers[Trigger.OnObjectiveAdded])
{
try
{
using (var a = player.ToLuaValue(f.Second)) using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second)) using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnObjectiveCompleted(Player player, int id) public void OnObjectiveCompleted(Player player, int id)
{ {
foreach (var f in Triggers[Trigger.OnObjectiveCompleted]) foreach (var f in Triggers[Trigger.OnObjectiveCompleted])
{
try
{
using (var a = player.ToLuaValue(f.Second)) using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second)) using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnObjectiveFailed(Player player, int id) public void OnObjectiveFailed(Player player, int id)
{ {
foreach (var f in Triggers[Trigger.OnObjectiveFailed]) foreach (var f in Triggers[Trigger.OnObjectiveFailed])
{
try
{
using (var a = player.ToLuaValue(f.Second)) using (var a = player.ToLuaValue(f.Second))
using (var b = id.ToLuaValue(f.Second)) using (var b = id.ToLuaValue(f.Second))
f.First.Call(a, b).Dispose(); f.First.Call(a, b).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner) public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
{ {
foreach (var f in Triggers[Trigger.OnCapture]) foreach (var f in Triggers[Trigger.OnCapture])
{
try
{
using (var a = self.ToLuaValue(f.Second)) using (var a = self.ToLuaValue(f.Second))
using (var b = captor.ToLuaValue(f.Second)) using (var b = captor.ToLuaValue(f.Second))
using (var c = oldOwner.ToLuaValue(f.Second)) using (var c = oldOwner.ToLuaValue(f.Second))
using (var d = newOwner.ToLuaValue(f.Second)) using (var d = newOwner.ToLuaValue(f.Second))
f.First.Call(a, b, c, d).Dispose(); f.First.Call(a, b, c, d).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void AddedToWorld(Actor self) public void AddedToWorld(Actor self)
{ {
foreach (var f in Triggers[Trigger.OnAddedToWorld]) foreach (var f in Triggers[Trigger.OnAddedToWorld])
{
try
{
using (var a = self.ToLuaValue(f.Second)) using (var a = self.ToLuaValue(f.Second))
f.First.Call(a).Dispose(); f.First.Call(a).Dispose();
} }
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
}
public void RemovedFromWorld(Actor self) public void RemovedFromWorld(Actor self)
{ {
// Run Lua callbacks // 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)) using (var a = self.ToLuaValue(f.Second))
f.First.Call(a).Dispose(); f.First.Call(a).Dispose();
}
catch (Exception ex)
{
f.Second.FatalError(ex.Message);
return;
}
}
// Run any internally bound callbacks // Run any internally bound callbacks
OnRemovedInternal(self); OnRemovedInternal(self);