Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Lua/LuaClasses.cs
2021-12-01 19:41:07 -03:00

1028 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using MoonSharp.Interpreter;
using Microsoft.Xna.Framework;
using Barotrauma.Networking;
using System.Threading.Tasks;
using Barotrauma.Items.Components;
using System.IO;
using System.Net;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics.Dynamics;
using System.Reflection;
using HarmonyLib;
using MoonSharp.Interpreter.Interop;
using System.Diagnostics;
using System.Reflection.Emit;
using Barotrauma.Extensions;
using System.Threading;
namespace Barotrauma
{
partial class LuaSetup
{
partial class LuaUserData
{
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
public static IUserDataDescriptor RegisterType(string typeName)
{
Type type = GetType(typeName);
MethodInfo method = typeof(UserData).GetMethod(nameof(UserData.RegisterType), new Type[2] { typeof(InteropAccessMode), typeof(string) });
MethodInfo generic = method.MakeGenericMethod(type);
return (IUserDataDescriptor)generic.Invoke(null, new object[] { null, null });
}
public static object CreateStatic(string typeName)
{
Type type = GetType(typeName);
MethodInfo method = typeof(UserData).GetMethod(nameof(UserData.CreateStatic), 1, new Type[0]);
MethodInfo generic = method.MakeGenericMethod(type);
return generic.Invoke(null, null);
}
public static void AddCallMetaMember(IUserDataDescriptor IUDD)
{
var descriptor = (StandardUserDataDescriptor)IUDD;
descriptor.RemoveMetaMember("__call");
descriptor.AddMetaMember("__call", new ObjectCallbackMemberDescriptor("__call", LuaSetup.luaSetup.HandleCall));
}
public static void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName)
{
var descriptor = (StandardUserDataDescriptor)IUUD;
var field = IUUD.Type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
descriptor.RemoveMember(fieldName);
descriptor.AddMember(fieldName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
}
public static void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName)
{
var descriptor = (StandardUserDataDescriptor)IUUD;
var method = IUUD.Type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
descriptor.RemoveMember(methodName);
descriptor.AddMember(methodName, new MethodMemberDescriptor(method, InteropAccessMode.Default));
}
public static void AddMethod(IUserDataDescriptor IUUD, string methodName, object function)
{
var descriptor = (StandardUserDataDescriptor)IUUD;
descriptor.RemoveMember(methodName);
descriptor.AddMember(methodName, new ObjectCallbackMemberDescriptor(methodName, (object arg1, ScriptExecutionContext arg2, CallbackArguments arg3) =>
{
if (luaSetup != null)
return luaSetup.CallFunction(function, arg3.GetArray());
return null;
}));
}
public static void RemoveMember(IUserDataDescriptor IUUD, string memberName)
{
var descriptor = (StandardUserDataDescriptor)IUUD;
descriptor.RemoveMember(memberName);
}
}
public partial class LuaGame
{
LuaSetup env;
public LuaGame(LuaSetup e)
{
env = e;
}
public bool allowWifiChat = false;
public bool overrideTraitors = false;
public bool overrideRespawnSub = false;
public bool overrideSignalRadio = false;
public bool disableSpamFilter = false;
public bool disableDisconnectCharacter = false;
public bool enableControlHusk = false;
public bool RoundStarted
{
get
{
#if SERVER
return GameMain.Server.GameStarted;
#else
return GameMain.Client.GameStarted;
#endif
}
}
public GameSession GameSession
{
get
{
return GameMain.GameSession;
}
}
public NetLobbyScreen NetLobbyScreen
{
get
{
return GameMain.NetLobbyScreen;
}
}
public GameScreen GameScreen
{
get
{
return GameMain.GameScreen;
}
}
public World World
{
get
{
return GameMain.World;
}
}
public void OverrideTraitors(bool o)
{
overrideTraitors = o;
}
public void OverrideRespawnSub(bool o)
{
overrideRespawnSub = o;
}
public void AllowWifiChat(bool o)
{
allowWifiChat = o;
}
public void OverrideSignalRadio(bool o)
{
overrideSignalRadio = o;
}
public void DisableSpamFilter(bool o)
{
disableSpamFilter = o;
}
public void DisableDisconnectCharacter(bool o)
{
disableDisconnectCharacter = o;
}
public void EnableControlHusk(bool o)
{
enableControlHusk = o;
}
public static void Explode(Vector2 pos, float range = 100, float force = 30, float damage = 30, float structureDamage = 30, float itemDamage = 30, float empStrength = 0, float ballastFloraStrength = 0)
{
new Explosion(range, force, damage, structureDamage, itemDamage, empStrength, ballastFloraStrength).Explode(pos, null);
}
public static Character Spawn(string name, Vector2 worldPos)
{
Character spawnedCharacter = null;
Vector2 spawnPosition = worldPos;
string characterLowerCase = name.ToLowerInvariant();
JobPrefab job = null;
if (!JobPrefab.Prefabs.ContainsKey(characterLowerCase))
{
job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(characterLowerCase, StringComparison.OrdinalIgnoreCase));
}
else
{
job = JobPrefab.Prefabs[characterLowerCase];
}
bool human = job != null || characterLowerCase == CharacterPrefab.HumanSpeciesName;
if (string.IsNullOrWhiteSpace(name)) { return null; }
if (human)
{
var variant = job != null ? Rand.Range(0, job.Variants, Rand.RandSync.Server) : 0;
CharacterInfo characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job, variant: variant);
spawnedCharacter = Character.Create(characterInfo, spawnPosition, ToolBox.RandomSeed(8));
if (GameMain.GameSession != null)
{
//TODO: a way to select which team to spawn to?
spawnedCharacter.TeamID = Character.Controlled != null ? Character.Controlled.TeamID : CharacterTeamType.Team1;
#if CLIENT
GameMain.GameSession.CrewManager.AddCharacter(spawnedCharacter);
#endif
}
spawnedCharacter.GiveJobItems(null);
spawnedCharacter.Info.StartItemsGiven = true;
}
else
{
if (CharacterPrefab.FindBySpeciesName(name) != null)
{
spawnedCharacter = Character.Create(name, spawnPosition, ToolBox.RandomSeed(8));
}
}
return spawnedCharacter;
}
public static string SpawnItem(string name, Vector2 pos, bool inventory = false, Character character = null)
{
string error;
DebugConsole.SpawnItem(new string[] { name, inventory ? "inventory" : "cursor" }, pos, character, out error);
return error;
}
public static void RemoveItem(Item item)
{
EntitySpawner.Spawner.AddToRemoveQueue(item);
}
public static ItemPrefab GetItemPrefab(string itemNameOrId)
{
ItemPrefab itemPrefab =
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
return itemPrefab;
}
public void AddItemPrefabToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, DynValue spawned = null)
{
EntitySpawner.Spawner.AddToSpawnQueue(itemPrefab, position, onSpawned: (Item item) => {
if (spawned?.Type == DataType.Function) env.lua.Call(spawned, UserData.Create(item));
});
}
public void AddItemPrefabToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, DynValue spawned = null)
{
EntitySpawner.Spawner.AddToSpawnQueue(itemPrefab, inventory, onSpawned: (Item item) => {
if (spawned?.Type == DataType.Function) env.lua.Call(spawned, UserData.Create(item));
});
}
public static Submarine GetRespawnSub()
{
#if SERVER
if (GameMain.Server.RespawnManager == null)
return null;
return GameMain.Server.RespawnManager.RespawnShuttle;
#else
if (GameMain.Client.RespawnManager == null)
return null;
return GameMain.Client.RespawnManager.RespawnShuttle;
#endif
}
public static Items.Components.Steering GetSubmarineSteering(Submarine sub)
{
foreach (Item item in Item.ItemList)
{
if (item.Submarine != sub) continue;
var steering = item.GetComponent<Items.Components.Steering>();
if (steering != null)
{
return steering;
}
}
return null;
}
public static WifiComponent GetWifiComponent(Item item)
{
if (item == null) return null;
return item.GetComponent<WifiComponent>();
}
public static LightComponent GetLightComponent(Item item)
{
if (item == null) return null;
return item.GetComponent<LightComponent>();
}
public static CustomInterface GetCustomInterface(Item item)
{
if (item == null) return null;
return item.GetComponent<CustomInterface>();
}
public static Fabricator GetFabricatorComponent(Item item)
{
if (item == null) return null;
return item.GetComponent<Fabricator>();
}
public static Holdable GetHoldableComponent(Item item)
{
if (item == null) return null;
return item.GetComponent<Holdable>();
}
public static void ExecuteCommand(string command)
{
DebugConsole.ExecuteCommand(command);
}
public static Signal CreateSignal(string value, int stepsTaken = 1, Character sender = null, Item source = null, float power = 0, float strength = 1)
{
return new Signal(value, stepsTaken, sender, source, power, strength);
}
public static ContentPackage[] GetEnabledContentPackages()
{
return GameMain.Config.AllEnabledPackages.ToArray();
}
public static List<string> GetEnabledPackagesDirectlyFromFile()
{
List<string> enabledPackages = new List<string>();
XDocument doc = XMLExtensions.LoadXml("config_player.xml");
var contentPackagesElement = doc.Root.Element("contentpackages");
string coreName = contentPackagesElement.Element("core")?.GetAttributeString("name", "");
enabledPackages.Add(coreName);
XElement regularElement = contentPackagesElement.Element("regular");
List<XElement> subElements = regularElement?.Elements()?.ToList();
foreach (var subElement in subElements)
{
if (!bool.TryParse(subElement.GetAttributeString("enabled", "false"), out bool enabled) || !enabled) { continue; }
string name = subElement.GetAttributeString("name", null);
enabledPackages.Add(name);
}
return enabledPackages;
}
private List<DebugConsole.Command> luaAddedCommand = new List<DebugConsole.Command>();
public void RemoveCommand(string name)
{
for (var i = 0; i < DebugConsole.Commands.Count; i++)
{
foreach (var cmdname in DebugConsole.Commands[i].names)
{
if (cmdname == name)
{
luaAddedCommand.Remove(DebugConsole.Commands[i]);
DebugConsole.Commands.RemoveAt(i);
continue;
}
}
}
}
public void AddCommand(string name, string help, object onExecute, object getValidArgs = null, bool isCheat = false)
{
var cmd = new DebugConsole.Command(name, help, (string[] arg1) => { env.CallFunction(onExecute, new object[] { arg1 }); },
() =>
{
var result = env.CallFunction(getValidArgs, new object[] { });
if (result == null || !(result is string[][])) { return null; }
return (string[][])result;
}, isCheat);
luaAddedCommand.Add(cmd);
DebugConsole.Commands.Add(cmd);
}
public void AssignOnExecute(string names, object onExecute) => DebugConsole.AssignOnExecute(names, (string[] a) => { env.CallFunction(onExecute, new object[] { a }); });
public void Stop()
{
foreach(var cmd in luaAddedCommand)
{
DebugConsole.Commands.Remove(cmd);
}
}
}
public partial class LuaTimer
{
public static long LastUpdateTime = 0;
public LuaSetup env;
public LuaTimer(LuaSetup e)
{
env = e;
}
public static double Time
{
get
{
return GetTime();
}
}
public void Wait(object function, int millisecondDelay)
{
Task.Delay(millisecondDelay).ContinueWith(t => env.hook.EnqueueFunction(function));
}
public static double GetTime()
{
return Timing.TotalTime;
}
public static float GetUsageMemory()
{
Process proc = Process.GetCurrentProcess();
float memory = MathF.Round(proc.PrivateMemorySize64 / (1024 * 1024), 2);
proc.Dispose();
return memory;
}
}
private partial class LuaRandom
{
Random random;
public LuaRandom()
{
random = new Random();
}
public int Range(int min, int max)
{
return random.Next(min, max);
}
public float RangeFloat(float min, float max)
{
double range = (double)max - (double)min;
double sample = random.NextDouble();
double scaled = (sample * range) + min;
float f = (float)scaled;
return f;
}
}
private partial class LuaFile
{
// TODO: SANDBOXING
public static bool IsPathAllowed(string path)
{
path = Path.GetFullPath(path).CleanUpPath();
if (path.StartsWith(Path.GetFullPath("Mods").CleanUpPath()))
return true;
if (path.StartsWith(Path.GetFullPath("Submarines").CleanUpPath()))
return true;
if (path.StartsWith(Path.GetFullPath("Data").CleanUpPath()))
return true;
if (path.StartsWith(Path.GetFullPath("Lua").CleanUpPath()))
return true;
if (path.StartsWith(Path.GetFullPath("Content").CleanUpPath()))
return true;
return false;
}
public static bool IsPathAllowedLuaException(string path)
{
if (IsPathAllowed(path))
return true;
else
luaSetup.HandleLuaException(new Exception("File access to \"" + path + "\" not allowed."));
return false;
}
public static string Read(string path)
{
if (!IsPathAllowedLuaException(path))
return "";
return File.ReadAllText(path);
}
public static void Write(string path, string text)
{
if (!IsPathAllowedLuaException(path))
return;
File.WriteAllText(path, text);
}
public static bool Exists(string path)
{
if (!IsPathAllowedLuaException(path))
return false;
return File.Exists(path);
}
public static bool DirectoryExists(string path)
{
if (!IsPathAllowedLuaException(path))
return false;
return Directory.Exists(path);
}
public static string[] GetFiles(string path)
{
return Directory.GetFiles(path);
}
public static string[] GetDirectories(string path)
{
if (!IsPathAllowedLuaException(path))
return new string[] { };
return Directory.GetDirectories(path);
}
public static string[] DirSearch(string sDir)
{
if (!IsPathAllowedLuaException(sDir))
return new string[] { };
List<string> files = new List<string>();
try
{
foreach (string f in Directory.GetFiles(sDir))
{
files.Add(f);
}
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d))
{
files.Add(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
return files.ToArray();
}
}
public partial class LuaNetworking
{
public LuaSetup env;
public LuaNetworking(LuaSetup e)
{
env = e;
}
public Dictionary<string, object> NetReceives = new Dictionary<string, object>();
[MoonSharpHidden]
public void NetMessageReceived(IReadMessage netMessage, Client client = null)
{
string netMessageName = netMessage.ReadString();
if (NetReceives[netMessageName] is Closure)
env.lua.Call(NetReceives[netMessageName], new object[] { netMessage, client });
}
public void Receive(string netMessageName, object callback)
{
NetReceives[netMessageName] = callback;
}
public IWriteMessage Start(string netMessageName)
{
var message = new WriteOnlyMessage();
#if SERVER
message.Write((byte)ServerPacketHeader.LUA_NET_MESSAGE);
#else
message.Write((byte)ClientPacketHeader.LUA_NET_MESSAGE);
#endif
message.Write(netMessageName);
return ((IWriteMessage)message);
}
public IWriteMessage Start()
{
return new WriteOnlyMessage();
}
#if SERVER
public void ClientWriteLobby(Client client) => GameMain.Server.ClientWriteLobby(client);
public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
if (connection == null)
{
foreach (NetworkConnection conn in Client.ClientList.Select(c => c.Connection))
{
GameMain.Server.ServerPeer.Send(netMessage, conn, deliveryMethod);
}
}
else
{
GameMain.Server.ServerPeer.Send(netMessage, connection, deliveryMethod);
}
}
#else
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
{
GameMain.Client.ClientPeer.Send(netMessage, deliveryMethod);
}
#endif
public void RequestPostHTTP(string url, object callback, string data, string contentType = "application/json")
{
try
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = contentType;
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
streamWriter.Write(data);
httpWebRequest.BeginGetResponse(new AsyncCallback((IAsyncResult result) =>
{
try
{
var httpResponse = httpWebRequest.EndGetResponse(result);
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
env.hook.EnqueueFunction(callback, streamReader.ReadToEnd() );
}catch(Exception e)
{
env.hook.EnqueueFunction(callback, e.ToString());
}
}), null);
}catch(Exception e)
{
env.hook.EnqueueFunction(callback, e.ToString());
}
}
public void RequestGetHTTP(string url, object callback)
{
try
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.BeginGetResponse(new AsyncCallback((IAsyncResult result) =>
{
try
{
var httpResponse = httpWebRequest.EndGetResponse(result);
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
env.hook.EnqueueFunction(callback, streamReader.ReadToEnd());
}catch (Exception e)
{
env.hook.EnqueueFunction(callback, e.ToString());
}
}), null);
}
catch(Exception e)
{
env.hook.EnqueueFunction(callback, e.ToString());
}
}
}
public partial class LuaHook
{
public LuaSetup env;
public LuaHook(LuaSetup e)
{
env = e;
_hookMethods = new Dictionary<string, object>();
}
public class HookFunction
{
public string name;
public string hookName;
public object function;
public HookFunction(string n, string hn, object func)
{
name = n;
hookName = hn;
function = func;
}
}
private Dictionary<string, Dictionary<string, HookFunction>> hookFunctions = new Dictionary<string, Dictionary<string, HookFunction>>();
private static Dictionary<string, object> _hookMethods;
private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>();
public enum HookMethodType
{
Before, After
}
static void _hookLuaPatch(MethodBase __originalMethod, object[] __params, object __instance, out LuaResult result, HookMethodType hookMethodType)
{
// Although it works correctly, the performance is low
result = new LuaResult(null);
try
{
var classType = __originalMethod.DeclaringType;
var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{__originalMethod.Name}";
var @params = __originalMethod.GetParameters();
var ptable = new Dictionary<string, object>();
for (int i = 0; i < @params.Length; i++)
{
ptable.Add(@params[i].Name, __params[i]);
}
if (_hookMethods.TryGetValue(methodPath, out object hookMethod))
{
result = new LuaResult(luaSetup.hook.env.lua.Call(hookMethod, __instance, ptable));
}
else
{
DebugConsole.ThrowError($"No hook method found in _hookMethods[{methodPath}]");
}
}
catch (Exception ex)
{
DebugConsole.ThrowError(nameof(_hookLuaPatch), ex);
}
}
static bool HookLuaPatchPrefix(MethodBase __originalMethod, object[] __params, object __instance)
{
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
return result.IsNull();
}
static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
if (!result.IsNull())
{
__result = result.Object();
return false;
}
return true;
}
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance)
{
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
}
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
if (!result.IsNull())
__result = result.Object();
}
private static MethodInfo _miHookLuaPatchPrefix = typeof(LuaHook).GetMethod("HookLuaPatchPrefix", BindingFlags.NonPublic | BindingFlags.Static);
private static MethodInfo _miHookLuaPatchRetPrefix = typeof(LuaHook).GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static);
private static MethodInfo _miHookLuaPatchPostfix = typeof(LuaHook).GetMethod("HookLuaPatchPostfix", BindingFlags.NonPublic | BindingFlags.Static);
private static MethodInfo _miHookLuaPatchRetPostfix = typeof(LuaHook).GetMethod("HookLuaPatchRetPostfix", BindingFlags.NonPublic | BindingFlags.Static);
public void HookMethod(string className, string methodName, object hookMethod, HookMethodType hookMethodType = HookMethodType.Before)
{
var classType = Type.GetType(className);
var methodInfos = classType.GetMethods();
HarmonyMethod harmonyMethod = new HarmonyMethod();
HarmonyMethod harmonyMethodRet = new HarmonyMethod();
if (hookMethodType == HookMethodType.Before)
{
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPrefix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPrefix);
}
else if (hookMethodType == HookMethodType.After)
{
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPostfix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPostfix);
}
foreach (var methodInfo in methodInfos)
{
if (methodInfo.Name == methodName)
{
if (hookMethodType == HookMethodType.Before)
{
if (methodInfo.ReturnType == typeof(void))
env.harmony.Patch(methodInfo, prefix: harmonyMethod);
else
env.harmony.Patch(methodInfo, prefix: harmonyMethodRet);
}
else if (hookMethodType == HookMethodType.After)
{
if (methodInfo.ReturnType == typeof(void))
env.harmony.Patch(methodInfo, postfix: harmonyMethod);
else
env.harmony.Patch(methodInfo, postfix: harmonyMethodRet);
}
// build an unique method path by patch type, class, method self
var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{methodInfo.Name}";
if (hookMethod != null)
{
if (!_hookMethods.TryAdd(methodPath, hookMethod))
DebugConsole.ThrowError($"Failed to add key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]");
#if DEBUG
else
DebugConsole.NewMessage($"Sucessfully added key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]", Color.LightSkyBlue);
#endif
}
break;
}
}
}
public void EnqueueFunction(object function, params object[] args)
{
queuedFunctionCalls.Enqueue(new Tuple<object, object[]>(function, args));
}
public void Add(string name, string hookName, object function)
{
if (name == null && hookName == null && function == null) return;
if (!hookFunctions.ContainsKey(name))
hookFunctions.Add(name, new Dictionary<string, HookFunction>());
hookFunctions[name][hookName] = new HookFunction(name, hookName, function);
}
public void Remove(string name, string hookName)
{
if (name == null && hookName == null) return;
if (!hookFunctions.ContainsKey(name))
return;
if(hookFunctions[name].ContainsKey(hookName))
hookFunctions[name].Remove(hookName);
}
public void Update()
{
if (queuedFunctionCalls.TryDequeue(out Tuple<object, object[]> result))
{
env.CallFunction(result.Item1, result.Item2);
}
}
public object Call(string name, params object[] args)
{
if (env == null) return null;
if (name == null) return null;
if (args == null) { args = new object[] { }; }
if (!hookFunctions.ContainsKey(name))
return null;
object lastResult = null;
foreach (HookFunction hf in hookFunctions[name].Values)
{
try
{
if (hf.function is Closure)
{
var result = env.lua.Call(hf.function, args);
if (!result.IsNil())
lastResult = result;
}
}
catch (Exception e)
{
StringBuilder argsSb = new StringBuilder();
foreach(var arg in args)
{
argsSb.Append(arg + " ");
}
env.HandleLuaException(e, $"Error in Hook '{name}'->'{hf.hookName}', with args '{argsSb}'");
}
}
return lastResult;
}
}
}
public class LuaResult
{
object result;
public LuaResult(object arg)
{
result = arg;
}
public bool IsNull()
{
if (result == null)
return true;
if (result is DynValue dynValue)
return dynValue.IsNil();
return false;
}
public bool Bool()
{
if (result is DynValue dynValue)
{
return dynValue.CastToBool();
}
return false;
}
public float Float()
{
if (result is DynValue dynValue)
{
var num = dynValue.CastToNumber();
if (num == null) { return 0f; }
return (float)num;
}
return 0f;
}
public double Double()
{
if (result is DynValue dynValue)
{
var num = dynValue.CastToNumber();
if (num == null) { return 0f; }
return (double)num;
}
return 0f;
}
public object Object()
{
if(result is DynValue dynValue)
{
return dynValue.ToObject();
}
return null;
}
}
}