#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); } } }