diff --git a/OpenRA.Game/Scripting/ScriptContext.cs b/OpenRA.Game/Scripting/ScriptContext.cs
index 9e274d5423..e98c5b410b 100644
--- a/OpenRA.Game/Scripting/ScriptContext.cs
+++ b/OpenRA.Game/Scripting/ScriptContext.cs
@@ -64,7 +64,19 @@ namespace OpenRA.Scripting
}
}
- // For global-level bindings
+ ///
+ /// Provides global bindings in Lua code.
+ ///
+ ///
+ /// Instance methods and properties declared in derived classes will be made available in Lua. Use
+ /// on your derived class to specify the name exposed in Lua. It is recommended
+ /// to apply against each method or property to provide a description of what it does.
+ ///
+ /// Any parameters to your method that are s will be disposed automatically when your method
+ /// completes. If you need to return any of these values, or need them to live longer than your method, you must
+ /// use to get your own copy of the value. Any copied values you return will
+ /// be disposed automatically, but you assume responsibility for disposing any other copies.
+ ///
public abstract class ScriptGlobal : ScriptObjectWrapper
{
protected override string DuplicateKeyError(string memberName) { return "Table '{0}' defines multiple members '{1}'".F(Name, memberName); }
@@ -78,11 +90,27 @@ namespace OpenRA.Scripting
var type = this.GetType();
var names = type.GetCustomAttributes(true);
if (names.Length != 1)
- throw new InvalidOperationException("[LuaGlobal] attribute not found for global table '{0}'".F(type));
+ throw new InvalidOperationException("[ScriptGlobal] attribute not found for global table '{0}'".F(type));
Name = names.First().Name;
Bind(new[] { this });
}
+
+ protected IEnumerable FilteredObjects(IEnumerable objects, LuaFunction filter)
+ {
+ if (filter != null)
+ {
+ objects = objects.Where(a =>
+ {
+ using (var luaObject = a.ToLuaValue(Context))
+ using (var filterResult = filter.Call(luaObject))
+ using (var result = filterResult.First())
+ return result.ToBoolean();
+ });
+ }
+
+ return objects;
+ }
}
public sealed class ScriptGlobalAttribute : Attribute
diff --git a/OpenRA.Game/Scripting/ScriptMemberWrapper.cs b/OpenRA.Game/Scripting/ScriptMemberWrapper.cs
index 9b470762a1..3eaa4d7f2d 100644
--- a/OpenRA.Game/Scripting/ScriptMemberWrapper.cs
+++ b/OpenRA.Game/Scripting/ScriptMemberWrapper.cs
@@ -44,31 +44,54 @@ namespace OpenRA.Scripting
LuaValue Invoke(LuaVararg args)
{
- if (!IsMethod)
- throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
-
- var mi = (MethodInfo)Member;
- var pi = mi.GetParameters();
-
- var clrArgs = new object[pi.Length];
- var argCount = args.Count;
- for (var i = 0; i < pi.Length; i++)
+ object[] clrArgs = null;
+ try
{
- if (i >= argCount)
- {
- if (!pi[i].IsOptional)
- throw new LuaException("Argument '{0}' of '{1}' is not optional.".F(pi[i].LuaDocString(), Member.LuaDocString()));
+ if (!IsMethod)
+ throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
- clrArgs[i] = pi[i].DefaultValue;
- continue;
+ var mi = (MethodInfo)Member;
+ var pi = mi.GetParameters();
+
+ clrArgs = new object[pi.Length];
+
+ var argCount = args.Count;
+ for (var i = 0; i < pi.Length; i++)
+ {
+ if (i >= argCount)
+ {
+ if (!pi[i].IsOptional)
+ throw new LuaException("Argument '{0}' of '{1}' is not optional.".F(pi[i].LuaDocString(), Member.LuaDocString()));
+
+ clrArgs[i] = pi[i].DefaultValue;
+ continue;
+ }
+
+ if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
+ throw new LuaException("Unable to convert parameter {0} to {1}".F(i, pi[i].ParameterType.Name));
}
- if (!args[i].TryGetClrValue(pi[i].ParameterType, out clrArgs[i]))
- throw new LuaException("Unable to convert parameter {0} to {1}".F(i, pi[i].ParameterType.Name));
+ return mi.Invoke(Target, clrArgs).ToLuaValue(context);
}
+ finally
+ {
+ // Clean up all the Lua arguments that were given to us.
+ foreach (var arg in args)
+ arg.Dispose();
+ args.Dispose();
- var ret = mi.Invoke(Target, clrArgs);
- return ret.ToLuaValue(context);
+ // If we created any arrays of LuaValues to pass around, we need to dispose those too.
+ if (clrArgs != null)
+ {
+ foreach (var arg in clrArgs)
+ {
+ if (!(arg is LuaValue[]))
+ continue;
+ foreach (var value in (LuaValue[])arg)
+ value.Dispose();
+ }
+ }
+ }
}
public LuaValue Get(LuaRuntime runtime)
@@ -77,10 +100,7 @@ namespace OpenRA.Scripting
return runtime.CreateFunctionFromDelegate((Func)Invoke);
if (IsGetProperty)
- {
- var pi = Member as PropertyInfo;
- return pi.GetValue(Target, null).ToLuaValue(context);
- }
+ return ((PropertyInfo)Member).GetValue(Target, null).ToLuaValue(context);
throw new LuaException("The property '{0}' is write-only".F(Member.Name));
}
@@ -89,7 +109,7 @@ namespace OpenRA.Scripting
{
if (IsSetProperty)
{
- var pi = Member as PropertyInfo;
+ var pi = (PropertyInfo)Member;
object clrValue;
if (!value.TryGetClrValue(pi.PropertyType, out clrValue))
throw new LuaException("Unable to convert '{0}' to Clr type '{1}'".F(value.WrappedClrType().Name, pi.PropertyType));
diff --git a/OpenRA.Game/Scripting/ScriptTypes.cs b/OpenRA.Game/Scripting/ScriptTypes.cs
index 14a792b7f6..d9011d2dac 100644
--- a/OpenRA.Game/Scripting/ScriptTypes.cs
+++ b/OpenRA.Game/Scripting/ScriptTypes.cs
@@ -107,13 +107,22 @@ namespace OpenRA.Scripting
foreach (var kv in table)
{
- object element;
- if (innerType == typeof(LuaValue))
- element = kv.Value;
- else if (!kv.Value.TryGetClrValue(innerType, out element))
- throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType));
+ using (kv.Key)
+ {
+ object element;
+ if (innerType == typeof(LuaValue))
+ element = kv.Value;
+ else
+ {
+ var elementHasClrValue = kv.Value.TryGetClrValue(innerType, out element);
+ if (!elementHasClrValue || !(element is LuaValue))
+ kv.Value.Dispose();
+ if (!elementHasClrValue)
+ throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType));
+ }
- array.SetValue(element, i++);
+ array.SetValue(element, i++);
+ }
}
clrObject = array;
@@ -159,11 +168,13 @@ namespace OpenRA.Scripting
if (obj is Array)
{
- var array = obj as Array;
+ var array = (Array)obj;
var i = 1;
var table = context.CreateTable();
+
foreach (var x in array)
- table.Add(i++, x.ToLuaValue(context));
+ using (LuaValue key = i++, value = x.ToLuaValue(context))
+ table.Add(key, value);
return table;
}
diff --git a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs
index 56da1f3bd1..4042f847e3 100644
--- a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs
@@ -29,26 +29,30 @@ namespace OpenRA.Mods.Common.Scripting
// Convert table entries into ActorInits
foreach (var kv in initTable)
{
- // Find the requested type
- var typeName = kv.Key.ToString();
- var initType = Game.ModData.ObjectCreator.FindType(typeName + "Init");
- if (initType == null)
- throw new LuaException("Unknown initializer type '{0}'".F(typeName));
+ using (kv.Key)
+ using (kv.Value)
+ {
+ // Find the requested type
+ var typeName = kv.Key.ToString();
+ var initType = Game.ModData.ObjectCreator.FindType(typeName + "Init");
+ if (initType == null)
+ throw new LuaException("Unknown initializer type '{0}'".F(typeName));
- // Cast it up to an IActorInit
- var genericType = initType.GetInterfaces()
- .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IActorInit<>));
- var innerType = genericType.GetGenericArguments().First();
- var valueType = innerType.IsEnum ? typeof(int) : innerType;
+ // Cast it up to an IActorInit
+ var genericType = initType.GetInterfaces()
+ .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IActorInit<>));
+ var innerType = genericType.GetGenericArguments().First();
+ var valueType = innerType.IsEnum ? typeof(int) : innerType;
- // Try and coerce the table value to the required type
- object value;
- if (!kv.Value.TryGetClrValue(valueType, out value))
- throw new LuaException("Invalid data type for '{0}' (expected '{1}')".F(typeName, valueType.Name));
+ // Try and coerce the table value to the required type
+ object value;
+ if (!kv.Value.TryGetClrValue(valueType, out value))
+ throw new LuaException("Invalid data type for '{0}' (expected '{1}')".F(typeName, valueType.Name));
- // Construct the ActorInit. Phew!
- var test = initType.GetConstructor(new[] { innerType }).Invoke(new[] { value });
- initDict.Add(test);
+ // Construct the ActorInit. Phew!
+ var test = initType.GetConstructor(new[] { innerType }).Invoke(new[] { value });
+ initDict.Add(test);
+ }
}
// The actor must be added to the world at the end of the tick
diff --git a/OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs
index aa0f794968..01fda97235 100644
--- a/OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/MapGlobal.cs
@@ -8,7 +8,6 @@
*/
#endregion
-using System;
using System.Linq;
using Eluant;
using OpenRA.Mods.Common.Traits;
@@ -34,34 +33,14 @@ namespace OpenRA.Mods.Common.Scripting
public Actor[] ActorsInCircle(WPos location, WDist radius, LuaFunction filter = null)
{
var actors = Context.World.FindActorsInCircle(location, radius);
-
- if (filter != null)
- {
- actors = actors.Where(a =>
- {
- using (var f = filter.Call(a.ToLuaValue(Context)))
- return f.First().ToBoolean();
- });
- }
-
- return actors.ToArray();
+ return FilteredObjects(actors, filter).ToArray();
}
[Desc("Returns a table of all actors within the requested rectangle, filtered using the specified function.")]
public Actor[] ActorsInBox(WPos topLeft, WPos bottomRight, LuaFunction filter = null)
{
var actors = Context.World.ActorMap.ActorsInBox(topLeft, bottomRight);
-
- if (filter != null)
- {
- actors = actors.Where(a =>
- {
- using (var f = filter.Call(a.ToLuaValue(Context)))
- return f.First().ToBoolean();
- });
- }
-
- return actors.ToArray();
+ return FilteredObjects(actors, filter).ToArray();
}
[Desc("Returns the location of the top-left corner of the map (assuming zero terrain height).")]
diff --git a/OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs
index f680774eea..7c4ef02d8b 100644
--- a/OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/MediaGlobal.cs
@@ -53,7 +53,6 @@ namespace OpenRA.Mods.Common.Scripting
Game.Sound.Play(file);
}
- Action onComplete;
[Desc("Play track defined in music.yaml or map.yaml, or keep track empty for playing a random song.")]
public void PlayMusic(string track = null, LuaFunction func = null)
{
@@ -65,8 +64,8 @@ namespace OpenRA.Mods.Common.Scripting
if (func != null)
{
- var f = func.CopyReference() as LuaFunction;
- onComplete = () =>
+ var f = (LuaFunction)func.CopyReference();
+ Action onComplete = () =>
{
try
{
@@ -113,13 +112,13 @@ namespace OpenRA.Mods.Common.Scripting
playlist.Stop();
}
- Action onCompleteFullscreen;
[Desc("Play a VQA video fullscreen. File name has to include the file extension.")]
public void PlayMovieFullscreen(string movie, LuaFunction func = null)
{
+ Action onCompleteFullscreen;
if (func != null)
{
- var f = func.CopyReference() as LuaFunction;
+ var f = (LuaFunction)func.CopyReference();
onCompleteFullscreen = () =>
{
try
@@ -139,15 +138,14 @@ namespace OpenRA.Mods.Common.Scripting
Media.PlayFMVFullscreen(world, movie, onCompleteFullscreen);
}
- Action onLoadComplete;
- Action onCompleteRadar;
[Desc("Play a VQA video in the radar window. File name has to include the file extension. " +
"Returns true on success, if the movie wasn't found the function returns false and the callback is executed.")]
public bool PlayMovieInRadar(string movie, LuaFunction playComplete = null)
{
+ Action onCompleteRadar;
if (playComplete != null)
{
- var f = playComplete.CopyReference() as LuaFunction;
+ var f = (LuaFunction)playComplete.CopyReference();
onCompleteRadar = () =>
{
try
@@ -178,7 +176,7 @@ namespace OpenRA.Mods.Common.Scripting
AsyncLoader l = new AsyncLoader(Media.LoadVqa);
IAsyncResult ar = l.BeginInvoke(s, null, null);
- onLoadComplete = () =>
+ Action onLoadComplete = () =>
{
Media.StopFMVInRadar();
world.AddFrameEndTask(_ => Media.PlayFMVInRadar(world, l.EndInvoke(ar), onCompleteRadar));
diff --git a/OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs
index d570de1982..d871db5c53 100644
--- a/OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/PlayerGlobal.cs
@@ -28,12 +28,7 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Returns a table of players filtered by the specified function.")]
public Player[] GetPlayers(LuaFunction filter)
{
- return Context.World.Players
- .Where(p =>
- {
- using (var f = filter.Call(p.ToLuaValue(Context)))
- return f.First().ToBoolean();
- }).ToArray();
+ return FilteredObjects(Context.World.Players, filter).ToArray();
}
}
}
diff --git a/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
index ec62bf1dd4..26210da7ca 100644
--- a/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/ReinforcementsGlobal.cs
@@ -71,7 +71,7 @@ namespace OpenRA.Mods.Common.Scripting
var actors = new List();
for (var i = 0; i < actorTypes.Length; i++)
{
- var af = actionFunc != null ? actionFunc.CopyReference() as LuaFunction : null;
+ var af = actionFunc != null ? (LuaFunction)actionFunc.CopyReference() : null;
var actor = CreateActor(owner, actorTypes[i], false, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null);
actors.Add(actor);
@@ -86,8 +86,9 @@ namespace OpenRA.Mods.Common.Scripting
{
actor.QueueActivity(new CallFunc(() =>
{
- af.Call(actor.ToLuaValue(Context));
- af.Dispose();
+ using (af)
+ using (var a = actor.ToLuaValue(Context))
+ af.Call(a);
}));
}
};
@@ -128,11 +129,12 @@ namespace OpenRA.Mods.Common.Scripting
if (actionFunc != null)
{
- var af = actionFunc.CopyReference() as LuaFunction;
+ var af = (LuaFunction)actionFunc.CopyReference();
transport.QueueActivity(new CallFunc(() =>
{
- af.Call(transport.ToLuaValue(Context), passengers.ToArray().ToLuaValue(Context));
- af.Dispose();
+ using (af)
+ using (LuaValue t = transport.ToLuaValue(Context), p = passengers.ToArray().ToLuaValue(Context))
+ af.Call(t, p);
}));
}
else
@@ -164,11 +166,12 @@ namespace OpenRA.Mods.Common.Scripting
if (exitFunc != null)
{
- var ef = exitFunc.CopyReference() as LuaFunction;
+ var ef = (LuaFunction)exitFunc.CopyReference();
transport.QueueActivity(new CallFunc(() =>
{
- ef.Call(transport.ToLuaValue(Context));
- ef.Dispose();
+ using (ef)
+ using (var t = transport.ToLuaValue(Context))
+ ef.Call(t);
}));
}
else if (exitPath != null)
@@ -180,8 +183,16 @@ namespace OpenRA.Mods.Common.Scripting
}
var ret = Context.CreateTable();
- ret.Add(1, transport.ToLuaValue(Context));
- ret.Add(2, passengers.ToArray().ToLuaValue(Context));
+ using (LuaValue
+ tKey = 1,
+ tValue = transport.ToLuaValue(Context),
+ pKey = 2,
+ pValue = passengers.ToArray().ToLuaValue(Context))
+ {
+ ret.Add(tKey, tValue);
+ ret.Add(pKey, pValue);
+ }
+
return ret;
}
}
diff --git a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs
index ac2d4b55db..6b49bfd0d7 100644
--- a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs
@@ -34,8 +34,7 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Call a function after a specified delay. The callback function will be called as func().")]
public void AfterDelay(int delay, LuaFunction func)
{
- var f = func.CopyReference() as LuaFunction;
-
+ var f = (LuaFunction)func.CopyReference();
Action doCall = () =>
{
try
@@ -78,17 +77,15 @@ namespace OpenRA.Mods.Common.Scripting
public void OnAllKilled(Actor[] actors, LuaFunction func)
{
var group = actors.ToList();
- var copy = (LuaFunction)func.CopyReference();
+ var f = (LuaFunction)func.CopyReference();
Action onMemberKilled = m =>
{
try
{
group.Remove(m);
if (!group.Any())
- {
- copy.Call();
- copy.Dispose();
- }
+ using (f)
+ f.Call();
}
catch (Exception e)
{
@@ -105,7 +102,7 @@ namespace OpenRA.Mods.Common.Scripting
public void OnAnyKilled(Actor[] actors, LuaFunction func)
{
var called = false;
- var copy = (LuaFunction)func.CopyReference();
+ var f = (LuaFunction)func.CopyReference();
Action onMemberKilled = m =>
{
try
@@ -113,10 +110,10 @@ namespace OpenRA.Mods.Common.Scripting
if (called)
return;
+ using (f)
using (var killed = m.ToLuaValue(Context))
- copy.Call(killed).Dispose();
+ f.Call(killed).Dispose();
- copy.Dispose();
called = true;
}
catch (Exception e)
@@ -191,7 +188,7 @@ namespace OpenRA.Mods.Common.Scripting
{
var group = actors.ToList();
- var copy = (LuaFunction)func.CopyReference();
+ var f = (LuaFunction)func.CopyReference();
Action onMemberRemoved = m =>
{
try
@@ -200,10 +197,8 @@ namespace OpenRA.Mods.Common.Scripting
return;
if (!group.Any())
- {
- copy.Call().Dispose();
- copy.Dispose();
- }
+ using (f)
+ f.Call().Dispose();
}
catch (Exception e)
{
@@ -228,7 +223,7 @@ namespace OpenRA.Mods.Common.Scripting
{
var called = false;
- var copy = (LuaFunction)func.CopyReference();
+ var f = (LuaFunction)func.CopyReference();
Action onKilledOrCaptured = m =>
{
try
@@ -236,8 +231,9 @@ namespace OpenRA.Mods.Common.Scripting
if (called)
return;
- copy.Call().Dispose();
- copy.Dispose();
+ using (f)
+ f.Call().Dispose();
+
called = true;
}
catch (Exception e)
@@ -256,7 +252,7 @@ namespace OpenRA.Mods.Common.Scripting
{
var group = actors.ToList();
- var copy = (LuaFunction)func.CopyReference();
+ var f = (LuaFunction)func.CopyReference();
Action onMemberKilledOrCaptured = m =>
{
try
@@ -265,10 +261,8 @@ namespace OpenRA.Mods.Common.Scripting
return;
if (!group.Any())
- {
- copy.Call().Dispose();
- copy.Dispose();
- }
+ using (f)
+ f.Call().Dispose();
}
catch (Exception e)
{
@@ -288,8 +282,9 @@ namespace OpenRA.Mods.Common.Scripting
"The callback function will be called as func(Actor a, int id).")]
public int OnEnteredFootprint(CPos[] cells, LuaFunction func)
{
- var triggerId = 0;
+ // We can't easily dispose onEntry, so we'll have to rely on finalization for it.
var onEntry = (LuaFunction)func.CopyReference();
+ var triggerId = 0;
Action invokeEntry = a =>
{
try
@@ -314,8 +309,9 @@ namespace OpenRA.Mods.Common.Scripting
"The callback function will be called as func(Actor a, int id).")]
public int OnExitedFootprint(CPos[] cells, LuaFunction func)
{
- var triggerId = 0;
+ // We can't easily dispose onExit, so we'll have to rely on finalization for it.
var onExit = (LuaFunction)func.CopyReference();
+ var triggerId = 0;
Action invokeExit = a =>
{
try
@@ -346,8 +342,9 @@ namespace OpenRA.Mods.Common.Scripting
"The callback function will be called as func(Actor a, int id).")]
public int OnEnteredProximityTrigger(WPos pos, WDist range, LuaFunction func)
{
- var triggerId = 0;
+ // We can't easily dispose onEntry, so we'll have to rely on finalization for it.
var onEntry = (LuaFunction)func.CopyReference();
+ var triggerId = 0;
Action invokeEntry = a =>
{
try
@@ -372,8 +369,9 @@ namespace OpenRA.Mods.Common.Scripting
"The callback function will be called as func(Actor a, int id).")]
public int OnExitedProximityTrigger(WPos pos, WDist range, LuaFunction func)
{
- var triggerId = 0;
+ // We can't easily dispose onExit, so we'll have to rely on finalization for it.
var onExit = (LuaFunction)func.CopyReference();
+ var triggerId = 0;
Action invokeExit = a =>
{
try
diff --git a/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs
index a7c4c55016..dcdfd71d61 100644
--- a/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs
+++ b/OpenRA.Mods.Common/Scripting/Global/UtilsGlobal.cs
@@ -33,14 +33,10 @@ namespace OpenRA.Mods.Common.Scripting
public bool Any(LuaValue[] collection, LuaFunction func)
{
foreach (var c in collection)
- {
using (var ret = func.Call(c))
- {
- var result = ret.FirstOrDefault();
+ using (var result = ret.FirstOrDefault())
if (result != null && result.ToBoolean())
return true;
- }
- }
return false;
}
@@ -49,14 +45,10 @@ namespace OpenRA.Mods.Common.Scripting
public bool All(LuaValue[] collection, LuaFunction func)
{
foreach (var c in collection)
- {
using (var ret = func.Call(c))
- {
- var result = ret.FirstOrDefault();
+ using (var result = ret.FirstOrDefault())
if (result == null || !result.ToBoolean())
return false;
- }
- }
return true;
}
@@ -64,7 +56,7 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Returns the first n values from a collection.")]
public LuaValue[] Take(int n, LuaValue[] source)
{
- return source.Take(n).ToArray();
+ return source.Take(n).Select(v => v.CopyReference()).ToArray();
}
[Desc("Skips over the first numElements members of a table and return the rest.")]
@@ -73,7 +65,8 @@ namespace OpenRA.Mods.Common.Scripting
var t = Context.CreateTable();
for (var i = numElements; i <= table.Count; i++)
- t.Add(t.Count + 1, table[i]);
+ using (LuaValue key = t.Count + 1, value = table[i])
+ t.Add(key, value);
return t;
}
@@ -81,7 +74,7 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Returns a random value from a collection.")]
public LuaValue Random(LuaValue[] collection)
{
- return collection.Random(Context.World.SharedRandom);
+ return collection.Random(Context.World.SharedRandom).CopyReference();
}
[Desc("Expands the given footprint one step along the coordinate axes, and (if requested) diagonals.")]
diff --git a/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
index 634e0528ef..68c1a856c9 100644
--- a/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
+++ b/OpenRA.Mods.RA/Scripting/Properties/ChronosphereProperties.cs
@@ -28,8 +28,13 @@ namespace OpenRA.Mods.RA.Scripting
{
Actor actor;
CPos cell;
- if (!kv.Key.TryGetClrValue(out actor) || !kv.Value.TryGetClrValue(out cell))
- throw new LuaException("Chronoshift requires a table of Actor,CPos pairs. Received {0},{1}".F(kv.Key.WrappedClrType().Name, kv.Value.WrappedClrType().Name));
+ using (kv.Key)
+ using (kv.Value)
+ {
+ if (!kv.Key.TryGetClrValue(out actor) || !kv.Value.TryGetClrValue(out cell))
+ throw new LuaException("Chronoshift requires a table of Actor,CPos pairs. Received {0},{1}".F(
+ kv.Key.WrappedClrType().Name, kv.Value.WrappedClrType().Name));
+ }
var cs = actor.TraitOrDefault();
if (cs != null && cs.CanChronoshiftTo(actor, cell))