diff --git a/Assets/dll/KeraLua.dll b/Assets/dll/KeraLua.dll
deleted file mode 100644
index e1aa1f948e..0000000000
Binary files a/Assets/dll/KeraLua.dll and /dev/null differ
diff --git a/Assets/dll/NLua.dll b/Assets/dll/NLua.dll
deleted file mode 100644
index ce3ea5a82b..0000000000
Binary files a/Assets/dll/NLua.dll and /dev/null differ
diff --git a/Assets/dll/liblua54.so b/Assets/dll/liblua54.so
deleted file mode 100644
index a1de693266..0000000000
Binary files a/Assets/dll/liblua54.so and /dev/null differ
diff --git a/ExternalProjects/NLua/LICENSE b/ExternalProjects/NLua/LICENSE
new file mode 100644
index 0000000000..a7ab4aaf71
--- /dev/null
+++ b/ExternalProjects/NLua/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2021 Vinicius Jarina (viniciusjarina@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/ExternalProjects/NLua/NLua.csproj b/ExternalProjects/NLua/NLua.csproj
new file mode 100644
index 0000000000..01ebbb95d1
--- /dev/null
+++ b/ExternalProjects/NLua/NLua.csproj
@@ -0,0 +1,30 @@
+
+
+ netstandard2.0
+ NLua
+ NLua
+ false
+
+
+
+ true
+
+
+
+
+ true
+ portable
+ true
+
+
+
+
+
+
+
+
+
+
+ disable
+
+
\ No newline at end of file
diff --git a/ExternalProjects/NLua/build_debug.sh b/ExternalProjects/NLua/build_debug.sh
new file mode 100644
index 0000000000..c2127aded1
--- /dev/null
+++ b/ExternalProjects/NLua/build_debug.sh
@@ -0,0 +1 @@
+../.build_debug.sh
\ No newline at end of file
diff --git a/ExternalProjects/NLua/build_release.sh b/ExternalProjects/NLua/build_release.sh
new file mode 100644
index 0000000000..801b8e5998
--- /dev/null
+++ b/ExternalProjects/NLua/build_release.sh
@@ -0,0 +1 @@
+../.build_release.sh
\ No newline at end of file
diff --git a/ExternalProjects/NLua/src/CheckType.cs b/ExternalProjects/NLua/src/CheckType.cs
new file mode 100644
index 0000000000..84832514a5
--- /dev/null
+++ b/ExternalProjects/NLua/src/CheckType.cs
@@ -0,0 +1,414 @@
+using System;
+using System.Collections.Generic;
+using NLua.Method;
+using NLua.Extensions;
+
+namespace NLua
+{
+ internal sealed class CheckType
+ {
+ internal readonly Dictionary _extractValues = new Dictionary();
+ internal readonly ExtractValue _extractNetObject;
+ internal readonly ObjectTranslator _translator;
+
+ public CheckType(ObjectTranslator translator)
+ {
+ _translator = translator;
+ _extractValues.Add(typeof(object), GetAsObject);
+ _extractValues.Add(typeof(sbyte), GetAsSbyte);
+ _extractValues.Add(typeof(byte), GetAsByte);
+ _extractValues.Add(typeof(short), GetAsShort);
+ _extractValues.Add(typeof(ushort), GetAsUshort);
+ _extractValues.Add(typeof(int), GetAsInt);
+ _extractValues.Add(typeof(uint), GetAsUint);
+ _extractValues.Add(typeof(long), GetAsLong);
+ _extractValues.Add(typeof(ulong), GetAsUlong);
+ _extractValues.Add(typeof(double), GetAsDouble);
+ _extractValues.Add(typeof(char), GetAsChar);
+ _extractValues.Add(typeof(float), GetAsFloat);
+ _extractValues.Add(typeof(decimal), GetAsDecimal);
+ _extractValues.Add(typeof(bool), GetAsBoolean);
+ _extractValues.Add(typeof(string), GetAsString);
+ _extractValues.Add(typeof(char[]), GetAsCharArray);
+ _extractValues.Add(typeof(byte[]), GetAsByteArray);
+ _extractValues.Add(typeof(LuaFunction), GetAsFunction);
+ _extractValues.Add(typeof(LuaTable), GetAsTable);
+ _extractValues.Add(typeof(LuaThread), GetAsThread);
+ _extractValues.Add(typeof(LuaUserData), GetAsUserdata);
+ _extractNetObject = 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(ProxyType paramType)
+ {
+ return GetExtractor(paramType.UnderlyingSystemType);
+ }
+
+ internal ExtractValue GetExtractor(Type paramType)
+ {
+ if (paramType.IsByRef)
+ paramType = paramType.GetElementType();
+
+ return _extractValues.ContainsKey(paramType) ? _extractValues[paramType] : _extractNetObject;
+ }
+
+ internal ExtractValue CheckLuaType(LuaState luaState, int stackPos, Type paramType)
+ {
+ LuaType luatype = luaState.Type(stackPos);
+
+ if (paramType.IsByRef)
+ paramType = paramType.GetElementType();
+
+ var underlyingType = Nullable.GetUnderlyingType(paramType);
+
+ if (underlyingType != null)
+ {
+ paramType = underlyingType; // Silently convert nullable types to their non null requics
+ }
+
+
+ bool netParamIsNumeric = paramType == typeof(int) ||
+ paramType == typeof(uint) ||
+ paramType == typeof(long) ||
+ paramType == typeof(ulong) ||
+ paramType == typeof(short) ||
+ paramType == typeof(ushort) ||
+ paramType == typeof(float) ||
+ paramType == typeof(double) ||
+ paramType == typeof(decimal) ||
+ paramType == typeof(byte) ||
+ paramType == typeof(sbyte) ||
+ paramType == typeof(char);
+
+ // If it is a nullable
+ if (underlyingType != null)
+ {
+ // null can always be assigned to nullable
+ if (luatype == LuaType.Nil)
+ {
+ // Return the correct extractor anyways
+ if (netParamIsNumeric || paramType == typeof(bool))
+ return _extractValues[paramType];
+ return _extractNetObject;
+ }
+ }
+
+ if (paramType == typeof(object))
+ return _extractValues[paramType];
+
+ //CP: Added support for generic parameters
+ if (paramType.IsGenericParameter)
+ {
+ if (luatype == LuaType.Boolean)
+ return _extractValues[typeof(bool)];
+ if (luatype == LuaType.String)
+ return _extractValues[typeof(string)];
+ if (luatype == LuaType.Table)
+ return _extractValues[typeof(LuaTable)];
+ if (luatype == LuaType.Thread)
+ return _extractValues[typeof(LuaThread)];
+ if (luatype == LuaType.UserData)
+ return _extractValues[typeof(object)];
+ if (luatype == LuaType.Function)
+ return _extractValues[typeof(LuaFunction)];
+ if (luatype == LuaType.Number)
+ return _extractValues[typeof(double)];
+ }
+ bool netParamIsString = paramType == typeof(string) || paramType == typeof(char[]) || paramType == typeof(byte[]);
+
+ if (netParamIsNumeric)
+ {
+ if (luaState.IsNumericType(stackPos) && !netParamIsString)
+ return _extractValues[paramType];
+ }
+ else if (paramType == typeof(bool))
+ {
+ if (luaState.IsBoolean(stackPos))
+ return _extractValues[paramType];
+ }
+ else if (netParamIsString)
+ {
+ if (luaState.IsStringOrNumber(stackPos) || luatype == LuaType.Nil)
+ return _extractValues[paramType];
+ }
+ else if (paramType == typeof(LuaTable))
+ {
+ if (luatype == LuaType.Table || luatype == LuaType.Nil)
+ return _extractValues[paramType];
+ }
+ else if (paramType == typeof(LuaThread))
+ {
+ if (luatype == LuaType.Thread || luatype == LuaType.Nil)
+ return _extractValues[paramType];
+ }
+ else if (paramType == typeof(LuaUserData))
+ {
+ if (luatype == LuaType.UserData || luatype == LuaType.Nil)
+ return _extractValues[paramType];
+ }
+ else if (paramType == typeof(LuaFunction))
+ {
+ if (luatype == LuaType.Function || luatype == LuaType.Nil)
+ return _extractValues[paramType];
+ }
+ else if (typeof(Delegate).IsAssignableFrom(paramType) && luatype == LuaType.Function && paramType.GetMethod("Invoke") != null)
+ return new DelegateGenerator(_translator, paramType).ExtractGenerated;
+ else if (paramType.IsInterface && luatype == LuaType.Table)
+ return new ClassGenerator(_translator, paramType).ExtractGenerated;
+ else if ((paramType.IsInterface || paramType.IsClass) && luatype == LuaType.Nil)
+ {
+ // kevinh - allow nil to be silently converted to null - extractNetObject will return null when the item ain't found
+ return _extractNetObject;
+ }
+ else if (luaState.Type(stackPos) == LuaType.Table)
+ {
+ if (luaState.GetMetaField(stackPos, "__index") != LuaType.Nil)
+ {
+ object obj = _translator.GetNetObject(luaState, -1);
+ luaState.SetTop(-2);
+ if (obj != null && paramType.IsInstanceOfType(obj))
+ return _extractNetObject;
+ }
+ else
+ return null;
+ }
+
+ object netObj = _translator.GetNetObject(luaState, stackPos);
+ if (netObj != null && paramType.IsInstanceOfType(netObj))
+ 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(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (sbyte)luaState.ToInteger(stackPos);
+
+ return (sbyte)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsByte(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (byte)luaState.ToInteger(stackPos);
+
+ return (byte)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsShort(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (short)luaState.ToInteger(stackPos);
+
+ return (short)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsUshort(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (ushort)luaState.ToInteger(stackPos);
+
+ return (ushort)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsInt(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (int)luaState.ToInteger(stackPos);
+
+ return (int)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsUint(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (uint)luaState.ToInteger(stackPos);
+
+ return (uint)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsLong(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return luaState.ToInteger(stackPos);
+
+ return (long)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsUlong(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (ulong)luaState.ToInteger(stackPos);
+
+ return (ulong)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsDouble(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (double)luaState.ToInteger(stackPos);
+
+ return luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsChar(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (char)luaState.ToInteger(stackPos);
+
+ return (char)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsFloat(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (float)luaState.ToInteger(stackPos);
+
+ return (float)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsDecimal(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsNumericType(stackPos))
+ return null;
+
+ if (luaState.IsInteger(stackPos))
+ return (decimal)luaState.ToInteger(stackPos);
+
+ return (decimal)luaState.ToNumber(stackPos);
+ }
+
+ private object GetAsBoolean(LuaState luaState, int stackPos)
+ {
+ return luaState.ToBoolean(stackPos);
+ }
+
+ private object GetAsCharArray(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsStringOrNumber(stackPos))
+ return null;
+ string retVal = luaState.ToString(stackPos, false);
+ return retVal.ToCharArray();
+ }
+
+ private object GetAsByteArray(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsStringOrNumber(stackPos))
+ return null;
+
+ byte [] retVal = luaState.ToBuffer(stackPos, false);
+ return retVal;
+ }
+
+ private object GetAsString(LuaState luaState, int stackPos)
+ {
+ if (!luaState.IsStringOrNumber(stackPos))
+ return null;
+ return luaState.ToString(stackPos, false);
+ }
+
+ private object GetAsTable(LuaState luaState, int stackPos)
+ {
+ return _translator.GetTable(luaState, stackPos);
+ }
+
+ private object GetAsThread(LuaState luaState, int stackPos)
+ {
+ return _translator.GetThread(luaState, stackPos);
+ }
+
+ private object GetAsFunction(LuaState luaState, int stackPos)
+ {
+ return _translator.GetFunction(luaState, stackPos);
+ }
+
+ private object GetAsUserdata(LuaState luaState, int stackPos)
+ {
+ return _translator.GetUserData(luaState, stackPos);
+ }
+
+ public object GetAsObject(LuaState luaState, int stackPos)
+ {
+ if (luaState.Type(stackPos) == LuaType.Table)
+ {
+ if (luaState.GetMetaField(stackPos, "__index") != LuaType.Nil)
+ {
+ if (luaState.CheckMetaTable(-1, _translator.Tag))
+ {
+ luaState.Insert(stackPos);
+ luaState.Remove(stackPos + 1);
+ }
+ else
+ luaState.SetTop(-2);
+ }
+ }
+
+ object obj = _translator.GetObject(luaState, stackPos);
+ return obj;
+ }
+
+ public object GetAsNetObject(LuaState luaState, int stackPos)
+ {
+ object obj = _translator.GetNetObject(luaState, stackPos);
+
+ if (obj != null || luaState.Type(stackPos) != LuaType.Table)
+ return obj;
+
+ if (luaState.GetMetaField(stackPos, "__index") == LuaType.Nil)
+ return null;
+
+ if (luaState.CheckMetaTable(-1, _translator.Tag))
+ {
+ luaState.Insert(stackPos);
+ luaState.Remove(stackPos + 1);
+ obj = _translator.GetNetObject(luaState, stackPos);
+ }
+ else
+ luaState.SetTop(-2);
+
+ return obj;
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Event/DebugHookEventArgs.cs b/ExternalProjects/NLua/src/Event/DebugHookEventArgs.cs
new file mode 100644
index 0000000000..ad8c36d14d
--- /dev/null
+++ b/ExternalProjects/NLua/src/Event/DebugHookEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NLua.Event
+{
+ ///
+ /// Event args for hook callback event
+ ///
+ public class DebugHookEventArgs : EventArgs
+ {
+ public DebugHookEventArgs(LuaDebug luaDebug)
+ {
+ LuaDebug = luaDebug;
+ }
+
+ ///
+ /// Lua Debug Information
+ ///
+ public LuaDebug LuaDebug { get; }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Event/HookExceptionEventArgs.cs b/ExternalProjects/NLua/src/Event/HookExceptionEventArgs.cs
new file mode 100644
index 0000000000..6224f53f00
--- /dev/null
+++ b/ExternalProjects/NLua/src/Event/HookExceptionEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace NLua.Event
+{
+ public class HookExceptionEventArgs : EventArgs
+ {
+ public Exception Exception { get; }
+
+ public HookExceptionEventArgs(Exception ex)
+ {
+ Exception = ex;
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Exceptions/LuaException.cs b/ExternalProjects/NLua/src/Exceptions/LuaException.cs
new file mode 100644
index 0000000000..d8eef01092
--- /dev/null
+++ b/ExternalProjects/NLua/src/Exceptions/LuaException.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NLua.Exceptions
+{
+ ///
+ /// Exceptions thrown by the Lua runtime
+ ///
+ [Serializable]
+ public class LuaException : Exception
+ {
+ public LuaException (string message) : base(message)
+ {
+ }
+
+ public LuaException (string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ }
+}
diff --git a/ExternalProjects/NLua/src/Exceptions/LuaScriptException.cs b/ExternalProjects/NLua/src/Exceptions/LuaScriptException.cs
new file mode 100644
index 0000000000..28a4440eca
--- /dev/null
+++ b/ExternalProjects/NLua/src/Exceptions/LuaScriptException.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace NLua.Exceptions
+{
+ ///
+ /// Exceptions thrown by the Lua runtime because of errors in the script
+ ///
+ ///
+ [Serializable]
+ public class LuaScriptException : LuaException
+ {
+ ///
+ /// Returns true if the exception has occured as the result of a .NET exception in user code
+ ///
+ public bool IsNetException { get; }
+
+ private readonly string _source;
+
+ ///
+ /// The position in the script where the exception was triggered.
+ ///
+ public override string Source => _source;
+
+ ///
+ /// Creates a new Lua-only exception.
+ ///
+ /// The message that describes the error.
+ /// The position in the script where the exception was triggered.
+ public LuaScriptException(string message, string source) : base(message)
+ {
+ _source = source;
+ }
+
+ ///
+ /// Creates a new .NET wrapping exception.
+ ///
+ /// The .NET exception triggered by user-code.
+ /// The position in the script where the exception was triggered.
+ public LuaScriptException(Exception innerException, string source)
+ : base("A .NET exception occured in user-code", innerException)
+ {
+ _source = source;
+ IsNetException = true;
+ }
+
+ public override string ToString()
+ {
+ // Prepend the error source
+ return GetType().FullName + ": " + _source + Message;
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Extensions/LuaExtensions.cs b/ExternalProjects/NLua/src/Extensions/LuaExtensions.cs
new file mode 100644
index 0000000000..6774bb0c84
--- /dev/null
+++ b/ExternalProjects/NLua/src/Extensions/LuaExtensions.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NLua.Extensions
+{
+ internal static class LuaExtensions
+ {
+ public static bool CheckMetaTable(this LuaState state, int index, IntPtr tag)
+ {
+ if (!state.GetMetaTable(index))
+ return false;
+
+ state.PushLightUserData(tag);
+ state.RawGet(-2);
+ bool isNotNil = !state.IsNil(-1);
+ state.SetTop(-3);
+ return isNotNil;
+ }
+
+ public static void PopGlobalTable(this LuaState luaState)
+ {
+ luaState.RawSetInteger(LuaRegistry.Index, (long) LuaRegistryIndex.Globals);
+ }
+
+ public static void GetRef (this LuaState luaState, int reference)
+ {
+ luaState.RawGetInteger(LuaRegistry.Index, reference);
+ }
+
+ // ReSharper disable once IdentifierTypo
+ public static void Unref (this LuaState luaState, int reference)
+ {
+ luaState.Unref(LuaRegistry.Index, reference);
+ }
+
+ public static bool AreEqual(this LuaState luaState, int ref1, int ref2)
+ {
+ return luaState.Compare(ref1, ref2, LuaCompare.Equal);
+ }
+
+ public static IntPtr CheckUData(this LuaState state, int ud, string name)
+ {
+ IntPtr p = state.ToUserData(ud);
+ if (p == IntPtr.Zero)
+ return IntPtr.Zero;
+ if (!state.GetMetaTable(ud))
+ return IntPtr.Zero;
+
+ state.GetField(LuaRegistry.Index, name);
+
+ bool isEqual = state.RawEqual(-1, -2);
+
+ state.Pop(2);
+
+ if (isEqual)
+ return p;
+
+ return IntPtr.Zero;
+ }
+
+ public static int ToNetObject(this LuaState state, int index, IntPtr tag)
+ {
+ if (state.Type(index) != LuaType.UserData)
+ return -1;
+
+ IntPtr userData;
+
+ if (state.CheckMetaTable(index, tag))
+ {
+ userData = state.ToUserData(index);
+ if (userData != IntPtr.Zero)
+ return Marshal.ReadInt32(userData);
+ }
+
+ userData = state.CheckUData(index, "luaNet_class");
+ if (userData != IntPtr.Zero)
+ return Marshal.ReadInt32(userData);
+
+ userData = state.CheckUData(index, "luaNet_searchbase");
+ if (userData != IntPtr.Zero)
+ return Marshal.ReadInt32(userData);
+
+ userData = state.CheckUData(index, "luaNet_function");
+ if (userData != IntPtr.Zero)
+ return Marshal.ReadInt32(userData);
+
+ return -1;
+ }
+
+ public static void NewUData(this LuaState state, int val)
+ {
+ IntPtr pointer = state.NewUserData(Marshal.SizeOf(typeof(int)));
+ Marshal.WriteInt32(pointer, val);
+ }
+
+ public static int RawNetObj(this LuaState state, int index)
+ {
+ IntPtr pointer = state.ToUserData(index);
+ if (pointer == IntPtr.Zero)
+ return -1;
+
+ return Marshal.ReadInt32(pointer);
+ }
+
+ public static int CheckUObject(this LuaState state, int index, string name)
+ {
+ IntPtr udata = state.CheckUData(index, name);
+ if (udata == IntPtr.Zero)
+ return -1;
+
+ return Marshal.ReadInt32(udata);
+ }
+
+ public static bool IsNumericType(this LuaState state, int index)
+ {
+ return state.Type(index) == LuaType.Number;
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Extensions/StringExtensions.cs b/ExternalProjects/NLua/src/Extensions/StringExtensions.cs
new file mode 100644
index 0000000000..b0c745fdc8
--- /dev/null
+++ b/ExternalProjects/NLua/src/Extensions/StringExtensions.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+
+namespace NLua.Extensions
+{
+ internal static class StringExtensions
+ {
+ public static IEnumerable SplitWithEscape(this string input, char separator, char escapeCharacter)
+ {
+ int start = 0;
+ int index = 0;
+ while (index < input.Length)
+ {
+ index = input.IndexOf(separator, index);
+ if (index == -1)
+ break;
+
+ if (input[index - 1] == escapeCharacter)
+ {
+ input = input.Remove(index - 1, 1);
+ continue;
+ }
+
+
+ yield return input.Substring(start, index - start);
+ index++;
+ start = index;
+ }
+ yield return input.Substring(start);
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/Extensions/TypeExtensions.cs b/ExternalProjects/NLua/src/Extensions/TypeExtensions.cs
new file mode 100644
index 0000000000..86c31efd3c
--- /dev/null
+++ b/ExternalProjects/NLua/src/Extensions/TypeExtensions.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace NLua.Extensions
+{
+ internal static class TypeExtensions
+ {
+ public static bool HasMethod(this Type t, string name)
+ {
+ var op = t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
+ return op.Any(m => m.Name == name);
+ }
+
+ public static bool HasAdditionOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_Addition");
+ }
+
+ public static bool HasSubtractionOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_Subtraction");
+ }
+
+ public static bool HasMultiplyOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_Multiply");
+ }
+
+ public static bool HasDivisionOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_Division");
+ }
+
+ public static bool HasModulusOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_Modulus");
+ }
+
+ public static bool HasUnaryNegationOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+ // Unary - will always have only one version.
+ var op = t.GetMethod("op_UnaryNegation", BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
+ return op != null;
+ }
+
+ public static bool HasEqualityOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+ return t.HasMethod("op_Equality");
+ }
+
+ public static bool HasLessThanOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+
+ return t.HasMethod("op_LessThan");
+ }
+
+ public static bool HasLessThanOrEqualOperator(this Type t)
+ {
+ if (t.IsPrimitive)
+ return true;
+ return t.HasMethod("op_LessThanOrEqual");
+ }
+
+ public static MethodInfo[] GetMethods(this Type t, string name, BindingFlags flags)
+ {
+ return t.GetMethods(flags).Where(m => m.Name == name).ToArray();
+ }
+
+ public static MethodInfo[] GetExtensionMethods(this Type type, string name, IEnumerable assemblies = null)
+ {
+ var types = new List();
+
+ types.AddRange(type.Assembly.GetTypes().Where(t => t.IsPublic));
+
+ if (assemblies != null)
+ {
+ foreach (Assembly item in assemblies)
+ {
+ if (item == type.Assembly)
+ continue;
+ types.AddRange(item.GetTypes().Where(t => t.IsPublic && t.IsClass && t.IsSealed && t.IsAbstract && !t.IsNested));
+ }
+ }
+
+ // this thing is complex and not sure recommended changes are safe
+#pragma warning disable MA0029 // Combine LINQ methods
+#pragma warning disable BHI1002 // Do not use anonymous types (classes)
+ var query = types
+ .SelectMany(extensionType => extensionType.GetMethods(name, BindingFlags.Static | BindingFlags.Public),
+ (extensionType, method) => new {extensionType, method})
+ .Where(t => t.method.IsDefined(typeof(ExtensionAttribute), false))
+ .Where(t =>
+ t.method.GetParameters()[0].ParameterType == type ||
+ t.method.GetParameters()[0].ParameterType.IsAssignableFrom(type) ||
+ type.GetInterfaces().Contains(t.method.GetParameters()[0].ParameterType))
+ .Select(t => t.method);
+#pragma warning restore BHI1002 // Do not use anonymous types (classes)
+#pragma warning restore MA0029 // Combine LINQ methods
+
+ return query.ToArray();
+ }
+
+ ///
+ /// Extends the System.Type-type to search for a given extended MethodeName.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static MethodInfo GetExtensionMethod(this Type t, string name, IEnumerable assemblies = null)
+ {
+ var mi = t.GetExtensionMethods(name, assemblies).ToArray();
+ if (mi.Length == 0)
+ return null;
+ return mi[0];
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/GenerateEventAssembly/ClassGenerator.cs b/ExternalProjects/NLua/src/GenerateEventAssembly/ClassGenerator.cs
new file mode 100644
index 0000000000..ab3eb1fc63
--- /dev/null
+++ b/ExternalProjects/NLua/src/GenerateEventAssembly/ClassGenerator.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace NLua
+{
+ internal class ClassGenerator
+ {
+ private readonly ObjectTranslator _translator;
+ private readonly Type _klass;
+
+ public ClassGenerator(ObjectTranslator objTranslator, Type typeClass)
+ {
+ _translator = objTranslator;
+ _klass = typeClass;
+ }
+
+ public object ExtractGenerated(LuaState luaState, int stackPos)
+ {
+ return CodeGeneration.Instance.GetClassInstance(_klass, _translator.GetTable(luaState, stackPos));
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/GenerateEventAssembly/CodeGeneration.cs b/ExternalProjects/NLua/src/GenerateEventAssembly/CodeGeneration.cs
new file mode 100644
index 0000000000..2410050f38
--- /dev/null
+++ b/ExternalProjects/NLua/src/GenerateEventAssembly/CodeGeneration.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Threading;
+using System.Reflection;
+
+using System.Reflection.Emit;
+using System.Collections.Generic;
+using NLua.Method;
+
+namespace NLua
+{
+ internal class CodeGeneration
+ {
+ private readonly Dictionary _classCollection = new Dictionary();
+ private readonly Dictionary _delegateCollection = new Dictionary();
+
+ static CodeGeneration()
+ {
+ }
+
+ private CodeGeneration()
+ {
+ }
+
+ ///
+ /// Singleton instance of the class
+ ///
+ public static CodeGeneration Instance { get; } = new CodeGeneration();
+
+ ///
+ /// Generates an event handler that calls a Lua function
+ ///
+ private Type GenerateEvent(Type eventHandlerType)
+ {
+ throw new NotImplementedException("Emit not available on .NET Standard ");
+ }
+
+ ///
+ /// Generates a type that can be used for instantiating a delegate
+ /// of the provided type, given a Lua function.
+ ///
+ private Type GenerateDelegate(Type delegateType)
+ {
+ throw new NotImplementedException("GenerateDelegate is not available on Windows Store, please register your LuaDelegate type with Lua.RegisterLuaDelegateType( yourDelegate, theLuaDelegateHandler) ");
+ }
+
+ internal void GetReturnTypesFromClass(Type klass, out Type[][] returnTypes)
+ {
+ var classMethods = klass.GetMethods();
+ returnTypes = new Type[classMethods.Length][];
+
+ int i = 0;
+
+ foreach (var method in classMethods)
+ {
+ if (klass.IsInterface)
+ {
+ GetReturnTypesFromMethod(method, out returnTypes[i]);
+ i++;
+ }
+ else if (!method.IsPrivate && !method.IsFinal && method.IsVirtual)
+ {
+ GetReturnTypesFromMethod(method, out returnTypes[i]);
+ i++;
+ }
+ }
+ }
+
+ /*
+ * 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)
+ {
+ throw new NotImplementedException (" Emit not available on .NET Standard ");
+ }
+
+ internal void GetReturnTypesFromMethod(MethodInfo method, out Type[] returnTypes)
+ {
+ var paramInfo = method.GetParameters();
+ var paramTypes = new Type[paramInfo.Length];
+ var returnTypesList = new List();
+
+ // Counts out and ref parameters, for later use,
+ // and creates the list of return types
+ int nOutParams = 0;
+ int nOutAndRefParams = 0;
+ var 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++;
+ }
+ }
+
+ returnTypes = returnTypesList.ToArray();
+ }
+
+ ///
+ /// 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)
+ {
+ throw new NotImplementedException("Emit not available on .NET Standard");
+ }
+
+ public void RegisterLuaDelegateType(Type delegateType, Type luaDelegateType)
+ {
+ _delegateCollection[delegateType] = luaDelegateType;
+ }
+
+ public void RegisterLuaClassType(Type klass, Type luaClass)
+ {
+ LuaClassType luaClassType = default;
+ luaClassType.klass = luaClass;
+ GetReturnTypesFromClass(klass, out luaClassType.returnTypes);
+ _classCollection[klass] = luaClassType;
+ }
+
+ ///
+ /// Gets a delegate with delegateType that calls the luaFunc Lua function
+ /// Caches the generated type.
+ ///
+ public Delegate GetDelegate(Type delegateType, LuaFunction luaFunc)
+ {
+ var returnTypes = new List();
+ Type luaDelegateType;
+
+ if (_delegateCollection.ContainsKey(delegateType))
+ {
+ luaDelegateType = _delegateCollection[delegateType];
+ }
+ else
+ {
+ luaDelegateType = GenerateDelegate(delegateType);
+ _delegateCollection[delegateType] = luaDelegateType;
+ }
+
+ var methodInfo = delegateType.GetMethod("Invoke");
+ returnTypes.Add(methodInfo.ReturnType);
+
+ foreach (ParameterInfo paramInfo in methodInfo.GetParameters())
+ {
+ if (paramInfo.ParameterType.IsByRef)
+ returnTypes.Add(paramInfo.ParameterType);
+ }
+
+ var 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 = default;
+ GenerateClass(klass, out luaClassType.klass, out luaClassType.returnTypes);
+ _classCollection[klass] = luaClassType;
+ }
+
+ return Activator.CreateInstance(luaClassType.klass, new object[] {
+ luaTable,
+ luaClassType.returnTypes
+ });
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/GenerateEventAssembly/DelegateGenerator.cs b/ExternalProjects/NLua/src/GenerateEventAssembly/DelegateGenerator.cs
new file mode 100644
index 0000000000..e9e23b59b5
--- /dev/null
+++ b/ExternalProjects/NLua/src/GenerateEventAssembly/DelegateGenerator.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace NLua
+{
+ internal class DelegateGenerator
+ {
+ private readonly ObjectTranslator _translator;
+ private readonly Type _delegateType;
+
+ public DelegateGenerator(ObjectTranslator objectTranslator, Type type)
+ {
+ _translator = objectTranslator;
+ _delegateType = type;
+ }
+
+ public object ExtractGenerated(LuaState luaState, int stackPos)
+ {
+ return CodeGeneration.Instance.GetDelegate(_delegateType, _translator.GetFunction(luaState, stackPos));
+ }
+ }
+}
diff --git a/ExternalProjects/NLua/src/GenerateEventAssembly/ILuaGeneratedType.cs b/ExternalProjects/NLua/src/GenerateEventAssembly/ILuaGeneratedType.cs
new file mode 100644
index 0000000000..8ca2dc4311
--- /dev/null
+++ b/ExternalProjects/NLua/src/GenerateEventAssembly/ILuaGeneratedType.cs
@@ -0,0 +1,11 @@
+namespace NLua
+{
+ ///
+ /// 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 LuaInterfaceGetLuaTable();
+ }
+}
\ No newline at end of file
diff --git a/ExternalProjects/NLua/src/GenerateEventAssembly/LuaClassType.cs b/ExternalProjects/NLua/src/GenerateEventAssembly/LuaClassType.cs
new file mode 100644
index 0000000000..5f22bc9938
--- /dev/null
+++ b/ExternalProjects/NLua/src/GenerateEventAssembly/LuaClassType.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace NLua
+{
+ ///
+ /// Structure to store a type and the return types of
+ /// its methods (the type of the returned value and out/ref
+ /// parameters).
+ ///
+ internal struct LuaClassType
+ {
+ public Type klass;
+ public Type[][] returnTypes;
+ }
+}
\ No newline at end of file
diff --git a/ExternalProjects/NLua/src/Lua.cs b/ExternalProjects/NLua/src/Lua.cs
new file mode 100644
index 0000000000..69b3c43e7a
--- /dev/null
+++ b/ExternalProjects/NLua/src/Lua.cs
@@ -0,0 +1,1370 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using NLua.Event;
+using NLua.Method;
+using NLua.Exceptions;
+using NLua.Extensions;
+
+
+namespace NLua
+{
+ public class Lua : IDisposable
+ {
+ ///
+ /// Event that is raised when an exception occures during a hook call.
+ ///
+ public event EventHandler HookException;
+
+ ///
+ /// Event when lua hook callback is called
+ ///
+ ///
+ /// Is only raised if SetDebugHook is called before.
+ ///
+ public event EventHandler DebugHook;
+
+ ///
+ /// lua hook calback delegate
+ ///
+ private LuaHookFunction _hookCallback;
+
+ private readonly LuaGlobals _globals = new LuaGlobals();
+
+ private LuaState _luaState;
+
+ ///
+ /// True while a script is being executed
+ ///
+ public bool IsExecuting => _executing;
+
+ public LuaState State => _luaState;
+
+ private ObjectTranslator _translator;
+
+ internal ObjectTranslator Translator => _translator;
+
+ private bool _StatePassed;
+ private bool _executing;
+
+ // The commented code bellow is the initLua, the code assigned here is minified for size/performance reasons.
+ private const string InitLuanet = @"local a={}local rawget=rawget;local b=luanet.import_type;local c=luanet.load_assembly;luanet.error,luanet.type=error,type;function a:__index(d)local e=rawget(self,'.fqn')e=(e and e..'.'or'')..d;local f=rawget(luanet,d)or b(e)if f==nil then pcall(c,e)f={['.fqn']=e}setmetatable(f,a)end;rawset(self,d,f)return f end;function a:__call(...)error('No such type: '..rawget(self,'.fqn'),2)end;luanet['.fqn']=false;setmetatable(luanet,a)luanet.load_assembly('mscorlib')";
+ //@"local metatable = {}
+ // local rawget = rawget
+ // local import_type = luanet.import_type
+ // local load_assembly = luanet.load_assembly
+ // luanet.error, luanet.type = error, type
+ // -- Lookup a .NET identifier component.
+ // function metatable:__index(key) -- key is e.g. 'Form'
+ // -- Get the fully-qualified name, e.g. 'System.Windows.Forms.Form'
+ // local fqn = rawget(self,'.fqn')
+ // fqn = ((fqn and fqn .. '.') or '') .. key
+
+ // -- Try to find either a luanet function or a CLR type
+ // local obj = rawget(luanet,key) or import_type(fqn)
+
+ // -- If key is neither a luanet function or a CLR type, then it is simply
+ // -- an identifier component.
+ // if obj == nil then
+ // -- It might be an assembly, so we load it too.
+ // pcall(load_assembly,fqn)
+ // obj = { ['.fqn'] = fqn }
+ // setmetatable(obj, metatable)
+ // end
+
+ // -- Cache this lookup
+ // rawset(self, key, obj)
+ // return obj
+ // end
+
+ // -- A non-type has been called; e.g. foo = System.Foo()
+ // function metatable:__call(...)
+ // error('No such type: ' .. rawget(self,'.fqn'), 2)
+ // end
+
+ // -- This is the root of the .NET namespace
+ // luanet['.fqn'] = false
+ // setmetatable(luanet, metatable)
+
+ // -- Preload the mscorlib assembly
+ // luanet.load_assembly('mscorlib')";
+
+ private const string ClrPackage = @"if not luanet then require'luanet'end;local a,b=luanet.import_type,luanet.load_assembly;local c={__index=function(d,e)local f=rawget(d,e)if f==nil then f=a(d.packageName.."".""..e)if f==nil then f=a(e)end;d[e]=f end;return f end}function luanet.namespace(g)if type(g)=='table'then local h={}for i=1,#g do h[i]=luanet.namespace(g[i])end;return unpack(h)end;local j={packageName=g}setmetatable(j,c)return j end;local k,l;local function m()l={}k={__index=function(n,e)for i,d in ipairs(l)do local f=d[e]if f then _G[e]=f;return f end end end}setmetatable(_G,k)end;function CLRPackage(o,p)p=p or o;local q=pcall(b,o)return luanet.namespace(p)end;function import(o,p)if not k then m()end;if not p then local i=o:find('%.dll$')if i then p=o:sub(1,i-1)else p=o end end;local j=CLRPackage(o,p)table.insert(l,j)return j end;function luanet.make_array(r,s)local t=r[#s]for i,u in ipairs(s)do t:SetValue(u,i-1)end;return t end;function luanet.each(v)local w=v:GetEnumerator()return function()if w:MoveNext()then return w.Current end end end";
+ //@"---
+ //--- This lua module provides auto importing of .net classes into a named package.
+ //--- Makes for super easy use of LuaInterface glue
+ //---
+ //--- example:
+ //--- Threading = CLRPackage(""System"", ""System.Threading"")
+ //--- Threading.Thread.Sleep(100)
+ //---
+ //--- Extensions:
+ //--- import() is a version of CLRPackage() which puts the package into a list which is used by a global __index lookup,
+ //--- and thus works rather like C#'s using statement. It also recognizes the case where one is importing a local
+ //--- assembly, which must end with an explicit .dll extension.
+
+ //--- Alternatively, luanet.namespace can be used for convenience without polluting the global namespace:
+ //--- local sys,sysi = luanet.namespace {'System','System.IO'}
+ //-- sys.Console.WriteLine(""we are at {0}"",sysi.Directory.GetCurrentDirectory())
+
+
+ //-- LuaInterface hosted with stock Lua interpreter will need to explicitly require this...
+ //if not luanet then require 'luanet' end
+
+ //local import_type, load_assembly = luanet.import_type, luanet.load_assembly
+
+ //local mt = {
+ // --- Lookup a previously unfound class and add it to our table
+ // __index = function(package, classname)
+ // local class = rawget(package, classname)
+ // if class == nil then
+ // class = import_type(package.packageName .. ""."" .. classname)
+ // if class == nil then class = import_type(classname) end
+ // package[classname] = class -- keep what we found around, so it will be shared
+ // end
+ // return class
+ // end
+ //}
+
+ //function luanet.namespace(ns)
+ // if type(ns) == 'table' then
+ // local res = {}
+ // for i = 1,#ns do
+ // res[i] = luanet.namespace(ns[i])
+ // end
+ // return unpack(res)
+ // end
+ // -- FIXME - table.packageName could instead be a private index (see Lua 13.4.4)
+ // local t = { packageName = ns }
+ // setmetatable(t,mt)
+ // return t
+ //end
+
+ //local globalMT, packages
+
+ //local function set_global_mt()
+ // packages = {}
+ // globalMT = {
+ // __index = function(T,classname)
+ // for i,package in ipairs(packages) do
+ // local class = package[classname]
+ // if class then
+ // _G[classname] = class
+ // return class
+ // end
+ // end
+ // end
+ // }
+ // setmetatable(_G, globalMT)
+ //end
+
+ //--- Create a new Package class
+ //function CLRPackage(assemblyName, packageName)
+ // -- a sensible default...
+ // packageName = packageName or assemblyName
+ // local ok = pcall(load_assembly,assemblyName) -- Make sure our assembly is loaded
+ // return luanet.namespace(packageName)
+ //end
+
+ //function import (assemblyName, packageName)
+ // if not globalMT then
+ // set_global_mt()
+ // end
+ // if not packageName then
+ // local i = assemblyName:find('%.dll$')
+ // if i then packageName = assemblyName:sub(1,i-1)
+ // else packageName = assemblyName end
+ // end
+ // local t = CLRPackage(assemblyName,packageName)
+ // table.insert(packages,t)
+ // return t
+ //end
+
+
+ //function luanet.make_array (tp,tbl)
+ // local arr = tp[#tbl]
+ // for i,v in ipairs(tbl) do
+ // arr:SetValue(v,i-1)
+ // end
+ // return arr
+ //end
+
+ //function luanet.each(o)
+ // local e = o:GetEnumerator()
+ // return function()
+ // if e:MoveNext() then
+ // return e.Current
+ // end
+ // end
+ //end
+ //";
+
+ public bool UseTraceback { get; set; } = false;
+
+ ///
+ /// The maximum number of recursive steps to take when adding global reference variables. Defaults to 2.
+ ///
+ public int MaximumRecursion
+ {
+ get
+ {
+ return _globals.MaximumRecursion;
+ }
+ set
+ {
+ _globals.MaximumRecursion = value;
+ }
+ }
+
+ ///
+ /// 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
+ {
+ return _globals.Globals;
+ }
+ }
+
+ ///
+ /// Get the thread object of this state.
+ ///
+ public LuaThread Thread
+ {
+ get
+ {
+ int oldTop = _luaState.GetTop();
+ _luaState.PushThread();
+ object returnValue = _translator.GetObject(_luaState, -1);
+
+ _luaState.SetTop(oldTop);
+ return (LuaThread)returnValue;
+ }
+ }
+
+ ///
+ /// Get the main thread object
+ ///
+ public LuaThread MainThread
+ {
+ get
+ {
+ LuaState mainThread = _luaState.MainThread;
+ int oldTop = mainThread.GetTop();
+ mainThread.PushThread();
+ object returnValue = _translator.GetObject(mainThread, -1);
+
+ mainThread.SetTop(oldTop);
+ return (LuaThread)returnValue;
+ }
+ }
+
+ public Lua(bool openLibs = true)
+ {
+ _luaState = new LuaState(openLibs);
+ Init();
+ // We need to keep this in a managed reference so the delegate doesn't get garbage collected
+ _luaState.AtPanic(PanicCallback);
+ }
+
+ // CAUTION: NLua.Lua instances can't share the same lua state!
+ public Lua(LuaState luaState)
+ {
+ luaState.PushString("NLua_Loaded");
+ luaState.GetTable((int)LuaRegistry.Index);
+
+ if (luaState.ToBoolean(-1))
+ {
+ luaState.SetTop(-2);
+ throw new LuaException("There is already a NLua.Lua instance associated with this Lua state");
+ }
+
+ _luaState = luaState;
+ _StatePassed = true;
+ luaState.SetTop(-2);
+ Init();
+ }
+
+ internal void Init()
+ {
+ _luaState.PushString("NLua_Loaded");
+ _luaState.PushBoolean(true);
+ _luaState.SetTable((int)LuaRegistry.Index);
+ if (_StatePassed == false)
+ {
+ _luaState.NewTable();
+ _luaState.SetGlobal("luanet");
+ }
+ _luaState.PushGlobalTable();
+ _luaState.GetGlobal("luanet");
+ _luaState.PushString("getmetatable");
+ _luaState.GetGlobal("getmetatable");
+ _luaState.SetTable(-3);
+ _luaState.PopGlobalTable();
+ _translator = new ObjectTranslator(this, _luaState);
+
+ ObjectTranslatorPool.Instance.Add(_luaState, _translator);
+
+ _luaState.PopGlobalTable();
+ _luaState.DoString(InitLuanet);
+ }
+
+ public void Close()
+ {
+ if (_StatePassed || _luaState == null)
+ return;
+
+ _luaState.Close();
+ ObjectTranslatorPool.Instance.Remove(_luaState);
+ _luaState = null;
+ }
+
+ internal static int PanicCallback(IntPtr state)
+ {
+ var luaState = LuaState.FromIntPtr(state);
+ string reason = string.Format("Unprotected error in call to Lua API ({0})", luaState.ToString(-1, false));
+ throw new LuaException(reason);
+ }
+
+ ///
+ /// 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
+ private void ThrowExceptionFromError(int oldTop)
+ {
+ object err = _translator.GetObject(_luaState, -1);
+ _luaState.SetTop(oldTop);
+
+ // A pre-wrapped exception - just rethrow it (stack trace of InnerException will be preserved)
+ var 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(), string.Empty);
+ }
+
+ ///
+ /// Push a debug.traceback reference onto the stack, for a pcall function to use as error handler. (Remember to increment any top-of-stack markers!)
+ ///
+ private static int PushDebugTraceback(LuaState luaState, int argCount)
+ {
+ luaState.GetGlobal("debug");
+ luaState.GetField(-1, "traceback");
+ luaState.Remove(-2);
+ int errIndex = -argCount - 2;
+ luaState.Insert(errIndex);
+ return errIndex;
+ }
+
+ ///
+ /// Return a debug.traceback() call result (a multi-line string, containing a full stack trace, including C calls.
+ /// Note: it won't return anything unless the interpreter is in the middle of execution - that is, it only makes sense to call it from a method called from Lua, or during a coroutine yield.
+ ///
+ public string GetDebugTraceback()
+ {
+ int oldTop = _luaState.GetTop();
+ _luaState.GetGlobal("debug"); // stack: debug
+ _luaState.GetField(-1, "traceback"); // stack: debug,traceback
+ _luaState.Remove(-2); // stack: traceback
+ _luaState.PCall(0, -1, 0);
+ return _translator.PopValues(_luaState, oldTop)[0] as string;
+ }
+
+ ///
+ /// Convert C# exceptions into Lua errors
+ ///
+ /// num of things on stack
+ /// null for no pending exception
+ internal int SetPendingException(Exception e)
+ {
+ var caughtExcept = e;
+
+ if (caughtExcept == null)
+ return 0;
+
+ _translator.ThrowError(_luaState, caughtExcept);
+ return 1;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public LuaFunction LoadString(string chunk, string name)
+ {
+ int oldTop = _luaState.GetTop();
+ _executing = true;
+
+ try
+ {
+ if (_luaState.LoadString(chunk, name) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+
+ var result = _translator.GetFunction(_luaState, -1);
+ _translator.PopValues(_luaState, oldTop);
+ return result;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public LuaFunction LoadString(byte[] chunk, string name)
+ {
+ int oldTop = _luaState.GetTop();
+ _executing = true;
+
+ try
+ {
+ if (_luaState.LoadBuffer(chunk, name) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+
+ var result = _translator.GetFunction(_luaState, -1);
+ _translator.PopValues(_luaState, oldTop);
+ return result;
+ }
+
+ ///
+ /// Load a File on, and return a LuaFunction to execute the file loaded (useful to see if the syntax of a file is ok)
+ ///
+ ///
+ ///
+ public LuaFunction LoadFile(string fileName)
+ {
+ int oldTop = _luaState.GetTop();
+
+ if (_luaState.LoadFile(fileName) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ var result = _translator.GetFunction(_luaState, -1);
+ _translator.PopValues(_luaState, oldTop);
+ return result;
+ }
+
+ ///
+ /// Executes a Lua chunk and returns all the chunk's return values in an array.
+ ///
+ /// Chunk to execute
+ /// Name to associate with the chunk. Defaults to "chunk".
+ ///
+ public object[] DoString(byte[] chunk, string chunkName = "chunk")
+ {
+ int oldTop = _luaState.GetTop();
+ _executing = true;
+
+ if (_luaState.LoadBuffer(chunk, chunkName) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ int errorFunctionIndex = 0;
+
+ if (UseTraceback)
+ {
+ errorFunctionIndex = PushDebugTraceback(_luaState, 0);
+ oldTop++;
+ }
+
+ try
+ {
+ if (_luaState.PCall(0, -1, errorFunctionIndex) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ return _translator.PopValues(_luaState, oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+ }
+
+ ///
+ /// Executes a Lua chunk and returns all the chunk's return values in an array.
+ ///
+ /// Chunk to execute
+ /// Name to associate with the chunk. Defaults to "chunk".
+ ///
+ public object[] DoString(string chunk, string chunkName = "chunk")
+ {
+ int oldTop = _luaState.GetTop();
+ _executing = true;
+
+ if (_luaState.LoadString(chunk, chunkName) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ int errorFunctionIndex = 0;
+
+ if (UseTraceback)
+ {
+ errorFunctionIndex = PushDebugTraceback(_luaState, 0);
+ oldTop++;
+ }
+
+ try
+ {
+ if (_luaState.PCall(0, -1, errorFunctionIndex) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ return _translator.PopValues(_luaState, oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+ }
+
+ ///
+ /// Executes a Lua file and returns all the chunk's return
+ /// values in an array
+ ///
+ public object[] DoFile(string fileName)
+ {
+ int oldTop = _luaState.GetTop();
+
+ if (_luaState.LoadFile(fileName) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ _executing = true;
+
+ int errorFunctionIndex = 0;
+ if (UseTraceback)
+ {
+ errorFunctionIndex = PushDebugTraceback(_luaState, 0);
+ oldTop++;
+ }
+
+ try
+ {
+ if (_luaState.PCall(0, -1, errorFunctionIndex) != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+
+ return _translator.PopValues(_luaState, oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+ }
+
+ public object GetObjectFromPath(string fullPath)
+ {
+ int oldTop = _luaState.GetTop();
+ string[] path = FullPathToArray(fullPath);
+ _luaState.GetGlobal(path[0]);
+ object returnValue = _translator.GetObject(_luaState, -1);
+
+ if (path.Length > 1)
+ {
+ var dispose = returnValue as LuaBase;
+ string[] remainingPath = new string[path.Length - 1];
+ Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
+ returnValue = GetObject(remainingPath);
+ dispose?.Dispose();
+ }
+
+ _luaState.SetTop(oldTop);
+ return returnValue;
+ }
+
+ public void SetObjectToPath(string fullPath, object value)
+ {
+ int oldTop = _luaState.GetTop();
+ string[] path = FullPathToArray(fullPath);
+
+ if (path.Length == 1)
+ {
+ _translator.Push(_luaState, value);
+ _luaState.SetGlobal(fullPath);
+ }
+ else
+ {
+ _luaState.GetGlobal(path[0]);
+ string[] remainingPath = new string[path.Length - 1];
+ Array.Copy(path, 1, remainingPath, 0, path.Length - 1);
+ SetObject(remainingPath, value);
+ }
+
+ _luaState.SetTop(oldTop);
+
+ // Globals auto-complete
+ if (value == null)
+ {
+ // Remove now obsolete entries
+ _globals.RemoveGlobal(fullPath);
+ }
+ else
+ {
+ // Add new entries
+ if (!_globals.Contains(fullPath))
+ _globals.RegisterGlobal(fullPath, value.GetType(), 0);
+ }
+ }
+
+ ///
+ /// Indexer for global variables from the LuaInterpreter
+ /// Supports navigation of tables by using . operator
+ ///
+ ///
+ ///
+ public object this[string fullPath] {
+ get
+ {
+ // Silently convert Lua integer to double for backward compatibility with index[] operator
+ object obj = GetObjectFromPath(fullPath);
+ if (obj is long l)
+ return (double)l;
+ return obj;
+ }
+ set
+ {
+ SetObjectToPath(fullPath, value);
+ }
+ }
+
+ ///
+ /// 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++)
+ {
+ _luaState.PushString(remainingPath[i]);
+ _luaState.GetTable(-2);
+ returnValue = _translator.GetObject(_luaState, -1);
+
+ if (returnValue == null)
+ break;
+ }
+
+ return returnValue;
+ }
+
+ ///
+ /// Gets a numeric global variable
+ ///
+ public double GetNumber(string fullPath)
+ {
+ // Silently convert Lua integer to double for backward compatibility with GetNumber method
+ object obj = GetObjectFromPath(fullPath);
+ if (obj is long l)
+ return l;
+ return (double)obj;
+ }
+
+ public int GetInteger(string fullPath)
+ {
+ object result = GetObjectFromPath(fullPath);
+ if (result == null)
+ return 0;
+
+ return (int)(long)result;
+ }
+
+ public long GetLong(string fullPath)
+ {
+ object result = GetObjectFromPath(fullPath);
+ if (result == null)
+ return 0L;
+
+ return (long)result;
+ }
+
+ ///
+ /// Gets a string global variable
+ ///
+ public string GetString(string fullPath)
+ {
+ object obj = GetObjectFromPath(fullPath);
+ if (obj == null)
+ return null;
+
+ return obj.ToString();
+ }
+
+ ///
+ /// Gets a table global variable
+ ///
+ public LuaTable GetTable(string fullPath)
+ {
+ return (LuaTable)GetObjectFromPath(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 thread global variable
+ ///
+ public LuaThread GetThread(string fullPath)
+ {
+ return (LuaThread)GetObjectFromPath(fullPath);
+ }
+
+ ///
+ /// Gets a function global variable
+ ///
+ public LuaFunction GetFunction(string fullPath)
+ {
+ object obj = GetObjectFromPath(fullPath);
+ var luaFunction = obj as LuaFunction;
+ if (luaFunction != null)
+ return luaFunction;
+
+ luaFunction = new LuaFunction((LuaNativeFunction) obj, this);
+ return luaFunction;
+ }
+
+ ///
+ /// Register a delegate type to be used to convert Lua functions to C# delegates (useful for iOS where there is no dynamic code generation)
+ /// type delegateType
+ ///
+ public void RegisterLuaDelegateType(Type delegateType, Type luaDelegateType)
+ {
+ CodeGeneration.Instance.RegisterLuaDelegateType(delegateType, luaDelegateType);
+ }
+
+ public void RegisterLuaClassType(Type klass, Type luaClass)
+ {
+ CodeGeneration.Instance.RegisterLuaClassType(klass, luaClass);
+ }
+
+ // ReSharper disable once InconsistentNaming
+ public void LoadCLRPackage()
+ {
+ _luaState.DoString(ClrPackage);
+ }
+
+ ///
+ /// 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 = _luaState.GetTop();
+
+ if (!_luaState.CheckStack(args.Length + 6))
+ throw new LuaException("Lua stack overflow");
+
+ _translator.Push(_luaState, function);
+
+ if (args.Length > 0)
+ {
+ nArgs = args.Length;
+
+ for (int i = 0; i < args.Length; i++)
+ _translator.Push(_luaState, args[i]);
+ }
+
+ _executing = true;
+
+ try
+ {
+ int errfunction = 0;
+ if (UseTraceback)
+ {
+ errfunction = PushDebugTraceback(_luaState, nArgs);
+ oldTop++;
+ }
+
+ LuaStatus error = _luaState.PCall(nArgs, -1, errfunction);
+ if (error != LuaStatus.OK)
+ ThrowExceptionFromError(oldTop);
+ }
+ finally
+ {
+ _executing = false;
+ }
+
+ if (returnTypes != null)
+ return _translator.PopValues(_luaState, oldTop, returnTypes);
+
+ 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++)
+ {
+ _luaState.PushString(remainingPath[i]);
+ _luaState.GetTable(-2);
+ }
+
+ _luaState.PushString(remainingPath[remainingPath.Length - 1]);
+ _translator.Push(_luaState, val);
+ _luaState.SetTable(-3);
+ }
+
+ ///
+ /// Creates a new empty table
+ ///
+ public LuaTable NewTable()
+ {
+ int oldTop = _luaState.GetTop();
+ _luaState.NewTable();
+ var ret = _translator.GetTable(_luaState, -1);
+ _luaState.SetTop(oldTop);
+ return ret;
+ }
+
+ internal string[] FullPathToArray(string fullPath)
+ {
+ return fullPath.SplitWithEscape('.', '\\').ToArray();
+ }
+
+ ///
+ /// Creates a new table as a global variable or as a field
+ /// inside an existing table
+ ///
+ ///
+ public void NewTable(string fullPath)
+ {
+ string[] path = FullPathToArray(fullPath);
+ int oldTop = _luaState.GetTop();
+
+ if (path.Length == 1)
+ {
+ _luaState.NewTable();
+ _luaState.SetGlobal(fullPath);
+ }
+ else
+ {
+ _luaState.GetGlobal(path[0]);
+
+ for (int i = 1; i < path.Length - 1; i++)
+ {
+ _luaState.PushString(path[i]);
+ _luaState.GetTable(-2);
+ }
+
+ _luaState.PushString(path[path.Length - 1]);
+ _luaState.NewTable();
+ _luaState.SetTable(-3);
+ }
+
+ _luaState.SetTop( oldTop);
+ }
+
+ public Dictionary