2025-06-27 21:31:38 +03:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
|
using HarmonyLib;
|
|
|
|
|
|
using Nebula.Shared;
|
|
|
|
|
|
using Nebula.Shared.Models;
|
|
|
|
|
|
using Nebula.Shared.Services;
|
2025-05-05 20:43:28 +03:00
|
|
|
|
using Nebula.Shared.Services.Logging;
|
2025-12-06 23:25:25 +03:00
|
|
|
|
using Nebula.Shared.Utils;
|
2024-12-27 19:15:33 +03:00
|
|
|
|
using Robust.LoaderApi;
|
|
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
namespace Nebula.Runner.Services;
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
|
|
|
|
|
[ServiceRegister]
|
2025-01-05 17:05:23 +03:00
|
|
|
|
public sealed class RunnerService(
|
|
|
|
|
|
ContentService contentService,
|
|
|
|
|
|
DebugService debugService,
|
|
|
|
|
|
ConfigurationService varService,
|
|
|
|
|
|
EngineService engineService,
|
2025-06-27 21:31:38 +03:00
|
|
|
|
AssemblyService assemblyService,
|
|
|
|
|
|
ReflectionService reflectionService,
|
|
|
|
|
|
HarmonyService harmonyService)
|
2024-12-27 19:15:33 +03:00
|
|
|
|
{
|
2025-05-05 20:43:28 +03:00
|
|
|
|
private ILogger _logger = debugService.GetLogger("RunnerService");
|
2025-06-27 21:31:38 +03:00
|
|
|
|
private bool MetricEnabled = false; //TODO: ADD METRIC THINKS LATER
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
|
public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi,
|
2025-12-06 23:25:25 +03:00
|
|
|
|
ILoadingHandlerFactory loadingHandler,
|
2024-12-27 19:15:33 +03:00
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
|
{
|
2025-05-05 20:43:28 +03:00
|
|
|
|
_logger.Log("Start Content!");
|
2025-12-06 23:25:25 +03:00
|
|
|
|
|
|
|
|
|
|
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion, loadingHandler, cancellationToken);
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
|
|
|
|
|
if (engine is null)
|
2025-03-13 14:58:47 +03:00
|
|
|
|
throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion);
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
2026-01-16 21:02:34 +03:00
|
|
|
|
var fileApi = await contentService.EnsureItems(buildInfo, loadingHandler, cancellationToken);
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
|
|
|
|
|
var extraMounts = new List<ApiMount>
|
|
|
|
|
|
{
|
2026-01-16 21:02:34 +03:00
|
|
|
|
new(fileApi, "/")
|
2024-12-27 19:15:33 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-01-16 21:02:34 +03:00
|
|
|
|
if (fileApi.TryOpen("manifest.yml", out var stream))
|
2025-05-02 20:06:33 +03:00
|
|
|
|
{
|
|
|
|
|
|
var modules = ContentManifestParser.ExtractModules(stream);
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
|
foreach (var moduleStr in modules)
|
|
|
|
|
|
{
|
|
|
|
|
|
var module =
|
2025-12-06 23:25:25 +03:00
|
|
|
|
await engineService.EnsureEngineModules(moduleStr, loadingHandler, buildInfo.BuildInfo.Build.EngineVersion);
|
2025-05-02 20:06:33 +03:00
|
|
|
|
if (module is not null)
|
|
|
|
|
|
extraMounts.Add(new ApiMount(module, "/"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await stream.DisposeAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-27 19:15:33 +03:00
|
|
|
|
var args = new MainArgs(runArgs, engine, redialApi, extraMounts);
|
|
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
|
if (!assemblyService.TryOpenAssembly(varService.GetConfigValue(CurrentConVar.RobustAssemblyName)!, engine,
|
|
|
|
|
|
out var clientAssembly))
|
2024-12-27 19:15:33 +03:00
|
|
|
|
throw new Exception("Unable to locate Robust.Client.dll in engine build!");
|
2025-06-27 21:31:38 +03:00
|
|
|
|
|
2025-01-05 17:05:23 +03:00
|
|
|
|
if (!assemblyService.TryGetLoader(clientAssembly, out var loader))
|
2024-12-27 19:15:33 +03:00
|
|
|
|
return;
|
2025-06-27 21:31:38 +03:00
|
|
|
|
|
|
|
|
|
|
if(!assemblyService.TryOpenAssembly("Prometheus.NetStandard", engine, out var prometheusAssembly))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
reflectionService.RegisterRobustAssemblies(engine);
|
|
|
|
|
|
harmonyService.CreateInstance();
|
|
|
|
|
|
|
|
|
|
|
|
IDisposable? metricServer = null;
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
if (MetricEnabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
MetricsEnabledPatcher.ApplyPatch(reflectionService, harmonyService);
|
|
|
|
|
|
metricServer = RunHelper.RunMetric(prometheusAssembly);
|
|
|
|
|
|
}
|
2025-12-06 23:25:25 +03:00
|
|
|
|
|
|
|
|
|
|
loadingHandler.Dispose();
|
2024-12-27 19:15:33 +03:00
|
|
|
|
await Task.Run(() => loader.Main(args), cancellationToken);
|
2025-06-27 21:31:38 +03:00
|
|
|
|
|
|
|
|
|
|
metricServer?.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class MetricsEnabledPatcher
|
|
|
|
|
|
{
|
|
|
|
|
|
public static void ApplyPatch(ReflectionService reflectionService, HarmonyService harmonyService)
|
|
|
|
|
|
{
|
|
|
|
|
|
var harmony = harmonyService.Instance.Harmony;
|
2025-06-28 14:05:19 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
var targetType = reflectionService.GetType("Robust.Shared.GameObjects.EntitySystemManager");
|
2025-09-08 21:26:12 +03:00
|
|
|
|
var targetMethod = targetType.GetProperty("MetricsEnabled")?.GetGetMethod() ??
|
|
|
|
|
|
throw new Exception("target method is null.. huh.. do we have patch a right think?");
|
2025-06-28 14:05:19 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
var prefix = typeof(MetricsEnabledPatcher).GetMethod(nameof(MetricsEnabledGetterPrefix),
|
|
|
|
|
|
BindingFlags.Static | BindingFlags.NonPublic);
|
2025-06-28 14:05:19 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
var prefixMethod = new HarmonyMethod(prefix);
|
2025-06-28 14:05:19 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
harmony.Patch(targetMethod, prefix: prefixMethod);
|
|
|
|
|
|
}
|
2025-06-28 14:05:19 +03:00
|
|
|
|
|
2025-06-27 21:31:38 +03:00
|
|
|
|
private static bool MetricsEnabledGetterPrefix(ref bool __result)
|
|
|
|
|
|
{
|
|
|
|
|
|
__result = true;
|
|
|
|
|
|
return false; // Skip original method
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class RunHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
public static IDisposable RunMetric(Assembly prometheusAssembly)
|
|
|
|
|
|
{
|
|
|
|
|
|
var metricServerType = prometheusAssembly.GetType("Prometheus.MetricServer");
|
|
|
|
|
|
var collectorRegistryType = prometheusAssembly.GetType("Prometheus.CollectorRegistry");
|
|
|
|
|
|
|
|
|
|
|
|
var ctor = metricServerType!.GetConstructor(new Type[]
|
|
|
|
|
|
{
|
|
|
|
|
|
typeof(string),
|
|
|
|
|
|
typeof(int),
|
|
|
|
|
|
typeof(string),
|
|
|
|
|
|
collectorRegistryType!,
|
|
|
|
|
|
typeof(bool)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var hostname = "localhost";
|
|
|
|
|
|
var port = 51235;
|
|
|
|
|
|
var url = "metrics/";
|
|
|
|
|
|
object? registry = null;
|
|
|
|
|
|
var useHttps = false;
|
|
|
|
|
|
|
|
|
|
|
|
var metricServerInstance = ctor!.Invoke(new object[] { hostname, port, url, registry!, useHttps });
|
|
|
|
|
|
metricServerType.GetMethod("Start")!.Invoke(metricServerInstance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture);
|
|
|
|
|
|
|
|
|
|
|
|
return (IDisposable)metricServerInstance;
|
2024-12-27 19:15:33 +03:00
|
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
|
}
|
|
|
|
|
|
|