diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 3d01a6b36..b7544631b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -3236,6 +3236,7 @@ namespace Barotrauma } GameMain.LuaCs.CsScript.Run(string.Join(" ", args)); + GameMain.LuaCs.RecreateCsScript(); })); commands.Add(new Command("cl_reloadlua", "reloads lua on the client", (string[] args) => diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index 40c6d5935..06d8f0e8c 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -1250,6 +1250,7 @@ namespace Barotrauma if(LuaCsSetup.GetPackage("CsForBarotrauma", false) == null) { return; } GameMain.LuaCs.CsScript.Run(string.Join(" ", args)); + GameMain.LuaCs.RecreateCsScript(); })); commands.Add(new Command("reloadlua", "reloads lua", (string[] args) => diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptBase.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptBase.cs new file mode 100644 index 000000000..921927678 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptBase.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.CodeAnalysis.Scripting; +using System.Reflection; +using Microsoft.CodeAnalysis.CSharp; +using System.Linq; +using Microsoft.CodeAnalysis; +using System.Runtime.Loader; +using System.Reflection.PortableExecutable; +using System.Reflection.Metadata; +using System.Text; +using System.Runtime.CompilerServices; + +namespace Barotrauma +{ + class CsScriptBase : AssemblyLoadContext + { + public const string NET_ONE_TIME_SCRIPT_ASSEMBLY = "NetOneTimeScriptAssembly"; + public const string NET_SCRIPT_ASSEMBLY = "NetScriptAssembly"; + + public static Dictionary Revision = new Dictionary() + { + { NET_SCRIPT_ASSEMBLY, 0}, + { NET_ONE_TIME_SCRIPT_ASSEMBLY, 0} + }; + + public CsScriptBase() : base(isCollectible: true) { } + + public static SyntaxTree AssemblyInfoSyntaxTree(string asmName = null) + { + Revision[asmName] = (int)Revision[asmName] + 1; + var asmInfo = new StringBuilder(); + asmInfo.AppendLine("using System.Reflection;"); + asmInfo.AppendLine($"[assembly: AssemblyMetadata(\"Revision\", \"{Revision[asmName]}\")]"); + asmInfo.AppendLine($"[assembly: AssemblyVersion(\"0.0.0.{Revision[asmName]}\")]"); + return CSharpSyntaxTree.ParseText(asmInfo.ToString(), CSharpParseOptions.Default); + } + + ~CsScriptBase() { } + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptFilter.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptFilter.cs index 7f65f78c4..99c4b2120 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptFilter.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptFilter.cs @@ -9,6 +9,11 @@ using System.Reflection.Metadata; namespace Barotrauma { class CsScriptFilter { + public static readonly string[] LoadedAssemblyName = { + CsScriptBase.NET_SCRIPT_ASSEMBLY, + CsScriptBase.NET_ONE_TIME_SCRIPT_ASSEMBLY + }; + private static readonly string[] typesPermitted = { // Basics "System", diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptLoader.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptLoader.cs index 98e372bc5..df2e9114d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptLoader.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptLoader.cs @@ -12,7 +12,7 @@ using System.Reflection.Metadata; namespace Barotrauma { - class CsScriptLoader : AssemblyLoadContext + class CsScriptLoader : CsScriptBase { public LuaCsSetup setup; private List defaultReferences; @@ -20,7 +20,7 @@ namespace Barotrauma private Dictionary> sources; public Assembly Assembly { get; private set; } - public CsScriptLoader(LuaCsSetup setup) : base(isCollectible: true) + public CsScriptLoader(LuaCsSetup setup) { this.setup = setup; @@ -62,6 +62,7 @@ namespace Barotrauma var syntaxTrees = new List(); if (sources.Count <= 0) throw new Exception("No Cs sources detected"); + syntaxTrees.Add(AssemblyInfoSyntaxTree(NET_SCRIPT_ASSEMBLY)); foreach ((var folder, var src) in sources) { try @@ -92,7 +93,7 @@ namespace Barotrauma .WithMetadataImportOptions(MetadataImportOptions.All) .WithOptimizationLevel(OptimizationLevel.Release) .WithAllowUnsafe(false); - var compilation = CSharpCompilation.Create("NetScriptAssembly",syntaxTrees, defaultReferences, options); + var compilation = CSharpCompilation.Create(NET_SCRIPT_ASSEMBLY,syntaxTrees, defaultReferences, options); using (var mem = new MemoryStream()) { @@ -153,10 +154,5 @@ namespace Barotrauma { Assembly = null; } - - ~CsScriptLoader() - { - - } } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptRunner.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptRunner.cs index da773600e..44f17be58 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptRunner.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptRunner.cs @@ -13,7 +13,7 @@ using MoonSharp.Interpreter; namespace Barotrauma { - class CsScriptRunner : AssemblyLoadContext + class CsScriptRunner : CsScriptBase { public LuaCsSetup setup; private List defaultReferences; @@ -25,7 +25,7 @@ namespace Barotrauma "System.Linq" }; - public CsScriptRunner(LuaCsSetup setup) : base(isCollectible: true) + public CsScriptRunner(LuaCsSetup setup) { this.setup = setup; @@ -56,7 +56,7 @@ namespace Barotrauma { code = ToOneTimeScript(code); var syntaxTree = SyntaxFactory.ParseSyntaxTree(code, CSharpParseOptions.Default); - var compilation = CSharpCompilation.Create("NetOneTimeScriptAssembly", new[] { syntaxTree }, defaultReferences, compileOptions); + var compilation = CSharpCompilation.Create(NET_ONE_TIME_SCRIPT_ASSEMBLY, new[] { AssemblyInfoSyntaxTree(NET_ONE_TIME_SCRIPT_ASSEMBLY), syntaxTree }, defaultReferences, compileOptions); Assembly assembly = null; using (var mem = new MemoryStream()) @@ -103,8 +103,7 @@ namespace Barotrauma scriptResilt = method.Invoke(runner, null); foreach (var type in assembly.GetTypes()) { - //UserData.UnregisterType(type, true); - UserData.UnregisterType(type); + UserData.UnregisterType(type, true); } } else LuaCsSetup.PrintCsError("Script Error - no run method detected"); @@ -115,6 +114,8 @@ namespace Barotrauma } } Unload(); + GC.Collect(); + GC.WaitForPendingFinalizers(); } catch (Exception ex) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs index 122428209..650cf9d54 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs @@ -13,8 +13,14 @@ namespace Barotrauma { var type = Type.GetType(typeName); if (type != null) return type; - foreach (var a in AppDomain.CurrentDomain.GetAssemblies().Reverse()) + foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { + if (CsScriptFilter.LoadedAssemblyName.Contains(a.GetName().Name)) + { + var attrs = a.GetCustomAttributes(); + var revision = attrs.FirstOrDefault(attr => attr.Key == "Revision")?.Value; + if (revision != null && int.Parse(revision) != (int)CsScriptBase.Revision[a.GetName().Name]) { continue; } + } type = a.GetType(typeName); if (type != null) return type; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index af81bb7a4..960a97981 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -12,8 +12,8 @@ using System.Runtime.CompilerServices; using System.Linq; using System.Reflection; -[assembly: InternalsVisibleTo("NetScriptAssembly", AllInternalsVisible = true)] -[assembly: InternalsVisibleTo("NetOneTimeScriptAssembly", AllInternalsVisible = true)] +[assembly: InternalsVisibleTo(Barotrauma.CsScriptBase.NET_SCRIPT_ASSEMBLY, AllInternalsVisible = true)] +[assembly: InternalsVisibleTo(Barotrauma.CsScriptBase.NET_ONE_TIME_SCRIPT_ASSEMBLY, AllInternalsVisible = true)] namespace Barotrauma { class LuaCsSetupConfig @@ -40,6 +40,17 @@ namespace Barotrauma private Script lua; public CsScriptRunner CsScript { get; private set; } + + /// + /// due to there's a race on the process and the unloaded AssemblyLoadContexts, + /// should recreate runner after the script runs + /// + public void RecreateCsScript() + { + GameMain.LuaCs.CsScript = new CsScriptRunner(GameMain.LuaCs.CsScript.setup); + lua.Globals["CsScript"] = CsScript; + } + public LuaGame Game { get; private set; } public LuaScriptLoader LuaScriptLoader { get; private set; } @@ -314,9 +325,9 @@ namespace Barotrauma public void Stop() { - foreach (var type in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "NetScriptAssembly").SelectMany(assembly => assembly.GetTypes())) + foreach (var type in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == CsScriptBase.NET_SCRIPT_ASSEMBLY).SelectMany(assembly => assembly.GetTypes())) { - UserData.UnregisterType(type); + UserData.UnregisterType(type, true); } foreach (var mod in ACsMod.LoadedMods.ToArray()) mod.Dispose(); ACsMod.LoadedMods.Clear(); @@ -450,7 +461,8 @@ modding needs. var modTypes = CsScriptLoader.Compile(); modTypes.ForEach(t => { - UserData.RegisterType(t); + //Please register `t` in lua-side + //UserData.RegisterType(t); t.GetConstructor(new Type[] { })?.Invoke(null); }); }