From 7c4d7d374bb575e4dcbdbdb23aa3406f5dc4cc5e Mon Sep 17 00:00:00 2001 From: zhurengong <2731412072@qq.com> Date: Wed, 20 Apr 2022 14:56:32 +0800 Subject: [PATCH] some improves and fixes: 1. should get the last loadded aseembly by checking its version instead of reversing. 2. fixed throw an exception when CsScriptRunner run the second time. 3. an argument carried by an UnregisterType called somewhere have had been accidentally removed. --- .../ClientSource/DebugConsole.cs | 1 + .../ServerSource/DebugConsole.cs | 1 + .../SharedSource/LuaCs/Cs/CsScriptBase.cs | 42 +++++++++++++++++++ .../SharedSource/LuaCs/Cs/CsScriptFilter.cs | 5 +++ .../SharedSource/LuaCs/Cs/CsScriptLoader.cs | 12 ++---- .../SharedSource/LuaCs/Cs/CsScriptRunner.cs | 11 ++--- .../LuaCs/Lua/LuaClasses/LuaUserData.cs | 8 +++- .../SharedSource/LuaCs/LuaCsSetup.cs | 22 +++++++--- 8 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Cs/CsScriptBase.cs diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index e896f8668..4bde715a1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -3234,6 +3234,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 9c66b7c26..7e9551294 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -1248,6 +1248,7 @@ namespace Barotrauma commands.Add(new Command("cs", "cs: runs a string", (string[] args) => { 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 9c8e90dec..b3e92e266 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); }); }