Merge pull request #4140 from ScottNZ/lua-wip
Add map scripting support
This commit is contained in:
13
.gitignore
vendored
13
.gitignore
vendored
@@ -14,11 +14,14 @@ _ReSharper.*/
|
||||
|
||||
# binaries
|
||||
mods/*/*.dll
|
||||
mods/*/*.mdb
|
||||
/*.dll
|
||||
/*.dll.config
|
||||
*.pdb
|
||||
*.mdb
|
||||
*.exe
|
||||
/*.so
|
||||
/*.dylib
|
||||
/*.pdb
|
||||
/*.mdb
|
||||
/*.exe
|
||||
|
||||
# backup files by various editors
|
||||
*~
|
||||
@@ -47,10 +50,6 @@ OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.perspectivev3
|
||||
OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3
|
||||
*.resources
|
||||
|
||||
# KDE
|
||||
*.kate-swp
|
||||
*.directory
|
||||
|
||||
# auto-generated documentation
|
||||
DOCUMENTATION.md
|
||||
*.html
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@@ -69,6 +69,9 @@ Also thanks to:
|
||||
Using GeoLite data created by MaxMind and
|
||||
distributed under the CC BY-SA 3.0 license.
|
||||
|
||||
Using MonoBoxedLua created by Sparklin Labs
|
||||
and distributed under the MIT license.
|
||||
|
||||
Finally, special thanks goes to the original teams
|
||||
at Westwood Studios and EA for creating the classic
|
||||
games which OpenRA aims to reimagine.
|
||||
|
||||
@@ -77,12 +77,14 @@ NEW:
|
||||
Fixed a desync related to projectile contrails.
|
||||
Fixed corrupted replays (which would immediately desync).
|
||||
Removed runtime mod merging.
|
||||
Added support for map scripting with Lua.
|
||||
Build system and packages:
|
||||
Added GeoIP to Makefile so it is installed properly.
|
||||
Added desktop shortcut creation support to the Makefile and Windows installer.
|
||||
COPYING and CHANGELOG are now shipped on all platforms.
|
||||
Fixed 'make docs' crashing when the game assets are not installed.
|
||||
Renamed Game.Mods launch argument to Game.Mod.
|
||||
Linux packages now install to /usr/lib/openra for consistency with other Mono applications.
|
||||
Mod / Custom map compatibility:
|
||||
Mods can now include traits from TD and D2K in RA.
|
||||
New sections MapFolders and Translations added to mod.yaml.
|
||||
|
||||
56
LuaInterface/AssemblyInfo.cs
Normal file
56
LuaInterface/AssemblyInfo.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Permissions;
|
||||
|
||||
//
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
[assembly: AssemblyTitle("LuaInterface")]
|
||||
[assembly: AssemblyDescription("Bridge between the Lua runtime and the CLR")]
|
||||
[assembly: AssemblyCopyright("Copyright 2003-2008 Fabio Mascarenhas, Kevin Hester")]
|
||||
[assembly: CLSCompliant(false)]
|
||||
|
||||
//
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("2.0.4.*")]
|
||||
|
||||
//
|
||||
// In order to sign your assembly you must specify a key to use. Refer to the
|
||||
// Microsoft .NET Framework documentation for more information on assembly signing.
|
||||
//
|
||||
// Use the attributes below to control which key is used for signing.
|
||||
//
|
||||
// Notes:
|
||||
// (*) If no key is specified, the assembly is not signed.
|
||||
// (*) KeyName refers to a key that has been installed in the Crypto Service
|
||||
// Provider (CSP) on your machine. KeyFile refers to a file which contains
|
||||
// a key.
|
||||
// (*) If the KeyFile and the KeyName values are both specified, the
|
||||
// following processing occurs:
|
||||
// (1) If the KeyName can be found in the CSP, that key is used.
|
||||
// (2) If the KeyName does not exist and the KeyFile does exist, the key
|
||||
// in the KeyFile is installed into the CSP and used.
|
||||
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
|
||||
// When specifying the KeyFile, the location of the KeyFile should be
|
||||
// relative to the project output directory which is
|
||||
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
|
||||
// located in the project directory, you would specify the AssemblyKeyFile
|
||||
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
|
||||
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
|
||||
// documentation for more information on this.
|
||||
//
|
||||
|
||||
// We call native DLLs (lua50) and we don't want to be fucked with or validated
|
||||
// [assembly: SecurityPermission(RequestMinimum, UnmanagedCode = true)]
|
||||
314
LuaInterface/CheckType.cs
Normal file
314
LuaInterface/CheckType.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/*
|
||||
* Type checking and conversion functions.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class CheckType
|
||||
{
|
||||
private ObjectTranslator translator;
|
||||
|
||||
ExtractValue extractNetObject;
|
||||
Dictionary<long, ExtractValue> extractValues = new Dictionary<long, ExtractValue>();
|
||||
|
||||
public CheckType(ObjectTranslator translator)
|
||||
{
|
||||
this.translator = translator;
|
||||
|
||||
extractValues.Add(typeof(object).TypeHandle.Value.ToInt64(), new ExtractValue(getAsObject));
|
||||
extractValues.Add(typeof(sbyte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsSbyte));
|
||||
extractValues.Add(typeof(byte).TypeHandle.Value.ToInt64(), new ExtractValue(getAsByte));
|
||||
extractValues.Add(typeof(short).TypeHandle.Value.ToInt64(), new ExtractValue(getAsShort));
|
||||
extractValues.Add(typeof(ushort).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUshort));
|
||||
extractValues.Add(typeof(int).TypeHandle.Value.ToInt64(), new ExtractValue(getAsInt));
|
||||
extractValues.Add(typeof(uint).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUint));
|
||||
extractValues.Add(typeof(long).TypeHandle.Value.ToInt64(), new ExtractValue(getAsLong));
|
||||
extractValues.Add(typeof(ulong).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUlong));
|
||||
extractValues.Add(typeof(double).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDouble));
|
||||
extractValues.Add(typeof(char).TypeHandle.Value.ToInt64(), new ExtractValue(getAsChar));
|
||||
extractValues.Add(typeof(float).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFloat));
|
||||
extractValues.Add(typeof(decimal).TypeHandle.Value.ToInt64(), new ExtractValue(getAsDecimal));
|
||||
extractValues.Add(typeof(bool).TypeHandle.Value.ToInt64(), new ExtractValue(getAsBoolean));
|
||||
extractValues.Add(typeof(string).TypeHandle.Value.ToInt64(), new ExtractValue(getAsString));
|
||||
extractValues.Add(typeof(LuaFunction).TypeHandle.Value.ToInt64(), new ExtractValue(getAsFunction));
|
||||
extractValues.Add(typeof(LuaTable).TypeHandle.Value.ToInt64(), new ExtractValue(getAsTable));
|
||||
extractValues.Add(typeof(LuaUserData).TypeHandle.Value.ToInt64(), new ExtractValue(getAsUserdata));
|
||||
|
||||
extractNetObject = new ExtractValue(getAsNetObject);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the value at Lua stack index stackPos matches paramType,
|
||||
* returning a conversion function if it does and null otherwise.
|
||||
*/
|
||||
internal ExtractValue getExtractor(IReflect paramType)
|
||||
{
|
||||
return getExtractor(paramType.UnderlyingSystemType);
|
||||
}
|
||||
internal ExtractValue getExtractor(Type paramType)
|
||||
{
|
||||
if(paramType.IsByRef) paramType=paramType.GetElementType();
|
||||
|
||||
long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64();
|
||||
|
||||
if(extractValues.ContainsKey(runtimeHandleValue))
|
||||
return extractValues[runtimeHandleValue];
|
||||
else
|
||||
return extractNetObject;
|
||||
}
|
||||
|
||||
internal ExtractValue checkType(IntPtr luaState,int stackPos,Type paramType)
|
||||
{
|
||||
LuaTypes luatype = LuaDLL.lua_type(luaState, stackPos);
|
||||
|
||||
if(paramType.IsByRef) paramType=paramType.GetElementType();
|
||||
|
||||
Type underlyingType = Nullable.GetUnderlyingType(paramType);
|
||||
if (underlyingType != null)
|
||||
{
|
||||
paramType = underlyingType; // Silently convert nullable types to their non null requics
|
||||
}
|
||||
|
||||
long runtimeHandleValue = paramType.TypeHandle.Value.ToInt64();
|
||||
|
||||
if (paramType.Equals(typeof(object)))
|
||||
return extractValues[runtimeHandleValue];
|
||||
|
||||
/*
|
||||
//CP: Added support for generic parameters
|
||||
if (paramType.IsGenericParameter)
|
||||
{
|
||||
if (luatype == LuaTypes.LUA_TBOOLEAN)
|
||||
return extractValues[typeof(bool).TypeHandle.Value.ToInt64()];
|
||||
else if (luatype == LuaTypes.LUA_TSTRING)
|
||||
return extractValues[typeof(string).TypeHandle.Value.ToInt64()];
|
||||
else if (luatype == LuaTypes.LUA_TTABLE)
|
||||
return extractValues[typeof(LuaTable).TypeHandle.Value.ToInt64()];
|
||||
else if (luatype == LuaTypes.LUA_TUSERDATA)
|
||||
return extractValues[typeof(object).TypeHandle.Value.ToInt64()];
|
||||
else if (luatype == LuaTypes.LUA_TFUNCTION)
|
||||
return extractValues[typeof(LuaFunction).TypeHandle.Value.ToInt64()];
|
||||
else if (luatype == LuaTypes.LUA_TNUMBER)
|
||||
return extractValues[typeof(double).TypeHandle.Value.ToInt64()];
|
||||
//else // suppress CS0642
|
||||
;//an unsupported type was encountered
|
||||
}
|
||||
*/
|
||||
|
||||
if (LuaDLL.lua_isnumber(luaState, stackPos))
|
||||
return extractValues[runtimeHandleValue];
|
||||
|
||||
if (paramType == typeof(bool))
|
||||
{
|
||||
if (LuaDLL.lua_isboolean(luaState, stackPos))
|
||||
return extractValues[runtimeHandleValue];
|
||||
}
|
||||
else if (paramType == typeof(string))
|
||||
{
|
||||
if (LuaDLL.lua_isstring(luaState, stackPos))
|
||||
return extractValues[runtimeHandleValue];
|
||||
else if (luatype == LuaTypes.LUA_TNIL)
|
||||
return extractNetObject; // kevinh - silently convert nil to a null string pointer
|
||||
}
|
||||
else if (paramType == typeof(LuaTable))
|
||||
{
|
||||
if (luatype == LuaTypes.LUA_TTABLE)
|
||||
return extractValues[runtimeHandleValue];
|
||||
else if (luatype == LuaTypes.LUA_TNIL)
|
||||
return extractNetObject; // tkopal - silently convert nil to a null table
|
||||
}
|
||||
else if (paramType == typeof(LuaUserData))
|
||||
{
|
||||
if (luatype == LuaTypes.LUA_TUSERDATA)
|
||||
return extractValues[runtimeHandleValue];
|
||||
}
|
||||
else if (paramType == typeof(LuaFunction))
|
||||
{
|
||||
if (luatype == LuaTypes.LUA_TFUNCTION)
|
||||
return extractValues[runtimeHandleValue];
|
||||
else if (luatype == LuaTypes.LUA_TNIL)
|
||||
return extractNetObject; // elisee - silently convert nil to a null string pointer
|
||||
}
|
||||
else if (typeof(Delegate).IsAssignableFrom(paramType) && luatype == LuaTypes.LUA_TFUNCTION)
|
||||
{
|
||||
return new ExtractValue(new DelegateGenerator(translator, paramType).extractGenerated);
|
||||
}
|
||||
else if (paramType.IsInterface && luatype == LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
return new ExtractValue(new ClassGenerator(translator, paramType).extractGenerated);
|
||||
}
|
||||
else if ((paramType.IsInterface || paramType.IsClass) && luatype == LuaTypes.LUA_TNIL)
|
||||
{
|
||||
// kevinh - allow nil to be silently converted to null - extractNetObject will return null when the item ain't found
|
||||
return extractNetObject;
|
||||
}
|
||||
else if (LuaDLL.lua_type(luaState, stackPos) == LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
if (LuaDLL.luaL_getmetafield(luaState, stackPos, "__index"))
|
||||
{
|
||||
object obj = translator.getNetObject(luaState, -1);
|
||||
LuaDLL.lua_settop(luaState, -2);
|
||||
if (obj != null && paramType.IsAssignableFrom(obj.GetType()))
|
||||
return extractNetObject;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
object obj = translator.getNetObject(luaState, stackPos);
|
||||
if (obj != null && paramType.IsAssignableFrom(obj.GetType()))
|
||||
return extractNetObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following functions return the value in the Lua stack
|
||||
* index stackPos as the desired type if it can, or null
|
||||
* otherwise.
|
||||
*/
|
||||
private object getAsSbyte(IntPtr luaState,int stackPos)
|
||||
{
|
||||
sbyte retVal=(sbyte)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsByte(IntPtr luaState,int stackPos)
|
||||
{
|
||||
byte retVal=(byte)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsShort(IntPtr luaState,int stackPos)
|
||||
{
|
||||
short retVal=(short)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsUshort(IntPtr luaState,int stackPos)
|
||||
{
|
||||
ushort retVal=(ushort)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsInt(IntPtr luaState,int stackPos)
|
||||
{
|
||||
int retVal=(int)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsUint(IntPtr luaState,int stackPos)
|
||||
{
|
||||
uint retVal=(uint)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsLong(IntPtr luaState,int stackPos)
|
||||
{
|
||||
long retVal=(long)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsUlong(IntPtr luaState,int stackPos)
|
||||
{
|
||||
ulong retVal=(ulong)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsDouble(IntPtr luaState,int stackPos)
|
||||
{
|
||||
double retVal=LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsChar(IntPtr luaState,int stackPos)
|
||||
{
|
||||
char retVal=(char)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsFloat(IntPtr luaState,int stackPos)
|
||||
{
|
||||
float retVal=(float)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsDecimal(IntPtr luaState,int stackPos)
|
||||
{
|
||||
decimal retVal=(decimal)LuaDLL.lua_tonumber(luaState,stackPos);
|
||||
if(retVal==0 && !LuaDLL.lua_isnumber(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsBoolean(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return LuaDLL.lua_toboolean(luaState,stackPos);
|
||||
}
|
||||
private object getAsString(IntPtr luaState,int stackPos)
|
||||
{
|
||||
string retVal=LuaDLL.lua_tostring(luaState,stackPos);
|
||||
if(retVal=="" && !LuaDLL.lua_isstring(luaState,stackPos)) return null;
|
||||
return retVal;
|
||||
}
|
||||
private object getAsTable(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return translator.getTable(luaState,stackPos);
|
||||
}
|
||||
private object getAsFunction(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return translator.getFunction(luaState,stackPos);
|
||||
}
|
||||
private object getAsUserdata(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return translator.getUserData(luaState,stackPos);
|
||||
}
|
||||
public object getAsObject(IntPtr luaState,int stackPos)
|
||||
{
|
||||
if(LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index"))
|
||||
{
|
||||
if(LuaDLL.luaL_checkmetatable(luaState,-1))
|
||||
{
|
||||
LuaDLL.lua_insert(luaState,stackPos);
|
||||
LuaDLL.lua_remove(luaState,stackPos+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
object obj=translator.getObject(luaState,stackPos);
|
||||
return obj;
|
||||
}
|
||||
public object getAsNetObject(IntPtr luaState,int stackPos)
|
||||
{
|
||||
object obj=translator.getNetObject(luaState,stackPos);
|
||||
if(obj==null && LuaDLL.lua_type(luaState,stackPos)==LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
if(LuaDLL.luaL_getmetafield(luaState,stackPos,"__index"))
|
||||
{
|
||||
if(LuaDLL.luaL_checkmetatable(luaState,-1))
|
||||
{
|
||||
LuaDLL.lua_insert(luaState,stackPos);
|
||||
LuaDLL.lua_remove(luaState,stackPos+1);
|
||||
obj=translator.getNetObject(luaState,stackPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
683
LuaInterface/GenerateEventAssembly.cs
Normal file
683
LuaInterface/GenerateEventAssembly.cs
Normal file
@@ -0,0 +1,683 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/*
|
||||
* Structure to store a type and the return types of
|
||||
* its methods (the type of the returned value and out/ref
|
||||
* parameters).
|
||||
*/
|
||||
struct LuaClassType
|
||||
{
|
||||
public Type klass;
|
||||
public Type[][] returnTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common interface for types generated from tables. The method
|
||||
* returns the table that overrides some or all of the type's methods.
|
||||
*/
|
||||
public interface ILuaGeneratedType
|
||||
{
|
||||
LuaTable __luaInterface_getLuaTable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class used for generating delegates that get a function from the Lua
|
||||
* stack as a delegate of a specific type.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class DelegateGenerator
|
||||
{
|
||||
private ObjectTranslator translator;
|
||||
private Type delegateType;
|
||||
|
||||
public DelegateGenerator(ObjectTranslator translator,Type delegateType)
|
||||
{
|
||||
this.translator=translator;
|
||||
this.delegateType=delegateType;
|
||||
}
|
||||
public object extractGenerated(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return CodeGeneration.Instance.GetDelegate(delegateType,translator.getFunction(luaState,stackPos));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class used for generating delegates that get a table from the Lua
|
||||
* stack as a an object of a specific type.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class ClassGenerator
|
||||
{
|
||||
private ObjectTranslator translator;
|
||||
private Type klass;
|
||||
|
||||
public ClassGenerator(ObjectTranslator translator,Type klass)
|
||||
{
|
||||
this.translator=translator;
|
||||
this.klass=klass;
|
||||
}
|
||||
public object extractGenerated(IntPtr luaState,int stackPos)
|
||||
{
|
||||
return CodeGeneration.Instance.GetClassInstance(klass,translator.getTable(luaState,stackPos));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dynamically generates new types from existing types and
|
||||
* Lua function and table values. Generated types are event handlers,
|
||||
* delegates, interface implementations and subclasses.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class CodeGeneration
|
||||
{
|
||||
private Type eventHandlerParent=typeof(LuaEventHandler);
|
||||
private Dictionary<Type, Type> eventHandlerCollection=new Dictionary<Type, Type>();
|
||||
|
||||
private Type delegateParent=typeof(LuaDelegate);
|
||||
private Dictionary<Type, Type> delegateCollection=new Dictionary<Type, Type>();
|
||||
|
||||
private Type classHelper=typeof(LuaClassHelper);
|
||||
private Dictionary<Type, LuaClassType> classCollection=new Dictionary<Type, LuaClassType>();
|
||||
|
||||
private AssemblyName assemblyName;
|
||||
private AssemblyBuilder newAssembly;
|
||||
private ModuleBuilder newModule;
|
||||
private int luaClassNumber=1;
|
||||
private static readonly CodeGeneration instance = new CodeGeneration();
|
||||
|
||||
static CodeGeneration()
|
||||
{
|
||||
}
|
||||
|
||||
private CodeGeneration()
|
||||
{
|
||||
// Create an assembly name
|
||||
assemblyName=new AssemblyName( );
|
||||
assemblyName.Name="LuaInterface_generatedcode";
|
||||
// Create a new assembly with one module.
|
||||
newAssembly=Thread.GetDomain().DefineDynamicAssembly(
|
||||
assemblyName, AssemblyBuilderAccess.Run);
|
||||
newModule=newAssembly.DefineDynamicModule("LuaInterface_generatedcode");
|
||||
}
|
||||
|
||||
/*
|
||||
* Singleton instance of the class
|
||||
*/
|
||||
public static CodeGeneration Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates an event handler that calls a Lua function
|
||||
*/
|
||||
private Type GenerateEvent(Type eventHandlerType)
|
||||
{
|
||||
string typeName;
|
||||
lock(this)
|
||||
{
|
||||
typeName = "LuaGeneratedClass" + luaClassNumber;
|
||||
luaClassNumber++;
|
||||
}
|
||||
// Define a public class in the assembly, called typeName
|
||||
TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,eventHandlerParent);
|
||||
|
||||
// Defines the handler method. Its signature is void(object,<subclassofEventArgs>)
|
||||
Type[] paramTypes = new Type[2];
|
||||
paramTypes[0]=typeof(object);
|
||||
paramTypes[1]=eventHandlerType;
|
||||
Type returnType=typeof(void);
|
||||
MethodBuilder handleMethod=myType.DefineMethod("HandleEvent",
|
||||
MethodAttributes.Public|MethodAttributes.HideBySig,
|
||||
returnType,paramTypes);
|
||||
|
||||
// Emits the IL for the method. It loads the arguments
|
||||
// and calls the handleEvent method of the base class
|
||||
ILGenerator generator=handleMethod.GetILGenerator( );
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
generator.Emit(OpCodes.Ldarg_2);
|
||||
MethodInfo miGenericEventHandler;
|
||||
miGenericEventHandler=eventHandlerParent.GetMethod("handleEvent");
|
||||
generator.Emit(OpCodes.Call,miGenericEventHandler);
|
||||
// returns
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
// creates the new type
|
||||
return myType.CreateType();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates a type that can be used for instantiating a delegate
|
||||
* of the provided type, given a Lua function.
|
||||
*/
|
||||
private Type GenerateDelegate(Type delegateType)
|
||||
{
|
||||
string typeName;
|
||||
lock(this)
|
||||
{
|
||||
typeName = "LuaGeneratedClass" + luaClassNumber;
|
||||
luaClassNumber++;
|
||||
}
|
||||
// Define a public class in the assembly, called typeName
|
||||
TypeBuilder myType=newModule.DefineType(typeName,TypeAttributes.Public,delegateParent);
|
||||
|
||||
// Defines the delegate method with the same signature as the
|
||||
// Invoke method of delegateType
|
||||
MethodInfo invokeMethod=delegateType.GetMethod("Invoke");
|
||||
ParameterInfo[] paramInfo=invokeMethod.GetParameters();
|
||||
Type[] paramTypes=new Type[paramInfo.Length];
|
||||
Type returnType=invokeMethod.ReturnType;
|
||||
|
||||
// Counts out and ref params, for use later
|
||||
int nOutParams=0; int nOutAndRefParams=0;
|
||||
for(int i=0;i<paramTypes.Length;i++)
|
||||
{
|
||||
paramTypes[i]=paramInfo[i].ParameterType;
|
||||
if((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
|
||||
nOutParams++;
|
||||
if(paramTypes[i].IsByRef)
|
||||
nOutAndRefParams++;
|
||||
}
|
||||
int[] refArgs=new int[nOutAndRefParams];
|
||||
|
||||
MethodBuilder delegateMethod=myType.DefineMethod("CallFunction",
|
||||
invokeMethod.Attributes,
|
||||
returnType,paramTypes);
|
||||
|
||||
// Generates the IL for the method
|
||||
ILGenerator generator=delegateMethod.GetILGenerator( );
|
||||
|
||||
generator.DeclareLocal(typeof(object[])); // original arguments
|
||||
generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
|
||||
generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
|
||||
if(!(returnType == typeof(void))) // return value
|
||||
generator.DeclareLocal(returnType);
|
||||
else
|
||||
generator.DeclareLocal(typeof(object));
|
||||
// Initializes local variables
|
||||
generator.Emit(OpCodes.Ldc_I4,paramTypes.Length);
|
||||
generator.Emit(OpCodes.Newarr,typeof(object));
|
||||
generator.Emit(OpCodes.Stloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,paramTypes.Length-nOutParams);
|
||||
generator.Emit(OpCodes.Newarr,typeof(object));
|
||||
generator.Emit(OpCodes.Stloc_1);
|
||||
generator.Emit(OpCodes.Ldc_I4,nOutAndRefParams);
|
||||
generator.Emit(OpCodes.Newarr,typeof(int));
|
||||
generator.Emit(OpCodes.Stloc_2);
|
||||
// Stores the arguments in the local variables
|
||||
for(int iArgs=0,iInArgs=0,iOutArgs=0;iArgs<paramTypes.Length;iArgs++)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,iArgs);
|
||||
generator.Emit(OpCodes.Ldarg,iArgs+1);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
if(paramTypes[iArgs].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
|
||||
} else generator.Emit(OpCodes.Ldind_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(paramTypes[iArgs].IsValueType)
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs]);
|
||||
}
|
||||
generator.Emit(OpCodes.Stelem_Ref);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_2);
|
||||
generator.Emit(OpCodes.Ldc_I4,iOutArgs);
|
||||
generator.Emit(OpCodes.Ldc_I4,iArgs);
|
||||
generator.Emit(OpCodes.Stelem_I4);
|
||||
refArgs[iOutArgs]=iArgs;
|
||||
iOutArgs++;
|
||||
}
|
||||
if(paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut))
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_1);
|
||||
generator.Emit(OpCodes.Ldc_I4,iInArgs);
|
||||
generator.Emit(OpCodes.Ldarg,iArgs+1);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
if(paramTypes[iArgs].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
|
||||
}
|
||||
else generator.Emit(OpCodes.Ldind_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(paramTypes[iArgs].IsValueType)
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs]);
|
||||
}
|
||||
generator.Emit(OpCodes.Stelem_Ref);
|
||||
iInArgs++;
|
||||
}
|
||||
}
|
||||
// Calls the callFunction method of the base class
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldloc_1);
|
||||
generator.Emit(OpCodes.Ldloc_2);
|
||||
MethodInfo miGenericEventHandler;
|
||||
miGenericEventHandler=delegateParent.GetMethod("callFunction");
|
||||
generator.Emit(OpCodes.Call,miGenericEventHandler);
|
||||
// Stores return value
|
||||
if(returnType == typeof(void))
|
||||
{
|
||||
generator.Emit(OpCodes.Pop);
|
||||
generator.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
else if(returnType.IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Unbox,returnType);
|
||||
generator.Emit(OpCodes.Ldobj,returnType);
|
||||
} else generator.Emit(OpCodes.Castclass,returnType);
|
||||
generator.Emit(OpCodes.Stloc_3);
|
||||
// Stores new value of out and ref params
|
||||
for(int i=0;i<refArgs.Length;i++)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldarg,refArgs[i]+1);
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,refArgs[i]);
|
||||
generator.Emit(OpCodes.Ldelem_Ref);
|
||||
if(paramTypes[refArgs[i]].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Unbox,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Stobj,paramTypes[refArgs[i]].GetElementType());
|
||||
}
|
||||
else
|
||||
{
|
||||
generator.Emit(OpCodes.Castclass,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Stind_Ref);
|
||||
}
|
||||
}
|
||||
// Returns
|
||||
if(!(returnType == typeof(void)))
|
||||
generator.Emit(OpCodes.Ldloc_3);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
// creates the new type
|
||||
return myType.CreateType();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates an implementation of klass, if it is an interface, or
|
||||
* a subclass of klass that delegates its virtual methods to a Lua table.
|
||||
*/
|
||||
public void GenerateClass(Type klass,out Type newType,out Type[][] returnTypes, LuaTable luaTable)
|
||||
{
|
||||
string typeName;
|
||||
lock(this)
|
||||
{
|
||||
typeName = "LuaGeneratedClass" + luaClassNumber;
|
||||
luaClassNumber++;
|
||||
}
|
||||
TypeBuilder myType;
|
||||
// Define a public class in the assembly, called typeName
|
||||
if(klass.IsInterface)
|
||||
myType=newModule.DefineType(typeName,TypeAttributes.Public,typeof(object),new Type[] { klass,typeof(ILuaGeneratedType) });
|
||||
else
|
||||
myType=newModule.DefineType(typeName,TypeAttributes.Public,klass,new Type[] { typeof(ILuaGeneratedType) });
|
||||
// Field that stores the Lua table
|
||||
FieldBuilder luaTableField=myType.DefineField("__luaInterface_luaTable",typeof(LuaTable),FieldAttributes.Public);
|
||||
// Field that stores the return types array
|
||||
FieldBuilder returnTypesField=myType.DefineField("__luaInterface_returnTypes",typeof(Type[][]),FieldAttributes.Public);
|
||||
// Generates the constructor for the new type, it takes a Lua table and an array
|
||||
// of return types and stores them in the respective fields
|
||||
ConstructorBuilder constructor=
|
||||
myType.DefineConstructor(MethodAttributes.Public,CallingConventions.Standard,new Type[] { typeof(LuaTable),typeof(Type[][]) });
|
||||
ILGenerator generator=constructor.GetILGenerator();
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
if(klass.IsInterface)
|
||||
generator.Emit(OpCodes.Call,typeof(object).GetConstructor(Type.EmptyTypes));
|
||||
else
|
||||
generator.Emit(OpCodes.Call,klass.GetConstructor(Type.EmptyTypes));
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
generator.Emit(OpCodes.Stfld,luaTableField);
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldarg_2);
|
||||
generator.Emit(OpCodes.Stfld,returnTypesField);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
// Generates overriden versions of the klass' public and protected virtual methods that have been explicitly specfied
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
MethodInfo[] classMethods=klass.GetMethods(flags);
|
||||
returnTypes=new Type[classMethods.Length][];
|
||||
int i=0;
|
||||
foreach(MethodInfo method in classMethods)
|
||||
{
|
||||
if(klass.IsInterface)
|
||||
{
|
||||
GenerateMethod(myType,method,
|
||||
MethodAttributes.HideBySig|MethodAttributes.Virtual|MethodAttributes.NewSlot,
|
||||
i,luaTableField,returnTypesField,false,out returnTypes[i]);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!method.IsPrivate && !method.IsFinal && method.IsVirtual)
|
||||
{
|
||||
if (luaTable[method.Name] != null) {
|
||||
GenerateMethod(myType,method,(method.Attributes|MethodAttributes.NewSlot)^MethodAttributes.NewSlot,i,
|
||||
luaTableField,returnTypesField,true,out returnTypes[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generates an implementation of the __luaInterface_getLuaTable method
|
||||
MethodBuilder returnTableMethod=myType.DefineMethod("__luaInterface_getLuaTable",
|
||||
MethodAttributes.Public|MethodAttributes.HideBySig|MethodAttributes.Virtual,
|
||||
typeof(LuaTable),new Type[0]);
|
||||
myType.DefineMethodOverride(returnTableMethod,typeof(ILuaGeneratedType).GetMethod("__luaInterface_getLuaTable"));
|
||||
generator=returnTableMethod.GetILGenerator();
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldfld,luaTableField);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
// Creates the type
|
||||
newType=myType.CreateType();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates an overriden implementation of method inside myType that delegates
|
||||
* to a function in a Lua table with the same name, if the function exists. If it
|
||||
* doesn't the method calls the base method (or does nothing, in case of interface
|
||||
* implementations).
|
||||
*/
|
||||
private void GenerateMethod(TypeBuilder myType,MethodInfo method,MethodAttributes attributes,
|
||||
int methodIndex,FieldInfo luaTableField,FieldInfo returnTypesField,bool generateBase,out Type[] returnTypes)
|
||||
{
|
||||
ParameterInfo[] paramInfo=method.GetParameters();
|
||||
Type[] paramTypes=new Type[paramInfo.Length];
|
||||
List<Type> returnTypesList=new List<Type>();
|
||||
|
||||
// Counts out and ref parameters, for later use,
|
||||
// and creates the list of return types
|
||||
int nOutParams=0; int nOutAndRefParams=0;
|
||||
Type returnType=method.ReturnType;
|
||||
returnTypesList.Add(returnType);
|
||||
for(int i=0;i<paramTypes.Length;i++)
|
||||
{
|
||||
paramTypes[i]=paramInfo[i].ParameterType;
|
||||
if((!paramInfo[i].IsIn) && paramInfo[i].IsOut)
|
||||
nOutParams++;
|
||||
if(paramTypes[i].IsByRef)
|
||||
{
|
||||
returnTypesList.Add(paramTypes[i].GetElementType());
|
||||
nOutAndRefParams++;
|
||||
}
|
||||
}
|
||||
int[] refArgs=new int[nOutAndRefParams];
|
||||
returnTypes=returnTypesList.ToArray();
|
||||
|
||||
// Generates a version of the method that calls the base implementation
|
||||
// directly, for use by the base field of the table
|
||||
if(generateBase)
|
||||
{
|
||||
String baseName = "__luaInterface_base_"+method.Name;
|
||||
MethodBuilder baseMethod=myType.DefineMethod(baseName,
|
||||
MethodAttributes.Public|MethodAttributes.NewSlot|MethodAttributes.HideBySig,
|
||||
returnType,paramTypes);
|
||||
ILGenerator generatorBase=baseMethod.GetILGenerator();
|
||||
generatorBase.Emit(OpCodes.Ldarg_0);
|
||||
for(int i=0;i<paramTypes.Length;i++)
|
||||
generatorBase.Emit(OpCodes.Ldarg,i+1);
|
||||
generatorBase.Emit(OpCodes.Call,method);
|
||||
//if (returnType == typeof(void))
|
||||
// generatorBase.Emit(OpCodes.Pop);
|
||||
generatorBase.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
// Defines the method
|
||||
MethodBuilder methodImpl=myType.DefineMethod(method.Name,attributes,
|
||||
returnType,paramTypes);
|
||||
// If it's an implementation of an interface tells what method it
|
||||
// is overriding
|
||||
if(myType.BaseType.Equals(typeof(object)))
|
||||
myType.DefineMethodOverride(methodImpl,method);
|
||||
|
||||
ILGenerator generator=methodImpl.GetILGenerator( );
|
||||
|
||||
generator.DeclareLocal(typeof(object[])); // original arguments
|
||||
generator.DeclareLocal(typeof(object[])); // with out-only arguments removed
|
||||
generator.DeclareLocal(typeof(int[])); // indexes of out and ref arguments
|
||||
if(!(returnType == typeof(void))) // return value
|
||||
generator.DeclareLocal(returnType);
|
||||
else
|
||||
generator.DeclareLocal(typeof(object));
|
||||
// Initializes local variables
|
||||
generator.Emit(OpCodes.Ldc_I4,paramTypes.Length);
|
||||
generator.Emit(OpCodes.Newarr,typeof(object));
|
||||
generator.Emit(OpCodes.Stloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,paramTypes.Length-nOutParams+1);
|
||||
generator.Emit(OpCodes.Newarr,typeof(object));
|
||||
generator.Emit(OpCodes.Stloc_1);
|
||||
generator.Emit(OpCodes.Ldc_I4,nOutAndRefParams);
|
||||
generator.Emit(OpCodes.Newarr,typeof(int));
|
||||
generator.Emit(OpCodes.Stloc_2);
|
||||
generator.Emit(OpCodes.Ldloc_1);
|
||||
generator.Emit(OpCodes.Ldc_I4_0);
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldfld,luaTableField);
|
||||
generator.Emit(OpCodes.Stelem_Ref);
|
||||
// Stores the arguments into the local variables, as needed
|
||||
for(int iArgs=0,iInArgs=1,iOutArgs=0;iArgs<paramTypes.Length;iArgs++)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,iArgs);
|
||||
generator.Emit(OpCodes.Ldarg,iArgs+1);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
if(paramTypes[iArgs].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
|
||||
}
|
||||
else generator.Emit(OpCodes.Ldind_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(paramTypes[iArgs].IsValueType)
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs]);
|
||||
}
|
||||
generator.Emit(OpCodes.Stelem_Ref);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_2);
|
||||
generator.Emit(OpCodes.Ldc_I4,iOutArgs);
|
||||
generator.Emit(OpCodes.Ldc_I4,iArgs);
|
||||
generator.Emit(OpCodes.Stelem_I4);
|
||||
refArgs[iOutArgs]=iArgs;
|
||||
iOutArgs++;
|
||||
}
|
||||
if(paramInfo[iArgs].IsIn || (!paramInfo[iArgs].IsOut))
|
||||
{
|
||||
generator.Emit(OpCodes.Ldloc_1);
|
||||
generator.Emit(OpCodes.Ldc_I4,iInArgs);
|
||||
generator.Emit(OpCodes.Ldarg,iArgs+1);
|
||||
if(paramTypes[iArgs].IsByRef)
|
||||
{
|
||||
if(paramTypes[iArgs].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[iArgs].GetElementType());
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs].GetElementType());
|
||||
}
|
||||
else generator.Emit(OpCodes.Ldind_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(paramTypes[iArgs].IsValueType)
|
||||
generator.Emit(OpCodes.Box,paramTypes[iArgs]);
|
||||
}
|
||||
generator.Emit(OpCodes.Stelem_Ref);
|
||||
iInArgs++;
|
||||
}
|
||||
}
|
||||
// Gets the function the method will delegate to by calling
|
||||
// the getTableFunction method of class LuaClassHelper
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldfld,luaTableField);
|
||||
generator.Emit(OpCodes.Ldstr,method.Name);
|
||||
generator.Emit(OpCodes.Call,classHelper.GetMethod("getTableFunction"));
|
||||
Label lab1=generator.DefineLabel();
|
||||
generator.Emit(OpCodes.Dup);
|
||||
generator.Emit(OpCodes.Brtrue_S,lab1);
|
||||
// Function does not exist, call base method
|
||||
generator.Emit(OpCodes.Pop);
|
||||
if(!method.IsAbstract)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
for(int i=0;i<paramTypes.Length;i++)
|
||||
generator.Emit(OpCodes.Ldarg,i+1);
|
||||
generator.Emit(OpCodes.Call,method);
|
||||
if(returnType == typeof(void))
|
||||
generator.Emit(OpCodes.Pop);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
generator.Emit(OpCodes.Ldnull);
|
||||
} else
|
||||
generator.Emit(OpCodes.Ldnull);
|
||||
Label lab2=generator.DefineLabel();
|
||||
generator.Emit(OpCodes.Br_S,lab2);
|
||||
generator.MarkLabel(lab1);
|
||||
// Function exists, call using method callFunction of LuaClassHelper
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldfld,returnTypesField);
|
||||
generator.Emit(OpCodes.Ldc_I4,methodIndex);
|
||||
generator.Emit(OpCodes.Ldelem_Ref);
|
||||
generator.Emit(OpCodes.Ldloc_1);
|
||||
generator.Emit(OpCodes.Ldloc_2);
|
||||
generator.Emit(OpCodes.Call,classHelper.GetMethod("callFunction"));
|
||||
generator.MarkLabel(lab2);
|
||||
// Stores the function return value
|
||||
if(returnType == typeof(void))
|
||||
{
|
||||
generator.Emit(OpCodes.Pop);
|
||||
generator.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
else if(returnType.IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Unbox,returnType);
|
||||
generator.Emit(OpCodes.Ldobj,returnType);
|
||||
}
|
||||
else generator.Emit(OpCodes.Castclass,returnType);
|
||||
generator.Emit(OpCodes.Stloc_3);
|
||||
// Sets return values of out and ref parameters
|
||||
for(int i=0;i<refArgs.Length;i++)
|
||||
{
|
||||
generator.Emit(OpCodes.Ldarg,refArgs[i]+1);
|
||||
generator.Emit(OpCodes.Ldloc_0);
|
||||
generator.Emit(OpCodes.Ldc_I4,refArgs[i]);
|
||||
generator.Emit(OpCodes.Ldelem_Ref);
|
||||
if(paramTypes[refArgs[i]].GetElementType().IsValueType)
|
||||
{
|
||||
generator.Emit(OpCodes.Unbox,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Ldobj,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Stobj,paramTypes[refArgs[i]].GetElementType());
|
||||
}
|
||||
else
|
||||
{
|
||||
generator.Emit(OpCodes.Castclass,paramTypes[refArgs[i]].GetElementType());
|
||||
generator.Emit(OpCodes.Stind_Ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns
|
||||
if(!(returnType == typeof(void)))
|
||||
generator.Emit(OpCodes.Ldloc_3);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets an event handler for the event type that delegates to the eventHandler Lua function.
|
||||
* Caches the generated type.
|
||||
*/
|
||||
public LuaEventHandler GetEvent(Type eventHandlerType, LuaFunction eventHandler)
|
||||
{
|
||||
Type eventConsumerType;
|
||||
if (eventHandlerCollection.ContainsKey(eventHandlerType))
|
||||
{
|
||||
eventConsumerType=eventHandlerCollection[eventHandlerType];
|
||||
}
|
||||
else
|
||||
{
|
||||
eventConsumerType=GenerateEvent(eventHandlerType);
|
||||
eventHandlerCollection[eventHandlerType] = eventConsumerType;
|
||||
}
|
||||
LuaEventHandler luaEventHandler=(LuaEventHandler)Activator.CreateInstance(eventConsumerType);
|
||||
luaEventHandler.handler=eventHandler;
|
||||
return luaEventHandler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a delegate with delegateType that calls the luaFunc Lua function
|
||||
* Caches the generated type.
|
||||
*/
|
||||
public Delegate GetDelegate(Type delegateType, LuaFunction luaFunc)
|
||||
{
|
||||
List<Type> returnTypes=new List<Type>();
|
||||
Type luaDelegateType;
|
||||
if (delegateCollection.ContainsKey(delegateType))
|
||||
{
|
||||
luaDelegateType=delegateCollection[delegateType];
|
||||
}
|
||||
else
|
||||
{
|
||||
luaDelegateType=GenerateDelegate(delegateType);
|
||||
delegateCollection[delegateType] = luaDelegateType;
|
||||
}
|
||||
MethodInfo methodInfo=delegateType.GetMethod("Invoke");
|
||||
returnTypes.Add(methodInfo.ReturnType);
|
||||
foreach(ParameterInfo paramInfo in methodInfo.GetParameters())
|
||||
if(paramInfo.ParameterType.IsByRef)
|
||||
returnTypes.Add(paramInfo.ParameterType);
|
||||
LuaDelegate luaDelegate=(LuaDelegate)Activator.CreateInstance(luaDelegateType);
|
||||
luaDelegate.function=luaFunc;
|
||||
luaDelegate.returnTypes=returnTypes.ToArray();
|
||||
return Delegate.CreateDelegate(delegateType,luaDelegate,"CallFunction");
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets an instance of an implementation of the klass interface or
|
||||
* subclass of klass that delegates public virtual methods to the
|
||||
* luaTable table.
|
||||
* Caches the generated type.
|
||||
*/
|
||||
public object GetClassInstance(Type klass, LuaTable luaTable)
|
||||
{
|
||||
LuaClassType luaClassType;
|
||||
if (classCollection.ContainsKey(klass))
|
||||
{
|
||||
luaClassType=classCollection[klass];
|
||||
}
|
||||
else
|
||||
{
|
||||
luaClassType=new LuaClassType();
|
||||
GenerateClass(klass,out luaClassType.klass,out luaClassType.returnTypes,luaTable);
|
||||
classCollection[klass] = luaClassType;
|
||||
}
|
||||
return Activator.CreateInstance(luaClassType.klass,new object[] {luaTable,luaClassType.returnTypes});
|
||||
}
|
||||
}
|
||||
}
|
||||
761
LuaInterface/Lua.cs
Normal file
761
LuaInterface/Lua.cs
Normal file
@@ -0,0 +1,761 @@
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using OpenRA;
|
||||
using System.Text;
|
||||
|
||||
/*
|
||||
* Main class of LuaInterface
|
||||
* Object-oriented wrapper to Lua API
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*
|
||||
* // steffenj: important changes in Lua class:
|
||||
* - removed all Open*Lib() functions
|
||||
* - all libs automatically open in the Lua class constructor (just assign nil to unwanted libs)
|
||||
* */
|
||||
public class Lua : IDisposable
|
||||
{
|
||||
/*readonly */ IntPtr luaState;
|
||||
ObjectTranslator translator;
|
||||
|
||||
LuaFunctionCallback panicCallback;
|
||||
LuaCSFunction tracebackFunction;
|
||||
// lockCallback, unlockCallback; used by debug code commented out for now
|
||||
|
||||
public Lua()
|
||||
{
|
||||
luaState = LuaDLL.luaL_newstate(); // steffenj: Lua 5.1.1 API change (lua_open is gone)
|
||||
|
||||
// Load libraries
|
||||
LuaDLL.luaL_openlibs(luaState);
|
||||
|
||||
// Add LuaInterface marker
|
||||
LuaDLL.lua_pushstring(luaState, "LUAINTERFACE LOADED");
|
||||
LuaDLL.lua_pushboolean(luaState, true);
|
||||
LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
|
||||
|
||||
translator=new ObjectTranslator(this,luaState);
|
||||
|
||||
tracebackFunction = new LuaCSFunction(traceback);
|
||||
|
||||
// We need to keep this in a managed reference so the delegate doesn't get garbage collected
|
||||
panicCallback = new LuaFunctionCallback(PanicCallback);
|
||||
LuaDLL.lua_atpanic(luaState, panicCallback);
|
||||
}
|
||||
|
||||
private bool _StatePassed;
|
||||
|
||||
/*
|
||||
* CAUTION: LuaInterface.Lua instances can't share the same lua state!
|
||||
*/
|
||||
public Lua( Int64 luaState )
|
||||
{
|
||||
// Check for existing LuaInterface marker
|
||||
IntPtr lState = new IntPtr(luaState);
|
||||
LuaDLL.lua_pushstring(lState, "LUAINTERFACE LOADED");
|
||||
LuaDLL.lua_gettable(lState, (int)LuaIndexes.LUA_REGISTRYINDEX);
|
||||
|
||||
if(LuaDLL.lua_toboolean(lState,-1))
|
||||
{
|
||||
LuaDLL.lua_settop(lState,-2);
|
||||
throw new LuaException("There is already a LuaInterface.Lua instance associated with this Lua state");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add LuaInterface marker
|
||||
LuaDLL.lua_settop(lState,-2);
|
||||
LuaDLL.lua_pushstring(lState, "LUAINTERFACE LOADED");
|
||||
LuaDLL.lua_pushboolean(lState, true);
|
||||
LuaDLL.lua_settable(lState, (int)LuaIndexes.LUA_REGISTRYINDEX);
|
||||
|
||||
this.luaState = lState;
|
||||
LuaDLL.lua_pushvalue(lState, (int)LuaIndexes.LUA_GLOBALSINDEX);
|
||||
|
||||
translator = new ObjectTranslator(this, this.luaState);
|
||||
}
|
||||
|
||||
_StatePassed = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_StatePassed)
|
||||
return;
|
||||
|
||||
if (luaState != IntPtr.Zero)
|
||||
LuaDLL.lua_close(luaState);
|
||||
//luaState = IntPtr.Zero; <- suggested by Christopher Cebulski http://luaforge.net/forum/forum.php?thread_id=44593&forum_id=146
|
||||
}
|
||||
|
||||
static int PanicCallback(IntPtr luaState)
|
||||
{
|
||||
// string desc = LuaDLL.lua_tostring(luaState, 1);
|
||||
string reason = String.Format("unprotected error in call to Lua API ({0})", LuaDLL.lua_tostring(luaState, -1));
|
||||
|
||||
// lua_tostring(L, -1);
|
||||
|
||||
throw new LuaException(reason);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app
|
||||
/// </summary>
|
||||
/// <exception cref="LuaScriptException">Thrown if the script caused an exception</exception>
|
||||
void ThrowExceptionFromError(int oldTop)
|
||||
{
|
||||
object err = translator.getObject(luaState, -1);
|
||||
LuaDLL.lua_settop(luaState, oldTop);
|
||||
|
||||
// A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
|
||||
LuaScriptException luaEx = err as LuaScriptException;
|
||||
if (luaEx != null) throw luaEx;
|
||||
|
||||
// A non-wrapped Lua error (best interpreted as a string) - wrap it and throw it
|
||||
if (err == null) err = "Unknown Lua Error";
|
||||
throw new LuaScriptException(err.ToString(), "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Convert C# exceptions into Lua errors
|
||||
/// </summary>
|
||||
/// <returns>num of things on stack</returns>
|
||||
/// <param name="e">null for no pending exception</param>
|
||||
internal int SetPendingException(Exception e)
|
||||
{
|
||||
Exception caughtExcept = e;
|
||||
|
||||
if (caughtExcept != null)
|
||||
{
|
||||
translator.throwError(luaState, caughtExcept);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
private bool executing;
|
||||
|
||||
/// <summary>
|
||||
/// True while a script is being executed
|
||||
/// </summary>
|
||||
public bool IsExecuting { get { return executing; } }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="chunk"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public LuaFunction LoadString(string chunk, string name)
|
||||
{
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
|
||||
executing = true;
|
||||
|
||||
// Somehow, on OS X, we need to use the UTF-8 byte count rather than the string length
|
||||
// Adapted for OpenRA
|
||||
var chunkLength = Platform.CurrentPlatform != PlatformType.Windows
|
||||
? Encoding.UTF8.GetByteCount(chunk)
|
||||
: chunk.Length;
|
||||
|
||||
try
|
||||
{
|
||||
if (LuaDLL.luaL_loadbuffer(luaState, chunk, chunkLength, name) != 0)
|
||||
ThrowExceptionFromError(oldTop);
|
||||
}
|
||||
finally { executing = false; }
|
||||
|
||||
LuaFunction result = translator.getFunction(luaState, -1);
|
||||
translator.popValues(luaState, oldTop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <returns></returns>
|
||||
public LuaFunction LoadFile(string fileName)
|
||||
{
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
if (LuaDLL.luaL_loadfile(luaState, fileName) != 0)
|
||||
ThrowExceptionFromError(oldTop);
|
||||
|
||||
LuaFunction result = translator.getFunction(luaState, -1);
|
||||
translator.popValues(luaState, oldTop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Excutes a Lua chunk and returns all the chunk's return
|
||||
* values in an array
|
||||
*/
|
||||
public object[] DoString(string chunk)
|
||||
{
|
||||
return DoString(chunk,"chunk");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a Lua chnk and returns all the chunk's return values in an array.
|
||||
/// </summary>
|
||||
/// <param name="chunk">Chunk to execute</param>
|
||||
/// <param name="chunkName">Name to associate with the chunk</param>
|
||||
/// <returns></returns>
|
||||
public object[] DoString(string chunk, string chunkName)
|
||||
{
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
executing = true;
|
||||
|
||||
// Somehow, on OS X, we need to use the UTF-8 byte count rather than the string length
|
||||
// Adapted for OpenRA
|
||||
var chunkLength = Platform.CurrentPlatform != PlatformType.Windows
|
||||
? Encoding.UTF8.GetByteCount(chunk)
|
||||
: chunk.Length;
|
||||
|
||||
if (LuaDLL.luaL_loadbuffer(luaState, chunk, chunkLength, chunkName) == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LuaDLL.lua_pcall(luaState, 0, -1, 0) == 0)
|
||||
return translator.popValues(luaState, oldTop);
|
||||
else
|
||||
ThrowExceptionFromError(oldTop);
|
||||
}
|
||||
finally { executing = false; }
|
||||
}
|
||||
else
|
||||
ThrowExceptionFromError(oldTop);
|
||||
|
||||
return null; // Never reached - keeps compiler happy
|
||||
}
|
||||
|
||||
private int traceback(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_getglobal(luaState,"debug");
|
||||
LuaDLL.lua_getfield(luaState,-1,"traceback");
|
||||
LuaDLL.lua_pushvalue(luaState,1);
|
||||
LuaDLL.lua_pushnumber(luaState,2);
|
||||
LuaDLL.lua_call (luaState,2,1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Excutes a Lua file and returns all the chunk's return
|
||||
* values in an array
|
||||
*/
|
||||
public object[] DoFile(string fileName)
|
||||
{
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,tracebackFunction);
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
if(LuaDLL.luaL_loadfile(luaState,fileName)==0)
|
||||
{
|
||||
executing = true;
|
||||
try
|
||||
{
|
||||
if (LuaDLL.lua_pcall(luaState, 0, -1, -2) == 0)
|
||||
return translator.popValues(luaState, oldTop);
|
||||
else
|
||||
ThrowExceptionFromError(oldTop);
|
||||
}
|
||||
finally { executing = false; }
|
||||
}
|
||||
else
|
||||
ThrowExceptionFromError(oldTop);
|
||||
|
||||
return null; // Never reached - keeps compiler happy
|
||||
}
|
||||
|
||||
|
||||
public void CollectGarbage()
|
||||
{
|
||||
LuaDLL.lua_gc( luaState, LuaGCOptions.LUA_GCCOLLECT, 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
* Indexer for global variables from the LuaInterpreter
|
||||
* Supports navigation of tables by using . operator
|
||||
*/
|
||||
public object this[string fullPath]
|
||||
{
|
||||
get
|
||||
{
|
||||
object returnValue=null;
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
string[] path=fullPath.Split(new char[] { '.' });
|
||||
LuaDLL.lua_getglobal(luaState,path[0]);
|
||||
returnValue=translator.getObject(luaState,-1);
|
||||
if(path.Length>1)
|
||||
{
|
||||
string[] remainingPath=new string[path.Length-1];
|
||||
Array.Copy(path,1,remainingPath,0,path.Length-1);
|
||||
returnValue=getObject(remainingPath);
|
||||
}
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return returnValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
string[] path=fullPath.Split(new char[] { '.' });
|
||||
if(path.Length==1)
|
||||
{
|
||||
translator.push(luaState,value);
|
||||
LuaDLL.lua_setglobal(luaState,fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaDLL.lua_getglobal(luaState,path[0]);
|
||||
string[] remainingPath=new string[path.Length-1];
|
||||
Array.Copy(path,1,remainingPath,0,path.Length-1);
|
||||
setObject(remainingPath,value);
|
||||
}
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
|
||||
// Globals auto-complete
|
||||
if (value == null)
|
||||
{
|
||||
// Remove now obsolete entries
|
||||
globals.Remove(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new entries
|
||||
if (!globals.Contains(fullPath))
|
||||
registerGlobal(fullPath, value.GetType(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Globals auto-complete
|
||||
private readonly List<string> globals = new List<string>();
|
||||
private bool globalsSorted;
|
||||
|
||||
/// <summary>
|
||||
/// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance
|
||||
/// </summary>
|
||||
/// <remarks>Members of globals are also listed. The formatting is optimized for text input auto-completion.</remarks>
|
||||
public IEnumerable<string> Globals
|
||||
{
|
||||
get
|
||||
{
|
||||
// Only sort list when necessary
|
||||
if (!globalsSorted)
|
||||
{
|
||||
globals.Sort();
|
||||
globalsSorted = true;
|
||||
}
|
||||
|
||||
return globals;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an entry to <see cref="globals"/> (recursivley handles 2 levels of members)
|
||||
/// </summary>
|
||||
/// <param name="path">The index accessor path ot the entry</param>
|
||||
/// <param name="type">The type of the entry</param>
|
||||
/// <param name="recursionCounter">How deep have we gone with recursion?</param>
|
||||
private void registerGlobal(string path, Type type, int recursionCounter)
|
||||
{
|
||||
// If the type is a global method, list it directly
|
||||
if (type == typeof(LuaCSFunction))
|
||||
{
|
||||
// Format for easy method invocation
|
||||
globals.Add(path + "(");
|
||||
}
|
||||
// If the type is a class or an interface and recursion hasn't been running too long, list the members
|
||||
else if ((type.IsClass || type.IsInterface) && type != typeof(string) && recursionCounter < 2)
|
||||
{
|
||||
#region Methods
|
||||
foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (
|
||||
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
|
||||
(method.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
|
||||
(method.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0) &&
|
||||
// Exclude some generic .NET methods that wouldn't be very usefull in Lua
|
||||
method.Name != "GetType" && method.Name != "GetHashCode" && method.Name != "Equals" &&
|
||||
method.Name != "ToString" && method.Name != "Clone" && method.Name != "Dispose" &&
|
||||
method.Name != "GetEnumerator" && method.Name != "CopyTo" &&
|
||||
!method.Name.StartsWith("get_", StringComparison.Ordinal) &&
|
||||
!method.Name.StartsWith("set_", StringComparison.Ordinal) &&
|
||||
!method.Name.StartsWith("add_", StringComparison.Ordinal) &&
|
||||
!method.Name.StartsWith("remove_", StringComparison.Ordinal))
|
||||
{
|
||||
// Format for easy method invocation
|
||||
string command = path + ":" + method.Name + "(";
|
||||
if (method.GetParameters().Length == 0) command += ")";
|
||||
globals.Add(command);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (
|
||||
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
|
||||
(field.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
|
||||
(field.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0))
|
||||
{
|
||||
// Go into recursion for members
|
||||
registerGlobal(path + "." + field.Name, field.FieldType, recursionCounter + 1);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (
|
||||
// Check that the LuaHideAttribute and LuaGlobalAttribute were not applied
|
||||
(property.GetCustomAttributes(typeof(LuaHideAttribute), false).Length == 0) &&
|
||||
(property.GetCustomAttributes(typeof(LuaGlobalAttribute), false).Length == 0)
|
||||
// Exclude some generic .NET properties that wouldn't be very usefull in Lua
|
||||
&& property.Name != "Item")
|
||||
{
|
||||
// Go into recursion for members
|
||||
registerGlobal(path + "." + property.Name, property.PropertyType, recursionCounter + 1);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
// Otherwise simply add the element to the list
|
||||
else globals.Add(path);
|
||||
|
||||
// List will need to be sorted on next access
|
||||
globalsSorted = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/*
|
||||
* Navigates a table in the top of the stack, returning
|
||||
* the value of the specified field
|
||||
*/
|
||||
internal object getObject(string[] remainingPath)
|
||||
{
|
||||
object returnValue=null;
|
||||
for(int i=0;i<remainingPath.Length;i++)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,remainingPath[i]);
|
||||
LuaDLL.lua_gettable(luaState,-2);
|
||||
returnValue=translator.getObject(luaState,-1);
|
||||
if(returnValue==null) break;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
/*
|
||||
* Gets a numeric global variable
|
||||
*/
|
||||
public double GetNumber(string fullPath)
|
||||
{
|
||||
return (double)this[fullPath];
|
||||
}
|
||||
/*
|
||||
* Gets a string global variable
|
||||
*/
|
||||
public string GetString(string fullPath)
|
||||
{
|
||||
return (string)this[fullPath];
|
||||
}
|
||||
/*
|
||||
* Gets a table global variable
|
||||
*/
|
||||
public LuaTable GetTable(string fullPath)
|
||||
{
|
||||
return (LuaTable)this[fullPath];
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a table global variable as an object implementing
|
||||
* the interfaceType interface
|
||||
*/
|
||||
public object GetTable(Type interfaceType, string fullPath)
|
||||
{
|
||||
return CodeGeneration.Instance.GetClassInstance(interfaceType, GetTable(fullPath));
|
||||
}
|
||||
/*
|
||||
* Gets a function global variable
|
||||
*/
|
||||
public LuaFunction GetFunction(string fullPath)
|
||||
{
|
||||
object obj = this[fullPath];
|
||||
return (obj is LuaCSFunction ? new LuaFunction((LuaCSFunction)obj, this) : (LuaFunction)obj);
|
||||
}
|
||||
/*
|
||||
* Gets a function global variable as a delegate of
|
||||
* type delegateType
|
||||
*/
|
||||
public Delegate GetFunction(Type delegateType,string fullPath)
|
||||
{
|
||||
return CodeGeneration.Instance.GetDelegate(delegateType, GetFunction(fullPath));
|
||||
}
|
||||
/*
|
||||
* Calls the object as a function with the provided arguments,
|
||||
* returning the function's returned values inside an array
|
||||
*/
|
||||
internal object[] callFunction(object function,object[] args)
|
||||
{
|
||||
return callFunction(function, args, null);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Calls the object as a function with the provided arguments and
|
||||
* casting returned values to the types in returnTypes before returning
|
||||
* them in an array
|
||||
*/
|
||||
internal object[] callFunction(object function,object[] args,Type[] returnTypes)
|
||||
{
|
||||
int nArgs=0;
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
if(!LuaDLL.lua_checkstack(luaState,args.Length+6))
|
||||
throw new LuaException("Lua stack overflow");
|
||||
translator.push(luaState,function);
|
||||
if(args!=null)
|
||||
{
|
||||
nArgs=args.Length;
|
||||
for(int i=0;i<args.Length;i++)
|
||||
{
|
||||
translator.push(luaState,args[i]);
|
||||
}
|
||||
}
|
||||
executing = true;
|
||||
try
|
||||
{
|
||||
int error = LuaDLL.lua_pcall(luaState, nArgs, -1, 0);
|
||||
if (error != 0)
|
||||
ThrowExceptionFromError(oldTop);
|
||||
}
|
||||
finally { executing = false; }
|
||||
|
||||
if(returnTypes != null)
|
||||
return translator.popValues(luaState,oldTop,returnTypes);
|
||||
else
|
||||
return translator.popValues(luaState, oldTop);
|
||||
}
|
||||
/*
|
||||
* Navigates a table to set the value of one of its fields
|
||||
*/
|
||||
internal void setObject(string[] remainingPath, object val)
|
||||
{
|
||||
for(int i=0; i<remainingPath.Length-1;i++)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,remainingPath[i]);
|
||||
LuaDLL.lua_gettable(luaState,-2);
|
||||
}
|
||||
LuaDLL.lua_pushstring(luaState,remainingPath[remainingPath.Length-1]);
|
||||
translator.push(luaState,val);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
}
|
||||
/*
|
||||
* Creates a new table as a global variable or as a field
|
||||
* inside an existing table
|
||||
*/
|
||||
public void NewTable(string fullPath)
|
||||
{
|
||||
string[] path=fullPath.Split(new char[] { '.' });
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
if(path.Length==1)
|
||||
{
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_setglobal(luaState,fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaDLL.lua_getglobal(luaState,path[0]);
|
||||
for(int i=1; i<path.Length-1;i++)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,path[i]);
|
||||
LuaDLL.lua_gettable(luaState,-2);
|
||||
}
|
||||
LuaDLL.lua_pushstring(luaState,path[path.Length-1]);
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
}
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new unnamed table
|
||||
*/
|
||||
public LuaTable NewTable()
|
||||
{
|
||||
int oldTop = LuaDLL.lua_gettop( luaState );
|
||||
LuaDLL.lua_newtable( luaState );
|
||||
LuaTable table = translator.getTable( luaState,-1 );
|
||||
LuaDLL.lua_settop( luaState, oldTop );
|
||||
|
||||
table.IsOrphaned = true;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
public ListDictionary GetTableDict(LuaTable table)
|
||||
{
|
||||
ListDictionary dict = new ListDictionary();
|
||||
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
translator.push(luaState, table);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
while (LuaDLL.lua_next(luaState, -2) != 0)
|
||||
{
|
||||
dict[translator.getObject(luaState, -2)] = translator.getObject(luaState, -1);
|
||||
LuaDLL.lua_settop(luaState, -2);
|
||||
}
|
||||
LuaDLL.lua_settop(luaState, oldTop);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lets go of a previously allocated reference to a table, function
|
||||
* or userdata
|
||||
*/
|
||||
|
||||
internal void dispose(int reference)
|
||||
{
|
||||
if (luaState != IntPtr.Zero) //Fix submitted by Qingrui Li
|
||||
LuaDLL.lua_unref(luaState,reference);
|
||||
}
|
||||
/*
|
||||
* Gets a field of the table corresponding to the provided reference
|
||||
* using rawget (do not use metatables)
|
||||
*/
|
||||
internal object rawGetObject(int reference,string field)
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,reference);
|
||||
LuaDLL.lua_pushstring(luaState,field);
|
||||
LuaDLL.lua_rawget(luaState,-2);
|
||||
object obj=translator.getObject(luaState,-1);
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return obj;
|
||||
}
|
||||
/*
|
||||
* Gets a field of the table or userdata corresponding to the provided reference
|
||||
*/
|
||||
internal object getObject(int reference,string field)
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,reference);
|
||||
object returnValue=getObject(field.Split(new char[] {'.'}));
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return returnValue;
|
||||
}
|
||||
/*
|
||||
* Gets a numeric field of the table or userdata corresponding the the provided reference
|
||||
*/
|
||||
internal object getObject(int reference,object field)
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,reference);
|
||||
translator.push(luaState,field);
|
||||
LuaDLL.lua_gettable(luaState,-2);
|
||||
object returnValue=translator.getObject(luaState,-1);
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return returnValue;
|
||||
}
|
||||
/*
|
||||
* Sets a field of the table or userdata corresponding the the provided reference
|
||||
* to the provided value
|
||||
*/
|
||||
internal void setObject(int reference, string field, object val)
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,reference);
|
||||
setObject(field.Split(new char[] {'.'}),val);
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
}
|
||||
/*
|
||||
* Sets a numeric field of the table or userdata corresponding the the provided reference
|
||||
* to the provided value
|
||||
*/
|
||||
internal void setObject(int reference, object field, object val)
|
||||
{
|
||||
int oldTop=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,reference);
|
||||
translator.push(luaState,field);
|
||||
translator.push(luaState,val);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers an object's method as a Lua function (global or table field)
|
||||
* The method may have any signature
|
||||
*/
|
||||
public LuaFunction RegisterFunction(string path, object target, MethodBase function /*MethodInfo function*/) //CP: Fix for struct constructor by Alexander Kappner (link: http://luaforge.net/forum/forum.php?thread_id=2859&forum_id=145)
|
||||
{
|
||||
// We leave nothing on the stack when we are done
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
|
||||
LuaMethodWrapper wrapper=new LuaMethodWrapper(translator,target,function.DeclaringType,function);
|
||||
translator.push(luaState,new LuaCSFunction(wrapper.call));
|
||||
|
||||
this[path]=translator.getObject(luaState,-1);
|
||||
LuaFunction f = GetFunction(path);
|
||||
|
||||
LuaDLL.lua_settop(luaState, oldTop);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compares the two values referenced by ref1 and ref2 for equality
|
||||
*/
|
||||
internal bool compareRef(int ref1, int ref2)
|
||||
{
|
||||
int top=LuaDLL.lua_gettop(luaState);
|
||||
LuaDLL.lua_getref(luaState,ref1);
|
||||
LuaDLL.lua_getref(luaState,ref2);
|
||||
int equal=LuaDLL.lua_equal(luaState,-1,-2);
|
||||
LuaDLL.lua_settop(luaState,top);
|
||||
return (equal!=0);
|
||||
}
|
||||
|
||||
internal void pushCSFunction(LuaCSFunction function)
|
||||
{
|
||||
translator.pushFunction(luaState,function);
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (translator != null)
|
||||
{
|
||||
translator.pendingEvents.Dispose();
|
||||
translator = null;
|
||||
}
|
||||
|
||||
this.Close();
|
||||
System.GC.Collect();
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
56
LuaInterface/LuaBase.cs
Normal file
56
LuaInterface/LuaBase.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class to provide consistent disposal flow across lua objects. Uses code provided by Yves Duhoux and suggestions by Hans Schmeidenbacher and Qingrui Li
|
||||
/// </summary>
|
||||
public abstract class LuaBase : IDisposable
|
||||
{
|
||||
private bool _Disposed;
|
||||
protected int _Reference;
|
||||
protected Lua _Interpreter;
|
||||
|
||||
~LuaBase()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public virtual void Dispose(bool disposeManagedResources)
|
||||
{
|
||||
if (!_Disposed)
|
||||
{
|
||||
if (disposeManagedResources)
|
||||
{
|
||||
if (_Reference != 0)
|
||||
_Interpreter.dispose(_Reference);
|
||||
}
|
||||
_Interpreter = null;
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if (o is LuaBase)
|
||||
{
|
||||
LuaBase l = (LuaBase)o;
|
||||
return _Interpreter.compareRef(l._Reference, _Reference);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _Reference;
|
||||
}
|
||||
}
|
||||
}
|
||||
456
LuaInterface/LuaDLL.cs
Normal file
456
LuaInterface/LuaDLL.cs
Normal file
@@ -0,0 +1,456 @@
|
||||
namespace LuaInterface
|
||||
{
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
|
||||
/*
|
||||
* Lua types for the API, returned by lua_type function
|
||||
*/
|
||||
public enum LuaTypes
|
||||
{
|
||||
LUA_TNONE=-1,
|
||||
LUA_TNIL=0,
|
||||
LUA_TNUMBER=3,
|
||||
LUA_TSTRING=4,
|
||||
LUA_TBOOLEAN=1,
|
||||
LUA_TTABLE=5,
|
||||
LUA_TFUNCTION=6,
|
||||
LUA_TUSERDATA=7,
|
||||
LUA_TLIGHTUSERDATA=2
|
||||
}
|
||||
|
||||
// steffenj: BEGIN lua garbage collector options
|
||||
/*
|
||||
* Lua Garbage Collector options (param "what")
|
||||
*/
|
||||
public enum LuaGCOptions
|
||||
{
|
||||
LUA_GCSTOP = 0,
|
||||
LUA_GCRESTART = 1,
|
||||
LUA_GCCOLLECT = 2,
|
||||
LUA_GCCOUNT = 3,
|
||||
LUA_GCCOUNTB = 4,
|
||||
LUA_GCSTEP = 5,
|
||||
LUA_GCSETPAUSE = 6,
|
||||
LUA_GCSETSTEPMUL = 7,
|
||||
}
|
||||
/*
|
||||
sealed class LuaGCOptions
|
||||
{
|
||||
public static int LUA_GCSTOP = 0;
|
||||
public static int LUA_GCRESTART = 1;
|
||||
public static int LUA_GCCOLLECT = 2;
|
||||
public static int LUA_GCCOUNT = 3;
|
||||
public static int LUA_GCCOUNTB = 4;
|
||||
public static int LUA_GCSTEP = 5;
|
||||
public static int LUA_GCSETPAUSE = 6;
|
||||
public static int LUA_GCSETSTEPMUL = 7;
|
||||
};
|
||||
*/
|
||||
// steffenj: END lua garbage collector options
|
||||
|
||||
/*
|
||||
* Special stack indexes
|
||||
*/
|
||||
sealed class LuaIndexes
|
||||
{
|
||||
public static int LUA_REGISTRYINDEX=-10000;
|
||||
public static int LUA_ENVIRONINDEX=-10001; // steffenj: added environindex
|
||||
public static int LUA_GLOBALSINDEX=-10002; // steffenj: globalsindex previously was -10001
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure used by the chunk reader
|
||||
*/
|
||||
[ StructLayout( LayoutKind.Sequential )]
|
||||
public struct ReaderInfo
|
||||
{
|
||||
public String chunkData;
|
||||
public bool finished;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delegate for functions passed to Lua as function pointers
|
||||
*/
|
||||
public delegate int LuaCSFunction(IntPtr luaState);
|
||||
|
||||
/*
|
||||
* Delegate for chunk readers used with lua_load
|
||||
*/
|
||||
public delegate string LuaChunkReader(IntPtr luaState,ref ReaderInfo data,ref uint size);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used to handle Lua panics
|
||||
/// </summary>
|
||||
/// <param name="luaState"></param>
|
||||
/// <returns></returns>
|
||||
public delegate int LuaFunctionCallback(IntPtr luaState);
|
||||
|
||||
/*
|
||||
* P/Invoke wrapper of the Lua API
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*
|
||||
* // steffenj: noteable changes in the LuaDLL API:
|
||||
* - luaopen_* functions are gone
|
||||
* (however Lua class constructor already calls luaL_openlibs now, so just remove these calls)
|
||||
* - deprecated functions: lua_open, lua_strlen, lua_dostring
|
||||
* (they still work but may be removed with next Lua version)
|
||||
*
|
||||
* list of functions of the Lua 5.1.1 C API that are not in LuaDLL
|
||||
* i thought this may come in handy for the next Lua version upgrade and for anyone to see
|
||||
* what the differences are in the APIs (C API vs LuaDLL API)
|
||||
lua_concat (use System.String concatenation or similar)
|
||||
lua_cpcall (no point in calling C functions)
|
||||
lua_dump (would write to unmanaged memory via lua_Writer)
|
||||
lua_getallocf (no C functions/pointers)
|
||||
lua_isthread (no threads)
|
||||
lua_newstate (use luaL_newstate)
|
||||
lua_newthread (no threads)
|
||||
lua_pushcclosure (no C functions/pointers)
|
||||
lua_pushcfunction (no C functions/pointers)
|
||||
lua_pushfstring (use lua_pushstring)
|
||||
lua_pushthread (no threads)
|
||||
lua_pushvfstring (use lua_pushstring)
|
||||
lua_register (all libs already opened, use require in scripts for external libs)
|
||||
lua_resume (no threads)
|
||||
lua_setallocf (no C functions/pointers)
|
||||
lua_status (no threads)
|
||||
lua_tointeger (use System.Convert)
|
||||
lua_tolstring (use lua_tostring)
|
||||
lua_topointer (no C functions/pointers)
|
||||
lua_tothread (no threads)
|
||||
lua_xmove (no threads)
|
||||
lua_yield (no threads)
|
||||
|
||||
luaL_add* (use System.String concatenation or similar)
|
||||
luaL_argcheck (function argument checking unnecessary)
|
||||
luaL_argerror (function argument checking unnecessary)
|
||||
luaL_buffinit (use System.String concatenation or similar)
|
||||
luaL_checkany (function argument checking unnecessary)
|
||||
luaL_checkint (function argument checking unnecessary)
|
||||
luaL_checkinteger (function argument checking unnecessary)
|
||||
luaL_checklong (function argument checking unnecessary)
|
||||
luaL_checklstring (function argument checking unnecessary)
|
||||
luaL_checknumber (function argument checking unnecessary)
|
||||
luaL_checkoption (function argument checking unnecessary)
|
||||
luaL_checkstring (function argument checking unnecessary)
|
||||
luaL_checktype (function argument checking unnecessary)
|
||||
luaL_prepbuffer (use System.String concatenation or similar)
|
||||
luaL_pushresult (use System.String concatenation or similar)
|
||||
luaL_register (all libs already opened, use require in scripts for external libs)
|
||||
luaL_typerror (function argument checking unnecessary)
|
||||
|
||||
(complete lua_Debug interface omitted)
|
||||
lua_gethook***
|
||||
lua_getinfo
|
||||
lua_getlocal
|
||||
lua_getstack
|
||||
lua_getupvalue
|
||||
lua_sethook
|
||||
lua_setlocal
|
||||
lua_setupvalue
|
||||
*/
|
||||
public class LuaDLL
|
||||
{
|
||||
// for debugging
|
||||
// const string BASEPATH = @"C:\development\software\dotnet\tools\PulseRecognizer\PulseRecognizer\bin\Debug\";
|
||||
// const string BASEPATH = @"C:\development\software\ThirdParty\lua\Built\";
|
||||
const string LUADLL = "lua51.dll";
|
||||
const string LUALIBDLL = LUADLL;
|
||||
const string STUBDLL = LUADLL;
|
||||
|
||||
// steffenj: BEGIN additional Lua API functions new in Lua 5.1
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_gc(IntPtr luaState, LuaGCOptions what, int data);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern string lua_typename(IntPtr luaState, LuaTypes type);
|
||||
public static string luaL_typename(IntPtr luaState, int stackPos)
|
||||
{
|
||||
return LuaDLL.lua_typename(luaState, LuaDLL.lua_type(luaState, stackPos));
|
||||
}
|
||||
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void luaL_error(IntPtr luaState, string message);
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern string luaL_gsub(IntPtr luaState, string str, string pattern, string replacement);
|
||||
|
||||
// the functions below are still untested
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_getfenv(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_isfunction(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_islightuserdata(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_istable(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_isuserdata(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_lessthan(IntPtr luaState, int stackPos1, int stackPos2);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_rawequal(IntPtr luaState, int stackPos1, int stackPos2);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_setfenv(IntPtr luaState, int stackPos);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_setfield(IntPtr luaState, int stackPos, string name);
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int luaL_callmeta(IntPtr luaState, int stackPos, string name);
|
||||
// steffenj: END additional Lua API functions new in Lua 5.1
|
||||
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (lua_open replaced by luaL_newstate)
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr luaL_newstate();
|
||||
/// <summary>DEPRECATED - use luaL_newstate() instead!</summary>
|
||||
public static IntPtr lua_open()
|
||||
{
|
||||
return LuaDLL.luaL_newstate();
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (lua_open replaced by luaL_newstate)
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_close(IntPtr luaState);
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (new function luaL_openlibs)
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void luaL_openlibs(IntPtr luaState);
|
||||
/*
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_base(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_io(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_table(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_string(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_math(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_debug(IntPtr luaState);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaopen_loadlib(IntPtr luaState);
|
||||
*/
|
||||
// steffenj: END Lua 5.1.1 API change (new function luaL_openlibs)
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (lua_strlen is now lua_objlen)
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int lua_objlen(IntPtr luaState, int stackPos);
|
||||
/// <summary>DEPRECATED - use lua_objlen(IntPtr luaState, int stackPos) instead!</summary>
|
||||
public static int lua_strlen(IntPtr luaState, int stackPos)
|
||||
{
|
||||
return lua_objlen(luaState, stackPos);
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (lua_strlen is now lua_objlen)
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (lua_dostring is now a macro luaL_dostring)
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int luaL_loadstring(IntPtr luaState, string chunk);
|
||||
public static int luaL_dostring(IntPtr luaState, string chunk)
|
||||
{
|
||||
int result = LuaDLL.luaL_loadstring(luaState, chunk);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
return LuaDLL.lua_pcall(luaState, 0, -1, 0);
|
||||
}
|
||||
/// <summary>DEPRECATED - use luaL_dostring(IntPtr luaState, string chunk) instead!</summary>
|
||||
public static int lua_dostring(IntPtr luaState, string chunk)
|
||||
{
|
||||
return LuaDLL.luaL_dostring(luaState, chunk);
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (lua_dostring is now a macro luaL_dostring)
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_createtable(IntPtr luaState, int narr, int nrec);
|
||||
public static void lua_newtable(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_createtable(luaState, 0, 0);
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile macro)
|
||||
//[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static int luaL_dofile(IntPtr luaState, string fileName)
|
||||
{
|
||||
int result = LuaDLL.luaL_loadfile(luaState, fileName);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
return LuaDLL.lua_pcall(luaState, 0, -1, 0);
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile)
|
||||
public static void lua_getglobal(IntPtr luaState, string name)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,name);
|
||||
LuaDLL.lua_gettable(luaState,LuaIndexes.LUA_GLOBALSINDEX);
|
||||
}
|
||||
public static void lua_setglobal(IntPtr luaState, string name)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,name);
|
||||
LuaDLL.lua_insert(luaState,-2);
|
||||
LuaDLL.lua_settable(luaState,LuaIndexes.LUA_GLOBALSINDEX);
|
||||
}
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_settop(IntPtr luaState, int newTop);
|
||||
// steffenj: BEGIN added lua_pop "macro"
|
||||
public static void lua_pop(IntPtr luaState, int amount)
|
||||
{
|
||||
LuaDLL.lua_settop(luaState, -(amount) - 1);
|
||||
}
|
||||
// steffenj: END added lua_pop "macro"
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_insert(IntPtr luaState, int newTop);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_remove(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_gettable(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_rawget(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_settable(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_rawset(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_setmetatable(IntPtr luaState, int objIndex);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_getmetatable(IntPtr luaState, int objIndex);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_equal(IntPtr luaState, int index1, int index2);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushvalue(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_replace(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_gettop(IntPtr luaState);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern LuaTypes lua_type(IntPtr luaState, int index);
|
||||
public static bool lua_isnil(IntPtr luaState, int index)
|
||||
{
|
||||
return (LuaDLL.lua_type(luaState,index)==LuaTypes.LUA_TNIL);
|
||||
}
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool lua_isnumber(IntPtr luaState, int index);
|
||||
public static bool lua_isboolean(IntPtr luaState, int index)
|
||||
{
|
||||
return LuaDLL.lua_type(luaState,index)==LuaTypes.LUA_TBOOLEAN;
|
||||
}
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luaL_ref(IntPtr luaState, int registryIndex);
|
||||
public static int lua_ref(IntPtr luaState, int lockRef)
|
||||
{
|
||||
if(lockRef!=0)
|
||||
{
|
||||
return LuaDLL.luaL_ref(luaState,LuaIndexes.LUA_REGISTRYINDEX);
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_rawgeti(IntPtr luaState, int tableIndex, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_rawseti(IntPtr luaState, int tableIndex, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern IntPtr lua_newuserdata(IntPtr luaState, int size);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern IntPtr lua_touserdata(IntPtr luaState, int index);
|
||||
public static void lua_getref(IntPtr luaState, int reference)
|
||||
{
|
||||
LuaDLL.lua_rawgeti(luaState,LuaIndexes.LUA_REGISTRYINDEX,reference);
|
||||
}
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaL_unref(IntPtr luaState, int registryIndex, int reference);
|
||||
public static void lua_unref(IntPtr luaState, int reference)
|
||||
{
|
||||
LuaDLL.luaL_unref(luaState,LuaIndexes.LUA_REGISTRYINDEX,reference);
|
||||
}
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool lua_isstring(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool lua_iscfunction(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushnil(IntPtr luaState);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushstdcallcfunction(IntPtr luaState, [MarshalAs(UnmanagedType.FunctionPtr)]LuaCSFunction function);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_call(IntPtr luaState, int nArgs, int nResults);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_pcall(IntPtr luaState, int nArgs, int nResults, int errfunc);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_rawcall(IntPtr luaState, int nArgs, int nResults);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern IntPtr lua_tocfunction(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern double lua_tonumber(IntPtr luaState, int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool lua_toboolean(IntPtr luaState, int index);
|
||||
|
||||
[DllImport(LUADLL,CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr lua_tolstring(IntPtr luaState, int index, out int strLen);
|
||||
|
||||
public static string lua_tostring(IntPtr luaState, int index)
|
||||
{
|
||||
int strlen;
|
||||
|
||||
IntPtr str = lua_tolstring(luaState, index, out strlen);
|
||||
if (str != IntPtr.Zero)
|
||||
return Marshal.PtrToStringAnsi(str, strlen);
|
||||
else
|
||||
return null; // treat lua nulls to as C# nulls
|
||||
}
|
||||
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_atpanic(IntPtr luaState, LuaFunctionCallback panicf);
|
||||
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushnumber(IntPtr luaState, double number);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushboolean(IntPtr luaState, bool value);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushlstring(IntPtr luaState, string str, int size);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushstring(IntPtr luaState, string str);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luaL_newmetatable(IntPtr luaState, string meta);
|
||||
// steffenj: BEGIN Lua 5.1.1 API change (luaL_getmetatable is now a macro using lua_getfield)
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void lua_getfield(IntPtr luaState, int stackPos, string meta);
|
||||
public static void luaL_getmetatable(IntPtr luaState, string meta)
|
||||
{
|
||||
LuaDLL.lua_getfield(luaState, LuaIndexes.LUA_REGISTRYINDEX, meta);
|
||||
}
|
||||
// steffenj: END Lua 5.1.1 API change (luaL_getmetatable is now a macro using lua_getfield)
|
||||
[DllImport(LUALIBDLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr luaL_checkudata(IntPtr luaState, int stackPos, string meta);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool luaL_getmetafield(IntPtr luaState, int stackPos, string field);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_load(IntPtr luaState, LuaChunkReader chunkReader, ref ReaderInfo data, string chunkName);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luaL_loadbuffer(IntPtr luaState, string buff, int size, string name);
|
||||
[DllImport(LUALIBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luaL_loadfile(IntPtr luaState, string filename);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern bool luaL_checkmetatable(IntPtr luaState,int obj);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luanet_tonetobject(IntPtr luaState,int obj);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luanet_newudata(IntPtr luaState,int val);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luanet_rawnetobj(IntPtr luaState,int obj);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int luanet_checkudata(IntPtr luaState,int obj,string meta);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_error(IntPtr luaState);
|
||||
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool lua_checkstack(IntPtr luaState,int extra);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int lua_next(IntPtr luaState,int index);
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void lua_pushlightuserdata(IntPtr luaState, IntPtr udata);
|
||||
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern IntPtr luanet_gettag();
|
||||
[DllImport(LUADLL,CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void luaL_where (IntPtr luaState, int level);
|
||||
}
|
||||
}
|
||||
29
LuaInterface/LuaException.cs
Normal file
29
LuaInterface/LuaException.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Exceptions thrown by the Lua runtime
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class LuaException : Exception
|
||||
{
|
||||
public LuaException()
|
||||
{}
|
||||
|
||||
public LuaException(string message) : base(message)
|
||||
{}
|
||||
|
||||
public LuaException(string message, Exception innerException) : base(message, innerException)
|
||||
{}
|
||||
|
||||
protected LuaException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
LuaInterface/LuaFunction.cs
Normal file
115
LuaInterface/LuaFunction.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
public class LuaFunction : LuaBase
|
||||
{
|
||||
//private Lua interpreter;
|
||||
internal LuaCSFunction function;
|
||||
//internal int reference;
|
||||
|
||||
public LuaFunction(int reference, Lua interpreter)
|
||||
{
|
||||
_Reference = reference;
|
||||
this.function = null;
|
||||
_Interpreter = interpreter;
|
||||
}
|
||||
|
||||
public LuaFunction(LuaCSFunction function, Lua interpreter)
|
||||
{
|
||||
_Reference = 0;
|
||||
this.function = function;
|
||||
_Interpreter = interpreter;
|
||||
}
|
||||
|
||||
//~LuaFunction()
|
||||
//{
|
||||
// if (reference != 0)
|
||||
// interpreter.dispose(reference);
|
||||
//}
|
||||
|
||||
//bool disposed = false;
|
||||
//~LuaFunction()
|
||||
//{
|
||||
// Dispose(false);
|
||||
//}
|
||||
|
||||
//public void Dispose()
|
||||
//{
|
||||
// Dispose(true);
|
||||
// GC.SuppressFinalize(this);
|
||||
//}
|
||||
|
||||
//public virtual void Dispose(bool disposeManagedResources)
|
||||
//{
|
||||
// if (!this.disposed)
|
||||
// {
|
||||
// if (disposeManagedResources)
|
||||
// {
|
||||
// if (_Reference != 0)
|
||||
// _Interpreter.dispose(_Reference);
|
||||
// }
|
||||
|
||||
// disposed = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
/*
|
||||
* Calls the function casting return values to the types
|
||||
* in returnTypes
|
||||
*/
|
||||
internal object[] call(object[] args, Type[] returnTypes)
|
||||
{
|
||||
return _Interpreter.callFunction(this, args, returnTypes);
|
||||
}
|
||||
/*
|
||||
* Calls the function and returns its return values inside
|
||||
* an array
|
||||
*/
|
||||
public object[] Call(params object[] args)
|
||||
{
|
||||
return _Interpreter.callFunction(this, args);
|
||||
}
|
||||
/*
|
||||
* Pushes the function into the Lua stack
|
||||
*/
|
||||
internal void push(IntPtr luaState)
|
||||
{
|
||||
if (_Reference != 0)
|
||||
LuaDLL.lua_getref(luaState, _Reference);
|
||||
else
|
||||
_Interpreter.pushCSFunction(function);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return "function";
|
||||
}
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if (o is LuaFunction)
|
||||
{
|
||||
LuaFunction l = (LuaFunction)o;
|
||||
if (this._Reference != 0 && l._Reference != 0)
|
||||
return _Interpreter.compareRef(l._Reference, this._Reference);
|
||||
else
|
||||
return this.function == l.function;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (_Reference != 0)
|
||||
// elisee: Used to return _Reference
|
||||
// which doesn't make sense as you can have different refs
|
||||
// to the same function
|
||||
return 0;
|
||||
else
|
||||
return function.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
23
LuaInterface/LuaGlobalAttribute.cs
Normal file
23
LuaInterface/LuaGlobalAttribute.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks a method for global usage in Lua scripts
|
||||
/// </summary>
|
||||
/// <see cref="LuaRegistrationHelper.TaggedInstanceMethods"/>
|
||||
/// <see cref="LuaRegistrationHelper.TaggedStaticMethods"/>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class LuaGlobalAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// An alternative name to use for calling the function in Lua - leave empty for CLR name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A description of the function
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
11
LuaInterface/LuaHideAttribute.cs
Normal file
11
LuaInterface/LuaHideAttribute.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks a method, field or property to be hidden from Lua auto-completion
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class LuaHideAttribute : Attribute
|
||||
{}
|
||||
}
|
||||
47
LuaInterface/LuaInterface.csproj
Normal file
47
LuaInterface/LuaInterface.csproj
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}</ProjectGuid>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||
<OutputPath>..\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="CheckType.cs" />
|
||||
<Compile Include="GenerateEventAssembly.cs" />
|
||||
<Compile Include="Lua.cs" />
|
||||
<Compile Include="LuaBase.cs" />
|
||||
<Compile Include="LuaDLL.cs" />
|
||||
<Compile Include="LuaException.cs" />
|
||||
<Compile Include="LuaFunction.cs" />
|
||||
<Compile Include="LuaGlobalAttribute.cs" />
|
||||
<Compile Include="LuaHideAttribute.cs" />
|
||||
<Compile Include="LuaRegistrationHelper.cs" />
|
||||
<Compile Include="LuaScriptException.cs" />
|
||||
<Compile Include="LuaTable.cs" />
|
||||
<Compile Include="LuaUserData.cs" />
|
||||
<Compile Include="MethodWrapper.cs" />
|
||||
<Compile Include="ObjectTranslator.cs" />
|
||||
<Compile Include="ProxyType.cs" />
|
||||
<Compile Include="Metatables.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
<Project>{bdaeab25-991e-46a7-af1e-4f0e03358daa}</Project>
|
||||
<Name>OpenRA.FileFormats</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
90
LuaInterface/LuaRegistrationHelper.cs
Normal file
90
LuaInterface/LuaRegistrationHelper.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
public static class LuaRegistrationHelper
|
||||
{
|
||||
#region Tagged instance methods
|
||||
/// <summary>
|
||||
/// Registers all public instance methods in an object tagged with <see cref="LuaGlobalAttribute"/> as Lua global functions
|
||||
/// </summary>
|
||||
/// <param name="lua">The Lua VM to add the methods to</param>
|
||||
/// <param name="o">The object to get the methods from</param>
|
||||
public static void TaggedInstanceMethods(Lua lua, object o)
|
||||
{
|
||||
#region Sanity checks
|
||||
if (lua == null) throw new ArgumentNullException("lua");
|
||||
if (o == null) throw new ArgumentNullException("o");
|
||||
#endregion
|
||||
|
||||
foreach (MethodInfo method in o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), true))
|
||||
{
|
||||
if (string.IsNullOrEmpty(attribute.Name))
|
||||
lua.RegisterFunction(method.Name, o, method); // CLR name
|
||||
else
|
||||
lua.RegisterFunction(attribute.Name, o, method); // Custom name
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Tagged static methods
|
||||
/// <summary>
|
||||
/// Registers all public static methods in a class tagged with <see cref="LuaGlobalAttribute"/> as Lua global functions
|
||||
/// </summary>
|
||||
/// <param name="lua">The Lua VM to add the methods to</param>
|
||||
/// <param name="type">The class type to get the methods from</param>
|
||||
public static void TaggedStaticMethods(Lua lua, Type type)
|
||||
{
|
||||
#region Sanity checks
|
||||
if (lua == null) throw new ArgumentNullException("lua");
|
||||
if (type == null) throw new ArgumentNullException("type");
|
||||
if (!type.IsClass) throw new ArgumentException("The type must be a class!", "type");
|
||||
#endregion
|
||||
|
||||
foreach (MethodInfo method in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
{
|
||||
foreach (LuaGlobalAttribute attribute in method.GetCustomAttributes(typeof(LuaGlobalAttribute), false))
|
||||
{
|
||||
if (string.IsNullOrEmpty(attribute.Name))
|
||||
lua.RegisterFunction(method.Name, null, method); // CLR name
|
||||
else
|
||||
lua.RegisterFunction(attribute.Name, null, method); // Custom name
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enumeration
|
||||
/// <summary>
|
||||
/// Registers an enumeration's values for usage as a Lua variable table
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The enum type to register</typeparam>
|
||||
/// <param name="lua">The Lua VM to add the enum to</param>
|
||||
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The type parameter is used to select an enum type")]
|
||||
public static void Enumeration<T>(Lua lua)
|
||||
{
|
||||
#region Sanity checks
|
||||
if (lua == null) throw new ArgumentNullException("lua");
|
||||
#endregion
|
||||
|
||||
Type type = typeof(T);
|
||||
if (!type.IsEnum) throw new ArgumentException("The type must be an enumeration!");
|
||||
|
||||
string[] names = Enum.GetNames(type);
|
||||
T[] values = (T[])Enum.GetValues(type);
|
||||
|
||||
lua.NewTable(type.Name);
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
string path = type.Name + "." + names[i];
|
||||
lua[path] = values[i];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
50
LuaInterface/LuaScriptException.cs
Normal file
50
LuaInterface/LuaScriptException.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Exceptions thrown by the Lua runtime because of errors in the script
|
||||
/// </summary>
|
||||
public class LuaScriptException : LuaException
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the exception has occured as the result of a .NET exception in user code
|
||||
/// </summary>
|
||||
public bool IsNetException { get; private set; }
|
||||
|
||||
private readonly string source;
|
||||
|
||||
/// <summary>
|
||||
/// The position in the script where the exception was triggered.
|
||||
/// </summary>
|
||||
public override string Source { get { return source; } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Lua-only exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="source">The position in the script where the exception was triggered.</param>
|
||||
public LuaScriptException(string message, string source) : base(message)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new .NET wrapping exception.
|
||||
/// </summary>
|
||||
/// <param name="innerException">The .NET exception triggered by user-code.</param>
|
||||
/// <param name="source">The position in the script where the exception was triggered.</param>
|
||||
public LuaScriptException(Exception innerException, string source)
|
||||
: base(innerException.Message, innerException)
|
||||
{
|
||||
this.source = source;
|
||||
this.IsNetException = true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Prepend the error source
|
||||
return GetType().FullName + ": " + source + Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
145
LuaInterface/LuaTable.cs
Normal file
145
LuaInterface/LuaTable.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/*
|
||||
* Wrapper class for Lua tables
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class LuaTable : LuaBase
|
||||
{
|
||||
public bool IsOrphaned;
|
||||
|
||||
//internal int _Reference;
|
||||
//private Lua _Interpreter;
|
||||
public LuaTable(int reference, Lua interpreter)
|
||||
{
|
||||
_Reference = reference;
|
||||
_Interpreter = interpreter;
|
||||
}
|
||||
|
||||
//bool disposed = false;
|
||||
//~LuaTable()
|
||||
//{
|
||||
// Dispose(false);
|
||||
//}
|
||||
|
||||
//public void Dispose()
|
||||
//{
|
||||
// Dispose(true);
|
||||
// GC.SuppressFinalize(this);
|
||||
//}
|
||||
|
||||
//public virtual void Dispose(bool disposeManagedResources)
|
||||
//{
|
||||
// if (!this.disposed)
|
||||
// {
|
||||
// if (disposeManagedResources)
|
||||
// {
|
||||
// if (_Reference != 0)
|
||||
// _Interpreter.dispose(_Reference);
|
||||
// }
|
||||
|
||||
// disposed = true;
|
||||
// }
|
||||
//}
|
||||
//~LuaTable()
|
||||
//{
|
||||
// _Interpreter.dispose(_Reference);
|
||||
//}
|
||||
/*
|
||||
* Indexer for string fields of the table
|
||||
*/
|
||||
public object this[string field]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Interpreter.getObject(_Reference, field);
|
||||
}
|
||||
set
|
||||
{
|
||||
_Interpreter.setObject(_Reference, field, value);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Indexer for numeric fields of the table
|
||||
*/
|
||||
public object this[object field]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Interpreter.getObject(_Reference, field);
|
||||
}
|
||||
set
|
||||
{
|
||||
_Interpreter.setObject(_Reference, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public System.Collections.IDictionaryEnumerator GetEnumerator()
|
||||
{
|
||||
return _Interpreter.GetTableDict(this).GetEnumerator();
|
||||
}
|
||||
|
||||
public ICollection Keys
|
||||
{
|
||||
get { return _Interpreter.GetTableDict(this).Keys; }
|
||||
}
|
||||
|
||||
public ICollection Values
|
||||
{
|
||||
get { return _Interpreter.GetTableDict(this).Values; }
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets an string fields of a table ignoring its metatable,
|
||||
* if it exists
|
||||
*/
|
||||
internal object rawget(string field)
|
||||
{
|
||||
return _Interpreter.rawGetObject(_Reference, field);
|
||||
}
|
||||
|
||||
internal object rawgetFunction(string field)
|
||||
{
|
||||
object obj = _Interpreter.rawGetObject(_Reference, field);
|
||||
|
||||
if (obj is LuaCSFunction)
|
||||
return new LuaFunction((LuaCSFunction)obj, _Interpreter);
|
||||
else
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes this table into the Lua stack
|
||||
*/
|
||||
internal void push(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_getref(luaState, _Reference);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return "table";
|
||||
}
|
||||
|
||||
//public override bool Equals(object o)
|
||||
//{
|
||||
// if (o is LuaTable)
|
||||
// {
|
||||
// LuaTable l = (LuaTable)o;
|
||||
// return _Interpreter.compareRef(l._Reference, _Reference);
|
||||
// }
|
||||
// else return false;
|
||||
//}
|
||||
//public override int GetHashCode()
|
||||
//{
|
||||
// return _Reference;
|
||||
//}
|
||||
}
|
||||
}
|
||||
82
LuaInterface/LuaUserData.cs
Normal file
82
LuaInterface/LuaUserData.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
public class LuaUserData : LuaBase
|
||||
{
|
||||
//internal int _Reference;
|
||||
//private Lua _Interpreter;
|
||||
public LuaUserData(int reference, Lua interpreter)
|
||||
{
|
||||
_Reference = reference;
|
||||
_Interpreter = interpreter;
|
||||
}
|
||||
//~LuaUserData()
|
||||
//{
|
||||
// if (_Reference != 0)
|
||||
// _Interpreter.dispose(_Reference);
|
||||
//}
|
||||
/*
|
||||
* Indexer for string fields of the userdata
|
||||
*/
|
||||
public object this[string field]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Interpreter.getObject(_Reference, field);
|
||||
}
|
||||
set
|
||||
{
|
||||
_Interpreter.setObject(_Reference, field, value);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Indexer for numeric fields of the userdata
|
||||
*/
|
||||
public object this[object field]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Interpreter.getObject(_Reference, field);
|
||||
}
|
||||
set
|
||||
{
|
||||
_Interpreter.setObject(_Reference, field, value);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Calls the userdata and returns its return values inside
|
||||
* an array
|
||||
*/
|
||||
public object[] Call(params object[] args)
|
||||
{
|
||||
return _Interpreter.callFunction(this, args);
|
||||
}
|
||||
/*
|
||||
* Pushes the userdata into the Lua stack
|
||||
*/
|
||||
internal void push(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_getref(luaState, _Reference);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return "userdata";
|
||||
}
|
||||
//public override bool Equals(object o)
|
||||
//{
|
||||
// if (o is LuaUserData)
|
||||
// {
|
||||
// LuaUserData l = (LuaUserData)o;
|
||||
// return _Interpreter.compareRef(l._Reference, _Reference);
|
||||
// }
|
||||
// else return false;
|
||||
//}
|
||||
//public override int GetHashCode()
|
||||
//{
|
||||
// return _Reference;
|
||||
//}
|
||||
}
|
||||
}
|
||||
935
LuaInterface/Metatables.cs
Normal file
935
LuaInterface/Metatables.cs
Normal file
@@ -0,0 +1,935 @@
|
||||
namespace LuaInterface
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/*
|
||||
* Functions used in the metatables of userdata representing
|
||||
* CLR objects
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class MetaFunctions
|
||||
{
|
||||
/*
|
||||
* __index metafunction for CLR objects. Implemented in Lua.
|
||||
*/
|
||||
internal static string luaIndexFunction =
|
||||
@"
|
||||
local function index(obj,name)
|
||||
local meta=getmetatable(obj)
|
||||
local cached=meta.cache[name]
|
||||
if cached then
|
||||
return cached
|
||||
else
|
||||
local value,isFunc = get_object_member(obj,name)
|
||||
if value==nil and type(isFunc)=='string' then error(isFunc,2) end
|
||||
if isFunc then
|
||||
meta.cache[name]=value
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
return index";
|
||||
|
||||
private ObjectTranslator translator;
|
||||
private Hashtable memberCache = new Hashtable();
|
||||
internal LuaCSFunction gcFunction, indexFunction, newindexFunction,
|
||||
baseIndexFunction, classIndexFunction, classNewindexFunction,
|
||||
execDelegateFunction, callConstructorFunction, toStringFunction;
|
||||
|
||||
public MetaFunctions(ObjectTranslator translator)
|
||||
{
|
||||
this.translator = translator;
|
||||
gcFunction = new LuaCSFunction(this.collectObject);
|
||||
toStringFunction = new LuaCSFunction(this.toString);
|
||||
indexFunction = new LuaCSFunction(this.getMethod);
|
||||
newindexFunction = new LuaCSFunction(this.setFieldOrProperty);
|
||||
baseIndexFunction = new LuaCSFunction(this.getBaseMethod);
|
||||
callConstructorFunction = new LuaCSFunction(this.callConstructor);
|
||||
classIndexFunction = new LuaCSFunction(this.getClassMethod);
|
||||
classNewindexFunction = new LuaCSFunction(this.setClassFieldOrProperty);
|
||||
execDelegateFunction = new LuaCSFunction(this.runFunctionDelegate);
|
||||
}
|
||||
|
||||
/*
|
||||
* __call metafunction of CLR delegates, retrieves and calls the delegate.
|
||||
*/
|
||||
private int runFunctionDelegate(IntPtr luaState)
|
||||
{
|
||||
LuaCSFunction func = (LuaCSFunction)translator.getRawNetObject(luaState, 1);
|
||||
LuaDLL.lua_remove(luaState, 1);
|
||||
return func(luaState);
|
||||
}
|
||||
/*
|
||||
* __gc metafunction of CLR objects.
|
||||
*/
|
||||
private int collectObject(IntPtr luaState)
|
||||
{
|
||||
int udata = LuaDLL.luanet_rawnetobj(luaState, 1);
|
||||
if (udata != -1)
|
||||
{
|
||||
translator.collectObject(udata);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.WriteLine("not found: " + udata);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* __tostring metafunction of CLR objects.
|
||||
*/
|
||||
private int toString(IntPtr luaState)
|
||||
{
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj != null)
|
||||
{
|
||||
translator.push(luaState, obj.ToString() + ": " + obj.GetHashCode());
|
||||
}
|
||||
else LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Debug tool to dump the lua stack
|
||||
/// </summary>
|
||||
/// FIXME, move somewhere else
|
||||
public static void dumpStack(ObjectTranslator translator, IntPtr luaState)
|
||||
{
|
||||
int depth = LuaDLL.lua_gettop(luaState);
|
||||
|
||||
Debug.WriteLine("lua stack depth: " + depth);
|
||||
for (int i = 1; i <= depth; i++)
|
||||
{
|
||||
LuaTypes type = LuaDLL.lua_type(luaState, i);
|
||||
// we dump stacks when deep in calls, calling typename while the stack is in flux can fail sometimes, so manually check for key types
|
||||
string typestr = (type == LuaTypes.LUA_TTABLE) ? "table" : LuaDLL.lua_typename(luaState, type);
|
||||
|
||||
string strrep = LuaDLL.lua_tostring(luaState, i);
|
||||
if (type == LuaTypes.LUA_TUSERDATA)
|
||||
{
|
||||
object obj = translator.getRawNetObject(luaState, i);
|
||||
strrep = obj.ToString();
|
||||
}
|
||||
|
||||
Debug.Print("{0}: ({1}) {2}", i, typestr, strrep);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the __index metafunction of CLR objects in case the
|
||||
* method is not cached or it is a field/property/event.
|
||||
* Receives the object and the member name as arguments and returns
|
||||
* either the value of the member or a delegate to call it.
|
||||
* If the member does not exist returns nil.
|
||||
*/
|
||||
private int getMethod(IntPtr luaState)
|
||||
{
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj == null)
|
||||
{
|
||||
translator.throwError(luaState, "trying to index an invalid object reference");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
object index = translator.getObject(luaState, 2);
|
||||
|
||||
string methodName = index as string; // will be null if not a string arg
|
||||
Type objType = obj.GetType();
|
||||
|
||||
// Handle the most common case, looking up the method by name.
|
||||
|
||||
// CP: This will fail when using indexers and attempting to get a value with the same name as a property of the object,
|
||||
// ie: xmlelement['item'] <- item is a property of xmlelement
|
||||
try
|
||||
{
|
||||
if (methodName != null && isMemberPresent(objType, methodName))
|
||||
return getMember(luaState, objType, obj, methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
}
|
||||
catch { }
|
||||
bool failed = true;
|
||||
|
||||
// Try to access by array if the type is right and index is an int (lua numbers always come across as double)
|
||||
if (objType.IsArray && index is double)
|
||||
{
|
||||
int intIndex = (int)((double)index);
|
||||
Array aa = obj as Array;
|
||||
if (intIndex >= aa.Length) {
|
||||
return translator.pushError(luaState,"array index out of bounds: "+intIndex + " " + aa.Length);
|
||||
}
|
||||
object val = aa.GetValue(intIndex);
|
||||
translator.push (luaState,val);
|
||||
failed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to use get_Item to index into this .net object
|
||||
//MethodInfo getter = objType.GetMethod("get_Item");
|
||||
// issue here is that there may be multiple indexers..
|
||||
MethodInfo[] methods = objType.GetMethods();
|
||||
|
||||
foreach (MethodInfo mInfo in methods)
|
||||
{
|
||||
if (mInfo.Name == "get_Item")
|
||||
{
|
||||
//check if the signature matches the input
|
||||
if (mInfo.GetParameters().Length == 1)
|
||||
{
|
||||
MethodInfo getter = mInfo;
|
||||
ParameterInfo[] actualParms = (getter != null) ? getter.GetParameters() : null;
|
||||
if (actualParms == null || actualParms.Length != 1)
|
||||
{
|
||||
return translator.pushError(luaState, "method not found (or no indexer): " + index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the index in a form acceptable to the getter
|
||||
index = translator.getAsType(luaState, 2, actualParms[0].ParameterType);
|
||||
// Just call the indexer - if out of bounds an exception will happen
|
||||
try
|
||||
{
|
||||
object result = getter.Invoke(obj, new object[]{index});
|
||||
translator.push(luaState, result);
|
||||
failed = false;
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
// Provide a more readable description for the common case of key not found
|
||||
if (e.InnerException is KeyNotFoundException)
|
||||
return translator.pushError(luaState, "key '" + index + "' not found ");
|
||||
else
|
||||
return translator.pushError(luaState, "exception indexing '" + index + "' " + e.Message);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (failed) {
|
||||
return translator.pushError(luaState,"cannot find " + index);
|
||||
}
|
||||
LuaDLL.lua_pushboolean(luaState, false);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* __index metafunction of base classes (the base field of Lua tables).
|
||||
* Adds a prefix to the method name to call the base version of the method.
|
||||
*/
|
||||
private int getBaseMethod(IntPtr luaState)
|
||||
{
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj == null)
|
||||
{
|
||||
translator.throwError(luaState, "trying to index an invalid object reference");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
LuaDLL.lua_pushboolean(luaState, false);
|
||||
return 2;
|
||||
}
|
||||
string methodName = LuaDLL.lua_tostring(luaState, 2);
|
||||
if (methodName == null)
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
LuaDLL.lua_pushboolean(luaState, false);
|
||||
return 2;
|
||||
}
|
||||
getMember(luaState, obj.GetType(), obj, "__luaInterface_base_" + methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
LuaDLL.lua_settop(luaState, -2);
|
||||
if (LuaDLL.lua_type(luaState, -1) == LuaTypes.LUA_TNIL)
|
||||
{
|
||||
LuaDLL.lua_settop(luaState, -2);
|
||||
return getMember(luaState, obj.GetType(), obj, methodName, BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
}
|
||||
LuaDLL.lua_pushboolean(luaState, false);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does this method exist as either an instance or static?
|
||||
/// </summary>
|
||||
/// <param name="objType"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <returns></returns>
|
||||
bool isMemberPresent(IReflect objType, string methodName)
|
||||
{
|
||||
object cachedMember = checkMemberCache(memberCache, objType, methodName);
|
||||
|
||||
if (cachedMember != null)
|
||||
return true;
|
||||
|
||||
//CP: Removed NonPublic binding search
|
||||
MemberInfo[] members = objType.GetMember(methodName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase/* | BindingFlags.NonPublic*/);
|
||||
return (members.Length > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes the value of a member or a delegate to call it, depending on the type of
|
||||
* the member. Works with static or instance members.
|
||||
* Uses reflection to find members, and stores the reflected MemberInfo object in
|
||||
* a cache (indexed by the type of the object and the name of the member).
|
||||
*/
|
||||
private int getMember(IntPtr luaState, IReflect objType, object obj, string methodName, BindingFlags bindingType)
|
||||
{
|
||||
bool implicitStatic = false;
|
||||
MemberInfo member = null;
|
||||
object cachedMember = checkMemberCache(memberCache, objType, methodName);
|
||||
//object cachedMember=null;
|
||||
if (cachedMember is LuaCSFunction)
|
||||
{
|
||||
translator.pushFunction(luaState, (LuaCSFunction)cachedMember);
|
||||
translator.push(luaState, true);
|
||||
return 2;
|
||||
}
|
||||
else if (cachedMember != null)
|
||||
{
|
||||
member = (MemberInfo)cachedMember;
|
||||
}
|
||||
else
|
||||
{
|
||||
//CP: Removed NonPublic binding search
|
||||
MemberInfo[] members = objType.GetMember(methodName, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
|
||||
if (members.Length > 0)
|
||||
member = members[0];
|
||||
else
|
||||
{
|
||||
// If we can't find any suitable instance members, try to find them as statics - but we only want to allow implicit static
|
||||
// lookups for fields/properties/events -kevinh
|
||||
//CP: Removed NonPublic binding search and made case insensitive
|
||||
members = objType.GetMember(methodName, bindingType | BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
|
||||
|
||||
if (members.Length > 0)
|
||||
{
|
||||
member = members[0];
|
||||
implicitStatic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (member != null)
|
||||
{
|
||||
if (member.MemberType == MemberTypes.Field)
|
||||
{
|
||||
FieldInfo field = (FieldInfo)member;
|
||||
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
|
||||
try
|
||||
{
|
||||
translator.push(luaState, field.GetValue(obj));
|
||||
}
|
||||
catch
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
}
|
||||
else if (member.MemberType == MemberTypes.Property)
|
||||
{
|
||||
PropertyInfo property = (PropertyInfo)member;
|
||||
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
|
||||
try
|
||||
{
|
||||
object val = property.GetValue(obj, null);
|
||||
|
||||
translator.push(luaState, val);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// If we can't find the getter in our class, recurse up to the base class and see
|
||||
// if they can help.
|
||||
|
||||
if (objType is Type && !(((Type)objType) == typeof(object)))
|
||||
return getMember(luaState, ((Type)objType).BaseType, obj, methodName, bindingType);
|
||||
else
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
catch (TargetInvocationException e) // Convert this exception into a Lua error
|
||||
{
|
||||
ThrowError(luaState, e);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
}
|
||||
else if (member.MemberType == MemberTypes.Event)
|
||||
{
|
||||
EventInfo eventInfo = (EventInfo)member;
|
||||
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
|
||||
translator.push(luaState, new RegisterEventHandler(translator.pendingEvents, obj, eventInfo));
|
||||
}
|
||||
else if (!implicitStatic)
|
||||
{
|
||||
if (member.MemberType == MemberTypes.NestedType)
|
||||
{
|
||||
// kevinh - added support for finding nested types
|
||||
|
||||
// cache us
|
||||
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, member);
|
||||
|
||||
// Find the name of our class
|
||||
string name = member.Name;
|
||||
Type dectype = member.DeclaringType;
|
||||
|
||||
// Build a new long name and try to find the type by name
|
||||
string longname = dectype.FullName + "+" + name;
|
||||
Type nestedType = translator.FindType(longname);
|
||||
|
||||
translator.pushType(luaState, nestedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Member type must be 'method'
|
||||
LuaCSFunction wrapper = new LuaCSFunction((new LuaMethodWrapper(translator, objType, methodName, bindingType)).call);
|
||||
|
||||
if (cachedMember == null) setMemberCache(memberCache, objType, methodName, wrapper);
|
||||
translator.pushFunction(luaState, wrapper);
|
||||
translator.push(luaState, true);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we reach this point we found a static method, but can't use it in this context because the user passed in an instance
|
||||
translator.throwError(luaState, "can't pass instance to static method " + methodName);
|
||||
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// kevinh - we want to throw an exception because meerly returning 'nil' in this case
|
||||
// is not sufficient. valid data members may return nil and therefore there must be some
|
||||
// way to know the member just doesn't exist.
|
||||
|
||||
translator.throwError(luaState, "unknown member name " + methodName);
|
||||
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
|
||||
// push false because we are NOT returning a function (see luaIndexFunction)
|
||||
translator.push(luaState, false);
|
||||
return 2;
|
||||
}
|
||||
/*
|
||||
* Checks if a MemberInfo object is cached, returning it or null.
|
||||
*/
|
||||
private object checkMemberCache(Hashtable memberCache, IReflect objType, string memberName)
|
||||
{
|
||||
Hashtable members = (Hashtable)memberCache[objType];
|
||||
if (members != null)
|
||||
return members[memberName];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
* Stores a MemberInfo object in the member cache.
|
||||
*/
|
||||
private void setMemberCache(Hashtable memberCache, IReflect objType, string memberName, object member)
|
||||
{
|
||||
Hashtable members = (Hashtable)memberCache[objType];
|
||||
if (members == null)
|
||||
{
|
||||
members = new Hashtable();
|
||||
memberCache[objType] = members;
|
||||
}
|
||||
members[memberName] = member;
|
||||
}
|
||||
/*
|
||||
* __newindex metafunction of CLR objects. Receives the object,
|
||||
* the member name and the value to be stored as arguments. Throws
|
||||
* and error if the assignment is invalid.
|
||||
*/
|
||||
private int setFieldOrProperty(IntPtr luaState)
|
||||
{
|
||||
object target = translator.getRawNetObject(luaState, 1);
|
||||
if (target == null)
|
||||
{
|
||||
translator.throwError(luaState, "trying to index and invalid object reference");
|
||||
return 0;
|
||||
}
|
||||
Type type = target.GetType();
|
||||
|
||||
// First try to look up the parameter as a property name
|
||||
string detailMessage;
|
||||
bool didMember = trySetMember(luaState, type, target, BindingFlags.Instance | BindingFlags.IgnoreCase, out detailMessage);
|
||||
|
||||
if (didMember)
|
||||
return 0; // Must have found the property name
|
||||
|
||||
// We didn't find a property name, now see if we can use a [] style this accessor to set array contents
|
||||
try
|
||||
{
|
||||
if (type.IsArray && LuaDLL.lua_isnumber(luaState, 2))
|
||||
{
|
||||
int index = (int)LuaDLL.lua_tonumber(luaState, 2);
|
||||
|
||||
Array arr = (Array)target;
|
||||
object val = translator.getAsType(luaState, 3, arr.GetType().GetElementType());
|
||||
arr.SetValue(val, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to see if we have a this[] accessor
|
||||
MethodInfo setter = type.GetMethod("set_Item");
|
||||
if (setter != null)
|
||||
{
|
||||
ParameterInfo[] args = setter.GetParameters();
|
||||
Type valueType = args[1].ParameterType;
|
||||
|
||||
// The new val ue the user specified
|
||||
object val = translator.getAsType(luaState, 3, valueType);
|
||||
|
||||
Type indexType = args[0].ParameterType;
|
||||
object index = translator.getAsType(luaState, 2, indexType);
|
||||
|
||||
object[] methodArgs = new object[2];
|
||||
|
||||
// Just call the indexer - if out of bounds an exception will happen
|
||||
methodArgs[0] = index;
|
||||
methodArgs[1] = val;
|
||||
|
||||
setter.Invoke(target, methodArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
translator.throwError(luaState, detailMessage); // Pass the original message from trySetMember because it is probably best
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SEHException)
|
||||
{
|
||||
// If we are seeing a C++ exception - this must actually be for Lua's private use. Let it handle it
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError(luaState, e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to set a named property or field
|
||||
/// </summary>
|
||||
/// <param name="luaState"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="bindingType"></param>
|
||||
/// <returns>false if unable to find the named member, true for success</returns>
|
||||
private bool trySetMember(IntPtr luaState, IReflect targetType, object target, BindingFlags bindingType, out string detailMessage)
|
||||
{
|
||||
detailMessage = null; // No error yet
|
||||
|
||||
// If not already a string just return - we don't want to call tostring - which has the side effect of
|
||||
// changing the lua typecode to string
|
||||
// Note: We don't use isstring because the standard lua C isstring considers either strings or numbers to
|
||||
// be true for isstring.
|
||||
if (LuaDLL.lua_type(luaState, 2) != LuaTypes.LUA_TSTRING)
|
||||
{
|
||||
detailMessage = "property names must be strings";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only look up property names by string
|
||||
string fieldName = LuaDLL.lua_tostring(luaState, 2);
|
||||
if (fieldName == null || fieldName.Length < 1 || !(char.IsLetter(fieldName[0]) || fieldName[0] == '_'))
|
||||
{
|
||||
detailMessage = "invalid property name";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find our member via reflection or the cache
|
||||
MemberInfo member = (MemberInfo)checkMemberCache(memberCache, targetType, fieldName);
|
||||
if (member == null)
|
||||
{
|
||||
//CP: Removed NonPublic binding search and made case insensitive
|
||||
MemberInfo[] members = targetType.GetMember(fieldName, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*| BindingFlags.NonPublic*/);
|
||||
if (members.Length > 0)
|
||||
{
|
||||
member = members[0];
|
||||
setMemberCache(memberCache, targetType, fieldName, member);
|
||||
}
|
||||
else
|
||||
{
|
||||
detailMessage = "field or property '" + fieldName + "' does not exist";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (member.MemberType == MemberTypes.Field)
|
||||
{
|
||||
FieldInfo field = (FieldInfo)member;
|
||||
object val = translator.getAsType(luaState, 3, field.FieldType);
|
||||
try
|
||||
{
|
||||
field.SetValue(target, val);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError(luaState, e);
|
||||
}
|
||||
// We did a call
|
||||
return true;
|
||||
}
|
||||
else if (member.MemberType == MemberTypes.Property)
|
||||
{
|
||||
PropertyInfo property = (PropertyInfo)member;
|
||||
object val = translator.getAsType(luaState, 3, property.PropertyType);
|
||||
try
|
||||
{
|
||||
property.SetValue(target, val, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError(luaState, e);
|
||||
}
|
||||
// We did a call
|
||||
return true;
|
||||
}
|
||||
|
||||
detailMessage = "'" + fieldName + "' is not a .net field or property";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Writes to fields or properties, either static or instance. Throws an error
|
||||
* if the operation is invalid.
|
||||
*/
|
||||
private int setMember(IntPtr luaState, IReflect targetType, object target, BindingFlags bindingType)
|
||||
{
|
||||
string detail;
|
||||
bool success = trySetMember(luaState, targetType, target, bindingType, out detail);
|
||||
|
||||
if (!success)
|
||||
translator.throwError(luaState, detail);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a C# exception into a Lua error
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// We try to look into the exception to give the most meaningful description
|
||||
void ThrowError(IntPtr luaState, Exception e)
|
||||
{
|
||||
// If we got inside a reflection show what really happened
|
||||
TargetInvocationException te = e as TargetInvocationException;
|
||||
|
||||
if (te != null)
|
||||
e = te.InnerException;
|
||||
|
||||
translator.throwError(luaState, e);
|
||||
}
|
||||
|
||||
/*
|
||||
* __index metafunction of type references, works on static members.
|
||||
*/
|
||||
private int getClassMethod(IntPtr luaState)
|
||||
{
|
||||
IReflect klass;
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj == null || !(obj is IReflect))
|
||||
{
|
||||
translator.throwError(luaState, "trying to index an invalid type reference");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
else klass = (IReflect)obj;
|
||||
if (LuaDLL.lua_isnumber(luaState, 2))
|
||||
{
|
||||
int size = (int)LuaDLL.lua_tonumber(luaState, 2);
|
||||
translator.push(luaState, Array.CreateInstance(klass.UnderlyingSystemType, size));
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
string methodName = LuaDLL.lua_tostring(luaState, 2);
|
||||
if (methodName == null)
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
} //CP: Ignore case
|
||||
else return getMember(luaState, klass, null, methodName, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.IgnoreCase);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* __newindex function of type references, works on static members.
|
||||
*/
|
||||
private int setClassFieldOrProperty(IntPtr luaState)
|
||||
{
|
||||
IReflect target;
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj == null || !(obj is IReflect))
|
||||
{
|
||||
translator.throwError(luaState, "trying to index an invalid type reference");
|
||||
return 0;
|
||||
}
|
||||
else target = (IReflect)obj;
|
||||
return setMember(luaState, target, null, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.IgnoreCase);
|
||||
}
|
||||
/*
|
||||
* __call metafunction of type references. Searches for and calls
|
||||
* a constructor for the type. Returns nil if the constructor is not
|
||||
* found or if the arguments are invalid. Throws an error if the constructor
|
||||
* generates an exception.
|
||||
*/
|
||||
private int callConstructor(IntPtr luaState)
|
||||
{
|
||||
MethodCache validConstructor = new MethodCache();
|
||||
IReflect klass;
|
||||
object obj = translator.getRawNetObject(luaState, 1);
|
||||
if (obj == null || !(obj is IReflect))
|
||||
{
|
||||
translator.throwError(luaState, "trying to call constructor on an invalid type reference");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
else klass = (IReflect)obj;
|
||||
LuaDLL.lua_remove(luaState, 1);
|
||||
ConstructorInfo[] constructors = klass.UnderlyingSystemType.GetConstructors();
|
||||
foreach (ConstructorInfo constructor in constructors)
|
||||
{
|
||||
bool isConstructor = matchParameters(luaState, constructor, ref validConstructor);
|
||||
if (isConstructor)
|
||||
{
|
||||
try
|
||||
{
|
||||
translator.push(luaState, constructor.Invoke(validConstructor.args));
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
ThrowError(luaState, e);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
catch
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
string constructorName = (constructors.Length == 0) ? "unknown" : constructors[0].Name;
|
||||
|
||||
translator.throwError(luaState, String.Format("{0} does not contain constructor({1}) argument match",
|
||||
klass.UnderlyingSystemType,
|
||||
constructorName));
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static bool IsInteger(double x) {
|
||||
return Math.Ceiling(x) == x;
|
||||
}
|
||||
|
||||
|
||||
internal Array TableToArray(object luaParamValue, Type paramArrayType) {
|
||||
Array paramArray;
|
||||
|
||||
if (luaParamValue is LuaTable) {
|
||||
LuaTable table = (LuaTable)luaParamValue;
|
||||
IDictionaryEnumerator tableEnumerator = table.GetEnumerator();
|
||||
tableEnumerator.Reset();
|
||||
paramArray = Array.CreateInstance(paramArrayType, table.Values.Count);
|
||||
|
||||
int paramArrayIndex = 0;
|
||||
|
||||
while(tableEnumerator.MoveNext()) {
|
||||
object o = tableEnumerator.Value;
|
||||
if (paramArrayType == typeof(object)) {
|
||||
if (o != null && o.GetType() == typeof(double) && IsInteger((double)o))
|
||||
o = Convert.ToInt32((double)o);
|
||||
}
|
||||
paramArray.SetValue(Convert.ChangeType(o, paramArrayType), paramArrayIndex);
|
||||
paramArrayIndex++;
|
||||
}
|
||||
} else {
|
||||
paramArray = Array.CreateInstance(paramArrayType, 1);
|
||||
paramArray.SetValue(luaParamValue, 0);
|
||||
}
|
||||
|
||||
return paramArray;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Matches a method against its arguments in the Lua stack. Returns
|
||||
* if the match was succesful. It it was also returns the information
|
||||
* necessary to invoke the method.
|
||||
*/
|
||||
internal bool matchParameters(IntPtr luaState, MethodBase method, ref MethodCache methodCache)
|
||||
{
|
||||
ExtractValue extractValue;
|
||||
bool isMethod = true;
|
||||
ParameterInfo[] paramInfo = method.GetParameters();
|
||||
int currentLuaParam = 1;
|
||||
int nLuaParams = LuaDLL.lua_gettop(luaState);
|
||||
ArrayList paramList = new ArrayList();
|
||||
List<int> outList = new List<int>();
|
||||
List<MethodArgs> argTypes = new List<MethodArgs>();
|
||||
foreach (ParameterInfo currentNetParam in paramInfo)
|
||||
{
|
||||
if (!currentNetParam.IsIn && currentNetParam.IsOut) // Skips out params
|
||||
{
|
||||
outList.Add(paramList.Add(null));
|
||||
}
|
||||
else if (currentLuaParam > nLuaParams) // Adds optional parameters
|
||||
{
|
||||
if (currentNetParam.IsOptional)
|
||||
{
|
||||
paramList.Add(currentNetParam.DefaultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
isMethod = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (_IsTypeCorrect(luaState, currentLuaParam, currentNetParam, out extractValue)) // Type checking
|
||||
{
|
||||
int index = paramList.Add(extractValue(luaState, currentLuaParam));
|
||||
|
||||
MethodArgs methodArg = new MethodArgs();
|
||||
methodArg.index = index;
|
||||
methodArg.extractValue = extractValue;
|
||||
argTypes.Add(methodArg);
|
||||
|
||||
if (currentNetParam.ParameterType.IsByRef)
|
||||
outList.Add(index);
|
||||
currentLuaParam++;
|
||||
} // Type does not match, ignore if the parameter is optional
|
||||
else if (_IsParamsArray(luaState, currentLuaParam, currentNetParam, out extractValue))
|
||||
{
|
||||
object luaParamValue = extractValue(luaState, currentLuaParam);
|
||||
Type paramArrayType = currentNetParam.ParameterType.GetElementType();
|
||||
|
||||
Array paramArray = TableToArray(luaParamValue, paramArrayType);
|
||||
int index = paramList.Add(paramArray);
|
||||
|
||||
MethodArgs methodArg = new MethodArgs();
|
||||
methodArg.index = index;
|
||||
methodArg.extractValue = extractValue;
|
||||
methodArg.isParamsArray = true;
|
||||
methodArg.paramsArrayType = paramArrayType;
|
||||
argTypes.Add(methodArg);
|
||||
|
||||
currentLuaParam++;
|
||||
}
|
||||
else if (currentNetParam.IsOptional)
|
||||
{
|
||||
paramList.Add(currentNetParam.DefaultValue);
|
||||
}
|
||||
else // No match
|
||||
{
|
||||
isMethod = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentLuaParam != nLuaParams + 1) // Number of parameters does not match
|
||||
isMethod = false;
|
||||
if (isMethod)
|
||||
{
|
||||
methodCache.args = paramList.ToArray();
|
||||
methodCache.cachedMethod = method;
|
||||
methodCache.outList = outList.ToArray();
|
||||
methodCache.argTypes = argTypes.ToArray();
|
||||
}
|
||||
return isMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CP: Fix for operator overloading failure
|
||||
/// Returns true if the type is set and assigns the extract value
|
||||
/// </summary>
|
||||
/// <param name="luaState"></param>
|
||||
/// <param name="currentLuaParam"></param>
|
||||
/// <param name="currentNetParam"></param>
|
||||
/// <param name="extractValue"></param>
|
||||
/// <returns></returns>
|
||||
private bool _IsTypeCorrect(IntPtr luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (extractValue = translator.typeChecker.checkType(luaState, currentLuaParam, currentNetParam.ParameterType)) != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
extractValue = null;
|
||||
Debug.WriteLine("Type wasn't correct");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsParamsArray(IntPtr luaState, int currentLuaParam, ParameterInfo currentNetParam, out ExtractValue extractValue)
|
||||
{
|
||||
extractValue = null;
|
||||
|
||||
if (currentNetParam.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0)
|
||||
{
|
||||
LuaTypes luaType;
|
||||
|
||||
try
|
||||
{
|
||||
luaType = LuaDLL.lua_type(luaState, currentLuaParam);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Could not retrieve lua type while attempting to determine params Array Status."+ ex.ToString());
|
||||
Debug.WriteLine(ex.Message);
|
||||
extractValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (luaType == LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
try
|
||||
{
|
||||
extractValue = translator.typeChecker.getExtractor(typeof(LuaTable));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("An error occurred during an attempt to retrieve a LuaTable extractor while checking for params array status." + ex.ToString());
|
||||
}
|
||||
|
||||
if (extractValue != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Type paramElementType = currentNetParam.ParameterType.GetElementType();
|
||||
|
||||
try
|
||||
{
|
||||
extractValue = translator.typeChecker.checkType(luaState, currentLuaParam, paramElementType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(string.Format("An error occurred during an attempt to retrieve an extractor ({0}) while checking for params array status:{1}", paramElementType.FullName,ex.ToString()));
|
||||
}
|
||||
|
||||
if (extractValue != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine("Type wasn't Params object.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
595
LuaInterface/MethodWrapper.cs
Normal file
595
LuaInterface/MethodWrapper.cs
Normal file
@@ -0,0 +1,595 @@
|
||||
namespace LuaInterface
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
/*
|
||||
* Cached method
|
||||
*/
|
||||
struct MethodCache
|
||||
{
|
||||
private MethodBase _cachedMethod;
|
||||
|
||||
public MethodBase cachedMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cachedMethod;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cachedMethod = value;
|
||||
MethodInfo mi = value as MethodInfo;
|
||||
if (mi != null)
|
||||
{
|
||||
//SJD this is guaranteed to be correct irrespective of actual name used for type..
|
||||
IsReturnVoid = mi.ReturnType == typeof(void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReturnVoid;
|
||||
|
||||
// List or arguments
|
||||
public object[] args;
|
||||
// Positions of out parameters
|
||||
public int[] outList;
|
||||
// Types of parameters
|
||||
public MethodArgs[] argTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parameter information
|
||||
*/
|
||||
struct MethodArgs
|
||||
{
|
||||
// Position of parameter
|
||||
public int index;
|
||||
// Type-conversion function
|
||||
public ExtractValue extractValue;
|
||||
|
||||
public bool isParamsArray;
|
||||
|
||||
public Type paramsArrayType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Argument extraction with type-conversion function
|
||||
*/
|
||||
delegate object ExtractValue(IntPtr luaState, int stackPos);
|
||||
|
||||
/*
|
||||
* Wrapper class for methods/constructors accessed from Lua.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class LuaMethodWrapper
|
||||
{
|
||||
private ObjectTranslator _Translator;
|
||||
private MethodBase _Method;
|
||||
private MethodCache _LastCalledMethod = new MethodCache();
|
||||
private string _MethodName;
|
||||
private MemberInfo[] _Members;
|
||||
private ExtractValue _ExtractTarget;
|
||||
private object _Target;
|
||||
private BindingFlags _BindingType;
|
||||
|
||||
/*
|
||||
* Constructs the wrapper for a known MethodBase instance
|
||||
*/
|
||||
public LuaMethodWrapper(ObjectTranslator translator, object target, IReflect targetType, MethodBase method)
|
||||
{
|
||||
_Translator = translator;
|
||||
_Target = target;
|
||||
if (targetType != null)
|
||||
_ExtractTarget = translator.typeChecker.getExtractor(targetType);
|
||||
_Method = method;
|
||||
_MethodName = method.Name;
|
||||
|
||||
if (method.IsStatic)
|
||||
{ _BindingType = BindingFlags.Static; }
|
||||
else
|
||||
{ _BindingType = BindingFlags.Instance; }
|
||||
}
|
||||
/*
|
||||
* Constructs the wrapper for a known method name
|
||||
*/
|
||||
public LuaMethodWrapper(ObjectTranslator translator, IReflect targetType, string methodName, BindingFlags bindingType)
|
||||
{
|
||||
_Translator = translator;
|
||||
_MethodName = methodName;
|
||||
|
||||
if (targetType != null)
|
||||
_ExtractTarget = translator.typeChecker.getExtractor(targetType);
|
||||
|
||||
_BindingType = bindingType;
|
||||
|
||||
//CP: Removed NonPublic binding search and added IgnoreCase
|
||||
_Members = targetType.UnderlyingSystemType.GetMember(methodName, MemberTypes.Method, bindingType | BindingFlags.Public | BindingFlags.IgnoreCase/*|BindingFlags.NonPublic*/);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Convert C# exceptions into Lua errors
|
||||
/// </summary>
|
||||
/// <returns>num of things on stack</returns>
|
||||
/// <param name="e">null for no pending exception</param>
|
||||
int SetPendingException(Exception e)
|
||||
{
|
||||
return _Translator.interpreter.SetPendingException(e);
|
||||
}
|
||||
|
||||
private static bool IsInteger(double x) {
|
||||
return Math.Ceiling(x) == x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Calls the method. Receives the arguments from the Lua stack
|
||||
* and returns values in it.
|
||||
*/
|
||||
public int call(IntPtr luaState)
|
||||
{
|
||||
MethodBase methodToCall = _Method;
|
||||
object targetObject = _Target;
|
||||
bool failedCall = true;
|
||||
int nReturnValues = 0;
|
||||
|
||||
if (!LuaDLL.lua_checkstack(luaState, 5))
|
||||
throw new LuaException("Lua stack overflow");
|
||||
|
||||
bool isStatic = (_BindingType & BindingFlags.Static) == BindingFlags.Static;
|
||||
|
||||
SetPendingException(null);
|
||||
|
||||
if (methodToCall == null) // Method from name
|
||||
{
|
||||
if (isStatic)
|
||||
targetObject = null;
|
||||
else
|
||||
targetObject = _ExtractTarget(luaState, 1);
|
||||
|
||||
//LuaDLL.lua_remove(luaState,1); // Pops the receiver
|
||||
if (_LastCalledMethod.cachedMethod != null) // Cached?
|
||||
{
|
||||
int numStackToSkip = isStatic ? 0 : 1; // If this is an instance invoe we will have an extra arg on the stack for the targetObject
|
||||
int numArgsPassed = LuaDLL.lua_gettop(luaState) - numStackToSkip;
|
||||
MethodBase method = _LastCalledMethod.cachedMethod;
|
||||
|
||||
if (numArgsPassed == _LastCalledMethod.argTypes.Length) // No. of args match?
|
||||
{
|
||||
if (!LuaDLL.lua_checkstack(luaState, _LastCalledMethod.outList.Length + 6))
|
||||
throw new LuaException("Lua stack overflow");
|
||||
|
||||
object[] args = _LastCalledMethod.args;
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _LastCalledMethod.argTypes.Length; i++)
|
||||
{
|
||||
MethodArgs type = _LastCalledMethod.argTypes[i];
|
||||
object luaParamValue = type.extractValue(luaState, i + 1 + numStackToSkip);
|
||||
if (_LastCalledMethod.argTypes[i].isParamsArray)
|
||||
{
|
||||
args[type.index] = _Translator.tableToArray(luaParamValue,type.paramsArrayType);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[type.index] = luaParamValue;
|
||||
}
|
||||
|
||||
if (args[type.index] == null &&
|
||||
!LuaDLL.lua_isnil(luaState, i + 1 + numStackToSkip))
|
||||
{
|
||||
throw new LuaException("argument number " + (i + 1) + " is invalid");
|
||||
}
|
||||
}
|
||||
if ((_BindingType & BindingFlags.Static) == BindingFlags.Static)
|
||||
{
|
||||
_Translator.push(luaState, method.Invoke(null, args));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_LastCalledMethod.cachedMethod.IsConstructor)
|
||||
_Translator.push(luaState, ((ConstructorInfo)method).Invoke(args));
|
||||
else
|
||||
_Translator.push(luaState, method.Invoke(targetObject,args));
|
||||
}
|
||||
failedCall = false;
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
// Failure of method invocation
|
||||
return SetPendingException(e.GetBaseException());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (_Members.Length == 1) // Is the method overloaded?
|
||||
// No, throw error
|
||||
return SetPendingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache miss
|
||||
if (failedCall)
|
||||
{
|
||||
// System.Diagnostics.Debug.WriteLine("cache miss on " + methodName);
|
||||
|
||||
// If we are running an instance variable, we can now pop the targetObject from the stack
|
||||
if (!isStatic)
|
||||
{
|
||||
if (targetObject == null)
|
||||
{
|
||||
_Translator.throwError(luaState, String.Format("instance method '{0}' requires a non null target object", _MethodName));
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LuaDLL.lua_remove(luaState, 1); // Pops the receiver
|
||||
}
|
||||
|
||||
bool hasMatch = false;
|
||||
string candidateName = null;
|
||||
|
||||
foreach (MemberInfo member in _Members)
|
||||
{
|
||||
candidateName = member.ReflectedType.Name + "." + member.Name;
|
||||
|
||||
MethodBase m = (MethodInfo)member;
|
||||
|
||||
bool isMethod = _Translator.matchParameters(luaState, m, ref _LastCalledMethod);
|
||||
if (isMethod)
|
||||
{
|
||||
hasMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasMatch)
|
||||
{
|
||||
string msg = (candidateName == null)
|
||||
? "invalid arguments to method call: " + _MethodName
|
||||
: ("invalid arguments to method: " + candidateName);
|
||||
|
||||
_Translator.throwError(luaState, msg);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Method from MethodBase instance
|
||||
{
|
||||
if (methodToCall.ContainsGenericParameters)
|
||||
{
|
||||
// bool isMethod = //* not used
|
||||
_Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod);
|
||||
|
||||
if (methodToCall.IsGenericMethodDefinition)
|
||||
{
|
||||
//need to make a concrete type of the generic method definition
|
||||
List<Type> typeArgs = new List<Type>();
|
||||
|
||||
foreach (object arg in _LastCalledMethod.args)
|
||||
typeArgs.Add(arg.GetType());
|
||||
|
||||
MethodInfo concreteMethod = (methodToCall as MethodInfo).MakeGenericMethod(typeArgs.ToArray());
|
||||
|
||||
_Translator.push(luaState, concreteMethod.Invoke(targetObject, _LastCalledMethod.args));
|
||||
failedCall = false;
|
||||
}
|
||||
else if (methodToCall.ContainsGenericParameters)
|
||||
{
|
||||
_Translator.throwError(luaState, "unable to invoke method on generic class as the current method is an open generic method");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!methodToCall.IsStatic && !methodToCall.IsConstructor && targetObject == null)
|
||||
{
|
||||
targetObject = _ExtractTarget(luaState, 1);
|
||||
LuaDLL.lua_remove(luaState, 1); // Pops the receiver
|
||||
}
|
||||
|
||||
if (!_Translator.matchParameters(luaState, methodToCall, ref _LastCalledMethod))
|
||||
{
|
||||
_Translator.throwError(luaState, string.Format("invalid arguments to method call {0} of type {1}", methodToCall, methodToCall.ReflectedType));
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failedCall)
|
||||
{
|
||||
if (!LuaDLL.lua_checkstack(luaState, _LastCalledMethod.outList.Length + 6))
|
||||
throw new LuaException("Lua stack overflow");
|
||||
try
|
||||
{
|
||||
if (isStatic)
|
||||
{
|
||||
_Translator.push(luaState, _LastCalledMethod.cachedMethod.Invoke(null, _LastCalledMethod.args));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_LastCalledMethod.cachedMethod.IsConstructor)
|
||||
_Translator.push(luaState, ((ConstructorInfo)_LastCalledMethod.cachedMethod).Invoke(_LastCalledMethod.args));
|
||||
else
|
||||
{
|
||||
object returnValue = _LastCalledMethod.cachedMethod.Invoke( targetObject, _LastCalledMethod.args );
|
||||
_Translator.push(luaState, returnValue );
|
||||
|
||||
LuaTable returnValueLuaBase = returnValue as LuaTable;
|
||||
if( returnValueLuaBase != null && returnValueLuaBase.IsOrphaned )
|
||||
{
|
||||
returnValueLuaBase.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
return SetPendingException(e.GetBaseException());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return SetPendingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes out and ref return values
|
||||
for (int index = 0; index < _LastCalledMethod.outList.Length; index++)
|
||||
{
|
||||
nReturnValues++;
|
||||
|
||||
object outArg = _LastCalledMethod.args[_LastCalledMethod.outList[index]];
|
||||
|
||||
_Translator.push(luaState, outArg );
|
||||
|
||||
LuaTable outArgLuaBase = outArg as LuaTable;
|
||||
if( outArgLuaBase != null && outArgLuaBase.IsOrphaned )
|
||||
{
|
||||
outArgLuaBase.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
//by isSingle 2010-09-10 11:26:31
|
||||
//Desc:
|
||||
// if not return void,we need add 1,
|
||||
// or we will lost the function's return value
|
||||
// when call dotnet function like "int foo(arg1,out arg2,out arg3)" in lua code
|
||||
if (!_LastCalledMethod.IsReturnVoid && nReturnValues > 0)
|
||||
{
|
||||
nReturnValues++;
|
||||
}
|
||||
|
||||
return nReturnValues < 1 ? 1 : nReturnValues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// We keep track of what delegates we have auto attached to an event - to allow us to cleanly exit a LuaInterface session
|
||||
/// </summary>
|
||||
class EventHandlerContainer : IDisposable
|
||||
{
|
||||
Dictionary<Delegate, RegisterEventHandler> dict = new Dictionary<Delegate, RegisterEventHandler>();
|
||||
|
||||
public void Add(Delegate handler, RegisterEventHandler eventInfo)
|
||||
{
|
||||
dict.Add(handler, eventInfo);
|
||||
}
|
||||
|
||||
public void Remove(Delegate handler)
|
||||
{
|
||||
bool found = dict.Remove(handler);
|
||||
Debug.Assert(found);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any still registered handlers
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (KeyValuePair<Delegate, RegisterEventHandler> pair in dict)
|
||||
{
|
||||
pair.Value.RemovePending(pair.Key);
|
||||
}
|
||||
|
||||
dict.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wrapper class for events that does registration/deregistration
|
||||
* of event handlers.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
class RegisterEventHandler
|
||||
{
|
||||
object target;
|
||||
EventInfo eventInfo;
|
||||
EventHandlerContainer pendingEvents;
|
||||
|
||||
public RegisterEventHandler(EventHandlerContainer pendingEvents, object target, EventInfo eventInfo)
|
||||
{
|
||||
this.target = target;
|
||||
this.eventInfo = eventInfo;
|
||||
this.pendingEvents = pendingEvents;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Adds a new event handler
|
||||
*/
|
||||
public Delegate Add(LuaFunction function)
|
||||
{
|
||||
//CP: Fix by Ben Bryant for event handling with one parameter
|
||||
//link: http://luaforge.net/forum/message.php?msg_id=9266
|
||||
Delegate handlerDelegate = CodeGeneration.Instance.GetDelegate(eventInfo.EventHandlerType, function);
|
||||
eventInfo.AddEventHandler(target, handlerDelegate);
|
||||
pendingEvents.Add(handlerDelegate, this);
|
||||
|
||||
return handlerDelegate;
|
||||
|
||||
|
||||
//MethodInfo mi = eventInfo.EventHandlerType.GetMethod("Invoke");
|
||||
//ParameterInfo[] pi = mi.GetParameters();
|
||||
//LuaEventHandler handler=CodeGeneration.Instance.GetEvent(pi[1].ParameterType,function);
|
||||
|
||||
//Delegate handlerDelegate=Delegate.CreateDelegate(eventInfo.EventHandlerType,handler,"HandleEvent");
|
||||
//eventInfo.AddEventHandler(target,handlerDelegate);
|
||||
//pendingEvents.Add(handlerDelegate, this);
|
||||
|
||||
//return handlerDelegate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes an existing event handler
|
||||
*/
|
||||
public void Remove(Delegate handlerDelegate)
|
||||
{
|
||||
RemovePending(handlerDelegate);
|
||||
pendingEvents.Remove(handlerDelegate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes an existing event handler (without updating the pending handlers list)
|
||||
*/
|
||||
internal void RemovePending(Delegate handlerDelegate)
|
||||
{
|
||||
eventInfo.RemoveEventHandler(target, handlerDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Base wrapper class for Lua function event handlers.
|
||||
* Subclasses that do actual event handling are created
|
||||
* at runtime.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class LuaEventHandler
|
||||
{
|
||||
public LuaFunction handler = null;
|
||||
|
||||
// CP: Fix provided by Ben Bryant for delegates with one param
|
||||
// link: http://luaforge.net/forum/message.php?msg_id=9318
|
||||
public void handleEvent(object[] args)
|
||||
{
|
||||
handler.Call(args);
|
||||
}
|
||||
//public void handleEvent(object sender,object data)
|
||||
//{
|
||||
// handler.call(new object[] { sender,data },new Type[0]);
|
||||
//}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper class for Lua functions as delegates
|
||||
* Subclasses with correct signatures are created
|
||||
* at runtime.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class LuaDelegate
|
||||
{
|
||||
public Type[] returnTypes;
|
||||
public LuaFunction function;
|
||||
public LuaDelegate()
|
||||
{
|
||||
function = null;
|
||||
returnTypes = null;
|
||||
}
|
||||
public object callFunction(object[] args, object[] inArgs, int[] outArgs)
|
||||
{
|
||||
// args is the return array of arguments, inArgs is the actual array
|
||||
// of arguments passed to the function (with in parameters only), outArgs
|
||||
// has the positions of out parameters
|
||||
object returnValue;
|
||||
int iRefArgs;
|
||||
object[] returnValues = function.call(inArgs, returnTypes);
|
||||
if (returnTypes[0] == typeof(void))
|
||||
{
|
||||
returnValue = null;
|
||||
iRefArgs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = returnValues[0];
|
||||
iRefArgs = 1;
|
||||
}
|
||||
// Sets the value of out and ref parameters (from
|
||||
// the values returned by the Lua function).
|
||||
for (int i = 0; i < outArgs.Length; i++)
|
||||
{
|
||||
args[outArgs[i]] = returnValues[iRefArgs];
|
||||
iRefArgs++;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Static helper methods for Lua tables acting as CLR objects.
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class LuaClassHelper
|
||||
{
|
||||
/*
|
||||
* Gets the function called name from the provided table,
|
||||
* returning null if it does not exist
|
||||
*/
|
||||
public static LuaFunction getTableFunction(LuaTable luaTable, string name)
|
||||
{
|
||||
object funcObj = luaTable.rawget(name);
|
||||
if (funcObj is LuaFunction)
|
||||
return (LuaFunction)funcObj;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
* Calls the provided function with the provided parameters
|
||||
*/
|
||||
public static object callFunction(LuaFunction function, object[] args, Type[] returnTypes, object[] inArgs, int[] outArgs)
|
||||
{
|
||||
// args is the return array of arguments, inArgs is the actual array
|
||||
// of arguments passed to the function (with in parameters only), outArgs
|
||||
// has the positions of out parameters
|
||||
object returnValue;
|
||||
int iRefArgs;
|
||||
object[] returnValues = function.call(inArgs, returnTypes);
|
||||
if (returnTypes[0] == typeof(void))
|
||||
{
|
||||
returnValue = null;
|
||||
iRefArgs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = returnValues[0];
|
||||
iRefArgs = 1;
|
||||
}
|
||||
for (int i = 0; i < outArgs.Length; i++)
|
||||
{
|
||||
args[outArgs[i]] = returnValues[iRefArgs];
|
||||
iRefArgs++;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
870
LuaInterface/ObjectTranslator.cs
Normal file
870
LuaInterface/ObjectTranslator.cs
Normal file
@@ -0,0 +1,870 @@
|
||||
namespace LuaInterface
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
/*
|
||||
* Passes objects from the CLR to Lua and vice-versa
|
||||
*
|
||||
* Author: Fabio Mascarenhas
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class ObjectTranslator
|
||||
{
|
||||
internal CheckType typeChecker;
|
||||
|
||||
// object # to object (FIXME - it should be possible to get object address as an object #)
|
||||
public readonly Dictionary<int, object> objects = new Dictionary<int, object>();
|
||||
// object to object #
|
||||
public readonly Dictionary<object, int> objectsBackMap = new Dictionary<object, int>();
|
||||
internal Lua interpreter;
|
||||
private MetaFunctions metaFunctions;
|
||||
private List<Assembly> assemblies;
|
||||
private LuaCSFunction getMethodSigFunction, getConstructorSigFunction, ctypeFunction, enumFromIntFunction;
|
||||
|
||||
internal EventHandlerContainer pendingEvents = new EventHandlerContainer();
|
||||
|
||||
public ObjectTranslator(Lua interpreter,IntPtr luaState)
|
||||
{
|
||||
this.interpreter = interpreter;
|
||||
typeChecker = new CheckType(this);
|
||||
metaFunctions = new MetaFunctions(this);
|
||||
assemblies = new List<Assembly>();
|
||||
|
||||
getMethodSigFunction= getMethodSignature;
|
||||
getConstructorSigFunction= getConstructorSignature;
|
||||
|
||||
ctypeFunction = ctype;
|
||||
enumFromIntFunction = enumFromInt;
|
||||
|
||||
createLuaObjectList(luaState);
|
||||
createIndexingMetaFunction(luaState);
|
||||
createBaseClassMetatable(luaState);
|
||||
createClassMetatable(luaState);
|
||||
createFunctionMetatable(luaState);
|
||||
setGlobalFunctions(luaState);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets up the list of objects in the Lua side
|
||||
*/
|
||||
private void createLuaObjectList(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,"luaNet_objects");
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_pushstring(luaState,"__mode");
|
||||
LuaDLL.lua_pushstring(luaState,"v");
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_setmetatable(luaState,-2);
|
||||
LuaDLL.lua_settable(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
|
||||
}
|
||||
/*
|
||||
* Registers the indexing function of CLR objects
|
||||
* passed to Lua
|
||||
*/
|
||||
private void createIndexingMetaFunction(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
|
||||
LuaDLL.luaL_dostring(luaState,MetaFunctions.luaIndexFunction); // steffenj: lua_dostring renamed to luaL_dostring
|
||||
//LuaDLL.lua_pushstdcallcfunction(luaState,indexFunction);
|
||||
LuaDLL.lua_rawset(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
|
||||
}
|
||||
/*
|
||||
* Creates the metatable for superclasses (the base
|
||||
* field of registered tables)
|
||||
*/
|
||||
private void createBaseClassMetatable(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.luaL_newmetatable(luaState,"luaNet_searchbase");
|
||||
LuaDLL.lua_pushstring(luaState,"__gc");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__tostring");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__index");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.baseIndexFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__newindex");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
}
|
||||
/*
|
||||
* Creates the metatable for type references
|
||||
*/
|
||||
private void createClassMetatable(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.luaL_newmetatable(luaState,"luaNet_class");
|
||||
LuaDLL.lua_pushstring(luaState,"__gc");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__tostring");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__index");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classIndexFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__newindex");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.classNewindexFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__call");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.callConstructorFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
}
|
||||
/*
|
||||
* Registers the global functions used by LuaInterface
|
||||
*/
|
||||
private void setGlobalFunctions(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.indexFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"get_object_member");
|
||||
/*LuaDLL.lua_pushstdcallcfunction(luaState,importTypeFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"import_type");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,loadAssemblyFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"load_assembly");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,registerTableFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"make_object");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,unregisterTableFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"free_object");*/
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,getMethodSigFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"get_method_bysig");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,getConstructorSigFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"get_constructor_bysig");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,ctypeFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"ctype");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,enumFromIntFunction);
|
||||
LuaDLL.lua_setglobal(luaState,"enum");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the metatable for delegates
|
||||
*/
|
||||
private void createFunctionMetatable(IntPtr luaState)
|
||||
{
|
||||
LuaDLL.luaL_newmetatable(luaState,"luaNet_function");
|
||||
LuaDLL.lua_pushstring(luaState,"__gc");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__call");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.execDelegateFunction);
|
||||
LuaDLL.lua_settable(luaState,-3);
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
}
|
||||
/*
|
||||
* Passes errors (argument e) to the Lua interpreter
|
||||
*/
|
||||
internal void throwError(IntPtr luaState, object e)
|
||||
{
|
||||
// We use this to remove anything pushed by luaL_where
|
||||
int oldTop = LuaDLL.lua_gettop(luaState);
|
||||
|
||||
// Stack frame #1 is our C# wrapper, so not very interesting to the user
|
||||
// Stack frame #2 must be the lua code that called us, so that's what we want to use
|
||||
LuaDLL.luaL_where(luaState, 1);
|
||||
object[] curlev = popValues(luaState, oldTop);
|
||||
|
||||
// Determine the position in the script where the exception was triggered
|
||||
string errLocation = "";
|
||||
if (curlev.Length > 0)
|
||||
errLocation = curlev[0].ToString();
|
||||
|
||||
string message = e as string;
|
||||
if (message != null)
|
||||
{
|
||||
// Wrap Lua error (just a string) and store the error location
|
||||
e = new LuaScriptException(message, errLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Exception ex = e as Exception;
|
||||
if (ex != null)
|
||||
{
|
||||
// Wrap generic .NET exception as an InnerException and store the error location
|
||||
e = new LuaScriptException(ex, errLocation);
|
||||
}
|
||||
}
|
||||
|
||||
push(luaState, e);
|
||||
LuaDLL.lua_error(luaState);
|
||||
}
|
||||
/*
|
||||
* Implementation of load_assembly. Throws an error
|
||||
* if the assembly is not found.
|
||||
*/
|
||||
private int loadAssembly(IntPtr luaState)
|
||||
{
|
||||
try
|
||||
{
|
||||
string assemblyName = LuaDLL.lua_tostring(luaState,1);
|
||||
|
||||
Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(assemblyName));
|
||||
|
||||
if (assembly != null && !assemblies.Contains(assembly))
|
||||
{
|
||||
assemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throwError(luaState,e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal Type FindType(string className)
|
||||
{
|
||||
foreach(Assembly assembly in assemblies)
|
||||
{
|
||||
Type klass=assembly.GetType(className);
|
||||
if(klass!=null)
|
||||
{
|
||||
return klass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of import_type. Returns nil if the
|
||||
* type is not found.
|
||||
*/
|
||||
private int importType(IntPtr luaState)
|
||||
{
|
||||
string className=LuaDLL.lua_tostring(luaState,1);
|
||||
Type klass=FindType(className);
|
||||
if(klass!=null)
|
||||
pushType(luaState,klass);
|
||||
else
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Implementation of make_object. Registers a table (first
|
||||
* argument in the stack) as an object subclassing the
|
||||
* type passed as second argument in the stack.
|
||||
*/
|
||||
private int registerTable(IntPtr luaState)
|
||||
{
|
||||
if(LuaDLL.lua_type(luaState,1)==LuaTypes.LUA_TTABLE)
|
||||
{
|
||||
LuaTable luaTable=getTable(luaState,1);
|
||||
string superclassName = LuaDLL.lua_tostring(luaState, 2);
|
||||
if (superclassName != null)
|
||||
{
|
||||
Type klass = FindType(superclassName);
|
||||
if (klass != null)
|
||||
{
|
||||
// Creates and pushes the object in the stack, setting
|
||||
// it as the metatable of the first argument
|
||||
object obj = CodeGeneration.Instance.GetClassInstance(klass, luaTable);
|
||||
pushObject(luaState, obj, "luaNet_metatable");
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_pushstring(luaState, "__index");
|
||||
LuaDLL.lua_pushvalue(luaState, -3);
|
||||
LuaDLL.lua_settable(luaState, -3);
|
||||
LuaDLL.lua_pushstring(luaState, "__newindex");
|
||||
LuaDLL.lua_pushvalue(luaState, -3);
|
||||
LuaDLL.lua_settable(luaState, -3);
|
||||
LuaDLL.lua_setmetatable(luaState, 1);
|
||||
// Pushes the object again, this time as the base field
|
||||
// of the table and with the luaNet_searchbase metatable
|
||||
LuaDLL.lua_pushstring(luaState, "base");
|
||||
int index = addObject(obj);
|
||||
pushNewObject(luaState, obj, index, "luaNet_searchbase");
|
||||
LuaDLL.lua_rawset(luaState, 1);
|
||||
}
|
||||
else
|
||||
throwError(luaState, "register_table: can not find superclass '" + superclassName + "'");
|
||||
}
|
||||
else
|
||||
throwError(luaState, "register_table: superclass name can not be null");
|
||||
}
|
||||
else throwError(luaState,"register_table: first arg is not a table");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Implementation of free_object. Clears the metatable and the
|
||||
* base field, freeing the created object for garbage-collection
|
||||
*/
|
||||
private int unregisterTable(IntPtr luaState)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(LuaDLL.lua_getmetatable(luaState,1)!=0)
|
||||
{
|
||||
LuaDLL.lua_pushstring(luaState,"__index");
|
||||
LuaDLL.lua_gettable(luaState,-2);
|
||||
object obj=getRawNetObject(luaState,-1);
|
||||
if(obj==null) throwError(luaState,"unregister_table: arg is not valid table");
|
||||
FieldInfo luaTableField=obj.GetType().GetField("__luaInterface_luaTable");
|
||||
if(luaTableField==null) throwError(luaState,"unregister_table: arg is not valid table");
|
||||
luaTableField.SetValue(obj,null);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
LuaDLL.lua_setmetatable(luaState,1);
|
||||
LuaDLL.lua_pushstring(luaState,"base");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
LuaDLL.lua_settable(luaState,1);
|
||||
}
|
||||
else throwError(luaState,"unregister_table: arg is not valid table");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throwError(luaState,e.Message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Implementation of get_method_bysig. Returns nil
|
||||
* if no matching method is not found.
|
||||
*/
|
||||
private int getMethodSignature(IntPtr luaState)
|
||||
{
|
||||
IReflect klass; object target;
|
||||
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
|
||||
if(udata!=-1)
|
||||
{
|
||||
klass=(IReflect)objects[udata];
|
||||
target=null;
|
||||
}
|
||||
else
|
||||
{
|
||||
target=getRawNetObject(luaState,1);
|
||||
if(target==null)
|
||||
{
|
||||
throwError(luaState,"get_method_bysig: first arg is not type or object reference");
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return 1;
|
||||
}
|
||||
klass=target.GetType();
|
||||
}
|
||||
string methodName=LuaDLL.lua_tostring(luaState,2);
|
||||
Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-2];
|
||||
for(int i=0;i<signature.Length;i++)
|
||||
signature[i]=FindType(LuaDLL.lua_tostring(luaState,i+3));
|
||||
try
|
||||
{
|
||||
//CP: Added ignore case
|
||||
MethodInfo method=klass.GetMethod(methodName,BindingFlags.Public | BindingFlags.Static |
|
||||
BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase, null, signature, null);
|
||||
pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(this,target,klass,method)).call));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throwError(luaState,e);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Implementation of get_constructor_bysig. Returns nil
|
||||
* if no matching constructor is found.
|
||||
*/
|
||||
private int getConstructorSignature(IntPtr luaState)
|
||||
{
|
||||
IReflect klass=null;
|
||||
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
|
||||
if(udata!=-1)
|
||||
{
|
||||
klass=(IReflect)objects[udata];
|
||||
}
|
||||
if(klass==null)
|
||||
{
|
||||
throwError(luaState,"get_constructor_bysig: first arg is invalid type reference");
|
||||
}
|
||||
Type[] signature=new Type[LuaDLL.lua_gettop(luaState)-1];
|
||||
for(int i=0;i<signature.Length;i++)
|
||||
signature[i]=FindType(LuaDLL.lua_tostring(luaState,i+2));
|
||||
try
|
||||
{
|
||||
ConstructorInfo constructor=klass.UnderlyingSystemType.GetConstructor(signature);
|
||||
pushFunction(luaState,new LuaCSFunction((new LuaMethodWrapper(this,null,klass,constructor)).call));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throwError(luaState,e);
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private Type typeOf(IntPtr luaState, int idx)
|
||||
{
|
||||
int udata=LuaDLL.luanet_checkudata(luaState,1,"luaNet_class");
|
||||
if (udata == -1) {
|
||||
return null;
|
||||
} else {
|
||||
ProxyType pt = (ProxyType)objects[udata];
|
||||
return pt.UnderlyingSystemType;
|
||||
}
|
||||
}
|
||||
|
||||
public int pushError(IntPtr luaState, string msg)
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
LuaDLL.lua_pushstring(luaState,msg);
|
||||
return 2;
|
||||
}
|
||||
|
||||
private int ctype(IntPtr luaState)
|
||||
{
|
||||
Type t = typeOf(luaState,1);
|
||||
if (t == null) {
|
||||
return pushError(luaState,"not a CLR class");
|
||||
}
|
||||
pushObject(luaState,t,"luaNet_metatable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int enumFromInt(IntPtr luaState)
|
||||
{
|
||||
Type t = typeOf(luaState,1);
|
||||
if (t == null || ! t.IsEnum) {
|
||||
return pushError(luaState,"not an enum");
|
||||
}
|
||||
object res = null;
|
||||
LuaTypes lt = LuaDLL.lua_type(luaState,2);
|
||||
if (lt == LuaTypes.LUA_TNUMBER) {
|
||||
int ival = (int)LuaDLL.lua_tonumber(luaState,2);
|
||||
res = Enum.ToObject(t,ival);
|
||||
} else
|
||||
if (lt == LuaTypes.LUA_TSTRING) {
|
||||
string sflags = LuaDLL.lua_tostring(luaState,2);
|
||||
string err = null;
|
||||
try {
|
||||
res = Enum.Parse(t,sflags);
|
||||
} catch (ArgumentException e) {
|
||||
err = e.Message;
|
||||
}
|
||||
if (err != null) {
|
||||
return pushError(luaState,err);
|
||||
}
|
||||
} else {
|
||||
return pushError(luaState,"second argument must be a integer or a string");
|
||||
}
|
||||
pushObject(luaState,res,"luaNet_metatable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes a type reference into the stack
|
||||
*/
|
||||
internal void pushType(IntPtr luaState, Type t)
|
||||
{
|
||||
pushObject(luaState,new ProxyType(t),"luaNet_class");
|
||||
}
|
||||
/*
|
||||
* Pushes a delegate into the stack
|
||||
*/
|
||||
internal void pushFunction(IntPtr luaState, LuaCSFunction func)
|
||||
{
|
||||
pushObject(luaState,func,"luaNet_function");
|
||||
}
|
||||
/*
|
||||
* Pushes a CLR object into the Lua stack as an userdata
|
||||
* with the provided metatable
|
||||
*/
|
||||
internal void pushObject(IntPtr luaState, object o, string metatable)
|
||||
{
|
||||
int index = -1;
|
||||
// Pushes nil
|
||||
if(o==null)
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
return;
|
||||
}
|
||||
|
||||
// Object already in the list of Lua objects? Push the stored reference.
|
||||
bool found = objectsBackMap.TryGetValue(o, out index);
|
||||
if(found)
|
||||
{
|
||||
LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
|
||||
LuaDLL.lua_rawgeti(luaState,-1,index);
|
||||
|
||||
// Note: starting with lua5.1 the garbage collector may remove weak reference items (such as our luaNet_objects values) when the initial GC sweep
|
||||
// occurs, but the actual call of the __gc finalizer for that object may not happen until a little while later. During that window we might call
|
||||
// this routine and find the element missing from luaNet_objects, but collectObject() has not yet been called. In that case, we go ahead and call collect
|
||||
// object here
|
||||
// did we find a non nil object in our table? if not, we need to call collect object
|
||||
LuaTypes type = LuaDLL.lua_type(luaState, -1);
|
||||
if (type != LuaTypes.LUA_TNIL)
|
||||
{
|
||||
LuaDLL.lua_remove(luaState, -2); // drop the metatable - we're going to leave our object on the stack
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// MetaFunctions.dumpStack(this, luaState);
|
||||
LuaDLL.lua_remove(luaState, -1); // remove the nil object value
|
||||
LuaDLL.lua_remove(luaState, -1); // remove the metatable
|
||||
|
||||
collectObject(o, index); // Remove from both our tables and fall out to get a new ID
|
||||
}
|
||||
index = addObject(o);
|
||||
|
||||
pushNewObject(luaState,o,index,metatable);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pushes a new object into the Lua stack with the provided
|
||||
* metatable
|
||||
*/
|
||||
private void pushNewObject(IntPtr luaState,object o,int index,string metatable)
|
||||
{
|
||||
if(metatable=="luaNet_metatable")
|
||||
{
|
||||
// Gets or creates the metatable for the object's type
|
||||
LuaDLL.luaL_getmetatable(luaState,o.GetType().AssemblyQualifiedName);
|
||||
|
||||
if(LuaDLL.lua_isnil(luaState,-1))
|
||||
{
|
||||
LuaDLL.lua_settop(luaState,-2);
|
||||
LuaDLL.luaL_newmetatable(luaState,o.GetType().AssemblyQualifiedName);
|
||||
LuaDLL.lua_pushstring(luaState,"cache");
|
||||
LuaDLL.lua_newtable(luaState);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
LuaDLL.lua_pushlightuserdata(luaState,LuaDLL.luanet_gettag());
|
||||
LuaDLL.lua_pushnumber(luaState,1);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__index");
|
||||
LuaDLL.lua_pushstring(luaState,"luaNet_indexfunction");
|
||||
LuaDLL.lua_rawget(luaState, (int) LuaIndexes.LUA_REGISTRYINDEX);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__gc");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.gcFunction);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__tostring");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.toStringFunction);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
LuaDLL.lua_pushstring(luaState,"__newindex");
|
||||
LuaDLL.lua_pushstdcallcfunction(luaState,metaFunctions.newindexFunction);
|
||||
LuaDLL.lua_rawset(luaState,-3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LuaDLL.luaL_getmetatable(luaState,metatable);
|
||||
}
|
||||
|
||||
// Stores the object index in the Lua list and pushes the
|
||||
// index into the Lua stack
|
||||
LuaDLL.luaL_getmetatable(luaState,"luaNet_objects");
|
||||
LuaDLL.luanet_newudata(luaState,index);
|
||||
LuaDLL.lua_pushvalue(luaState,-3);
|
||||
LuaDLL.lua_remove(luaState,-4);
|
||||
LuaDLL.lua_setmetatable(luaState,-2);
|
||||
LuaDLL.lua_pushvalue(luaState,-1);
|
||||
LuaDLL.lua_rawseti(luaState,-3,index);
|
||||
LuaDLL.lua_remove(luaState,-2);
|
||||
}
|
||||
/*
|
||||
* Gets an object from the Lua stack with the desired type, if it matches, otherwise
|
||||
* returns null.
|
||||
*/
|
||||
internal object getAsType(IntPtr luaState,int stackPos,Type paramType)
|
||||
{
|
||||
ExtractValue extractor=typeChecker.checkType(luaState,stackPos,paramType);
|
||||
if(extractor!=null) return extractor(luaState,stackPos);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Given the Lua int ID for an object remove it from our maps
|
||||
/// </summary>
|
||||
/// <param name="udata"></param>
|
||||
internal void collectObject(int udata)
|
||||
{
|
||||
object o;
|
||||
bool found = objects.TryGetValue(udata, out o);
|
||||
|
||||
// The other variant of collectObject might have gotten here first, in that case we will silently ignore the missing entry
|
||||
if (found)
|
||||
{
|
||||
// Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
|
||||
|
||||
objects.Remove(udata);
|
||||
objectsBackMap.Remove(o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Given an object reference, remove it from our maps
|
||||
/// </summary>
|
||||
/// <param name="udata"></param>
|
||||
void collectObject(object o, int udata)
|
||||
{
|
||||
// Debug.WriteLine("Removing " + o.ToString() + " @ " + udata);
|
||||
|
||||
objects.Remove(udata);
|
||||
objectsBackMap.Remove(o);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// We want to ensure that objects always have a unique ID
|
||||
/// </summary>
|
||||
int nextObj = 0;
|
||||
|
||||
int addObject(object obj)
|
||||
{
|
||||
// New object: inserts it in the list
|
||||
int index = nextObj++;
|
||||
|
||||
// Debug.WriteLine("Adding " + obj.ToString() + " @ " + index);
|
||||
|
||||
objects[index] = obj;
|
||||
objectsBackMap[obj] = index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Gets an object from the Lua stack according to its Lua type.
|
||||
*/
|
||||
internal object getObject(IntPtr luaState,int index)
|
||||
{
|
||||
LuaTypes type=LuaDLL.lua_type(luaState,index);
|
||||
switch(type)
|
||||
{
|
||||
case LuaTypes.LUA_TNUMBER:
|
||||
{
|
||||
return LuaDLL.lua_tonumber(luaState,index);
|
||||
}
|
||||
case LuaTypes.LUA_TSTRING:
|
||||
{
|
||||
return LuaDLL.lua_tostring(luaState,index);
|
||||
}
|
||||
case LuaTypes.LUA_TBOOLEAN:
|
||||
{
|
||||
return LuaDLL.lua_toboolean(luaState,index);
|
||||
}
|
||||
case LuaTypes.LUA_TTABLE:
|
||||
{
|
||||
return getTable(luaState,index);
|
||||
}
|
||||
case LuaTypes.LUA_TFUNCTION:
|
||||
{
|
||||
return getFunction(luaState,index);
|
||||
}
|
||||
case LuaTypes.LUA_TUSERDATA:
|
||||
{
|
||||
int udata=LuaDLL.luanet_tonetobject(luaState,index);
|
||||
if(udata!=-1)
|
||||
return objects[udata];
|
||||
else
|
||||
return getUserData(luaState,index);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Gets the table in the index positon of the Lua stack.
|
||||
*/
|
||||
internal LuaTable getTable(IntPtr luaState,int index)
|
||||
{
|
||||
LuaDLL.lua_pushvalue(luaState,index);
|
||||
return new LuaTable(LuaDLL.lua_ref(luaState,1),interpreter);
|
||||
}
|
||||
/*
|
||||
* Gets the userdata in the index positon of the Lua stack.
|
||||
*/
|
||||
internal LuaUserData getUserData(IntPtr luaState,int index)
|
||||
{
|
||||
LuaDLL.lua_pushvalue(luaState,index);
|
||||
return new LuaUserData(LuaDLL.lua_ref(luaState,1),interpreter);
|
||||
}
|
||||
/*
|
||||
* Gets the function in the index positon of the Lua stack.
|
||||
*/
|
||||
internal LuaFunction getFunction(IntPtr luaState,int index)
|
||||
{
|
||||
LuaDLL.lua_pushvalue(luaState,index);
|
||||
return new LuaFunction(LuaDLL.lua_ref(luaState,1),interpreter);
|
||||
}
|
||||
/*
|
||||
* Gets the CLR object in the index positon of the Lua stack. Returns
|
||||
* delegates as Lua functions.
|
||||
*/
|
||||
internal object getNetObject(IntPtr luaState,int index)
|
||||
{
|
||||
int idx=LuaDLL.luanet_tonetobject(luaState,index);
|
||||
if(idx!=-1)
|
||||
return objects[idx];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
* Gets the CLR object in the index positon of the Lua stack. Returns
|
||||
* delegates as is.
|
||||
*/
|
||||
internal object getRawNetObject(IntPtr luaState,int index)
|
||||
{
|
||||
int udata=LuaDLL.luanet_rawnetobj(luaState,index);
|
||||
if(udata!=-1)
|
||||
{
|
||||
return objects[udata];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
* Pushes the entire array into the Lua stack and returns the number
|
||||
* of elements pushed.
|
||||
*/
|
||||
internal int returnValues(IntPtr luaState, object[] returnValues)
|
||||
{
|
||||
if(LuaDLL.lua_checkstack(luaState,returnValues.Length+5))
|
||||
{
|
||||
for(int i=0;i<returnValues.Length;i++)
|
||||
{
|
||||
push(luaState,returnValues[i]);
|
||||
}
|
||||
return returnValues.Length;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Gets the values from the provided index to
|
||||
* the top of the stack and returns them in an array.
|
||||
*/
|
||||
internal object[] popValues(IntPtr luaState,int oldTop)
|
||||
{
|
||||
int newTop=LuaDLL.lua_gettop(luaState);
|
||||
if(oldTop==newTop)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayList returnValues=new ArrayList();
|
||||
for(int i=oldTop+1;i<=newTop;i++)
|
||||
{
|
||||
returnValues.Add(getObject(luaState,i));
|
||||
}
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return returnValues.ToArray();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Gets the values from the provided index to
|
||||
* the top of the stack and returns them in an array, casting
|
||||
* them to the provided types.
|
||||
*/
|
||||
internal object[] popValues(IntPtr luaState,int oldTop,Type[] popTypes)
|
||||
{
|
||||
int newTop=LuaDLL.lua_gettop(luaState);
|
||||
if(oldTop==newTop)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iTypes;
|
||||
ArrayList returnValues=new ArrayList();
|
||||
if(popTypes[0] == typeof(void))
|
||||
iTypes=1;
|
||||
else
|
||||
iTypes=0;
|
||||
for(int i=oldTop+1;i<=newTop;i++)
|
||||
{
|
||||
returnValues.Add(getAsType(luaState,i,popTypes[iTypes]));
|
||||
iTypes++;
|
||||
}
|
||||
LuaDLL.lua_settop(luaState,oldTop);
|
||||
return returnValues.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// kevinh - the following line doesn't work for remoting proxies - they always return a match for 'is'
|
||||
// else if(o is ILuaGeneratedType)
|
||||
static bool IsILua(object o)
|
||||
{
|
||||
if (o is ILuaGeneratedType)
|
||||
{
|
||||
// Make sure we are _really_ ILuaGenerated
|
||||
Type type = o.GetType();
|
||||
|
||||
return type.GetInterface("ILuaGeneratedType") != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes the object into the Lua stack according to its type.
|
||||
*/
|
||||
internal void push(IntPtr luaState, object o)
|
||||
{
|
||||
if(o==null)
|
||||
{
|
||||
LuaDLL.lua_pushnil(luaState);
|
||||
}
|
||||
else if(o is sbyte || o is byte || o is short || o is ushort ||
|
||||
o is int || o is uint || o is long || o is float ||
|
||||
o is ulong || o is decimal || o is double)
|
||||
{
|
||||
double d=Convert.ToDouble(o);
|
||||
LuaDLL.lua_pushnumber(luaState,d);
|
||||
}
|
||||
else if(o is char)
|
||||
{
|
||||
double d = (char)o;
|
||||
LuaDLL.lua_pushnumber(luaState,d);
|
||||
}
|
||||
else if(o is string)
|
||||
{
|
||||
string str=(string)o;
|
||||
LuaDLL.lua_pushstring(luaState,str);
|
||||
}
|
||||
else if(o is bool)
|
||||
{
|
||||
bool b=(bool)o;
|
||||
LuaDLL.lua_pushboolean(luaState,b);
|
||||
}
|
||||
else if(IsILua(o))
|
||||
{
|
||||
(((ILuaGeneratedType)o).__luaInterface_getLuaTable()).push(luaState);
|
||||
}
|
||||
else if(o is LuaTable)
|
||||
{
|
||||
((LuaTable)o).push(luaState);
|
||||
}
|
||||
else if(o is LuaCSFunction)
|
||||
{
|
||||
pushFunction(luaState,(LuaCSFunction)o);
|
||||
}
|
||||
else if(o is LuaFunction)
|
||||
{
|
||||
((LuaFunction)o).push(luaState);
|
||||
}
|
||||
else
|
||||
{
|
||||
pushObject(luaState,o,"luaNet_metatable");
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Checks if the method matches the arguments in the Lua stack, getting
|
||||
* the arguments if it does.
|
||||
*/
|
||||
internal bool matchParameters(IntPtr luaState,MethodBase method,ref MethodCache methodCache)
|
||||
{
|
||||
return metaFunctions.matchParameters(luaState,method,ref methodCache);
|
||||
}
|
||||
|
||||
internal Array tableToArray(object luaParamValue, Type paramArrayType) {
|
||||
return metaFunctions.TableToArray(luaParamValue,paramArrayType);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
LuaInterface/ProxyType.cs
Normal file
94
LuaInterface/ProxyType.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LuaInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for ProxyType.
|
||||
/// </summary>
|
||||
public class ProxyType : IReflect
|
||||
{
|
||||
|
||||
Type proxy;
|
||||
|
||||
public ProxyType(Type proxy)
|
||||
{
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide human readable short hand for this proxy object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "ProxyType(" + UnderlyingSystemType + ")";
|
||||
}
|
||||
|
||||
|
||||
public Type UnderlyingSystemType
|
||||
{
|
||||
get
|
||||
{
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
public FieldInfo GetField(string name, BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetField(name, bindingAttr);
|
||||
}
|
||||
|
||||
public FieldInfo[] GetFields(BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetFields(bindingAttr);
|
||||
}
|
||||
|
||||
public MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetMember(name, bindingAttr);
|
||||
}
|
||||
|
||||
public MemberInfo[] GetMembers(BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetMembers(bindingAttr);
|
||||
}
|
||||
|
||||
public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetMethod(name, bindingAttr);
|
||||
}
|
||||
|
||||
public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
|
||||
{
|
||||
return proxy.GetMethod(name, bindingAttr, binder, types, modifiers);
|
||||
}
|
||||
|
||||
public MethodInfo[] GetMethods(BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetMethods(bindingAttr);
|
||||
}
|
||||
|
||||
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetProperty(name, bindingAttr);
|
||||
}
|
||||
|
||||
public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
|
||||
{
|
||||
return proxy.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
|
||||
}
|
||||
|
||||
public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
|
||||
{
|
||||
return proxy.GetProperties(bindingAttr);
|
||||
}
|
||||
|
||||
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
|
||||
{
|
||||
return proxy.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
44
Makefile
44
Makefile
@@ -48,8 +48,10 @@ prefix ?= /usr/local
|
||||
datarootdir ?= $(prefix)/share
|
||||
datadir ?= $(datarootdir)
|
||||
bindir ?= $(prefix)/bin
|
||||
libexecdir ?= $(prefix)/lib
|
||||
BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
|
||||
DATA_INSTALL_DIR = $(DESTDIR)$(datadir)/openra
|
||||
# TODO: separate data and binaries properly
|
||||
DATA_INSTALL_DIR = $(DESTDIR)$(libexecdir)/openra
|
||||
|
||||
# install tools
|
||||
RM = rm
|
||||
@@ -63,7 +65,7 @@ INSTALL_PROGRAM = $(INSTALL) -m755
|
||||
INSTALL_DATA = $(INSTALL) -m644
|
||||
|
||||
# program targets
|
||||
CORE = fileformats rcg rgl rsdl rnull game utility geoip irc
|
||||
CORE = fileformats rcg rgl rsdl rnull game utility geoip irc lua
|
||||
TOOLS = editor tsbuild ralint
|
||||
|
||||
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
|
||||
@@ -77,7 +79,7 @@ fileformats_SRCS := $(shell find OpenRA.FileFormats/ -iname '*.cs')
|
||||
fileformats_TARGET = OpenRA.FileFormats.dll
|
||||
fileformats_KIND = library
|
||||
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll System.Windows.Forms.dll
|
||||
PROGRAMS = fileformats
|
||||
PROGRAMS = fileformats
|
||||
fileformats: $(fileformats_TARGET)
|
||||
|
||||
geoip_SRCS := $(shell find GeoIP/ -iname '*.cs')
|
||||
@@ -105,6 +107,14 @@ irc_LIBS = $(COMMON_LIBS) $(irc_DEPS)
|
||||
PROGRAMS += irc
|
||||
irc: $(irc_TARGET)
|
||||
|
||||
lua_SRCS := $(shell find LuaInterface/ -name '*.cs')
|
||||
lua_TARGET = LuaInterface.dll
|
||||
lua_KIND = library
|
||||
lua_DEPS = $(fileformats_TARGET)
|
||||
lua_LIBS = $(COMMON_LIBS) $(lua_DEPS)
|
||||
PROGRAMS += lua
|
||||
lua: $(lua_TARGET)
|
||||
|
||||
# Renderer dlls
|
||||
rsdl_SRCS := $(shell find OpenRA.Renderer.SdlCommon/ -iname '*.cs')
|
||||
rsdl_TARGET = OpenRA.Renderer.SdlCommon.dll
|
||||
@@ -126,13 +136,13 @@ rgl_DEPS = $(fileformats_TARGET) $(game_TARGET) $(rsdl_TARGET)
|
||||
rgl_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.OpenGl.dll $(rgl_DEPS)
|
||||
|
||||
rsdl2_SRCS := $(shell find OpenRA.Renderer.Sdl2/ -iname '*.cs')
|
||||
rsdl2_TARGET = OpenRA.Renderer.Sdl2.dll
|
||||
rsdl2_TARGET = OpenRA.Renderer.Sdl2.dll
|
||||
rsdl2_KIND = library
|
||||
rsdl2_DEPS = $(fileformats_TARGET) $(game_TARGET) $(rsdl_TARGET) $(rgl_TARGET)
|
||||
rsdl2_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.OpenGl.dll thirdparty/SDL2\#.dll $(rsdl2_DEPS)
|
||||
|
||||
rnull_SRCS := $(shell find OpenRA.Renderer.Null/ -iname '*.cs')
|
||||
rnull_TARGET = OpenRA.Renderer.Null.dll
|
||||
rnull_TARGET = OpenRA.Renderer.Null.dll
|
||||
rnull_KIND = library
|
||||
rnull_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
rnull_LIBS = $(COMMON_LIBS) $(rnull_DEPS)
|
||||
@@ -149,8 +159,8 @@ STD_MOD_DEPS = $(STD_MOD_LIBS) $(ralint_TARGET)
|
||||
mod_ra_SRCS := $(shell find OpenRA.Mods.RA/ -iname '*.cs')
|
||||
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
|
||||
mod_ra_KIND = library
|
||||
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET)
|
||||
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET)
|
||||
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET) $(lua_TARGET)
|
||||
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET) $(lua_TARGET)
|
||||
PROGRAMS += mod_ra
|
||||
mod_ra: $(mod_ra_TARGET)
|
||||
|
||||
@@ -189,7 +199,7 @@ editor_TARGET = OpenRA.Editor.exe
|
||||
editor_KIND = winexe
|
||||
editor_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
editor_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll System.Data.dll $(editor_DEPS)
|
||||
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.MapSelect.resources
|
||||
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.MapSelect.resources
|
||||
editor_FLAGS = -win32icon:OpenRA.Editor/OpenRA.Editor.Icon.ico
|
||||
|
||||
PROGRAMS += editor
|
||||
@@ -225,7 +235,7 @@ tsbuild_KIND = winexe
|
||||
tsbuild_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
tsbuild_LIBS = $(COMMON_LIBS) $(tsbuild_DEPS) System.Windows.Forms.dll
|
||||
tsbuild_EXTRA = -resource:OpenRA.TilesetBuilder.FormBuilder.resources -resource:OpenRA.TilesetBuilder.FormNew.resources -resource:OpenRA.TilesetBuilder.Surface.resources
|
||||
PROGRAMS += tsbuild
|
||||
PROGRAMS += tsbuild
|
||||
OpenRA.TilesetBuilder.FormBuilder.resources:
|
||||
resgen2 OpenRA.TilesetBuilder/FormBuilder.resx OpenRA.TilesetBuilder.FormBuilder.resources 1> /dev/null
|
||||
OpenRA.TilesetBuilder.FormNew.resources:
|
||||
@@ -243,7 +253,7 @@ utility_TARGET = OpenRA.Utility.exe
|
||||
utility_KIND = exe
|
||||
utility_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) thirdparty/ICSharpCode.SharpZipLib.dll System.Windows.Forms.dll
|
||||
PROGRAMS += utility
|
||||
PROGRAMS += utility
|
||||
utility: $(utility_TARGET)
|
||||
|
||||
|
||||
@@ -293,6 +303,8 @@ distclean: clean
|
||||
|
||||
dependencies:
|
||||
@ $(CP_R) thirdparty/*.dl* .
|
||||
@ $(CP_R) thirdparty/*.dylib .
|
||||
@ $(CP_R) thirdparty/*.so .
|
||||
@ $(CP_R) thirdparty/Tao/* .
|
||||
|
||||
version: mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml
|
||||
@@ -336,12 +348,16 @@ install-core: default
|
||||
@$(INSTALL_PROGRAM) thirdparty/SharpFont.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(CP) thirdparty/SharpFont.dll.config "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) thirdparty/Mono.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(CP) thirdparty/LuaInterface.dll.config "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) thirdparty/liblua-linux32.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) thirdparty/liblua-linux64.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) thirdparty/liblua-osx.dylib "$(DATA_INSTALL_DIR)"
|
||||
|
||||
@echo "#!/bin/sh" > openra
|
||||
@echo 'BINDIR=$$(dirname $$(readlink -f $$0))' >> openra
|
||||
@echo 'ROOTDIR="$${BINDIR%'"$(bindir)"'}"' >> openra
|
||||
@echo 'DATADIR="$${ROOTDIR}'"$(datadir)"'"' >> openra
|
||||
@echo 'cd "$${DATADIR}/openra"' >> openra
|
||||
@echo 'EXECDIR="$${ROOTDIR}'"$(libexecdir)"'"' >> openra
|
||||
@echo 'cd "$${EXECDIR}/openra"' >> openra
|
||||
@echo 'exec mono OpenRA.Game.exe "$$@"' >> openra
|
||||
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx openra "$(BIN_INSTALL_DIR)"
|
||||
@@ -355,8 +371,8 @@ install-tools: tools
|
||||
@echo "#!/bin/sh" > openra-editor
|
||||
@echo 'BINDIR=$$(dirname $$(readlink -f $$0))' >> openra-editor
|
||||
@echo 'ROOTDIR="$${BINDIR%'"$(bindir)"'}"' >> openra-editor
|
||||
@echo 'DATADIR="$${ROOTDIR}/'"$(datadir)"'"' >> openra-editor
|
||||
@echo 'cd "$${DATADIR}/openra"' >> openra-editor
|
||||
@echo 'EXECDIR="$${ROOTDIR}'"$(libexecdir)"'"' >> openra-editor
|
||||
@echo 'cd "$${EXECDIR}/openra"' >> openra-editor
|
||||
@echo 'exec mono OpenRA.Editor.exe "$$@"' >> openra-editor
|
||||
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx openra-editor "$(BIN_INSTALL_DIR)"
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.FileFormats
|
||||
Folders, MapFolders, Rules, ServerTraits,
|
||||
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Notifications, Music, Movies, Translations, TileSets,
|
||||
ChromeMetrics, PackageContents;
|
||||
ChromeMetrics, PackageContents, LuaScripts;
|
||||
|
||||
public readonly Dictionary<string, string> Packages;
|
||||
public readonly MiniYaml LoadScreen;
|
||||
@@ -59,6 +59,7 @@ namespace OpenRA.FileFormats
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
||||
PackageContents = YamlList(yaml, "PackageContents");
|
||||
LuaScripts = YamlList(yaml, "LuaScripts");
|
||||
|
||||
LoadScreen = yaml["LoadScreen"];
|
||||
LobbyDefaults = yaml["LobbyDefaults"];
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA
|
||||
|
||||
public class Map
|
||||
{
|
||||
[FieldLoader.Ignore] IFolder container;
|
||||
[FieldLoader.Ignore] public IFolder Container;
|
||||
public string Path { get; private set; }
|
||||
|
||||
// Yaml map data
|
||||
@@ -132,7 +132,7 @@ namespace OpenRA
|
||||
|
||||
void AssertExists(string filename)
|
||||
{
|
||||
using (var s = container.GetContent(filename))
|
||||
using (var s = Container.GetContent(filename))
|
||||
if (s == null)
|
||||
throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
|
||||
}
|
||||
@@ -142,12 +142,12 @@ namespace OpenRA
|
||||
public Map(string path)
|
||||
{
|
||||
Path = path;
|
||||
container = FileSystem.OpenPackage(path, null, int.MaxValue);
|
||||
Container = FileSystem.OpenPackage(path, null, int.MaxValue);
|
||||
|
||||
AssertExists("map.yaml");
|
||||
AssertExists("map.bin");
|
||||
|
||||
var yaml = new MiniYaml(null, MiniYaml.FromStream(container.GetContent("map.yaml")));
|
||||
var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));
|
||||
FieldLoader.Load(this, yaml);
|
||||
Uid = ComputeHash();
|
||||
|
||||
@@ -263,17 +263,17 @@ namespace OpenRA
|
||||
|
||||
// Create a new map package
|
||||
// TODO: Add other files (custom assets) to the entries list
|
||||
container = FileSystem.CreatePackage(Path, int.MaxValue, entries);
|
||||
Container = FileSystem.CreatePackage(Path, int.MaxValue, entries);
|
||||
}
|
||||
|
||||
// Update existing package
|
||||
container.Write(entries);
|
||||
Container.Write(entries);
|
||||
}
|
||||
|
||||
public TileReference<ushort, byte>[,] LoadMapTiles()
|
||||
{
|
||||
var tiles = new TileReference<ushort, byte>[MapSize.X, MapSize.Y];
|
||||
using (var dataStream = container.GetContent("map.bin"))
|
||||
using (var dataStream = Container.GetContent("map.bin"))
|
||||
{
|
||||
if (dataStream.ReadUInt8() != 1)
|
||||
throw new InvalidDataException("Unknown binary map format");
|
||||
@@ -305,7 +305,7 @@ namespace OpenRA
|
||||
{
|
||||
var resources = new TileReference<byte, byte>[MapSize.X, MapSize.Y];
|
||||
|
||||
using (var dataStream = container.GetContent("map.bin"))
|
||||
using (var dataStream = Container.GetContent("map.bin"))
|
||||
{
|
||||
if (dataStream.ReadUInt8() != 1)
|
||||
throw new InvalidDataException("Unknown binary map format");
|
||||
@@ -391,8 +391,8 @@ namespace OpenRA
|
||||
{
|
||||
// UID is calculated by taking an SHA1 of the yaml and binary data
|
||||
// Read the relevant data into a buffer
|
||||
var data = container.GetContent("map.yaml").ReadAllBytes()
|
||||
.Concat(container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||
var data = Container.GetContent("map.yaml").ReadAllBytes()
|
||||
.Concat(Container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||
|
||||
// Take the SHA1
|
||||
using (var csp = SHA1.Create())
|
||||
|
||||
@@ -19,13 +19,15 @@ namespace OpenRA
|
||||
{
|
||||
public class ObjectCreator
|
||||
{
|
||||
Pair<Assembly, string>[] modAssemblies;
|
||||
Pair<Assembly, string>[] assemblies;
|
||||
|
||||
public ObjectCreator(Manifest manifest)
|
||||
{
|
||||
// All the core namespaces
|
||||
var asms = typeof(Game).Assembly.GetNamespaces()
|
||||
var asms = typeof(Game).Assembly.GetNamespaces() // Game
|
||||
.Select(c => Pair.New(typeof(Game).Assembly, c))
|
||||
.Concat(typeof(Mod).Assembly.GetNamespaces() // FileFormats
|
||||
.Select(c => Pair.New(typeof(Mod).Assembly, c)))
|
||||
.ToList();
|
||||
|
||||
// Namespaces from each mod assembly
|
||||
@@ -35,7 +37,7 @@ namespace OpenRA
|
||||
asms.AddRange(asm.GetNamespaces().Select(ns => Pair.New(asm, ns)));
|
||||
}
|
||||
|
||||
modAssemblies = asms.ToArray();
|
||||
assemblies = asms.ToArray();
|
||||
}
|
||||
|
||||
public static Action<string> MissingTypeAction =
|
||||
@@ -48,24 +50,30 @@ namespace OpenRA
|
||||
|
||||
public T CreateObject<T>(string className, Dictionary<string, object> args)
|
||||
{
|
||||
foreach (var mod in modAssemblies)
|
||||
var type = FindType(className);
|
||||
if (type == null)
|
||||
{
|
||||
var type = mod.First.GetType(mod.Second + "." + className, false);
|
||||
if (type == null) continue;
|
||||
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
||||
var ctors = type.GetConstructors(flags)
|
||||
.Where(x => x.HasAttribute<UseCtorAttribute>()).ToList();
|
||||
|
||||
if (ctors.Count == 0)
|
||||
return (T)CreateBasic(type);
|
||||
else if (ctors.Count == 1)
|
||||
return (T)CreateUsingArgs(ctors[0], args);
|
||||
else
|
||||
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
|
||||
MissingTypeAction(className);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
MissingTypeAction(className);
|
||||
return default(T);
|
||||
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
|
||||
var ctors = type.GetConstructors(flags)
|
||||
.Where(x => x.HasAttribute<UseCtorAttribute>()).ToList();
|
||||
|
||||
if (ctors.Count == 0)
|
||||
return (T)CreateBasic(type);
|
||||
else if (ctors.Count == 1)
|
||||
return (T)CreateUsingArgs(ctors[0], args);
|
||||
else
|
||||
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
|
||||
}
|
||||
|
||||
public Type FindType(string className)
|
||||
{
|
||||
return assemblies
|
||||
.Select(pair => pair.First.GetType(pair.Second + "." + className, false))
|
||||
.FirstOrDefault(t => t != null);
|
||||
}
|
||||
|
||||
public object CreateBasic(Type type)
|
||||
@@ -90,7 +98,7 @@ namespace OpenRA
|
||||
public IEnumerable<Type> GetTypesImplementing<T>()
|
||||
{
|
||||
var it = typeof(T);
|
||||
return modAssemblies.Select(ma => ma.First).Distinct()
|
||||
return assemblies.Select(ma => ma.First).Distinct()
|
||||
.SelectMany(ma => ma.GetTypes()
|
||||
.Where(t => t != it && it.IsAssignableFrom(t)));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Mods.RA.Air;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Mods.RA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
@@ -338,8 +338,10 @@
|
||||
<Compile Include="RepairsUnits.cs" />
|
||||
<Compile Include="Reservable.cs" />
|
||||
<Compile Include="ScaredyCat.cs" />
|
||||
<Compile Include="Scripting\LuaScriptInterface.cs" />
|
||||
<Compile Include="Scripting\Media.cs" />
|
||||
<Compile Include="Scripting\RASpecialPowers.cs" />
|
||||
<Compile Include="Scripting\LuaScriptContext.cs" />
|
||||
<Compile Include="SeedsResource.cs" />
|
||||
<Compile Include="SelfHealing.cs" />
|
||||
<Compile Include="Sellable.cs" />
|
||||
@@ -476,6 +478,10 @@
|
||||
<Compile Include="Effects\Rank.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LuaInterface\LuaInterface.csproj">
|
||||
<Project>{e915a0a4-2641-4f7e-8a88-8f123fa88bf1}</Project>
|
||||
<Name>LuaInterface</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
<Project>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</Project>
|
||||
<Name>OpenRA.FileFormats</Name>
|
||||
|
||||
134
OpenRA.Mods.RA/Scripting/LuaScriptContext.cs
Normal file
134
OpenRA.Mods.RA/Scripting/LuaScriptContext.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 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.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using LuaInterface;
|
||||
|
||||
namespace OpenRA.Mods.RA.Scripting
|
||||
{
|
||||
public class LuaScriptContext : IDisposable
|
||||
{
|
||||
public Lua Lua { get; private set; }
|
||||
|
||||
public LuaScriptContext()
|
||||
{
|
||||
Log.Write("debug", "Creating Lua script context");
|
||||
Lua = new Lua();
|
||||
}
|
||||
|
||||
public void RegisterObject(object target, string tableName, bool exposeAllMethods)
|
||||
{
|
||||
Log.Write("debug", "Registering object {0}", target);
|
||||
|
||||
if (tableName != null && Lua.GetTable(tableName) == null)
|
||||
Lua.NewTable(tableName);
|
||||
|
||||
var type = target.GetType();
|
||||
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
|
||||
RegisterMethods(tableName, target, methods, exposeAllMethods);
|
||||
}
|
||||
|
||||
public void RegisterType(Type type, string tableName, bool exposeAllMethods)
|
||||
{
|
||||
Log.Write("debug", "Registering type {0}", type);
|
||||
|
||||
if (tableName != null && Lua.GetTable(tableName) == null)
|
||||
Lua.NewTable(tableName);
|
||||
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
|
||||
RegisterMethods(tableName, null, methods, exposeAllMethods);
|
||||
}
|
||||
|
||||
void RegisterMethods(string tableName, object target, IEnumerable<MethodInfo> methods, bool allMethods)
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
string methodName;
|
||||
|
||||
var attr = method.GetCustomAttributes<LuaGlobalAttribute>(true).FirstOrDefault();
|
||||
if (attr == null)
|
||||
{
|
||||
if (allMethods)
|
||||
methodName = method.Name;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
methodName = attr.Name ?? method.Name;
|
||||
|
||||
var methodTarget = method.IsStatic ? null : target;
|
||||
|
||||
if (tableName != null)
|
||||
Lua.RegisterFunction(tableName + "." + methodName, methodTarget, method);
|
||||
else
|
||||
Lua.RegisterFunction(methodName, methodTarget, method);
|
||||
}
|
||||
}
|
||||
|
||||
void LogException(Exception e)
|
||||
{
|
||||
Game.Debug("{0}", e.Message);
|
||||
Game.Debug("See debug.log for details");
|
||||
Log.Write("debug", "{0}", e);
|
||||
}
|
||||
|
||||
public void LoadLuaScripts(Func<string, string> getFileContents, params string[] files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Write("debug", "Loading Lua script {0}", file);
|
||||
var content = getFileContents(file);
|
||||
Lua.DoString(content, file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object[] InvokeLuaFunction(string name, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var function = Lua.GetFunction(name);
|
||||
if (function == null)
|
||||
return null;
|
||||
return function.Call(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Lua == null)
|
||||
return;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
Lua.Dispose();
|
||||
Lua = null;
|
||||
}
|
||||
|
||||
~LuaScriptContext()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
165
OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs
Normal file
165
OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 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.Linq;
|
||||
using LuaInterface;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using WorldRenderer = OpenRA.Graphics.WorldRenderer;
|
||||
|
||||
namespace OpenRA.Mods.RA.Scripting
|
||||
{
|
||||
public class LuaScriptInterfaceInfo : ITraitInfo, Requires<SpawnMapActorsInfo>
|
||||
{
|
||||
public readonly string[] LuaScripts = { };
|
||||
|
||||
public object Create(ActorInitializer init) { return new LuaScriptInterface(this); }
|
||||
}
|
||||
|
||||
public class LuaScriptInterface : IWorldLoaded, ITick
|
||||
{
|
||||
World world;
|
||||
readonly LuaScriptContext context = new LuaScriptContext();
|
||||
readonly LuaScriptInterfaceInfo info;
|
||||
|
||||
public LuaScriptInterface(LuaScriptInterfaceInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
world = w;
|
||||
AddMapActorGlobals();
|
||||
context.Lua["World"] = w;
|
||||
context.Lua["WorldRenderer"] = wr;
|
||||
context.RegisterObject(this, "_OpenRA", false);
|
||||
context.RegisterType(typeof(WVec), "WVec", true);
|
||||
context.RegisterType(typeof(WPos), "WPos", true);
|
||||
context.RegisterType(typeof(CPos), "CPos", true);
|
||||
context.RegisterType(typeof(WRot), "WRot", true);
|
||||
context.RegisterType(typeof(WAngle), "WAngle", true);
|
||||
context.RegisterType(typeof(WRange), "WRange", true);
|
||||
context.RegisterType(typeof(int2), "int2", true);
|
||||
context.RegisterType(typeof(float2), "float2", true);
|
||||
var sharedScripts = Game.modData.Manifest.LuaScripts ?? new string[0];
|
||||
if (sharedScripts.Any())
|
||||
context.LoadLuaScripts(f => FileSystem.Open(f).ReadAllText(), sharedScripts);
|
||||
context.LoadLuaScripts(f => w.Map.Container.GetContent(f).ReadAllText(), info.LuaScripts);
|
||||
context.InvokeLuaFunction("WorldLoaded");
|
||||
}
|
||||
|
||||
void AddMapActorGlobals()
|
||||
{
|
||||
foreach (var kv in world.WorldActor.Trait<SpawnMapActors>().Actors)
|
||||
context.Lua[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
context.InvokeLuaFunction("Tick");
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public object New(string typeName, LuaTable args)
|
||||
{
|
||||
var type = Game.modData.ObjectCreator.FindType(typeName);
|
||||
if (type == null)
|
||||
throw new InvalidOperationException("Cannot locate type: {0}".F(typeName));
|
||||
if (args == null)
|
||||
return Activator.CreateInstance(type);
|
||||
var argsArray = ConvertArgs(args);
|
||||
return Activator.CreateInstance(type, argsArray);
|
||||
}
|
||||
|
||||
object[] ConvertArgs(LuaTable args)
|
||||
{
|
||||
var argsArray = new object[args.Keys.Count];
|
||||
for (var i = 1; i <= args.Keys.Count; i++)
|
||||
{
|
||||
var arg = args[i] as LuaTable;
|
||||
if (arg != null && arg[1] != null && arg[2] != null)
|
||||
argsArray[i - 1] = Convert.ChangeType(arg[1], Enum<TypeCode>.Parse(arg[2].ToString()));
|
||||
else
|
||||
argsArray[i - 1] = args[i];
|
||||
}
|
||||
return argsArray;
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public void Debug(object obj)
|
||||
{
|
||||
if (obj != null)
|
||||
Game.Debug(obj.ToString());
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public object TraitOrDefault(Actor actor, string className)
|
||||
{
|
||||
var type = Game.modData.ObjectCreator.FindType(className);
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var method = typeof(Actor).GetMethod("TraitOrDefault");
|
||||
var genericMethod = method.MakeGenericMethod(type);
|
||||
return genericMethod.Invoke(actor, null);
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public object Trait(Actor actor, string className)
|
||||
{
|
||||
var ret = TraitOrDefault(actor, className);
|
||||
if (ret == null)
|
||||
throw new InvalidOperationException("Actor {0} does not have trait of type {1}".F(actor, className));
|
||||
return ret;
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public bool HasTrait(Actor actor, string className)
|
||||
{
|
||||
var ret = TraitOrDefault(actor, className);
|
||||
return ret != null;
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public object TraitInfoOrDefault(string actorType, string className)
|
||||
{
|
||||
var type = Game.modData.ObjectCreator.FindType(className);
|
||||
if (type == null || !Rules.Info.ContainsKey(actorType))
|
||||
return null;
|
||||
|
||||
return Rules.Info[actorType].Traits.GetOrDefault(type);
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public object TraitInfo(string actorType, string className)
|
||||
{
|
||||
var ret = TraitInfoOrDefault(actorType, className);
|
||||
if (ret == null)
|
||||
throw new InvalidOperationException("Actor type {0} does not have trait info of type {1}".F(actorType, className));
|
||||
return ret;
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public bool HasTraitInfo(string actorType, string className)
|
||||
{
|
||||
var ret = TraitInfoOrDefault(actorType, className);
|
||||
return ret != null;
|
||||
}
|
||||
|
||||
[LuaGlobal]
|
||||
public void RunAfterDelay(double delay, Action func)
|
||||
{
|
||||
world.AddFrameEndTask(w => w.Add(new DelayedAction((int)delay, func)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,8 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
namespace OpenRA.Mods.RA.Scripting
|
||||
{
|
||||
public class RASpecialPowers
|
||||
{
|
||||
|
||||
10
OpenRA.sln
10
OpenRA.sln
@@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Irc", "OpenRA.Irc\Op
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Renderer.Sdl2", "OpenRA.Renderer.Sdl2\OpenRA.Renderer.Sdl2.csproj", "{33D03738-C154-4028-8EA8-63A3C488A651}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuaInterface", "LuaInterface\LuaInterface.csproj", "{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -184,6 +186,14 @@ Global
|
||||
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{85B48234-8B31-4BE6-AF9C-665CC6866841}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{E915A0A4-2641-4F7E-8A88-8F123FA88BF1}.Release|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
StartupItem = OpenRA.Game\OpenRA.Game.csproj
|
||||
|
||||
@@ -20,11 +20,14 @@ cd ../..
|
||||
make install-all prefix="/usr" DESTDIR="$PWD/packaging/linux/$ROOTDIR"
|
||||
|
||||
# Launch scripts (executed by Desura)
|
||||
cp *.sh "$PWD/packaging/linux/$ROOTDIR/usr/share/openra/" || exit 3
|
||||
cp *.sh "$PWD/packaging/linux/$ROOTDIR/usr/lib/openra/" || exit 3
|
||||
|
||||
# Icons and .desktop files
|
||||
make install-shortcuts prefix="/usr" DESTDIR="$PWD/packaging/linux/$ROOTDIR"
|
||||
|
||||
# Remove Mac OS X libaries
|
||||
rm -rf "$PWD/packaging/linux/$ROOTDIR/usr/lib/openra/liblua-osx.dylib" || exit 3
|
||||
|
||||
cd packaging/linux
|
||||
|
||||
(
|
||||
|
||||
@@ -13,7 +13,7 @@ sed -i "s/{VERSION_FIELD}/$PKGVERSION/" openra.spec
|
||||
rootdir=`readlink -f $2`
|
||||
sed -i "s|{ROOT_DIR}|$rootdir|" openra.spec
|
||||
|
||||
for x in `find $rootdir/usr/share/openra -type d`
|
||||
for x in `find $rootdir/usr/lib/openra -type d`
|
||||
do
|
||||
y="${x#$rootdir}"
|
||||
sed -i "/%files/ a ${y}" openra.spec
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
%define name openra
|
||||
%define version {VERSION_FIELD}
|
||||
%define root {ROOT_DIR}
|
||||
%define _binaries_in_noarch_packages_terminate_build 0
|
||||
%define _use_internal_dependency_generator 0
|
||||
%define __find_provides ""
|
||||
%define __find_requires ""
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: 1
|
||||
|
||||
@@ -21,6 +21,10 @@ cp -rv $2/* "OpenRA.app/Contents/Resources/" || exit 3
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.ico
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.Editor.exe
|
||||
|
||||
# Install the stripped down Lua library
|
||||
cp ../../liblua-osx.dylib OpenRA.app/Contents/Resources/
|
||||
cp ../../LuaInterface.dll.config OpenRA.app/Contents/Resources/
|
||||
|
||||
# SDL2 is the only supported renderer
|
||||
rm -rf OpenRA.app/Contents/Resources/cg
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.Renderer.Cg.dll
|
||||
|
||||
@@ -26,15 +26,13 @@ markdown CONTRIBUTING.md > CONTRIBUTING.html
|
||||
markdown DOCUMENTATION.md > DOCUMENTATION.html
|
||||
|
||||
# List of files that are packaged on all platforms
|
||||
# Note that the Tao dlls are shipped on all platforms except osx and that
|
||||
# they are now installed to the game directory instead of placed in the gac
|
||||
FILES=('OpenRA.Game.exe' 'OpenRA.Editor.exe' 'OpenRA.Utility.exe' \
|
||||
'OpenRA.FileFormats.dll' 'OpenRA.Renderer.SdlCommon.dll' 'OpenRA.Renderer.Sdl2.dll' 'OpenRA.Renderer.Cg.dll' 'OpenRA.Renderer.Gl.dll' 'OpenRA.Renderer.Null.dll' 'OpenRA.Irc.dll' \
|
||||
'FreeSans.ttf' 'FreeSansBold.ttf' \
|
||||
'cg' 'glsl' 'mods/ra' 'mods/cnc' 'mods/d2k' \
|
||||
'AUTHORS' 'CHANGELOG' 'COPYING' \
|
||||
'README.html' 'CONTRIBUTING.html' 'DOCUMENTATION.html' \
|
||||
'global mix database.dat' 'GeoIP.dll' 'GeoIP.dat')
|
||||
'global mix database.dat' 'GeoIP.dll' 'GeoIP.dat' 'LuaInterface.dll')
|
||||
|
||||
echo "Copying files..."
|
||||
for i in "${FILES[@]}"; do
|
||||
|
||||
@@ -83,6 +83,8 @@ Section "Game" GAME
|
||||
File "${SRCDIR}\global mix database.dat"
|
||||
File "${SRCDIR}\GeoIP.dll"
|
||||
File "${SRCDIR}\GeoIP.dat"
|
||||
File "${SRCDIR}\LuaInterface.dll"
|
||||
File lua51.dll
|
||||
File OpenAL32.dll
|
||||
File SDL.dll
|
||||
File freetype6.dll
|
||||
@@ -201,6 +203,8 @@ Function ${UN}Clean
|
||||
Delete "$INSTDIR\global mix database.dat"
|
||||
Delete $INSTDIR\GeoIP.dat
|
||||
Delete $INSTDIR\GeoIP.dll
|
||||
Delete $INSTDIR\LuaInterface.dll
|
||||
Delete $INSTDIR\lua51.dll
|
||||
Delete $INSTDIR\OpenAL32.dll
|
||||
Delete $INSTDIR\SDL.dll
|
||||
Delete $INSTDIR\freetype6.dll
|
||||
|
||||
BIN
packaging/windows/lua51.dll
Normal file
BIN
packaging/windows/lua51.dll
Normal file
Binary file not shown.
5
thirdparty/LuaInterface.dll.config
vendored
Normal file
5
thirdparty/LuaInterface.dll.config
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="lua51.dll" target="liblua-linux32.so" cpu="x86" />
|
||||
<dllmap os="linux" dll="lua51.dll" target="liblua-linux64.so" cpu="x86-64" />
|
||||
<dllmap os="osx" dll="lua51.dll" target="liblua-osx.dylib" />
|
||||
</configuration>
|
||||
BIN
thirdparty/liblua-linux32.so
vendored
Normal file
BIN
thirdparty/liblua-linux32.so
vendored
Normal file
Binary file not shown.
BIN
thirdparty/liblua-linux64.so
vendored
Normal file
BIN
thirdparty/liblua-linux64.so
vendored
Normal file
Binary file not shown.
BIN
thirdparty/liblua-osx.dylib
vendored
Normal file
BIN
thirdparty/liblua-osx.dylib
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user