using CringeBootstrap.Abstractions; using CringeLauncher.Loader; using HarmonyLib; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using NLog; using Sandbox; using Sandbox.Game; using Sandbox.Game.Entities.Blocks; using Sandbox.Game.EntityComponents; using Sandbox.Game.Gui; using Sandbox.Game.Localization; using Sandbox.Game.World; using Sandbox.Graphics.GUI; using Sandbox.ModAPI; using Sandbox.ModAPI.Ingame; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Text; using VRage; using VRage.ModAPI; using VRage.Scripting; using Message = VRage.Scripting.Message; namespace CringeLauncher.Patches; [HarmonyPatch] public static class ModScriptCompilerPatch { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static ModAssemblyLoadContext _modContext; private static readonly HashSet LoadedModAssemblyNames = []; private static readonly ConditionalWeakTable LoadContexts = []; private static readonly FieldInfo InstanceField = AccessTools.Field(typeof(MyProgrammableBlock), "m_instance"); private static readonly PropertyInfo AssemblyProperty = AccessTools.Property(typeof(MyProgrammableBlock), "CurrentAssembly"); private static readonly FieldInfo CompilerErrorsField = AccessTools.Field(typeof(MyProgrammableBlock), "m_compilerErrors"); private static readonly MethodInfo CreateInstanceMethod = AccessTools.Method(typeof(MyProgrammableBlock), "CreateInstance"); private static readonly MethodInfo SetDetailedInfoMethod = AccessTools.Method(typeof(MyProgrammableBlock), "SetDetailedInfo"); private static readonly ICoreLoadContext CoreContext = (ICoreLoadContext)AssemblyLoadContext.GetLoadContext(typeof(MySession).Assembly)!; private static readonly DiagnosticAnalyzer ModWhitelistAnalyzer = AccessTools.FieldRefAccess( MyScriptCompiler.Static, "m_modApiWhitelistDiagnosticAnalyzer"); private static readonly DiagnosticAnalyzer ScriptWhitelistAnalyzer = AccessTools.FieldRefAccess(MyScriptCompiler.Static, "m_inGameWhitelistDiagnosticAnalyzer"); private static readonly Func InjectMod = AccessTools.MethodDelegate>( AccessTools.Method(typeof(MyScriptCompiler), "InjectMod"), MyScriptCompiler.Static); private static readonly Func InjectResourceMonitoring = AccessTools.MethodDelegate>( AccessTools.Method(typeof(MyScriptCompiler), "InjectResourceMonitoring"), MyScriptCompiler.Static); private static readonly Func, bool, Task> EmitDiagnostics = AccessTools.MethodDelegate, bool, Task>>( AccessTools.Method(typeof(MyScriptCompiler), "EmitDiagnostics"), MyScriptCompiler.Static); private static readonly Func MakeAssemblyName = AccessTools.MethodDelegate>(AccessTools.Method(typeof(MyScriptCompiler), "MakeAssemblyName")); private static readonly Func, bool, CSharpCompilation> CreateCompilation = AccessTools.MethodDelegate, bool, CSharpCompilation>>(AccessTools.Method(typeof(MyScriptCompiler), "CreateCompilation")); static ModScriptCompilerPatch() { MySession.OnUnloaded += OnUnloaded; _modContext = new(CoreContext); } private static void OnUnloaded() { LoadedModAssemblyNames.Clear(); if (!_modContext.Assemblies.Any()) return; _modContext.Unload(); _modContext = new(CoreContext); } [HarmonyPatch(typeof(MyProgrammableBlock), "Compile")] [HarmonyPrefix] private static bool CompilePrefix(MyProgrammableBlock __instance, string program, string storage, bool instantiate, ref MyProgrammableBlock.ScriptTerminationReason ___m_terminationReason, MyIngameScriptComponent ___m_scriptComponent) { if (!MySession.Static.EnableIngameScripts || __instance.CubeGrid is { IsPreview: true } or { CreatePhysics: false }) return false; ___m_terminationReason = MyProgrammableBlock.ScriptTerminationReason.None; CompileAsync(__instance, program, storage, instantiate, ___m_scriptComponent); return false; } [HarmonyPatch(typeof(MyGuiScreenEditor), "CheckCodeButtonClicked")] [HarmonyPrefix] private static bool GuiCompilePrefix(List ___m_compilerErrors, MyGuiScreenEditor __instance) { ___m_compilerErrors.Clear(); var progress = new MyGuiScreenProgress(MyTexts.Get(MySpaceTexts.ProgrammableBlock_Editor_CheckingCode)); MyScreenManager.AddScreen(progress); if (__instance.Description.Text.Length > 0) { var task = CompileAsync(__instance, ___m_compilerErrors, __instance.Description.Text.ToString(), progress); task.ConfigureAwait(false).GetAwaiter().GetResult(); MyScreenManager.RemoveScreen(progress); MyVRage.Platform.ImeProcessor?.RegisterActiveScreen(__instance); __instance.FocusedControl = __instance.Description; } return false; } [HarmonyPatch(typeof(MyScriptCompiler), nameof(MyScriptCompiler.Compile))] [HarmonyPrefix] private static bool Prefix(ref Task __result, MyApiTarget target, string assemblyName, IEnumerable