Move LuaUserData and registration into a proper service
This commit is contained in:
committed by
Maplewheels
parent
9b9529107c
commit
5777b64a18
@@ -189,6 +189,10 @@ namespace Barotrauma
|
||||
servicesProvider.RegisterServiceType<IAssemblyLoaderService.IFactory, AssemblyLoader.Factory>(ServiceLifetime.Transient);
|
||||
servicesProvider.RegisterServiceResolver<IPluginManagementService>(factory => factory.GetInstance<IAssemblyManagementService>());
|
||||
servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
||||
servicesProvider.RegisterServiceType<IDefaultLuaRegistrar, DefaultLuaRegistrar>(ServiceLifetime.Singleton);
|
||||
servicesProvider.RegisterServiceType<ILuaUserDataService, LuaUserDataService>(ServiceLifetime.Singleton);
|
||||
servicesProvider.RegisterServiceType<ISafeLuaUserDataService, SafeLuaUserDataService>(ServiceLifetime.Singleton);
|
||||
|
||||
servicesProvider.RegisterServiceType<ILuaScriptLoader, LuaScriptLoader>(ServiceLifetime.Transient);
|
||||
servicesProvider.RegisterServiceType<LuaGame, LuaGame>(ServiceLifetime.Singleton);
|
||||
servicesProvider.RegisterServiceType<ILuaCsTimer, LuaCsTimer>(ServiceLifetime.Singleton);
|
||||
|
||||
+37
-31
@@ -17,16 +17,10 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Barotrauma.GameSettings;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
@@ -39,20 +33,26 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
private List<ILuaScriptResourceInfo> _resourcesInfo = new List<ILuaScriptResourceInfo>();
|
||||
|
||||
private readonly AsyncReaderWriterLock _operationsLock = new ();
|
||||
|
||||
|
||||
private readonly ILuaUserDataService _userDataService;
|
||||
private readonly ISafeLuaUserDataService _safeUserDataService;
|
||||
|
||||
private readonly ILuaScriptLoader _luaScriptLoader;
|
||||
private readonly ILuaScriptServicesConfig _luaScriptServicesConfig;
|
||||
private readonly ILoggerService _loggerService;
|
||||
private readonly LuaGame _luaGame;
|
||||
private readonly ILuaCsHook _luaCsHook;
|
||||
private readonly ILuaCsTimer _luaCsTimer;
|
||||
private readonly IDefaultLuaRegistrar _defaultLuaRegistrar;
|
||||
//private readonly ILuaCsNetworking _luaCsNetworking;
|
||||
//private readonly ILuaCsUtility _luaCsUtility;
|
||||
//private readonly ILuaCsTimer _luaCsTimer;
|
||||
|
||||
public LuaScriptManagementService(
|
||||
ILoggerService loggerService,
|
||||
ILuaScriptLoader loader,
|
||||
ILoggerService loggerService,
|
||||
ILuaScriptLoader loader,
|
||||
ILuaUserDataService userDataService,
|
||||
ISafeLuaUserDataService safeUserDataService,
|
||||
IDefaultLuaRegistrar defaultLuaRegistrar,
|
||||
ILuaScriptServicesConfig luaScriptServicesConfig,
|
||||
LuaGame luaGame,
|
||||
ILuaCsHook luaCsHook,
|
||||
@@ -62,6 +62,9 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
)
|
||||
{
|
||||
_luaScriptLoader = loader;
|
||||
_userDataService = userDataService;
|
||||
_safeUserDataService = safeUserDataService;
|
||||
_defaultLuaRegistrar = defaultLuaRegistrar;
|
||||
_luaScriptServicesConfig = luaScriptServicesConfig;
|
||||
_loggerService = loggerService;
|
||||
|
||||
@@ -168,16 +171,15 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
|
||||
Script.GlobalOptions.ShouldPCallCatchException = (Exception ex) => { return true; };
|
||||
|
||||
RegisterType(typeof(LuaGame));
|
||||
RegisterType(typeof(EventService));
|
||||
RegisterType(typeof(ILuaCsNetworking));
|
||||
RegisterType(typeof(ILuaCsUtility));
|
||||
RegisterType(typeof(ILuaCsTimer));
|
||||
RegisterType(typeof(LuaCsFile));
|
||||
RegisterType(typeof(ILuaScriptResourceInfo));
|
||||
RegisterType(typeof(IResourceInfo));
|
||||
RegisterType(typeof(LuaUserData));
|
||||
RegisterType(typeof(IUserDataDescriptor));
|
||||
UserData.RegisterType(typeof(LuaGame));
|
||||
UserData.RegisterType(typeof(EventService));
|
||||
UserData.RegisterType(typeof(ILuaCsNetworking));
|
||||
UserData.RegisterType(typeof(ILuaCsUtility));
|
||||
UserData.RegisterType(typeof(ILuaCsTimer));
|
||||
UserData.RegisterType(typeof(LuaCsFile));
|
||||
UserData.RegisterType(typeof(ILuaScriptResourceInfo));
|
||||
UserData.RegisterType(typeof(IResourceInfo));
|
||||
UserData.RegisterType(typeof(IUserDataDescriptor));
|
||||
|
||||
new LuaConverters(_script).RegisterLuaConverters();
|
||||
|
||||
@@ -193,20 +195,31 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
|
||||
_script.Globals["dostring"] = (Func<string, Table, string, DynValue>)_script.DoString;
|
||||
_script.Globals["load"] = (Func<string, Table, string, DynValue>)_script.LoadString;
|
||||
|
||||
_script.Globals["Game"] = _luaGame;
|
||||
_script.Globals["Hook"] = _luaCsHook;
|
||||
_script.Globals["Timer"] = _luaCsTimer;
|
||||
_script.Globals["File"] = UserData.CreateStatic<LuaCsFile>();
|
||||
//_script.Globals["Networking"] = _luaCsNetworking;
|
||||
//_script.Globals["Steam"] = Steam;
|
||||
_script.Globals["LuaUserData"] = UserData.CreateStatic<LuaUserData>();
|
||||
|
||||
if (GameMain.LuaCs.IsCsEnabled)
|
||||
{
|
||||
UserData.RegisterType(typeof(LuaUserDataService));
|
||||
_script.Globals["LuaUserData"] = _userDataService;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserData.RegisterType(typeof(SafeLuaUserDataService));
|
||||
_script.Globals["LuaUserData"] = _safeUserDataService;
|
||||
}
|
||||
|
||||
_script.Globals["ExecutionNumber"] = 0;
|
||||
_script.Globals["CSActive"] = false;
|
||||
|
||||
_script.Globals["SERVER"] = LuaCsSetup.IsServer;
|
||||
_script.Globals["CLIENT"] = LuaCsSetup.IsClient;
|
||||
|
||||
_defaultLuaRegistrar.RegisterAll();
|
||||
}
|
||||
|
||||
public FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaScriptResourceInfo> executionOrder)
|
||||
@@ -291,24 +304,17 @@ class LuaScriptManagementService : ILuaScriptManagementService, ILuaDataService
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
_userDataService.Reset();
|
||||
return DisposeAllPackageResources();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_userDataService.Dispose();
|
||||
_luaScriptLoader.Dispose();
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterType(Type type)
|
||||
{
|
||||
return UserData.RegisterType(type);
|
||||
}
|
||||
public void UnregisterType(Type type)
|
||||
{
|
||||
UserData.UnregisterType(type, true);
|
||||
}
|
||||
|
||||
public object? GetGlobalTableValue(string tableName)
|
||||
{
|
||||
if (!IsRunning) { return null; }
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
using Barotrauma.Networking;
|
||||
using MoonSharp.Interpreter;
|
||||
using Sigil;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface IDefaultLuaRegistrar : IService
|
||||
{
|
||||
public void RegisterAll();
|
||||
}
|
||||
|
||||
public class DefaultLuaRegistrar : IDefaultLuaRegistrar
|
||||
{
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
private readonly ILuaUserDataService _userDataService;
|
||||
private readonly ISafeLuaUserDataService _safeUserDataService;
|
||||
private readonly ILoggerService _loggerService;
|
||||
|
||||
public DefaultLuaRegistrar(ILoggerService loggerService, ILuaUserDataService userDataService, ISafeLuaUserDataService safeUserDataService)
|
||||
{
|
||||
_userDataService = userDataService;
|
||||
_safeUserDataService = safeUserDataService;
|
||||
_loggerService = loggerService;
|
||||
}
|
||||
|
||||
private void RegisterShared()
|
||||
{
|
||||
_userDataService.RegisterType("System.TimeSpan");
|
||||
_userDataService.RegisterType("System.Exception");
|
||||
_userDataService.RegisterType("System.Console");
|
||||
_userDataService.RegisterType("System.Exception");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.Success`2");
|
||||
_userDataService.RegisterType("Barotrauma.Failure`2");
|
||||
_userDataService.RegisterType("Barotrauma.Range`1");
|
||||
|
||||
List<Assembly> assembliesToScan = [typeof(DefaultLuaRegistrar).Assembly, typeof(Identifier).Assembly, typeof(Microsoft.Xna.Framework.Vector2).Assembly];
|
||||
|
||||
foreach (var type in assembliesToScan.SelectMany(a => a.GetTypes()))
|
||||
{
|
||||
if (type.IsEnum || type.IsDefined(typeof(CompilerGeneratedAttribute)) || !_safeUserDataService.IsAllowed(type.FullName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_loggerService.LogMessage($"Registered {type.FullName}");
|
||||
|
||||
_userDataService.RegisterType(type.FullName);
|
||||
}
|
||||
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.LuaSByte");
|
||||
_userDataService.RegisterType("Barotrauma.LuaByte");
|
||||
_userDataService.RegisterType("Barotrauma.LuaInt16");
|
||||
_userDataService.RegisterType("Barotrauma.LuaUInt16");
|
||||
_userDataService.RegisterType("Barotrauma.LuaInt32");
|
||||
_userDataService.RegisterType("Barotrauma.LuaUInt32");
|
||||
_userDataService.RegisterType("Barotrauma.LuaInt64");
|
||||
_userDataService.RegisterType("Barotrauma.LuaUInt64");
|
||||
_userDataService.RegisterType("Barotrauma.LuaSingle");
|
||||
_userDataService.RegisterType("Barotrauma.LuaDouble");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.Level+InterestingPosition");
|
||||
_userDataService.RegisterType("Barotrauma.Networking.RespawnManager+TeamSpecificState");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+AIParams");
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+TargetParams");
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+InventoryParams");
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+HealthParams");
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+ParticleParams");
|
||||
_userDataService.RegisterType("Barotrauma.CharacterParams+SoundParams");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.FabricationRecipe+RequiredItemByIdentifier");
|
||||
_userDataService.RegisterType("Barotrauma.FabricationRecipe+RequiredItemByTag");
|
||||
|
||||
_userDataService.MakeFieldAccessible(_userDataService.RegisterType("Barotrauma.StatusEffect"), "user");
|
||||
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.ContentPackageManager+PackageSource");
|
||||
_userDataService.RegisterType("Barotrauma.ContentPackageManager+EnabledPackages");
|
||||
|
||||
_userDataService.RegisterType("System.Xml.Linq.XElement");
|
||||
_userDataService.RegisterType("System.Xml.Linq.XName");
|
||||
_userDataService.RegisterType("System.Xml.Linq.XAttribute");
|
||||
_userDataService.RegisterType("System.Xml.Linq.XContainer");
|
||||
_userDataService.RegisterType("System.Xml.Linq.XDocument");
|
||||
_userDataService.RegisterType("System.Xml.Linq.XNode");
|
||||
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.Networking.ServerSettings+SavedClientPermission");
|
||||
_userDataService.RegisterType("Barotrauma.Inventory+ItemSlot");
|
||||
|
||||
|
||||
_userDataService.MakeFieldAccessible(_userDataService.RegisterType("Barotrauma.Items.Components.CustomInterface"), "customInterfaceElementList");
|
||||
_userDataService.RegisterType("Barotrauma.Items.Components.CustomInterface+CustomInterfaceElement");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.DebugConsole+Command");
|
||||
|
||||
{
|
||||
var descriptor = _userDataService.RegisterType("Barotrauma.NetLobbyScreen");
|
||||
|
||||
#if SERVER
|
||||
_userDataService.MakeFieldAccessible(descriptor, "subs");
|
||||
#endif
|
||||
}
|
||||
|
||||
_userDataService.RegisterType("FarseerPhysics.Dynamics.Body");
|
||||
_userDataService.RegisterType("FarseerPhysics.Dynamics.World");
|
||||
_userDataService.RegisterType("FarseerPhysics.Dynamics.Fixture");
|
||||
_userDataService.RegisterType("FarseerPhysics.ConvertUnits");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.AABB");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ContactFeature");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ManifoldPoint");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ContactID");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.Manifold");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.RayCastInput");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ClipVertex");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.RayCastOutput");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.EPAxis");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.ReferenceFace");
|
||||
_userDataService.RegisterType("FarseerPhysics.Collision.Collision");
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.PrefabCollection`1");
|
||||
_userDataService.RegisterType("Barotrauma.PrefabSelector`1");
|
||||
_userDataService.RegisterType("Barotrauma.Pair`2");
|
||||
|
||||
var toolBox = UserData.RegisterType(typeof(ToolBox));
|
||||
#if CLIENT
|
||||
_userDataService.RemoveMember(toolBox, "OpenFileWithShell");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
private void RegisterClient()
|
||||
{
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Graphics.Effect");
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Graphics.EffectParameterCollection");
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Graphics.EffectParameter");
|
||||
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Graphics.SpriteBatch");
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Graphics.Texture2D");
|
||||
_userDataService.RegisterType("EventInput.KeyboardDispatcher");
|
||||
_userDataService.RegisterType("EventInput.KeyEventArgs");
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Input.Keys");
|
||||
_userDataService.RegisterType("Microsoft.Xna.Framework.Input.KeyboardState");
|
||||
|
||||
|
||||
_userDataService.RegisterType("Barotrauma.Inventory+SlotReference");
|
||||
}
|
||||
#elif SERVER
|
||||
private void RegisterServer()
|
||||
{
|
||||
_userDataService.RegisterType("Barotrauma.Character+TeamChangeEventData");
|
||||
}
|
||||
#endif
|
||||
|
||||
public void RegisterAll()
|
||||
{
|
||||
RegisterShared();
|
||||
#if CLIENT
|
||||
RegisterClient();
|
||||
#elif SERVER
|
||||
RegisterServer();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
-367
@@ -1,367 +0,0 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class LuaUserData
|
||||
{
|
||||
[Obsolete("Use IPluginManagementService::GetTypesByName()")]
|
||||
public static Type GetType(string typeName) => GameMain.LuaCs.PluginManagementService.GetType(typeName, includeInterfaces: true); //LuaCsSetup.GetType(typeName);
|
||||
|
||||
public static IUserDataDescriptor RegisterType(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to register a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
return UserData.RegisterType(type);
|
||||
}
|
||||
|
||||
public static void RegisterExtensionType(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to register a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
UserData.RegisterExtensionType(type);
|
||||
}
|
||||
|
||||
public static bool IsRegistered(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return UserData.GetDescriptorForType(type, true) != null;
|
||||
}
|
||||
|
||||
public static void UnregisterType(string typeName, bool deleteHistory = false)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to unregister a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
UserData.UnregisterType(type, deleteHistory);
|
||||
}
|
||||
public static IUserDataDescriptor RegisterGenericType(string typeName, params string[] typeNameArguements)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
Type[] typeArguements = typeNameArguements.Select(x => GetType(x)).ToArray();
|
||||
Type genericType = type.MakeGenericType(typeArguements);
|
||||
return UserData.RegisterType(genericType);
|
||||
}
|
||||
|
||||
public static void UnregisterGenericType(string typeName, params string[] typeNameArguements)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
Type[] typeArguements = typeNameArguements.Select(x => GetType(x)).ToArray();
|
||||
Type genericType = type.MakeGenericType(typeArguements);
|
||||
UserData.UnregisterType(genericType);
|
||||
}
|
||||
|
||||
public static bool IsTargetType(object obj, string typeName)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("userdata is nil"); }
|
||||
Type targetType = GetType(typeName);
|
||||
if (targetType == null) { throw new ScriptRuntimeException("target type not found"); }
|
||||
|
||||
Type type = obj is Type ? (Type)obj : obj.GetType();
|
||||
return targetType.IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public static string TypeOf(object obj)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("userdata is nil"); }
|
||||
|
||||
return obj.GetType().FullName;
|
||||
}
|
||||
|
||||
public static object CreateStatic(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to create a static userdata of a type that doesn't exist: {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 object CreateEnumTable(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to create an enum table with a type that doesn't exist:: {typeName}.");
|
||||
}
|
||||
|
||||
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||
|
||||
foreach (var value in Enum.GetValues(type))
|
||||
{
|
||||
string name = Enum.GetName(type, value);
|
||||
|
||||
result[name] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FieldInfo FindFieldRecursively(Type type, string fieldName)
|
||||
{
|
||||
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
|
||||
if (field == null && type.BaseType != null)
|
||||
{
|
||||
return FindFieldRecursively(type.BaseType, fieldName);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
public static void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {fieldName} accessible.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
FieldInfo field = FindFieldRecursively(IUUD.Type, fieldName);
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make field '{fieldName}' accessible, but the field doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.RemoveMember(fieldName);
|
||||
descriptor.AddMember(fieldName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
|
||||
}
|
||||
|
||||
private static MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null)
|
||||
{
|
||||
MethodInfo method;
|
||||
|
||||
if (types == null)
|
||||
{
|
||||
method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
}
|
||||
else
|
||||
{
|
||||
method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, types);
|
||||
}
|
||||
|
||||
if (method == null && type.BaseType != null)
|
||||
{
|
||||
return FindMethodRecursively(type.BaseType, methodName, types);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
public static void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName, string[] parameters = null)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {methodName} accessible.");
|
||||
}
|
||||
|
||||
Type[] parameterTypes = null;
|
||||
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
parameterTypes = new Type[parameters.Length];
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
Type type = LuaUserData.GetType(parameters[i]);
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"invalid parameter type '{parameters[i]}'");
|
||||
}
|
||||
parameterTypes[i] = type;
|
||||
}
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
|
||||
MethodBase method;
|
||||
|
||||
try
|
||||
{
|
||||
method = FindMethodRecursively(IUUD.Type, methodName, parameterTypes);
|
||||
}
|
||||
catch (AmbiguousMatchException ex)
|
||||
{
|
||||
throw new ScriptRuntimeException("ambiguous method signature.");
|
||||
}
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make method '{methodName}' accessible, but the method doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.AddMember(methodName, new MethodMemberDescriptor(method, InteropAccessMode.Default));
|
||||
}
|
||||
|
||||
private static PropertyInfo FindPropertyRecursively(Type type, string propertyName)
|
||||
{
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
|
||||
if (property == null && type.BaseType != null)
|
||||
{
|
||||
return FindPropertyRecursively(type.BaseType, propertyName);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
public static void MakePropertyAccessible(IUserDataDescriptor IUUD, string propertyName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {propertyName} accessible.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
PropertyInfo property = FindPropertyRecursively(IUUD.Type, propertyName);
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make property '{propertyName}' accessible, but the property doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.RemoveMember(propertyName);
|
||||
descriptor.AddMember(propertyName, new PropertyMemberDescriptor(property, InteropAccessMode.Default, property.GetGetMethod(true), property.GetSetMethod(true)));
|
||||
}
|
||||
|
||||
public static void AddMethod(IUserDataDescriptor IUUD, string methodName, object function)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to add method {methodName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
|
||||
descriptor.RemoveMember(methodName);
|
||||
descriptor.AddMember(methodName, new ObjectCallbackMemberDescriptor(methodName, (object arg1, ScriptExecutionContext arg2, CallbackArguments arg3) =>
|
||||
{
|
||||
/*if (GameMain.LuaCs != null)
|
||||
return GameMain.LuaCs.CallLuaFunction(function, arg3.GetArray());
|
||||
return null;*/
|
||||
throw new NotImplementedException();
|
||||
}));
|
||||
}
|
||||
|
||||
public static void AddField(IUserDataDescriptor IUUD, string fieldName, DynValue value)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to add field {fieldName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
descriptor.RemoveMember(fieldName);
|
||||
descriptor.AddMember(fieldName, new DynValueMemberDescriptor(fieldName, value));
|
||||
}
|
||||
|
||||
public static void RemoveMember(IUserDataDescriptor IUUD, string memberName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to remove the member {memberName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
descriptor.RemoveMember(memberName);
|
||||
}
|
||||
|
||||
public static bool HasMember(object obj, string memberName)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("object is nil"); }
|
||||
|
||||
Type type;
|
||||
if (obj is Type)
|
||||
{
|
||||
type = (Type)obj;
|
||||
}
|
||||
else if(obj is IUserDataDescriptor descriptor)
|
||||
{
|
||||
type = descriptor.Type;
|
||||
|
||||
if (((StandardUserDataDescriptor)descriptor).HasMember(memberName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = obj.GetType();
|
||||
}
|
||||
|
||||
if (type.GetMember(memberName).Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="CreateUserDataFromType"/>.
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredTypeDescriptor">Descriptor of the type of the object to convert the Lua value to. Uses MoonSharp ScriptToClr converters.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type as described by <paramref name="desiredTypeDescriptor"/>.</returns>
|
||||
public static DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor desiredTypeDescriptor)
|
||||
{
|
||||
return UserData.Create(scriptObject.ToObject(desiredTypeDescriptor.Type), desiredTypeDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Lua value to a CLR object of a desired type and wraps it in a userdata.
|
||||
/// If the type is not registered, then a new <see cref="MoonSharp.Interpreter.Interop.StandardUserDataDescriptor"/> will be created and used.
|
||||
/// The goal of this method is to allow Lua scripts to create userdata to wrap certain data without having to register types.
|
||||
/// <remarks>Wrapping the value in a userdata preserves the original type during script-to-CLR conversions.</remarks>
|
||||
/// <example>A Lua script needs to pass a List`1 to a CLR method expecting System.Object, MoonSharp gets
|
||||
/// in the way by converting the List`1 to a MoonSharp.Interpreter.Table and breaking everything.
|
||||
/// Registering the List`1 type can break other scripts relying on default converters, so instead
|
||||
/// it is better to manually wrap the List`1 object into a userdata.
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredType">Type describing the CLR type of the object to convert the Lua value to.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type.</returns>
|
||||
public static DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType)
|
||||
{
|
||||
IUserDataDescriptor descriptor = UserData.GetDescriptorForType(desiredType, true);
|
||||
descriptor ??= new StandardUserDataDescriptor(desiredType, InteropAccessMode.Default);
|
||||
return CreateUserDataFromDescriptor(scriptObject, descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ILuaUserDataService : IReusableService
|
||||
{
|
||||
IReadOnlyDictionary<string, IUserDataDescriptor> Descriptors { get; }
|
||||
IUserDataDescriptor RegisterType(string typeName);
|
||||
void RegisterExtensionType(string typeName);
|
||||
bool IsRegistered(string typeName);
|
||||
void UnregisterType(string typeName, bool deleteHistory = false);
|
||||
object CreateStatic(string typeName);
|
||||
bool IsTargetType(object obj, string typeName);
|
||||
string TypeOf(object obj);
|
||||
object CreateEnumTable(string typeName);
|
||||
void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName);
|
||||
void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName, string[] parameters = null);
|
||||
void MakePropertyAccessible(IUserDataDescriptor IUUD, string propertyName);
|
||||
void AddMethod(IUserDataDescriptor IUUD, string methodName, object function);
|
||||
void AddField(IUserDataDescriptor IUUD, string fieldName, DynValue value);
|
||||
void RemoveMember(IUserDataDescriptor IUUD, string memberName);
|
||||
bool HasMember(object obj, string memberName);
|
||||
/// <summary>
|
||||
/// See <see cref="CreateUserDataFromType"/>.
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredTypeDescriptor">Descriptor of the type of the object to convert the Lua value to. Uses MoonSharp ScriptToClr converters.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type as described by <paramref name="desiredTypeDescriptor"/>.</returns>
|
||||
DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor desiredTypeDescriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Lua value to a CLR object of a desired type and wraps it in a userdata.
|
||||
/// If the type is not registered, then a new <see cref="MoonSharp.Interpreter.Interop.StandardUserDataDescriptor"/> will be created and used.
|
||||
/// The goal of this method is to allow Lua scripts to create userdata to wrap certain data without having to register types.
|
||||
/// <remarks>Wrapping the value in a userdata preserves the original type during script-to-CLR conversions.</remarks>
|
||||
/// <example>A Lua script needs to pass a List`1 to a CLR method expecting System.Object, MoonSharp gets
|
||||
/// in the way by converting the List`1 to a MoonSharp.Interpreter.Table and breaking everything.
|
||||
/// Registering the List`1 type can break other scripts relying on default converters, so instead
|
||||
/// it is better to manually wrap the List`1 object into a userdata.
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredType">Type describing the CLR type of the object to convert the Lua value to.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type.</returns>
|
||||
DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType);
|
||||
|
||||
void AddCallMetaTable(object userdata);
|
||||
}
|
||||
|
||||
public class LuaUserDataService : ILuaUserDataService
|
||||
{
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public IReadOnlyDictionary<string, IUserDataDescriptor> Descriptors => descriptors;
|
||||
private ConcurrentDictionary<string, IUserDataDescriptor> descriptors;
|
||||
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
|
||||
public LuaUserDataService(IPluginManagementService pluginManagementService)
|
||||
{
|
||||
descriptors = new ConcurrentDictionary<string, IUserDataDescriptor>();
|
||||
_pluginManagementService = pluginManagementService;
|
||||
}
|
||||
|
||||
public IUserDataDescriptor this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return descriptors.GetValueOrDefault(key);
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetType(string typeName) => _pluginManagementService.GetType(typeName, includeInterfaces: true);
|
||||
|
||||
public IUserDataDescriptor RegisterType(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to register a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
return UserData.RegisterType(type, new CallableUserDataDescriptor(type));
|
||||
}
|
||||
|
||||
public void RegisterExtensionType(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to register a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
UserData.RegisterExtensionType(type);
|
||||
}
|
||||
|
||||
public bool IsRegistered(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return UserData.GetDescriptorForType(type, true) != null;
|
||||
}
|
||||
|
||||
public void UnregisterType(string typeName, bool deleteHistory = false)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to unregister a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
UserData.UnregisterType(type, deleteHistory);
|
||||
}
|
||||
|
||||
public bool IsTargetType(object obj, string typeName)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("userdata is nil"); }
|
||||
Type targetType = GetType(typeName);
|
||||
if (targetType == null) { throw new ScriptRuntimeException("target type not found"); }
|
||||
|
||||
Type type = obj is Type ? (Type)obj : obj.GetType();
|
||||
return targetType.IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public string TypeOf(object obj)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("userdata is nil"); }
|
||||
|
||||
return obj.GetType().FullName;
|
||||
}
|
||||
|
||||
public object CreateEnumTable(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to create an enum table with a type that doesn't exist:: {typeName}.");
|
||||
}
|
||||
|
||||
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||
|
||||
foreach (var value in Enum.GetValues(type))
|
||||
{
|
||||
string name = Enum.GetName(type, value);
|
||||
|
||||
result[name] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public object CreateStatic(string typeName)
|
||||
{
|
||||
Type type = GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to create a static userdata of a type that doesn't exist: {typeName}.");
|
||||
}
|
||||
|
||||
MethodInfo method = typeof(UserData).GetMethod(nameof(UserData.CreateStatic), 1, new Type[0]);
|
||||
MethodInfo generic = method.MakeGenericMethod(type);
|
||||
return generic.Invoke(null, null);
|
||||
}
|
||||
|
||||
private FieldInfo FindFieldRecursively(Type type, string fieldName)
|
||||
{
|
||||
var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
|
||||
if (field == null && type.BaseType != null)
|
||||
{
|
||||
return FindFieldRecursively(type.BaseType, fieldName);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
public void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {fieldName} accessible.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
FieldInfo field = FindFieldRecursively(IUUD.Type, fieldName);
|
||||
|
||||
if (field == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make field '{fieldName}' accessible, but the field doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.RemoveMember(fieldName);
|
||||
descriptor.AddMember(fieldName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
|
||||
}
|
||||
|
||||
private MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null)
|
||||
{
|
||||
MethodInfo method;
|
||||
|
||||
if (types == null)
|
||||
{
|
||||
method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
}
|
||||
else
|
||||
{
|
||||
method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, types);
|
||||
}
|
||||
|
||||
if (method == null && type.BaseType != null)
|
||||
{
|
||||
return FindMethodRecursively(type.BaseType, methodName, types);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
public void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName, string[] parameters = null)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {methodName} accessible.");
|
||||
}
|
||||
|
||||
Type[] parameterTypes = null;
|
||||
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
parameterTypes = new Type[parameters.Length];
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
Type type = GetType(parameters[i]);
|
||||
if (type == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"invalid parameter type '{parameters[i]}'");
|
||||
}
|
||||
parameterTypes[i] = type;
|
||||
}
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
|
||||
MethodBase method;
|
||||
|
||||
try
|
||||
{
|
||||
method = FindMethodRecursively(IUUD.Type, methodName, parameterTypes);
|
||||
}
|
||||
catch (AmbiguousMatchException ex)
|
||||
{
|
||||
throw new ScriptRuntimeException("ambiguous method signature.");
|
||||
}
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make method '{methodName}' accessible, but the method doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.AddMember(methodName, new MethodMemberDescriptor(method, InteropAccessMode.Default));
|
||||
}
|
||||
|
||||
private PropertyInfo FindPropertyRecursively(Type type, string propertyName)
|
||||
{
|
||||
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
|
||||
if (property == null && type.BaseType != null)
|
||||
{
|
||||
return FindPropertyRecursively(type.BaseType, propertyName);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
public void MakePropertyAccessible(IUserDataDescriptor IUUD, string propertyName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to make {propertyName} accessible.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
PropertyInfo property = FindPropertyRecursively(IUUD.Type, propertyName);
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to make property '{propertyName}' accessible, but the property doesn't exist.");
|
||||
}
|
||||
|
||||
descriptor.RemoveMember(propertyName);
|
||||
descriptor.AddMember(propertyName, new PropertyMemberDescriptor(property, InteropAccessMode.Default, property.GetGetMethod(true), property.GetSetMethod(true)));
|
||||
}
|
||||
|
||||
public void AddMethod(IUserDataDescriptor IUUD, string methodName, object function)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to add method {methodName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
|
||||
descriptor.RemoveMember(methodName);
|
||||
descriptor.AddMember(methodName, new ObjectCallbackMemberDescriptor(methodName, (object arg1, ScriptExecutionContext arg2, CallbackArguments arg3) =>
|
||||
{
|
||||
/*if (GameMain.LuaCs != null)
|
||||
return GameMain.LuaCs.CallLuaFunction(function, arg3.GetArray());
|
||||
return null;*/
|
||||
throw new NotImplementedException();
|
||||
}));
|
||||
}
|
||||
|
||||
public void AddField(IUserDataDescriptor IUUD, string fieldName, DynValue value)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to add field {fieldName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
descriptor.RemoveMember(fieldName);
|
||||
descriptor.AddMember(fieldName, new DynValueMemberDescriptor(fieldName, value));
|
||||
}
|
||||
|
||||
public void RemoveMember(IUserDataDescriptor IUUD, string memberName)
|
||||
{
|
||||
if (IUUD == null)
|
||||
{
|
||||
throw new ScriptRuntimeException($"tried to use a UserDataDescriptor that is null to remove the member {memberName}.");
|
||||
}
|
||||
|
||||
var descriptor = (StandardUserDataDescriptor)IUUD;
|
||||
descriptor.RemoveMember(memberName);
|
||||
}
|
||||
|
||||
public bool HasMember(object obj, string memberName)
|
||||
{
|
||||
if (obj == null) { throw new ScriptRuntimeException("object is nil"); }
|
||||
|
||||
Type type;
|
||||
if (obj is Type)
|
||||
{
|
||||
type = (Type)obj;
|
||||
}
|
||||
else if (obj is IUserDataDescriptor descriptor)
|
||||
{
|
||||
type = descriptor.Type;
|
||||
|
||||
if (((StandardUserDataDescriptor)descriptor).HasMember(memberName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = obj.GetType();
|
||||
}
|
||||
|
||||
if (type.GetMember(memberName).Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor desiredTypeDescriptor)
|
||||
{
|
||||
return UserData.Create(scriptObject.ToObject(desiredTypeDescriptor.Type), desiredTypeDescriptor);
|
||||
}
|
||||
|
||||
public DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType)
|
||||
{
|
||||
IUserDataDescriptor descriptor = UserData.GetDescriptorForType(desiredType, true);
|
||||
descriptor ??= new StandardUserDataDescriptor(desiredType, InteropAccessMode.Default);
|
||||
return CreateUserDataFromDescriptor(scriptObject, descriptor);
|
||||
}
|
||||
|
||||
public void AddCallMetaTable(object userdata) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
descriptors.Clear();
|
||||
}
|
||||
|
||||
public FluentResults.Result Reset()
|
||||
{
|
||||
descriptors.Clear();
|
||||
return FluentResults.Result.Ok();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class CallableUserDataDescriptor : StandardUserDataDescriptor
|
||||
{
|
||||
public CallableUserDataDescriptor(Type type)
|
||||
: base(type, InteropAccessMode.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public override DynValue MetaIndex(Script script, object obj, string metaname)
|
||||
{
|
||||
if (metaname == "__call")
|
||||
{
|
||||
return DynValue.NewCallback((ctx, args) =>
|
||||
{
|
||||
var self = args[0];
|
||||
|
||||
var ctor = base.Index(script, obj, DynValue.NewString("__new"), true);
|
||||
|
||||
if (ctor == null || ctor.IsNil())
|
||||
{
|
||||
throw new ScriptRuntimeException("Attempted to call userdata without __new.");
|
||||
}
|
||||
|
||||
var callArgs = args.GetArray().Skip(1).ToArray();
|
||||
return script.Call(ctor, callArgs);
|
||||
});
|
||||
}
|
||||
|
||||
return base.MetaIndex(script, obj, metaname);
|
||||
}
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.LuaCs.Services;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma.LuaCs.Services;
|
||||
|
||||
public interface ISafeLuaUserDataService : IService
|
||||
{
|
||||
bool IsAllowed(string typeName);
|
||||
IUserDataDescriptor RegisterType(string typeName);
|
||||
void RegisterExtensionType(string typeName);
|
||||
bool IsRegistered(string typeName);
|
||||
void UnregisterType(string typeName, bool deleteHistory = false);
|
||||
object CreateStatic(string typeName);
|
||||
bool IsTargetType(object obj, string typeName);
|
||||
string TypeOf(object obj);
|
||||
object CreateEnumTable(string typeName);
|
||||
void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName);
|
||||
void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName, string[] parameters = null);
|
||||
void MakePropertyAccessible(IUserDataDescriptor IUUD, string propertyName);
|
||||
void AddMethod(IUserDataDescriptor IUUD, string methodName, object function);
|
||||
void AddField(IUserDataDescriptor IUUD, string fieldName, DynValue value);
|
||||
void RemoveMember(IUserDataDescriptor IUUD, string memberName);
|
||||
bool HasMember(object obj, string memberName);
|
||||
/// <summary>
|
||||
/// See <see cref="CreateUserDataFromType"/>.
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredTypeDescriptor">Descriptor of the type of the object to convert the Lua value to. Uses MoonSharp ScriptToClr converters.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type as described by <paramref name="desiredTypeDescriptor"/>.</returns>
|
||||
DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor desiredTypeDescriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Lua value to a CLR object of a desired type and wraps it in a userdata.
|
||||
/// If the type is not registered, then a new <see cref="MoonSharp.Interpreter.Interop.StandardUserDataDescriptor"/> will be created and used.
|
||||
/// The goal of this method is to allow Lua scripts to create userdata to wrap certain data without having to register types.
|
||||
/// <remarks>Wrapping the value in a userdata preserves the original type during script-to-CLR conversions.</remarks>
|
||||
/// <example>A Lua script needs to pass a List`1 to a CLR method expecting System.Object, MoonSharp gets
|
||||
/// in the way by converting the List`1 to a MoonSharp.Interpreter.Table and breaking everything.
|
||||
/// Registering the List`1 type can break other scripts relying on default converters, so instead
|
||||
/// it is better to manually wrap the List`1 object into a userdata.
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="scriptObject">Lua value to convert and wrap in a userdata.</param>
|
||||
/// <param name="desiredType">Type describing the CLR type of the object to convert the Lua value to.</param>
|
||||
/// <returns>A userdata that wraps the Lua value converted to an object of the desired type.</returns>
|
||||
DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType);
|
||||
void AddCallMetaTable(object userdata);
|
||||
}
|
||||
|
||||
public class SafeLuaUserDataService : ISafeLuaUserDataService
|
||||
{
|
||||
private readonly ILuaUserDataService _userDataService;
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public SafeLuaUserDataService(ILuaUserDataService userDataService)
|
||||
{
|
||||
_userDataService = userDataService;
|
||||
}
|
||||
|
||||
public IUserDataDescriptor this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userDataService.Descriptors.GetValueOrDefault(key);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanBeRegistered(string typeName)
|
||||
{
|
||||
if (typeName.StartsWith("Barotrauma.Lua", StringComparison.Ordinal) ||
|
||||
typeName.StartsWith("Barotrauma.Cs", StringComparison.Ordinal) ||
|
||||
typeName.StartsWith("Barotrauma.LuaCs", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeName == "System.Single") { return true; }
|
||||
|
||||
if (typeName.StartsWith("System.Collections", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
||||
if (typeName.StartsWith("Microsoft.Xna", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
||||
if (typeName.StartsWith("Barotrauma.IO", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
if (typeName.StartsWith("Barotrauma.ToolBox", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
if (typeName.StartsWith("Barotrauma.SaveUtil", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
if (typeName.StartsWith("Barotrauma.", StringComparison.Ordinal))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CanBeReRegistered(string typeName)
|
||||
{
|
||||
if (typeName.StartsWith("Barotrauma.Lua", StringComparison.Ordinal) ||
|
||||
typeName.StartsWith("Barotrauma.Cs", StringComparison.Ordinal) ||
|
||||
typeName.StartsWith("Barotrauma.LuaCs", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsAllowed(string typeName)
|
||||
{
|
||||
if (!CanBeReRegistered(typeName) && IsRegistered(typeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanBeRegistered(typeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CheckAllowed(string typeName)
|
||||
{
|
||||
if (!IsAllowed(typeName))
|
||||
{
|
||||
throw new ScriptRuntimeException($"Type {typeName} can't be registered");
|
||||
}
|
||||
}
|
||||
|
||||
public IUserDataDescriptor RegisterType(string typeName)
|
||||
{
|
||||
CheckAllowed(typeName);
|
||||
return _userDataService.RegisterType(typeName);
|
||||
}
|
||||
|
||||
public void RegisterExtensionType(string typeName)
|
||||
{
|
||||
CheckAllowed(typeName);
|
||||
_userDataService.RegisterExtensionType(typeName);
|
||||
}
|
||||
|
||||
public bool IsRegistered(string typeName)
|
||||
{
|
||||
return _userDataService.IsRegistered(typeName);
|
||||
}
|
||||
|
||||
public void UnregisterType(string typeName, bool deleteHistory = false)
|
||||
{
|
||||
IsAllowed(typeName);
|
||||
_userDataService.UnregisterType(typeName, deleteHistory);
|
||||
}
|
||||
public object CreateStatic(string typeName)
|
||||
{
|
||||
return _userDataService.CreateStatic(typeName);
|
||||
}
|
||||
|
||||
public bool IsTargetType(object obj, string typeName)
|
||||
{
|
||||
return _userDataService.IsTargetType(obj, typeName);
|
||||
}
|
||||
|
||||
public string TypeOf(object obj)
|
||||
{
|
||||
return _userDataService.TypeOf(obj);
|
||||
}
|
||||
|
||||
public object CreateEnumTable(string typeName)
|
||||
{
|
||||
return _userDataService.CreateEnumTable(typeName);
|
||||
}
|
||||
|
||||
public void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName)
|
||||
{
|
||||
_userDataService.MakeFieldAccessible(IUUD, fieldName);
|
||||
}
|
||||
|
||||
public void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName, string[] parameters = null)
|
||||
{
|
||||
_userDataService.MakeMethodAccessible(IUUD, methodName, parameters);
|
||||
}
|
||||
|
||||
public void MakePropertyAccessible(IUserDataDescriptor IUUD, string propertyName)
|
||||
{
|
||||
_userDataService.MakePropertyAccessible(IUUD, propertyName);
|
||||
}
|
||||
|
||||
public void AddMethod(IUserDataDescriptor IUUD, string methodName, object function)
|
||||
{
|
||||
_userDataService.AddMethod(IUUD, methodName, function);
|
||||
}
|
||||
|
||||
public void AddField(IUserDataDescriptor IUUD, string fieldName, DynValue value)
|
||||
{
|
||||
_userDataService.AddField(IUUD, fieldName, value);
|
||||
}
|
||||
|
||||
public void RemoveMember(IUserDataDescriptor IUUD, string memberName)
|
||||
{
|
||||
_userDataService.RemoveMember(IUUD, memberName);
|
||||
}
|
||||
|
||||
public bool HasMember(object obj, string memberName)
|
||||
{
|
||||
return _userDataService.HasMember(obj, memberName);
|
||||
}
|
||||
|
||||
public DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor desiredTypeDescriptor)
|
||||
{
|
||||
return _userDataService.CreateUserDataFromDescriptor(scriptObject, desiredTypeDescriptor);
|
||||
}
|
||||
|
||||
public DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType)
|
||||
{
|
||||
return _userDataService.CreateUserDataFromType(scriptObject, desiredType);
|
||||
}
|
||||
|
||||
public void AddCallMetaTable(object userdata) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
-6
@@ -58,10 +58,4 @@ public interface ILuaScriptManagementService : IReusableService
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type_Registration
|
||||
|
||||
IUserDataDescriptor RegisterType(Type type);
|
||||
void UnregisterType(Type type);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user