#if UNITY_IPHONE
#define __NOGEN__
#endif
namespace LuaInterface
{
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.Security;
using System.Runtime.InteropServices;
using System.Threading;
using System.Text;
public class LuaState : IDisposable
{
public IntPtr L;
internal LuaCSFunction tracebackFunction;
internal ObjectTranslator translator;
internal LuaCSFunction panicCallback;
public LuaState()
{
// Create State
L = LuaDLL.luaL_newstate();
// Create LuaInterface library
LuaDLL.luaL_openlibs(L);
LuaDLL.lua_pushstring(L, "LUAINTERFACE LOADED");
LuaDLL.lua_pushboolean(L, true);
LuaDLL.lua_settable(L, (int) LuaIndexes.LUA_REGISTRYINDEX);
LuaDLL.lua_newtable(L);
LuaDLL.lua_setglobal(L, "luanet");
LuaDLL.lua_pushvalue(L, (int)LuaIndexes.LUA_GLOBALSINDEX);
LuaDLL.lua_getglobal(L, "luanet");
LuaDLL.lua_pushstring(L, "getmetatable");
LuaDLL.lua_getglobal(L, "getmetatable");
LuaDLL.lua_settable(L, -3);
// Set luanet as global for object translator
LuaDLL.lua_replace(L, (int)LuaIndexes.LUA_GLOBALSINDEX);
translator = new ObjectTranslator(this,L);
LuaDLL.lua_replace(L, (int)LuaIndexes.LUA_GLOBALSINDEX);
GCHandle handle = GCHandle.Alloc(translator, GCHandleType.Pinned);
IntPtr thisptr = GCHandle.ToIntPtr(handle);
LuaDLL.lua_pushlightuserdata(L, thisptr);
LuaDLL.lua_setglobal(L, "_translator");
}
public void Close()
{
if (L != IntPtr.Zero)
{
LuaDLL.lua_close(L);
}
}
///
/// Assuming we have a Lua error string sitting on the stack, throw a C# exception out to the user's app
///
/// Thrown if the script caused an exception
public void ThrowExceptionFromError(int oldTop)
{
object err = translator.getObject(L, -1);
LuaDLL.lua_settop(L, 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(), "");
}
///
/// Convert C# exceptions into Lua errors
///
/// num of things on stack
/// null for no pending exception
internal int SetPendingException(Exception e)
{
Exception caughtExcept = e;
if (caughtExcept != null)
{
translator.throwError(L, caughtExcept);
LuaDLL.lua_pushnil(L);
return 1;
}
else
return 0;
}
///
///
///
///
///
///
public LuaFunction LoadString(string chunk, string name, LuaTable env)
{
int oldTop = LuaDLL.lua_gettop(L);
if (LuaDLL.luaL_loadbuffer(L, chunk, Encoding.UTF8.GetByteCount(chunk), name) != 0)
ThrowExceptionFromError(oldTop);
if (env != null)
{
env.push(L);
LuaDLL.lua_setfenv(L, -2);
}
LuaFunction result = translator.getFunction(L, -1);
translator.popValues(L, oldTop);
return result;
}
public LuaFunction LoadString(string chunk, string name)
{
return LoadString(chunk, name, null);
}
///
///
///
///
///
protected LuaFunction LoadFile(string fileName)
{
int oldTop = LuaDLL.lua_gettop(L);
if (LuaDLL.luaL_loadfile(L, fileName) != 0)
ThrowExceptionFromError(oldTop);
LuaFunction result = translator.getFunction(L, -1);
translator.popValues(L, 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", null);
}
///
/// Executes a Lua chnk and returns all the chunk's return values in an array.
///
/// Chunk to execute
/// Name to associate with the chunk
///
public object[] DoString(string chunk, string chunkName, LuaTable env)
{
int oldTop = LuaDLL.lua_gettop(L);
if (LuaDLL.luaL_loadbuffer(L, chunk, Encoding.UTF8.GetByteCount(chunk), chunkName) == 0)
{
if (env != null)
{
env.push(L);
//LuaDLL.lua_setfenv(L, -1);
LuaDLL.lua_setfenv(L, -2);
}
if (LuaDLL.lua_pcall(L, 0, -1, 0) == 0)
return translator.popValues(L, oldTop);
else
ThrowExceptionFromError(oldTop);
}
else
ThrowExceptionFromError(oldTop);
return null; // Never reached - keeps compiler happy
}
public object[] DoFile(string fileName)
{
return DoFile(fileName, null);
}
/*
* Excutes a Lua file and returns all the chunk's return
* values in an array
*/
protected object[] DoFile(string fileName, LuaTable env)
{
LuaDLL.lua_pushstdcallcfunction(L,tracebackFunction);
int oldTop = LuaDLL.lua_gettop(L);
if (LuaDLL.luaL_loadfile(L, fileName) == 0)
{
try
{
if (LuaDLL.lua_pcall(L, 0, -1, -2) == 0)
return translator.popValues(L, oldTop);
else
ThrowExceptionFromError(oldTop);
}
finally {}
}
else
ThrowExceptionFromError(oldTop);
return null; // Never reached - keeps compiler happy
}
/*
* 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(L);
string[] path=fullPath.Split(new char[] { '.' });
LuaDLL.lua_getglobal(L,path[0]);
returnValue=translator.getObject(L,-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(L,oldTop);
return returnValue;
}
set
{
int oldTop=LuaDLL.lua_gettop(L);
string[] path=fullPath.Split(new char[] { '.' });
if(path.Length==1)
{
translator.push(L,value);
LuaDLL.lua_setglobal(L,fullPath);
}
else
{
LuaDLL.lua_getglobal(L,path[0]);
string[] remainingPath=new string[path.Length-1];
Array.Copy(path,1,remainingPath,0,path.Length-1);
setObject(remainingPath,value);
}
LuaDLL.lua_settop(L,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 globals = new List();
private bool globalsSorted;
///
/// An alphabetically sorted list of all globals (objects, methods, etc.) externally added to this Lua instance
///
/// Members of globals are also listed. The formatting is optimized for text input auto-completion.
public IEnumerable Globals
{
get
{
// Only sort list when necessary
if (!globalsSorted)
{
globals.Sort();
globalsSorted = true;
}
return globals;
}
}
///
/// Adds an entry to (recursivley handles 2 levels of members)
///
/// The index accessor path ot the entry
/// The type of the entry
/// How deep have we gone with recursion?
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 (int)LuaThreadStatus.LUA_YIELD )
{
// Error
int top = LuaDLL.lua_gettop(L);
ThrowExceptionFromError(top);
}
return r;
}
public bool IsStarted()
{
return start;
}
public bool IsSuspended()
{
int status = LuaDLL.lua_status( L );
return (status == (int)LuaThreadStatus.LUA_YIELD);
}
public bool IsDead()
{
int status = LuaDLL.lua_status( L );
return (status > (int)LuaThreadStatus.LUA_YIELD);
}
public bool IsInactive()
{
int status = LuaDLL.lua_status( L );
return (status == 0);
}
}
}