// Copyright (c) Pixel Crushers. All rights reserved.
#if !(USE_NLUA || OVERRIDE_LUA)
using UnityEngine;
using System;
using System.Reflection;
using Language.Lua;
namespace PixelCrushers.DialogueSystem
{
///
/// A static class that provides a global Lua virtual machine. This class provides a layer of
/// abstraction between the low level Lua implementation (in this case LuaInterpreter) and
/// the Dialogue System.
///
public sealed class Lua
{
///
/// Stores a Lua interpreter result (LuaValue) and provides easy conversion to basic types.
///
public struct Result
{
public Language.Lua.LuaValue luaValue;
public LuaTableWrapper luaTableWrapper;
public Result(Language.Lua.LuaValue luaValue)
{
this.luaValue = luaValue;
this.luaTableWrapper = null;
}
public bool hasReturnValue { get { return luaValue != null; } }
public string asString { get { return hasReturnValue ? luaValue.ToString() : string.Empty; } }
public bool asBool { get { return (hasReturnValue && (luaValue is Language.Lua.LuaBoolean)) ? (luaValue as Language.Lua.LuaBoolean).BoolValue : string.Compare(asString, "True", StringComparison.OrdinalIgnoreCase) == 0; } }
public float asFloat { get { return hasReturnValue ? Tools.StringToFloat(luaValue.ToString()) : 0; } }
public int asInt { get { return hasReturnValue ? Tools.StringToInt(luaValue.ToString()) : 0; } }
public LuaTableWrapper asTable { get { if (luaTableWrapper == null) luaTableWrapper = new LuaTableWrapper(luaValue as Language.Lua.LuaTable); return luaTableWrapper; } }
public bool isString { get { return hasReturnValue && luaValue is Language.Lua.LuaString; } }
public bool isBool { get { return hasReturnValue && luaValue is Language.Lua.LuaBoolean; } }
public bool isNumber { get { return hasReturnValue && luaValue is Language.Lua.LuaNumber; } }
public bool isTable { get { return hasReturnValue & (luaValue is Language.Lua.LuaTable); } }
/// @cond FOR_V1_COMPATIBILITY
public bool HasReturnValue { get { return hasReturnValue; } }
public string AsString { get { return asString; } }
public bool AsBool { get { return asBool; } }
public float AsFloat { get { return asFloat; } }
public int AsInt { get { return asInt; } }
public LuaTableWrapper AsTable { get { return asTable; } }
public bool IsString { get { return isString; } }
public bool IsBool { get { return isBool; } }
public bool IsNumber { get { return isNumber; } }
public bool IsTable { get { return isTable; } }
/// @endcond
}
private static Result m_noResult = new Result(null);
public static Result noResult { get { return m_noResult; } }
public static Result NoResult { get { return m_noResult; } }
///
/// Lua.RunRaw sets this Boolean flag whenever it's invoked.
///
///
/// true when Lua.RunRaw is invoked.
///
public static bool wasInvoked { get; set; }
///
/// Set true to not log exceptions to the Console.
///
/// true if mute exceptions; otherwise, false.
public static bool muteExceptions { get; set; }
///
/// Set true to log warnings if trying to register a function under a name that's already registered.
///
public static bool warnRegisteringExistingFunction { get; set; }
///
/// Provides direct access to the Lua Interpreter environment.
///
/// The Interpreter environment.
public static Language.Lua.LuaTable environment { get { return m_environment; } }
public static Language.Lua.LuaTable Environment { get { return m_environment; } }
///
/// The Lua Interpreter environment.
///
private static Language.Lua.LuaTable m_environment = Language.Lua.LuaInterpreter.CreateGlobalEnviroment();
#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void InitStaticVariables()
{
m_environment = Language.Lua.LuaInterpreter.CreateGlobalEnviroment();
m_noResult = new Result(null);
Assignment.InitializeVariableMonitoring();
}
#endif
/// @cond FOR_V1_COMPATIBILITY
public static bool WasInvoked { get { return wasInvoked; } set { wasInvoked = value; } }
public static bool MuteExceptions { get { return muteExceptions; } set { muteExceptions = value; } }
public static bool WarnRegisteringExistingFunction { get { return warnRegisteringExistingFunction; } set { warnRegisteringExistingFunction = value; } }
/// @endcond
///
/// Runs the specified luaCode.
///
///
/// A Result struct from which you can retrieve basic data types from the first return value.
///
///
/// The Lua code to run. Generally, if you want a return value, this string should start with "return".
///
///
/// If true, logs the Lua command to the console.
///
///
/// If true, exceptions are passed up to the caller. Otherwise they're caught and ignored.
///
///
/// float myHeight = Lua.Run("return height").asFloat;
///
public static Result Run(string luaCode, bool debug, bool allowExceptions)
{
return new Result(RunRaw(luaCode, debug, allowExceptions));
}
///
/// Runs the specified luaCode. Exceptions are ignored.
///
/// The Lua code to run. Generally, if you want a return value, this string should start with "return".
/// If set to true, logs the Lua command to the console.
public static Result Run(string luaCode, bool debug)
{
return Run(luaCode, debug, false);
}
///
/// Run the specified luaCode. The code is not logged to the console, and exceptions are ignored.
///
/// The Lua code to run. Generally, if you want a return value, this string should start with "return".
public static Result Run(string luaCode)
{
return Run(luaCode, false, false);
}
///
/// Evaluates a boolean condition defined in Lua code.
///
///
/// true if luaCode evaluates to true; otherwise, false.
///
///
/// The conditional expression to evaluate. Do not include "return" in front.
///
///
/// If true, logs the Lua command to the console.
///
///
/// If true, exceptions are passed up to the caller. Otherwise they're caught and ignored.
///
///
/// if (Lua.IsTrue("height > 6")) { ... }
///
public static bool IsTrue(string luaCondition, bool debug, bool allowExceptions)
{
return (Tools.IsStringNullOrEmptyOrWhitespace(luaCondition) || IsOnlyComment(luaCondition)) ? true
: Run("return " + luaCondition, debug, allowExceptions).asBool;
}
///
/// Evaluates a boolean condition defined in Lua code. Exceptions are ignored.
///
/// true if luaCode evaluates to true; otherwise, false
/// The conditional expression to evaluate. Do not include "return" in front.
/// If true, logs the Lua command to the console.
public static bool IsTrue(string luaCondition, bool debug)
{
return IsTrue(luaCondition, debug, false);
}
///
/// Evaluates a boolean condition defined in Lua code. The code is not logged to the console, and exceptions are ignored.
///
/// true if luaCode evaluates to true; otherwise, false
/// The conditional expression to evaluate. Do not include "return" in front.
public static bool IsTrue(string luaCondition)
{
return IsTrue(luaCondition, false, false);
}
public static bool IsOnlyComment(string luaCode)
{
if (luaCode.StartsWith("--"))
{
if (!luaCode.Contains("\n"))
{
return true;
}
else
{
var lines = luaCode.Split('\n');
foreach (var line in lines)
{
if (!line.StartsWith("--")) return false;
}
return true;
}
}
return false;
}
///
/// Runs Lua code and returns an array of return values.
///
///
/// An array of return values, or null if the code generates an error.
///
///
/// The Lua code to run. If you want a return value, this string should usually start with
/// "return".
///
///
/// If true, logs the Lua command to the console.
///
///
/// If true, exceptions are passed up to the caller. Otherwise they're caught and logged but ignored.
///
public static Language.Lua.LuaValue RunRaw(string luaCode, bool debug, bool allowExceptions)
{
try
{
if (string.IsNullOrEmpty(luaCode))
{
return null;
}
else
{
if (Debug.isDebugBuild && debug) Debug.Log(string.Format("{0}: Lua({1})", new System.Object[] { DialogueDebug.Prefix, luaCode }));
wasInvoked = true;
return Language.Lua.LuaInterpreter.Interpreter(luaCode, environment);
}
}
catch (Exception e)
{
if (Debug.isDebugBuild && !muteExceptions) Debug.LogError(string.Format("{0}: Lua code '{1}' threw exception '{2}'", new System.Object[] { DialogueDebug.Prefix, luaCode, e.Message }));
if (allowExceptions) throw e; else return null;
}
}
///
/// Runs Lua code and returns an array of return values. Ignores exceptions.
///
///
/// An array of return values, or null if the code generates an error.
///
///
/// The Lua code to run. If you want a return value, this string should usually start with
/// "return".
///
///
/// If true, logs the Lua command to the console.
///
public static Language.Lua.LuaValue RunRaw(string luaCode, bool debug)
{
return RunRaw(luaCode, debug, false);
}
///
/// Runs Lua code and returns an array of return values. Does not log to console and ignores exceptions.
///
///
/// An array of return values, or null if the code generates an error.
///
///
/// The Lua code to run. If you want a return value, this string should usually start with
/// "return".
///
public static Language.Lua.LuaValue RunRaw(string luaCode)
{
return RunRaw(luaCode, false, false);
}
///
/// Registers a C# function with the Lua interpreter so it can be used in Lua.
///
///
/// The name of the function in Lua.
///
///
/// Target object containing the registered method. Can be null if a static method.
///
///
/// The method that will be called from Lua.
///
public static void RegisterFunction(string functionName, object target, MethodInfo method)
{
if (environment.ContainsKey(new Language.Lua.LuaString(functionName)))
{
if (warnRegisteringExistingFunction && DialogueDebug.logWarnings) Debug.LogWarning(string.Format("{0}: Can't register Lua function {1}. A function with that name is already registered.", new System.Object[] { DialogueDebug.Prefix, functionName }));
}
else
{
if (DialogueDebug.logInfo) Debug.Log(string.Format("{0}: Registering Lua function {1}", new System.Object[] { DialogueDebug.Prefix, functionName }));
environment.RegisterMethodFunction(functionName, target, method);
}
}
///
/// Unregisters a C# function.
///
/// Function name.
public static void UnregisterFunction(string functionName)
{
if (DialogueDebug.logInfo) Debug.Log(string.Format("{0}: Unregistering Lua function {1}", new System.Object[] { DialogueDebug.Prefix, functionName }));
environment.SetNameValue(functionName, Language.Lua.LuaNil.Nil);
}
}
}
#endif