internal Func <object, string> GetExec <T>(CsModPermission csScriptsAllowed, T rootObjectCompileTime, string script) where T : IScriptRootData
        {
            var        messages   = new List <string>();
            bool       success    = true;
            MethodInfo mainMethod = null;

            Script <object> csScript = null;
            CsModPermission permissionNeeded;
            var             rootCompileTime = rootObjectCompileTime as IScriptRootData;

            using (var loader = new InteractiveAssemblyLoader())
            {
                var options = ScriptOptions.Default
                              .WithAllowUnsafe(false)
                              .WithEmitDebugInformation(false)
                              .WithCheckOverflow(true)
                              .WithOptimizationLevel(OptimizationLevel.Release)

                              .WithImports(DefaultConfiguration.Current.Usings)
                              .AddImports(Configuration.Current.Usings)

                              .WithReferences(DefaultConfiguration.Current.AssemblyReferences)
                              .AddReferences(Configuration.Current.AssemblyReferences)
                              .AddReferences(typeof(EmpyrionScripting).Assembly.Location, typeof(IScriptRootData).Assembly.Location)
                              .AddReferences(CustomAssemblies.Values.Select(A => A.LoadedAssembly));

                csScript = CSharpScript.Create <object>(script, options, typeof(IScriptModData), loader);
                var compilation = csScript.GetCompilation();

                var WhitelistDiagnosticAnalyzer = new WhitelistDiagnosticAnalyzer(DefaultConfiguration, Configuration);

                var analyzerCompilation = compilation
                                          .WithAnalyzers(ImmutableArray.Create <DiagnosticAnalyzer>(WhitelistDiagnosticAnalyzer))
                                          .GetAnalysisResultAsync(CancellationToken.None)
                                          .GetAwaiter().GetResult().CompilationDiagnostics;

                analyzerCompilation.ForEach(A => AnalyzeDiagnostics(A.Value, messages, ref success));

                if (WhitelistDiagnosticAnalyzer.ConfigurationIsChanged)
                {
                    Configuration.Current.PrepareForSave();
                    Configuration.Save();

                    DefaultConfiguration.Current.PrepareForSave();
                    DefaultConfiguration.Save();
                }

                permissionNeeded = WhitelistDiagnosticAnalyzer.PermissionNeeded;

                Assembly assembly = null;

                if (compilation.Assembly.TypeNames.Contains("ModMain"))
                {
                    using (var assemblyStream = new MemoryStream())
                    {
                        try
                        {
                            var result        = compilation.Emit(assemblyStream);
                            var resultSuccess = result.Success;

                            if (resultSuccess)
                            {
                                assembly = Assembly.ReflectionOnlyLoad(assemblyStream.ToArray());
                                var callMainType = assembly.GetTypes().SingleOrDefault(MT => MT.Name == "ModMain");
                                mainMethod = callMainType.GetMethod("Main");

                                if (mainMethod != null)
                                {
                                    assemblyStream.Seek(0, SeekOrigin.Begin);
                                    assembly = Assembly.Load(assemblyStream.ToArray());

                                    callMainType = assembly.GetTypes().SingleOrDefault(MT => MT.Name == "ModMain");
                                    mainMethod   = callMainType.GetMethod("Main");
                                }
                            }
                        }
                        catch (Exception loadError)
                        {
                            messages.Add($"Assembly:{loadError}");
                        }
                    }
                }
            }

            if (messages.Count > 0)
            {
                Log?.Invoke($"C# Compile [{rootCompileTime.ScriptId}]:{string.Join("\n", messages)}", LogLevel.Error);

                if (EmpyrionScripting.Configuration.Current.ScriptTrackingError)
                {
                    File.AppendAllText(rootObjectCompileTime is ScriptSaveGameRootData root
                        ? EmpyrionScripting.GetTrackingFileName(root)
                        : EmpyrionScripting.GetTrackingFileName(rootObjectCompileTime.E.GetCurrent(), rootObjectCompileTime.Script.GetHashCode().ToString()) + ".error",
                                       string.Join("\n", messages));
                }
            }

            return(rootObject =>
            {
                if (!success)
                {
                    return string.Join("\n", messages);
                }

                var root = rootObject as IScriptRootData;
                if (csScriptsAllowed == CsModPermission.SaveGame && !(root is ScriptSaveGameRootData))
                {
                    return "C# scripts are only allowed in SaveGameScripts";
                }
                if (csScriptsAllowed == CsModPermission.Admin && root.E.GetCurrent().Faction.Group != FactionGroup.Admin)
                {
                    return "C# scripts are only allowed on admin structures";
                }

                if (permissionNeeded == CsModPermission.SaveGame && !(root is ScriptSaveGameRootData))
                {
                    return "This script is only allowed in SaveGameScripts";
                }
                if (permissionNeeded == CsModPermission.Admin && root.E.GetCurrent().Faction.Group != FactionGroup.Admin)
                {
                    return "This script is only allowed on admin structures";
                }

                string exceptionMessage = null;
                using (var output = new StringWriter())
                {
                    root.ScriptOutput = output;

                    try
                    {
                        object result = null;

                        if (mainMethod != null)
                        {
                            if (root.CsRoot is CsScriptFunctions csRoot)
                            {
                                csRoot.ScriptRoot = root;
                            }
                            result = mainMethod.Invoke(null, new[] { root as IScriptModData });
                        }
                        else
                        {
                            result = csScript
                                     .RunAsync(root, ex => { exceptionMessage = $"Exception: {(root.IsElevatedScript ? ex.ToString() : ex.Message)}"; return true; })
                                     .ConfigureAwait(true)
                                     .GetAwaiter()
                                     .GetResult()
                                     .ReturnValue;
                        }

                        if (result is Action action)
                        {
                            action();
                        }
                        else if (result is Action <IScriptModData> simpleaction)
                        {
                            simpleaction(root);
                        }
                        else if (result is Func <IScriptModData, object> func)
                        {
                            output.Write(func(root)?.ToString());
                        }
                        else if (result is Task task)
                        {
                            task.RunSynchronously();
                        }
                        else
                        {
                            output.Write(result?.ToString());
                        }

                        return exceptionMessage == null?output.ToString() : $"{exceptionMessage}\n\nScript output up to exception:\n{output}";
                    }
                    catch (Exception error)
                    {
                        exceptionMessage = error.ToString();
                        return root.IsElevatedScript ? error.ToString() : error.Message;
                    }
                    finally
                    {
                        if (!string.IsNullOrEmpty(exceptionMessage))
                        {
                            Log?.Invoke($"C# Run [{root.ScriptId}]:{exceptionMessage}\n{output}", LogLevel.Error);

                            if (EmpyrionScripting.Configuration.Current.ScriptTrackingError)
                            {
                                File.AppendAllText(root is ScriptSaveGameRootData saveGameRoot
                                    ? EmpyrionScripting.GetTrackingFileName(saveGameRoot)
                                    : EmpyrionScripting.GetTrackingFileName(root.E.GetCurrent(), root.Script.GetHashCode().ToString()) + ".error",
                                                   $"{exceptionMessage}\n\nScript output up to exception:\n{output}");
                            }
                        }
                    }
                }
            });
        }