Add a new native-lua implementation.
This commit is contained in:
45
OpenRA.Game/Scripting/ScriptActorInterface.cs
Normal file
45
OpenRA.Game/Scripting/ScriptActorInterface.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptActorInterface : ScriptObjectWrapper
|
||||
{
|
||||
readonly Actor actor;
|
||||
|
||||
protected override string DuplicateKeyError(string memberName) { return "Actor '{0}' defines the command '{1}' on multiple traits".F(actor.Info.Name, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Actor '{0}' does not define a property '{1}'".F(actor.Info.Name, memberName); }
|
||||
|
||||
public ScriptActorInterface(ScriptContext context, Actor actor)
|
||||
: base(context)
|
||||
{
|
||||
this.actor = actor;
|
||||
|
||||
var args = new [] { actor };
|
||||
var objects = context.ActorCommands[actor.Info].Select(cg =>
|
||||
{
|
||||
var groupCtor = cg.GetConstructor(new Type[] { typeof(Actor) });
|
||||
return groupCtor.Invoke(args);
|
||||
});
|
||||
|
||||
Bind(objects);
|
||||
}
|
||||
}
|
||||
}
|
||||
233
OpenRA.Game/Scripting/ScriptContext.cs
Normal file
233
OpenRA.Game/Scripting/ScriptContext.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
// Tag interfaces specifying the type of bindings to create
|
||||
public interface IScriptBindable { }
|
||||
|
||||
// For objects that need the context to create their bindings
|
||||
public interface IScriptNotifyBind
|
||||
{
|
||||
void OnScriptBind(ScriptContext context);
|
||||
}
|
||||
|
||||
// For traitinfos that provide actor / player commands
|
||||
public class ScriptPropertyGroupAttribute : Attribute
|
||||
{
|
||||
public readonly string Category;
|
||||
public ScriptPropertyGroupAttribute(string category) { Category = category; }
|
||||
}
|
||||
|
||||
public class ScriptActorPropertyActivityAttribute : Attribute { }
|
||||
|
||||
public abstract class ScriptActorProperties
|
||||
{
|
||||
protected readonly Actor self;
|
||||
public ScriptActorProperties(Actor self) { this.self = self; }
|
||||
}
|
||||
|
||||
public abstract class ScriptPlayerProperties
|
||||
{
|
||||
protected readonly Player player;
|
||||
public ScriptPlayerProperties(Player player) { this.player = player; }
|
||||
}
|
||||
|
||||
// For global-level bindings
|
||||
public abstract class ScriptGlobal : ScriptObjectWrapper
|
||||
{
|
||||
protected override string DuplicateKeyError(string memberName) { return "Table '{0}' defines multiple members '{1}'".F(Name, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Table '{0}' does not define a property '{1}'".F(Name, memberName); }
|
||||
|
||||
public readonly string Name;
|
||||
public ScriptGlobal(ScriptContext context)
|
||||
: base(context)
|
||||
{
|
||||
// The 'this.' resolves the actual (subclass) type
|
||||
var type = this.GetType();
|
||||
var names = type.GetCustomAttributes<ScriptGlobalAttribute>(true);
|
||||
if (names.Count() != 1)
|
||||
throw new InvalidOperationException("[LuaGlobal] attribute not found for global table '{0}'".F(type));
|
||||
|
||||
Name = names.First().Name;
|
||||
Bind(new [] { this });
|
||||
}
|
||||
}
|
||||
|
||||
public class ScriptGlobalAttribute : Attribute
|
||||
{
|
||||
public readonly string Name;
|
||||
public ScriptGlobalAttribute(string name) { Name = name; }
|
||||
}
|
||||
|
||||
public class ScriptContext
|
||||
{
|
||||
public World World { get; private set; }
|
||||
public WorldRenderer WorldRenderer { get; private set; }
|
||||
|
||||
readonly MemoryConstrainedLuaRuntime runtime;
|
||||
readonly LuaFunction tick;
|
||||
|
||||
// Restrict user scripts (excluding system libraries) to 50 MB of memory use
|
||||
const int MaxUserScriptMemory = 50 * 1024 * 1024;
|
||||
|
||||
// Restrict the number of instructions that will be run per map function call
|
||||
const int MaxUserScriptInstructions = 1000000;
|
||||
|
||||
readonly Type[] knownActorCommands;
|
||||
public readonly Cache<ActorInfo, Type[]> ActorCommands;
|
||||
public readonly Type[] PlayerCommands;
|
||||
|
||||
public ScriptContext(World world, WorldRenderer worldRenderer,
|
||||
IEnumerable<string> scripts)
|
||||
{
|
||||
runtime = new MemoryConstrainedLuaRuntime();
|
||||
|
||||
World = world;
|
||||
WorldRenderer = worldRenderer;
|
||||
knownActorCommands = Game.modData.ObjectCreator
|
||||
.GetTypesImplementing<ScriptActorProperties>()
|
||||
.ToArray();
|
||||
|
||||
ActorCommands = new Cache<ActorInfo, Type[]>(FilterActorCommands);
|
||||
PlayerCommands = Game.modData.ObjectCreator
|
||||
.GetTypesImplementing<ScriptPlayerProperties>()
|
||||
.ToArray();
|
||||
|
||||
runtime.DoBuffer(GlobalFileSystem.Open(Path.Combine("lua", "scriptwrapper.lua")).ReadAllText(), "scriptwrapper.lua").Dispose();
|
||||
tick = (LuaFunction)runtime.Globals["Tick"];
|
||||
|
||||
// Register globals
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)FatalError))
|
||||
runtime.Globals["FatalError"] = fn;
|
||||
|
||||
runtime.Globals["MaxUserScriptInstructions"] = MaxUserScriptInstructions;
|
||||
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
{
|
||||
using (var fn = runtime.CreateFunctionFromDelegate((Action<string>)Console.WriteLine))
|
||||
registerGlobal.Call("print", fn).Dispose();
|
||||
|
||||
// Register global tables
|
||||
var bindings = Game.modData.ObjectCreator.GetTypesImplementing<ScriptGlobal>();
|
||||
foreach (var b in bindings)
|
||||
{
|
||||
var ctor = b.GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(c =>
|
||||
{
|
||||
var p = c.GetParameters();
|
||||
return p.Length == 1 && p.First().ParameterType == typeof(ScriptContext);
|
||||
});
|
||||
|
||||
if (ctor == null)
|
||||
throw new InvalidOperationException("{0} must define a constructor that takes a ScriptContext context parameter".F(b.Name));
|
||||
|
||||
var binding = (ScriptGlobal)ctor.Invoke(new [] { this });
|
||||
using (var obj = binding.ToLuaValue(this))
|
||||
registerGlobal.Call(binding.Name, obj).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// System functions do not count towards the memory limit
|
||||
runtime.MaxMemoryUse = runtime.MemoryUse + MaxUserScriptMemory;
|
||||
|
||||
using (var loadScript = (LuaFunction)runtime.Globals["ExecuteSandboxedScript"])
|
||||
{
|
||||
foreach (var s in scripts)
|
||||
loadScript.Call(s, GlobalFileSystem.Open(s).ReadAllText()).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
bool error;
|
||||
public void FatalError(string message)
|
||||
{
|
||||
Console.WriteLine("Fatal Lua Error: {0}", message);
|
||||
error = true;
|
||||
}
|
||||
|
||||
public void RegisterMapActor(string name, Actor a)
|
||||
{
|
||||
using (var registerGlobal = (LuaFunction)runtime.Globals["RegisterSandboxedGlobal"])
|
||||
{
|
||||
if (runtime.Globals.ContainsKey(name))
|
||||
throw new LuaException("The global name '{0}' is reserved, and may not be used by a map actor".F(name));
|
||||
|
||||
using (var obj = a.ToLuaValue(this))
|
||||
registerGlobal.Call(name, obj).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void WorldLoaded()
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
using (var worldLoaded = (LuaFunction)runtime.Globals["WorldLoaded"])
|
||||
worldLoaded.Call().Dispose();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (error)
|
||||
return;
|
||||
|
||||
using (new PerfSample("tick_lua"))
|
||||
tick.Call().Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (runtime == null)
|
||||
return;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
runtime.Dispose();
|
||||
}
|
||||
|
||||
~ScriptContext()
|
||||
{
|
||||
if (runtime != null)
|
||||
Game.RunAfterTick(Dispose);
|
||||
}
|
||||
|
||||
static Type[] ExtractRequiredTypes(Type t)
|
||||
{
|
||||
// Returns the inner types of all the Requires<T> interfaces on this type
|
||||
var outer = t.GetInterfaces()
|
||||
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>));
|
||||
|
||||
return outer.SelectMany(i => i.GetGenericArguments()).ToArray();
|
||||
}
|
||||
|
||||
static readonly object[] NoArguments = new object[0];
|
||||
Type[] FilterActorCommands(ActorInfo ai)
|
||||
{
|
||||
var method = typeof(TypeDictionary).GetMethod("Contains");
|
||||
return knownActorCommands.Where(c => ExtractRequiredTypes(c)
|
||||
.All(t => (bool)method.MakeGenericMethod(t).Invoke(ai.Traits, NoArguments)))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public LuaTable CreateTable() { return runtime.CreateTable(); }
|
||||
}
|
||||
}
|
||||
74
OpenRA.Game/Scripting/ScriptMemberExts.cs
Normal file
74
OpenRA.Game/Scripting/ScriptMemberExts.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public static class ScriptMemberExts
|
||||
{
|
||||
static readonly Dictionary<string, string> LuaTypeNameReplacements = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Void", "void" },
|
||||
{ "Int32", "int" },
|
||||
{ "String", "string" },
|
||||
{ "Boolean", "bool" }
|
||||
};
|
||||
|
||||
public static string LuaDocString(this Type t)
|
||||
{
|
||||
string ret;
|
||||
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out ret))
|
||||
ret = t.Name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string LuaDocString(this ParameterInfo pi)
|
||||
{
|
||||
var ret = "{0} {1}".F(pi.ParameterType.LuaDocString(), pi.Name);
|
||||
if (pi.IsOptional)
|
||||
ret += " = {0}".F(pi.DefaultValue);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static string LuaDocString(this MemberInfo mi)
|
||||
{
|
||||
if (mi is MethodInfo)
|
||||
{
|
||||
var methodInfo = mi as MethodInfo;
|
||||
var parameters = methodInfo.GetParameters().Select(pi => pi.LuaDocString());
|
||||
return "{0} {1}({2})".F(methodInfo.ReturnType.LuaDocString(), mi.Name, parameters.JoinWith(", "));
|
||||
}
|
||||
|
||||
if (mi is PropertyInfo)
|
||||
{
|
||||
var pi = mi as PropertyInfo;
|
||||
var types = new List<string>();
|
||||
if (pi.GetGetMethod() != null)
|
||||
types.Add("get;");
|
||||
if (pi.GetSetMethod() != null)
|
||||
types.Add("set;");
|
||||
|
||||
return "{0} {1} {{ {2} }}".F(pi.PropertyType.LuaDocString(), mi.Name, types.JoinWith(" "));
|
||||
}
|
||||
|
||||
return "Unknown field: {0}".F(mi.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Game/Scripting/ScriptMemberWrapper.cs
Normal file
127
OpenRA.Game/Scripting/ScriptMemberWrapper.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptMemberWrapper
|
||||
{
|
||||
readonly ScriptContext context;
|
||||
public readonly object Target;
|
||||
public readonly MemberInfo Member;
|
||||
|
||||
public readonly bool IsMethod;
|
||||
public readonly bool IsGetProperty;
|
||||
public readonly bool IsSetProperty;
|
||||
|
||||
public ScriptMemberWrapper(ScriptContext context, object target, MemberInfo mi)
|
||||
{
|
||||
this.context = context;
|
||||
Target = target;
|
||||
Member = mi;
|
||||
|
||||
var property = mi as PropertyInfo;
|
||||
if (property != null)
|
||||
{
|
||||
IsGetProperty = property.GetGetMethod() != null;
|
||||
IsSetProperty = property.GetSetMethod() != null;
|
||||
}
|
||||
else
|
||||
IsMethod = true;
|
||||
}
|
||||
|
||||
LuaValue Invoke(LuaVararg args)
|
||||
{
|
||||
if (!IsMethod)
|
||||
throw new LuaException("Trying to invoke a ScriptMemberWrapper that isn't a method!");
|
||||
|
||||
var mi = Member as MethodInfo;
|
||||
var pi = mi.GetParameters();
|
||||
|
||||
object[] 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));
|
||||
}
|
||||
|
||||
var ret = (Member as MethodInfo).Invoke(Target, clrArgs);
|
||||
return ret.ToLuaValue(context);
|
||||
}
|
||||
|
||||
public LuaValue Get(LuaRuntime runtime)
|
||||
{
|
||||
if (IsMethod)
|
||||
return runtime.CreateFunctionFromDelegate((Func<LuaVararg, LuaValue>)Invoke);
|
||||
|
||||
if (IsGetProperty)
|
||||
{
|
||||
var pi = Member as PropertyInfo;
|
||||
return pi.GetValue(Target, null).ToLuaValue(context);
|
||||
}
|
||||
|
||||
throw new LuaException("The property '{0}' is write-only".F(Member.Name));
|
||||
}
|
||||
|
||||
public void Set(LuaRuntime runtime, LuaValue value)
|
||||
{
|
||||
if (IsSetProperty)
|
||||
{
|
||||
var pi = Member as PropertyInfo;
|
||||
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));
|
||||
|
||||
pi.SetValue(Target, clrValue, null);
|
||||
}
|
||||
else
|
||||
throw new LuaException("The property '{0}' is read-only".F(Member.Name));
|
||||
}
|
||||
|
||||
public static IEnumerable<MemberInfo> 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 =>
|
||||
{
|
||||
// Properties are always wrappable
|
||||
if (mi is PropertyInfo)
|
||||
return true;
|
||||
|
||||
// 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;
|
||||
|
||||
// Fields aren't allowed
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
77
OpenRA.Game/Scripting/ScriptObjectWrapper.cs
Normal file
77
OpenRA.Game/Scripting/ScriptObjectWrapper.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public abstract class ScriptObjectWrapper : IScriptBindable, ILuaTableBinding
|
||||
{
|
||||
protected abstract string DuplicateKeyError(string memberName);
|
||||
protected abstract string MemberNotFoundError(string memberName);
|
||||
|
||||
protected readonly ScriptContext context;
|
||||
Dictionary<string, ScriptMemberWrapper> members;
|
||||
|
||||
public ScriptObjectWrapper(ScriptContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
protected void Bind(IEnumerable<object> clrObjects)
|
||||
{
|
||||
members = new Dictionary<string, ScriptMemberWrapper>();
|
||||
foreach (var obj in clrObjects)
|
||||
{
|
||||
var wrappable = ScriptMemberWrapper.WrappableMembers(obj.GetType());
|
||||
foreach (var m in wrappable)
|
||||
{
|
||||
if (members.ContainsKey(m.Name))
|
||||
throw new LuaException(DuplicateKeyError(m.Name));
|
||||
|
||||
members.Add(m.Name, new ScriptMemberWrapper(context, obj, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key) { return members.ContainsKey(key); }
|
||||
|
||||
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]
|
||||
{
|
||||
get
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
return wrapper.Get(runtime);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
wrapper.Set(runtime, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
OpenRA.Game/Scripting/ScriptPlayerInterface.cs
Normal file
45
OpenRA.Game/Scripting/ScriptPlayerInterface.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public class ScriptPlayerInterface : ScriptObjectWrapper
|
||||
{
|
||||
readonly Player player;
|
||||
|
||||
protected override string DuplicateKeyError(string memberName) { return "Player '{0}' defines the command '{1}' on multiple traits".F(player.PlayerName, memberName); }
|
||||
protected override string MemberNotFoundError(string memberName) { return "Player '{0}' does not define a property '{1}'".F(player.PlayerName, memberName); }
|
||||
|
||||
public ScriptPlayerInterface(ScriptContext context, Player player)
|
||||
: base(context)
|
||||
{
|
||||
this.player = player;
|
||||
|
||||
var args = new [] { player };
|
||||
var objects = context.PlayerCommands.Select(cg =>
|
||||
{
|
||||
var groupCtor = cg.GetConstructor(new Type[] { typeof(Player) });
|
||||
return groupCtor.Invoke(args);
|
||||
});
|
||||
|
||||
Bind(objects);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
OpenRA.Game/Scripting/ScriptTypes.cs
Normal file
153
OpenRA.Game/Scripting/ScriptTypes.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Eluant;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
{
|
||||
public static class LuaValueExts
|
||||
{
|
||||
public static Type WrappedClrType(this LuaValue value)
|
||||
{
|
||||
object inner;
|
||||
if (value.TryGetClrObject(out inner))
|
||||
return inner.GetType();
|
||||
|
||||
return value.GetType();
|
||||
}
|
||||
|
||||
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
|
||||
{
|
||||
object temp;
|
||||
var ret = value.TryGetClrValue(typeof(T), out temp);
|
||||
clrObject = ret ? (T)temp : default(T);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
|
||||
{
|
||||
object temp;
|
||||
|
||||
// Value wraps a CLR object
|
||||
if (value.TryGetClrObject(out temp))
|
||||
{
|
||||
if (temp.GetType() == t)
|
||||
{
|
||||
clrObject = temp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (value is LuaNil && !t.IsValueType)
|
||||
{
|
||||
clrObject = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaBoolean && t.IsAssignableFrom(typeof(bool)))
|
||||
{
|
||||
clrObject = value.ToBoolean();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaNumber && 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;
|
||||
}
|
||||
|
||||
if (value is LuaString && t.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
clrObject = value.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaFunction && t.IsAssignableFrom(typeof(LuaFunction)))
|
||||
{
|
||||
clrObject = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value is LuaTable && t.IsAssignableFrom(typeof(LuaTable)))
|
||||
{
|
||||
clrObject = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Value isn't of the requested type.
|
||||
// Set a default output value and return false
|
||||
// Value types are assumed to specify a default constructor
|
||||
clrObject = t.IsValueType ? Activator.CreateInstance(t) : null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static LuaValue ToLuaValue(this object obj, ScriptContext context)
|
||||
{
|
||||
if (obj is LuaValue)
|
||||
return (LuaValue)obj;
|
||||
|
||||
if (obj == null)
|
||||
return LuaNil.Instance;
|
||||
|
||||
if (obj is double)
|
||||
return (LuaValue)(double)obj;
|
||||
|
||||
if (obj is int)
|
||||
return (LuaValue)(int)obj;
|
||||
|
||||
if (obj is bool)
|
||||
return (LuaValue)(bool)obj;
|
||||
|
||||
if (obj is string)
|
||||
return (LuaValue)(string)obj;
|
||||
|
||||
if (obj is IScriptBindable)
|
||||
{
|
||||
// Object needs additional notification / context
|
||||
var notify = obj as IScriptNotifyBind;
|
||||
if (notify != null)
|
||||
notify.OnScriptBind(context);
|
||||
|
||||
return new LuaCustomClrObject(obj);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Cannot convert type '{0}' to Lua. Class must implement IScriptBindable.".F(obj.GetType()));
|
||||
}
|
||||
|
||||
public static LuaTable ToLuaTable(this IEnumerable collection, ScriptContext context)
|
||||
{
|
||||
var i = 1;
|
||||
var table = context.CreateTable();
|
||||
foreach (var x in collection)
|
||||
table.Add(i++, x.ToLuaValue(context));
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user