From 215898c7ec6b4276dc25b6e5b47b027e1300aa74 Mon Sep 17 00:00:00 2001 From: Vapre Date: Tue, 26 Jul 2022 08:00:00 +0200 Subject: [PATCH] ScriptActorInterfaces, unbind on actor destroy. --- OpenRA.Game/Scripting/ScriptActorInterface.cs | 22 ++++------ OpenRA.Game/Scripting/ScriptMemberWrapper.cs | 9 ++-- OpenRA.Game/Scripting/ScriptObjectWrapper.cs | 30 +++++++++++-- .../Scripting/ScriptPlayerInterface.cs | 13 +----- OpenRA.Game/Scripting/ScriptTypes.cs | 43 ++++++++++--------- 5 files changed, 63 insertions(+), 54 deletions(-) diff --git a/OpenRA.Game/Scripting/ScriptActorInterface.cs b/OpenRA.Game/Scripting/ScriptActorInterface.cs index f490ef6ec977..90a556f5b4ea 100644 --- a/OpenRA.Game/Scripting/ScriptActorInterface.cs +++ b/OpenRA.Game/Scripting/ScriptActorInterface.cs @@ -9,7 +9,6 @@ */ #endregion -using System; using System.Linq; namespace OpenRA.Scripting @@ -38,26 +37,21 @@ public ScriptActorInterface(ScriptContext context, Actor actor) void InitializeBindings() { - var commandClasses = Context.ActorCommands[actor.Info].AsEnumerable(); + var commandClasses = Context.ActorCommands[actor.Info]; - // Destroyed actors cannot have their traits queried + // Destroyed actors cannot have their traits queried. In rare cases the actor may have already been destroyed. if (actor.Disposed) - commandClasses = commandClasses.Where(c => c.HasAttribute()); + commandClasses = commandClasses.Where(c => c.HasAttribute()).ToArray(); - var args = new object[] { Context, actor }; - var objects = commandClasses.Select(cg => - { - var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Actor) }); - return groupCtor.Invoke(args); - }); - - Bind(objects); + Bind(CreateObjects(commandClasses, new object[] { Context, actor })); } public void OnActorDestroyed() { - // Regenerate bindings to remove access to bogus trait state - InitializeBindings(); + // Remove bindings not available to destroyed actors. + foreach (var commandClass in Context.ActorCommands[actor.Info]) + if (!commandClass.HasAttribute()) + Unbind(commandClass); } } } diff --git a/OpenRA.Game/Scripting/ScriptMemberWrapper.cs b/OpenRA.Game/Scripting/ScriptMemberWrapper.cs index 25ef10d22196..2ce5330f4693 100644 --- a/OpenRA.Game/Scripting/ScriptMemberWrapper.cs +++ b/OpenRA.Game/Scripting/ScriptMemberWrapper.cs @@ -126,20 +126,19 @@ public static IEnumerable WrappableMembers(Type t) { // Only expose defined public non-static methods that were explicitly declared by the author var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; - return t.GetMembers(flags).Where(mi => + foreach (var mi in t.GetMembers(flags)) { // Properties are always wrappable if (mi is PropertyInfo) - return true; + yield return mi; // Methods are allowed if they aren't generic, and aren't generated by the compiler var method = mi as MethodInfo; if (method != null && !method.IsGenericMethodDefinition && !method.IsSpecialName) - return true; + yield return mi; // Fields aren't allowed - return false; - }); + } } public static string[] RequiredTraitNames(Type t) diff --git a/OpenRA.Game/Scripting/ScriptObjectWrapper.cs b/OpenRA.Game/Scripting/ScriptObjectWrapper.cs index 1ef54ba1f545..7961eff8da1a 100644 --- a/OpenRA.Game/Scripting/ScriptObjectWrapper.cs +++ b/OpenRA.Game/Scripting/ScriptObjectWrapper.cs @@ -9,6 +9,7 @@ */ #endregion +using System; using System.Collections.Generic; using Eluant; using Eluant.ObjectBinding; @@ -21,16 +22,32 @@ public abstract class ScriptObjectWrapper : IScriptBindable, ILuaTableBinding protected abstract string MemberNotFoundError(string memberName); protected readonly ScriptContext Context; - Dictionary members; + readonly Dictionary members = new Dictionary(); public ScriptObjectWrapper(ScriptContext context) { Context = context; } - protected void Bind(IEnumerable clrObjects) + protected static object[] CreateObjects(Type[] types, object[] constructorArgs) { - members = new Dictionary(); + var i = 0; + var argTypes = new Type[constructorArgs.Length]; + foreach (var ca in constructorArgs) + argTypes[i++] = ca.GetType(); + + var objects = new object[types.Length]; + i = 0; + foreach (var type in types) + objects[i++] = type.GetConstructor(argTypes).Invoke(constructorArgs); + + return objects; + } + + protected void Bind(object[] clrObjects) + { + members.Clear(); + foreach (var obj in clrObjects) { var wrappable = ScriptMemberWrapper.WrappableMembers(obj.GetType()); @@ -44,6 +61,13 @@ protected void Bind(IEnumerable clrObjects) } } + protected void Unbind(Type targetType) + { + foreach (var m in members) + if (targetType == m.Value.Target.GetType()) + members.Remove(m.Key); + } + public bool ContainsKey(string key) { return members.ContainsKey(key); } public LuaValue this[LuaRuntime runtime, LuaValue keyValue] diff --git a/OpenRA.Game/Scripting/ScriptPlayerInterface.cs b/OpenRA.Game/Scripting/ScriptPlayerInterface.cs index bdf90341b5f1..672442094a00 100644 --- a/OpenRA.Game/Scripting/ScriptPlayerInterface.cs +++ b/OpenRA.Game/Scripting/ScriptPlayerInterface.cs @@ -9,9 +9,6 @@ */ #endregion -using System; -using System.Linq; - namespace OpenRA.Scripting { public class ScriptPlayerInterface : ScriptObjectWrapper @@ -25,15 +22,7 @@ public ScriptPlayerInterface(ScriptContext context, Player player) : base(context) { this.player = player; - - var args = new object[] { context, player }; - var objects = context.PlayerCommands.Select(cg => - { - var groupCtor = cg.GetConstructor(new Type[] { typeof(ScriptContext), typeof(Player) }); - return groupCtor.Invoke(args); - }); - - Bind(objects); + Bind(CreateObjects(context.PlayerCommands, new object[] { context, player })); } } } diff --git a/OpenRA.Game/Scripting/ScriptTypes.cs b/OpenRA.Game/Scripting/ScriptTypes.cs index 31bbded1c260..f1fd214d7cce 100644 --- a/OpenRA.Game/Scripting/ScriptTypes.cs +++ b/OpenRA.Game/Scripting/ScriptTypes.cs @@ -61,30 +61,33 @@ public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObj return true; } - if (value is LuaNumber && t.IsAssignableFrom(typeof(double))) + if (value is LuaNumber) { - clrObject = value.ToNumber().Value; - return true; - } + if (t.IsAssignableFrom(typeof(double))) + { + clrObject = value.ToNumber().Value; + return true; + } - // Need an explicit test for double -> int - // TODO: Lua 5.3 will introduce an integer type, so this will be able to go away - if (value is LuaNumber && t.IsAssignableFrom(typeof(int))) - { - clrObject = (int)value.ToNumber().Value; - return true; - } + // Need an explicit test for double -> int + // TODO: Lua 5.3 will introduce an integer type, so this will be able to go away + if (t.IsAssignableFrom(typeof(int))) + { + clrObject = (int)value.ToNumber().Value; + return true; + } - if (value is LuaNumber && t.IsAssignableFrom(typeof(short))) - { - clrObject = (short)value.ToNumber().Value; - return true; - } + if (t.IsAssignableFrom(typeof(short))) + { + clrObject = (short)value.ToNumber().Value; + return true; + } - if (value is LuaNumber && t.IsAssignableFrom(typeof(byte))) - { - clrObject = (byte)value.ToNumber().Value; - return true; + if (t.IsAssignableFrom(typeof(byte))) + { + clrObject = (byte)value.ToNumber().Value; + return true; + } } if (value is LuaString && t.IsAssignableFrom(typeof(string)))