diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigBase.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigBase.cs new file mode 100644 index 0000000000..a1172125b9 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigBase.cs @@ -0,0 +1,6 @@ +namespace Barotrauma.Configuration; + +public interface IConfigBase +{ + +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigVar.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigVar.cs new file mode 100644 index 0000000000..708eb9e161 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Configuration/IConfigVar.cs @@ -0,0 +1,6 @@ +namespace Barotrauma.Configuration; + +public interface IConfigVar : IConfigBase +{ + +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 5e06d19e58..03ad8d9bab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -11,6 +11,7 @@ using System.Reflection; using System.Runtime.Loader; using System.Xml.Linq; +using Barotrauma.LuaCs.Services; using Barotrauma.Networking; namespace Barotrauma @@ -99,7 +100,7 @@ public static bool IsRunningInsideWorkshop public CsPackageManager PluginPackageManager => _pluginPackageManager ??= new CsPackageManager(AssemblyManager, this); public LuaCsModStore ModStore { get; private set; } - private LuaRequire require { get; set; } + private LuaRequire Require { get; set; } public LuaCsSetupConfig Config { get; private set; } public MoonSharpVsCodeDebugServer DebugServer { get; private set; } public bool IsInitialized { get; private set; } @@ -394,7 +395,7 @@ public void Initialize(bool forceEnableCs = false) Lua.Options.CheckThreadAccess = false; Script.GlobalOptions.ShouldPCallCatchException = (Exception ex) => { return true; }; - require = new LuaRequire(Lua); + Require = new LuaRequire(Lua); Game = new LuaGame(); Networking = new LuaCsNetworking(); @@ -430,7 +431,7 @@ public void Initialize(bool forceEnableCs = false) Lua.Globals["dofile"] = (Func)DoFile; Lua.Globals["loadfile"] = (Func)LoadFile; - Lua.Globals["require"] = (Func)require.Require; + Lua.Globals["require"] = (Func)Require.Require; Lua.Globals["dostring"] = (Func)Lua.DoString; Lua.Globals["load"] = (Func)Lua.LoadString; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs new file mode 100644 index 0000000000..f498da3721 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Networking/INetCallback.cs @@ -0,0 +1,15 @@ +using System; + +namespace Barotrauma.LuaCs.Networking; + +public partial interface INetCallback +{ + public ushort CallbackId { get; } +} + +#if SERVER +public partial interface INetCallback +{ + +} +#endif diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs index 8ba1f8921c..4015e882e2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading; +using Barotrauma.LuaCs.Services; using Barotrauma.Steam; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs index dd61c0108f..e01db5f4c3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs @@ -7,13 +7,14 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Loader; +using Barotrauma.LuaCs.Services; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; // ReSharper disable ConditionIsAlwaysTrueOrFalse [assembly: InternalsVisibleTo("CompiledAssembly")] -namespace Barotrauma; +namespace Barotrauma.LuaCs; /// /// AssemblyLoadContext to compile from syntax trees in memory and to load from disk/file. Provides dependency resolution. @@ -31,11 +32,11 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext // internal private readonly Dictionary _dependencyResolvers = new(); // path-folder, resolver protected bool IsResolving; //this is to avoid circular dependency lookup. - private AssemblyManager _assemblyManager; + private IAssemblyManagementService _assemblyManager; public bool IsTemplateMode { get; set; } public bool IsDisposed { get; private set; } - public MemoryFileAssemblyContextLoader(AssemblyManager assemblyManager) : base(isCollectible: true) + public MemoryFileAssemblyContextLoader(IAssemblyManagementService assemblyManager) : base(isCollectible: true) { this._assemblyManager = assemblyManager; this.IsDisposed = false; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/AssemblyManager.cs similarity index 80% rename from Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs rename to Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/AssemblyManager.cs index c7f582395a..e832cfe9a2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/AssemblyManager.cs @@ -14,7 +14,7 @@ // ReSharper disable EventNeverSubscribedTo.Global // ReSharper disable InconsistentNaming -namespace Barotrauma; +namespace Barotrauma.LuaCs.Services; /*** * Note: This class was written to be thread-safe in order to allow parallelization in loading in the future if the need @@ -25,36 +25,14 @@ namespace Barotrauma; /// Provides functionality for the loading, unloading and management of plugins implementing IAssemblyPlugin. /// All plugins are loaded into their own AssemblyLoadContext along with their dependencies. /// -public class AssemblyManager +public class AssemblyManager : IAssemblyManagementService { #region ExternalAPI - - /// - /// Called when an assembly is loaded. - /// + public event Action OnAssemblyLoaded; - - /// - /// Called when an assembly is marked for unloading, before unloading begins. You should use this to cleanup - /// any references that you have to this assembly. - /// public event Action OnAssemblyUnloading; - - /// - /// Called whenever an exception is thrown. First arg is a formatted message, Second arg is the Exception. - /// public event Action OnException; - - /// - /// For unloading issue debugging. Called whenever MemoryFileAssemblyContextLoader [load context] is unloaded. - /// public event Action OnACLUnload; - - - /// - /// [DEBUG ONLY] - /// Returns a list of the current unloading ACLs. - /// public ImmutableList> StillUnloadingACLs { get @@ -70,12 +48,6 @@ public ImmutableList> StillUnload } } } - - - // ReSharper disable once MemberCanBePrivate.Global - /// - /// Checks if there are any AssemblyLoadContexts still in the process of unloading. - /// public bool IsCurrentlyUnloading { get @@ -95,21 +67,6 @@ public bool IsCurrentlyUnloading } } } - - // Old API compatibility - public IEnumerable GetSubTypesInLoadedAssemblies() - { - return GetSubTypesInLoadedAssemblies(false); - } - - - /// - /// Allows iteration over all non-interface types in all loaded assemblies in the AsmMgr that are assignable to the given type (IsAssignableFrom). - /// Warning: care should be used when using this method in hot paths as performance may be affected. - /// - /// The type to compare against - /// Forces caches to clear and for the lists of types to be rebuilt. - /// An Enumerator for matching types. public IEnumerable GetSubTypesInLoadedAssemblies(bool rebuildList) { Type targetType = typeof(T); @@ -165,14 +122,6 @@ public IEnumerable GetSubTypesInLoadedAssemblies(bool rebuildList) OpsLockLoaded.ExitReadLock(); } } - - /// - /// Tries to get types assignable to type from the ACL given the Guid. - /// - /// - /// - /// - /// public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types) { Type targetType = typeof(T); @@ -188,13 +137,6 @@ public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types) types = null; return false; } - - /// - /// Tries to get types from the ACL given the Guid. - /// - /// - /// - /// public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types) { if (TryGetACL(id, out var acl)) @@ -206,14 +148,6 @@ public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types) types = null; return false; } - - - /// - /// Allows iteration over all types, including interfaces, in all loaded assemblies in the AsmMgr who's names match the string. - /// Note: Will return the by-reference equivalent type if the type name is prefixed with "out " or "ref ". - /// - /// The string name of the type to search for. - /// An Enumerator for matching types. List will be empty if bad params are supplied. public IEnumerable GetTypesByName(string typeName) { List types = new(); @@ -299,12 +233,6 @@ void TypesListHelper() } } } - - /// - /// Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr. - /// Warning: High usage may result in performance issues. - /// - /// An Enumerator for iteration. public IEnumerable GetAllTypesInLoadedAssemblies() { OpsLockLoaded.EnterReadLock(); @@ -325,13 +253,6 @@ public IEnumerable GetAllTypesInLoadedAssemblies() OpsLockLoaded.ExitReadLock(); } } - - /// - /// Returns a list of all loaded ACLs. - /// WARNING: References to these ACLs outside of the AssemblyManager should be kept in a WeakReference in order - /// to avoid causing issues with unloading/disposal. - /// - /// public IEnumerable GetAllLoadedACLs() { OpsLockLoaded.EnterReadLock(); @@ -358,36 +279,14 @@ public IEnumerable GetAllLoadedACLs() #region InternalAPI - /// - /// [Unsafe] Warning: only for use in nested threading functions. Requires care to manage access. - /// Does not make any guarantees about the state of the ACL after the list has been returned. - /// - /// [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.NoInlining)] - internal ImmutableList UnsafeGetAllLoadedACLs() + ImmutableList IAssemblyManagementService.UnsafeGetAllLoadedACLs() { if (LoadedACLs.IsEmpty) return ImmutableList.Empty; return LoadedACLs.Select(kvp => kvp.Value).ToImmutableList(); } - - /// - /// Used by content package and plugin management to stop unloading of a given ACL until all plugins have gracefully closed. - /// public event System.Func IsReadyToUnloadACL; - - /// - /// Compiles an assembly from supplied references and syntax trees into the specified AssemblyContextLoader. - /// A new ACL will be created if the Guid supplied is Guid.Empty. - /// - /// - /// - /// - /// - /// A non-unique name for later reference. Optional, set to null if unused. - /// The guid of the assembly - /// - /// public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName, [NotNull] IEnumerable syntaxTree, IEnumerable externalMetadataReferences, @@ -440,14 +339,6 @@ public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compi return state; } - - /// - /// Switches the ACL with the given Guid to Template Mode, which disables assembly name resolution for any assemblies loaded in it. - /// These ACLs are intended to be used to host Assemblies for information only and not for code execution. - /// WARNING: This process is irreversible. - /// - /// Guid of the ACL. - /// Whether or not an ACL was found with the given ID. public bool SetACLToTemplateMode(Guid guid) { if (!TryGetACL(guid, out var acl)) @@ -455,16 +346,6 @@ public bool SetACLToTemplateMode(Guid guid) acl.Acl.IsTemplateMode = true; return true; } - - /// - /// Tries to load all assemblies at the supplied file paths list into the ACl with the given Guid. - /// If the supplied Guid is Empty, then a new ACl will be created and the Guid will be assigned to it. - /// - /// List of assemblies to try and load. - /// A non-unique name for later reference. Optional. - /// Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var. - /// Operation success messages. - /// public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable filePaths, string friendlyName, ref Guid id) { @@ -507,9 +388,7 @@ public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumer return AssemblyLoadingSuccessState.ACLLoadFailure; } - - - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Synchronized)] + [MethodImpl(MethodImplOptions.NoInlining)] public bool TryBeginDispose() { OpsLockLoaded.EnterWriteLock(); @@ -563,8 +442,6 @@ public bool TryBeginDispose() OpsLockLoaded.ExitWriteLock(); } } - - [MethodImpl(MethodImplOptions.NoInlining)] public bool FinalizeDispose() { @@ -606,15 +483,7 @@ public bool FinalizeDispose() return isUnloaded; } - - /// - /// Tries to retrieve the LoadedACL with the given ID or null if none is found. - /// WARNING: External references to this ACL with long lifespans should be kept in a WeakReference - /// to avoid causing unloading/disposal issues. - /// - /// GUID of the ACL. - /// The found ACL or null if none was found. - /// Whether or not an ACL was found. + [MethodImpl(MethodImplOptions.NoInlining)] public bool TryGetACL(Guid id, out LoadedACL acl) { @@ -865,6 +734,11 @@ internal void ClearTypesList() } #endregion + + public void Dispose() + { + TryBeginDispose(); + } } public static class AssemblyExtensions diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/IAssemblyManagementService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/IAssemblyManagementService.cs index 96bf79e3c8..1ba46fa9a5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/IAssemblyManagementService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/IAssemblyManagementService.cs @@ -1,7 +1,189 @@ -namespace Barotrauma.LuaCs.Services; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +// ReSharper disable InconsistentNaming + +namespace Barotrauma.LuaCs.Services; public interface IAssemblyManagementService : IService { + #region Public API + + /// + /// Called when an assembly is loaded. + /// + public event Action OnAssemblyLoaded; + + /// + /// Called when an assembly is marked for unloading, before unloading begins. You should use this to cleanup + /// any references that you have to this assembly. + /// + public event Action OnAssemblyUnloading; + + /// + /// Called whenever an exception is thrown. First arg is a formatted message, Second arg is the Exception. + /// + public event Action OnException; + + /// + /// For unloading issue debugging. Called whenever MemoryFileAssemblyContextLoader [load context] is unloaded. + /// + // ReSharper disable once InconsistentNaming + public event Action OnACLUnload; + + + /// + /// [DEBUG ONLY] + /// Returns a list of the current unloading ACLs. + /// + // ReSharper disable once InconsistentNaming + public ImmutableList> StillUnloadingACLs { get; } + // ReSharper disable once MemberCanBePrivate.Global + /// + /// Checks if there are any AssemblyLoadContexts still in the process of unloading. + /// + public bool IsCurrentlyUnloading { get; } + + /// + /// Allows iteration over all non-interface types in all loaded assemblies in the AsmMgr that are assignable to the given type (IsAssignableFrom). + /// Warning: care should be used when using this method in hot paths as performance may be affected. + /// + /// The type to compare against + /// Forces caches to clear and for the lists of types to be rebuilt. + /// An Enumerator for matching types. + public IEnumerable GetSubTypesInLoadedAssemblies(bool rebuildList); + + /// + /// Tries to get types assignable to type from the ACL given the Guid. + /// + /// + /// + /// + /// Operation success. + public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types); + + /// + /// Tries to get types from the ACL given the Guid. + /// + /// + /// + /// + public bool TryGetSubTypesFromACL(Guid id, out IEnumerable types); + + /// + /// Allows iteration over all types, including interfaces, in all loaded assemblies in the AsmMgr who's names match the string. + /// Note: Will return the by-reference equivalent type if the type name is prefixed with "out " or "ref ". + /// + /// The string name of the type to search for. + /// An Enumerator for matching types. List will be empty if bad params are supplied. + public IEnumerable GetTypesByName(string typeName); + + /// + /// Allows iteration over all types (including interfaces) in all loaded assemblies managed by the AsmMgr. + /// Warning: High usage may result in performance issues. + /// + /// An Enumerator for iteration. + public IEnumerable GetAllTypesInLoadedAssemblies(); + + /// + /// Returns a list of all loaded ACLs. + /// WARNING: References to these ACLs outside the AssemblyManager should be kept in a WeakReference in order + /// to avoid causing issues with unloading/disposal. + /// + /// + public IEnumerable GetAllLoadedACLs(); + + #endregion + + #region InternalAPI + /*** Notes: Internal API uses the 'public' modifier because of the common and recommended use of publicized APIs + * by third-party add-ins. + */ + + /// + /// [Unsafe] Warning: only for use in nested threading functions. Requires care to manage access. + /// Does not make any guarantees about the state of the ACL after the list has been returned. + /// + /// + public ImmutableList UnsafeGetAllLoadedACLs(); + + /// + /// Used by content package and plugin management to stop unloading of a given ACL until all plugins have gracefully closed. + /// + public event System.Func IsReadyToUnloadACL; + + /// + /// Compiles an assembly from supplied references and syntax trees into the specified AssemblyContextLoader. + /// A new ACL will be created if the Guid supplied is Guid.Empty. + /// + /// + /// + /// + /// + /// A non-unique name for later reference. Optional, set to null if unused. + /// The guid of the assembly + /// + /// + public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName, + [NotNull] IEnumerable syntaxTree, + IEnumerable externalMetadataReferences, + [NotNull] CSharpCompilationOptions compilationOptions, + string friendlyName, + ref Guid id, + IEnumerable externFileAssemblyRefs = null); + + /// + /// Switches the ACL with the given Guid to Template Mode, which disables assembly name resolution for any assemblies loaded in it. + /// These ACLs are intended to be used to host Assemblies for information only and not for code execution. + /// WARNING: This process is irreversible. + /// + /// Guid of the ACL. + /// Whether an ACL was found with the given ID. + public bool SetACLToTemplateMode(Guid guid); + + + /// + /// Tries to load all assemblies at the supplied file paths list into the ACl with the given Guid. + /// If the supplied Guid is Empty, then a new ACl will be created and the Guid will be assigned to it. + /// + /// List of assemblies to try and load. + /// A non-unique name for later reference. Optional. + /// Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var. + /// Operation success messages. + /// + public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable filePaths, + string friendlyName, ref Guid id); + + + /// + /// Tries to begin the disposal process of ACLs. + /// + /// Returns whether the unloading process could be initiated. + public bool TryBeginDispose(); + + + /// + /// Returns whether unloading is completed and updates the styate of the unloading cache. + /// + /// + public bool FinalizeDispose(); + + /// + /// Tries to retrieve the LoadedACL with the given ID or null if none is found. + /// WARNING: External references to this ACL with long lifespans should be kept in a WeakReference + /// to avoid causing unloading/disposal issues. + /// + /// GUID of the ACL. + /// The found ACL or null if none was found. + /// Whether an ACL was found. + public bool TryGetACL(Guid id, out AssemblyManager.LoadedACL acl); + + #endregion }