- add: Metrics
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Nebula.Runner.Services;
|
||||
using Nebula.Shared;
|
||||
using Nebula.Shared.Models;
|
||||
using Nebula.Shared.Services;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lib.Harmony" Version="2.3.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0"/>
|
||||
<PackageReference Include="SharpZstd.Interop" Version="1.5.6"/>
|
||||
</ItemGroup>
|
||||
|
||||
55
Nebula.Runner/Services/HarmonyService.cs
Normal file
55
Nebula.Runner/Services/HarmonyService.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Data;
|
||||
using HarmonyLib;
|
||||
using Nebula.Shared;
|
||||
|
||||
namespace Nebula.Runner.Services;
|
||||
|
||||
[ServiceRegister]
|
||||
public class HarmonyService(ReflectionService reflectionService)
|
||||
{
|
||||
private HarmonyInstance? _instance;
|
||||
|
||||
public HarmonyInstance Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance is null)
|
||||
CreateInstance();
|
||||
return _instance!;
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateInstance()
|
||||
{
|
||||
if (_instance is not null)
|
||||
throw new Exception();
|
||||
|
||||
_instance = new HarmonyInstance();
|
||||
UnShittyWizard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Я помню пенис большой,Я помню пенис большой, Я помню пенис большой, я помню....
|
||||
/// </summary>
|
||||
private void UnShittyWizard()
|
||||
{
|
||||
var method = reflectionService.GetType("Robust.Client.GameController").TypeInitializer;
|
||||
_instance!.Harmony.Patch(method, new HarmonyMethod(Prefix));
|
||||
}
|
||||
|
||||
static bool Prefix()
|
||||
{
|
||||
// Returning false skips the execution of the original static constructor
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class HarmonyInstance
|
||||
{
|
||||
public readonly Harmony Harmony;
|
||||
|
||||
internal HarmonyInstance()
|
||||
{
|
||||
Harmony = new Harmony("ru.cinka.patch");
|
||||
}
|
||||
}
|
||||
59
Nebula.Runner/Services/ReflectionService.cs
Normal file
59
Nebula.Runner/Services/ReflectionService.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Reflection;
|
||||
using Nebula.Shared;
|
||||
using Nebula.Shared.FileApis;
|
||||
using Nebula.Shared.Services;
|
||||
|
||||
namespace Nebula.Runner.Services;
|
||||
|
||||
[ServiceRegister]
|
||||
public class ReflectionService(AssemblyService assemblyService)
|
||||
{
|
||||
private Dictionary<string, Assembly> _typeCache = new();
|
||||
|
||||
public void RegisterAssembly(Assembly robustAssembly)
|
||||
{
|
||||
_typeCache.Add(robustAssembly.GetName().Name!, robustAssembly);
|
||||
}
|
||||
|
||||
public void RegisterRobustAssemblies(AssemblyApi engine)
|
||||
{
|
||||
RegisterAssembly(GetRobustAssembly("Robust.Shared", engine));
|
||||
RegisterAssembly(GetRobustAssembly("Robust.Client", engine));
|
||||
}
|
||||
|
||||
private Assembly GetRobustAssembly(string assemblyName, AssemblyApi engine)
|
||||
{
|
||||
if(!assemblyService.TryOpenAssembly(assemblyName, engine, out var assembly))
|
||||
throw new Exception($"Unable to locate {assemblyName}.dll in engine build!");
|
||||
return assembly;
|
||||
}
|
||||
|
||||
public Type? GetTypeImp(string name)
|
||||
{
|
||||
foreach (var (prefix,assembly) in _typeCache)
|
||||
{
|
||||
string appendedName = prefix + name;
|
||||
var theType = assembly.GetType(appendedName);
|
||||
if (theType != null)
|
||||
{
|
||||
return theType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Type GetType(string name)
|
||||
{
|
||||
var prefix = ExtrackPrefix(name);
|
||||
return !_typeCache.TryGetValue(prefix, out var assembly)
|
||||
? GetTypeImp(name)!
|
||||
: assembly.GetType(name)!;
|
||||
}
|
||||
|
||||
private string ExtrackPrefix(string path)
|
||||
{
|
||||
var sp = path.Split(".");
|
||||
return sp[0] + "." + sp[1];
|
||||
}
|
||||
}
|
||||
187
Nebula.Runner/Services/RunnerService.cs
Normal file
187
Nebula.Runner/Services/RunnerService.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using Nebula.Shared;
|
||||
using Nebula.Shared.Models;
|
||||
using Nebula.Shared.Services;
|
||||
using Nebula.Shared.Services.Logging;
|
||||
using Robust.LoaderApi;
|
||||
|
||||
namespace Nebula.Runner.Services;
|
||||
|
||||
[ServiceRegister]
|
||||
public sealed class RunnerService(
|
||||
ContentService contentService,
|
||||
DebugService debugService,
|
||||
ConfigurationService varService,
|
||||
EngineService engineService,
|
||||
AssemblyService assemblyService,
|
||||
ReflectionService reflectionService,
|
||||
HarmonyService harmonyService)
|
||||
{
|
||||
private ILogger _logger = debugService.GetLogger("RunnerService");
|
||||
private bool MetricEnabled = false; //TODO: ADD METRIC THINKS LATER
|
||||
|
||||
public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi,
|
||||
ILoadingHandler loadingHandler,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.Log("Start Content!");
|
||||
|
||||
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
|
||||
|
||||
if (engine is null)
|
||||
throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion);
|
||||
|
||||
var hashApi = await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken);
|
||||
|
||||
var extraMounts = new List<ApiMount>
|
||||
{
|
||||
new(hashApi, "/")
|
||||
};
|
||||
|
||||
if (hashApi.TryOpen("manifest.yml", out var stream))
|
||||
{
|
||||
var modules = ContentManifestParser.ExtractModules(stream);
|
||||
|
||||
foreach (var moduleStr in modules)
|
||||
{
|
||||
var module =
|
||||
await engineService.EnsureEngineModules(moduleStr, buildInfo.BuildInfo.Build.EngineVersion);
|
||||
if (module is not null)
|
||||
extraMounts.Add(new ApiMount(module, "/"));
|
||||
}
|
||||
|
||||
await stream.DisposeAsync();
|
||||
}
|
||||
|
||||
var args = new MainArgs(runArgs, engine, redialApi, extraMounts);
|
||||
|
||||
if (!assemblyService.TryOpenAssembly(varService.GetConfigValue(CurrentConVar.RobustAssemblyName)!, engine,
|
||||
out var clientAssembly))
|
||||
throw new Exception("Unable to locate Robust.Client.dll in engine build!");
|
||||
|
||||
if (!assemblyService.TryGetLoader(clientAssembly, out var loader))
|
||||
return;
|
||||
|
||||
if(!assemblyService.TryOpenAssembly("Prometheus.NetStandard", engine, out var prometheusAssembly))
|
||||
return;
|
||||
|
||||
reflectionService.RegisterRobustAssemblies(engine);
|
||||
harmonyService.CreateInstance();
|
||||
|
||||
IDisposable? metricServer = null;
|
||||
|
||||
if (MetricEnabled)
|
||||
{
|
||||
MetricsEnabledPatcher.ApplyPatch(reflectionService, harmonyService);
|
||||
metricServer = RunHelper.RunMetric(prometheusAssembly);
|
||||
}
|
||||
|
||||
|
||||
await Task.Run(() => loader.Main(args), cancellationToken);
|
||||
|
||||
metricServer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsEnabledPatcher
|
||||
{
|
||||
public static void ApplyPatch(ReflectionService reflectionService, HarmonyService harmonyService)
|
||||
{
|
||||
var harmony = harmonyService.Instance.Harmony;
|
||||
|
||||
// Get the target method: the getter of MetricsEnabled
|
||||
var targetType = reflectionService.GetType("Robust.Shared.GameObjects.EntitySystemManager");
|
||||
var targetMethod = targetType.GetProperty("MetricsEnabled").GetGetMethod();
|
||||
|
||||
// Get MethodInfo for the prefix
|
||||
var prefix = typeof(MetricsEnabledPatcher).GetMethod(nameof(MetricsEnabledGetterPrefix),
|
||||
BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
// Create HarmonyMethod
|
||||
var prefixMethod = new HarmonyMethod(prefix);
|
||||
|
||||
// Patch it!
|
||||
harmony.Patch(targetMethod, prefix: prefixMethod);
|
||||
}
|
||||
|
||||
// This prefix will override the getter and force return true
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContentManifestParser
|
||||
{
|
||||
public static List<string> ExtractModules(Stream manifestStream)
|
||||
{
|
||||
using var reader = new StreamReader(manifestStream);
|
||||
return ExtractModules(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
public static List<string> ExtractModules(string manifestContent)
|
||||
{
|
||||
var modules = new List<string>();
|
||||
var lines = manifestContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
bool inModulesSection = false;
|
||||
|
||||
foreach (var rawLine in lines)
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
|
||||
if (line.StartsWith("modules:"))
|
||||
{
|
||||
inModulesSection = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inModulesSection)
|
||||
{
|
||||
if (line.StartsWith("- "))
|
||||
{
|
||||
modules.Add(line.Substring(2).Trim());
|
||||
}
|
||||
else if (!line.StartsWith(" "))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user