diff --git a/Nebula.Launcher/Nebula.Launcher.csproj b/Nebula.Launcher/Nebula.Launcher.csproj
index f6e067d..d96641b 100644
--- a/Nebula.Launcher/Nebula.Launcher.csproj
+++ b/Nebula.Launcher/Nebula.Launcher.csproj
@@ -54,6 +54,8 @@
+
+
@@ -61,6 +63,8 @@
+
+
diff --git a/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs
index c15c938..73aeb8f 100644
--- a/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs
+++ b/Nebula.Launcher/ViewModels/ServerCompoundEntryModelView.cs
@@ -27,6 +27,8 @@ public sealed partial class ServerCompoundEntryViewModel :
[ObservableProperty] private bool _loading = true;
private string? _name;
+ private RobustUrl? _url;
+ private ServerFilter? _currentFilter;
public string? Name
{
@@ -58,14 +60,16 @@ public sealed partial class ServerCompoundEntryViewModel :
{
Task.Run(async () =>
{
+ _url = url;
try
{
Message = "Loading server entry...";
- var status = await RestService.GetAsync(url.StatusUri, cancellationToken);
+ var status = await RestService.GetAsync(_url.StatusUri, cancellationToken);
- CurrentEntry = ServiceProvider.GetService()!.WithData(url,name, status);
+ CurrentEntry = ServiceProvider.GetService()!.WithData(_url,name, status);
CurrentEntry.IsFavorite = IsFavorite;
CurrentEntry.Loading = false;
+ CurrentEntry.ProcessFilter(_currentFilter);
Loading = false;
}
catch (Exception e)
@@ -79,12 +83,20 @@ public sealed partial class ServerCompoundEntryViewModel :
public void ToggleFavorites()
{
+ if (CurrentEntry is null && _url is not null)
+ {
+ IsFavorite = !IsFavorite;
+ if(IsFavorite) FavoriteServerListProvider.AddFavorite(_url);
+ else FavoriteServerListProvider.RemoveFavorite(_url);
+ }
+
CurrentEntry?.ToggleFavorites();
}
public void ProcessFilter(ServerFilter? serverFilter)
{
+ _currentFilter = serverFilter;
if(CurrentEntry is IFilterConsumer filterConsumer)
filterConsumer.ProcessFilter(serverFilter);
}
diff --git a/Nebula.Runner/App.cs b/Nebula.Runner/App.cs
index 45bfaba..73221ad 100644
--- a/Nebula.Runner/App.cs
+++ b/Nebula.Runner/App.cs
@@ -1,3 +1,4 @@
+using Nebula.Runner.Services;
using Nebula.Shared;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
diff --git a/Nebula.Runner/Nebula.Runner.csproj b/Nebula.Runner/Nebula.Runner.csproj
index 66664cc..321412a 100644
--- a/Nebula.Runner/Nebula.Runner.csproj
+++ b/Nebula.Runner/Nebula.Runner.csproj
@@ -12,6 +12,7 @@
+
diff --git a/Nebula.Runner/Services/HarmonyService.cs b/Nebula.Runner/Services/HarmonyService.cs
new file mode 100644
index 0000000..b769567
--- /dev/null
+++ b/Nebula.Runner/Services/HarmonyService.cs
@@ -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();
+ }
+
+ ///
+ /// Я помню пенис большой,Я помню пенис большой, Я помню пенис большой, я помню....
+ ///
+ 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");
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Runner/Services/ReflectionService.cs b/Nebula.Runner/Services/ReflectionService.cs
new file mode 100644
index 0000000..d1432e2
--- /dev/null
+++ b/Nebula.Runner/Services/ReflectionService.cs
@@ -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 _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];
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/RunnerService.cs b/Nebula.Runner/Services/RunnerService.cs
similarity index 50%
rename from Nebula.Shared/Services/RunnerService.cs
rename to Nebula.Runner/Services/RunnerService.cs
index 628bd2e..9151bb8 100644
--- a/Nebula.Shared/Services/RunnerService.cs
+++ b/Nebula.Runner/Services/RunnerService.cs
@@ -1,8 +1,14 @@
-using Nebula.Shared.Models;
+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.Shared.Services;
+namespace Nebula.Runner.Services;
[ServiceRegister]
public sealed class RunnerService(
@@ -10,9 +16,12 @@ public sealed class RunnerService(
DebugService debugService,
ConfigurationService varService,
EngineService engineService,
- AssemblyService assemblyService)
+ 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,
@@ -52,11 +61,86 @@ public sealed class RunnerService(
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;
}
}
diff --git a/Nebula.Shared/FileApis/FtpFileApi.cs b/Nebula.Shared/FileApis/FtpFileApi.cs
deleted file mode 100644
index 43d7103..0000000
--- a/Nebula.Shared/FileApis/FtpFileApi.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Net;
-using FluentFTP;
-using Nebula.Shared.FileApis.Interfaces;
-
-namespace Nebula.Shared.FileApis;
-
-public class FtpFileApi : IWriteFileApi, IDisposable
-{
- private readonly string _ftpHost;
- private readonly string _username;
- private readonly string _password;
-
- private readonly FtpClient Client;
-
- public FtpFileApi(string ftpHost, string username, string password)
- {
- _ftpHost = ftpHost;
- _username = username;
- _password = password;
- Client = CreateClient();
- Client.AutoConnect();
- }
-
- public bool Save(string path, Stream input)
- {
- try
- {
- var result = Client.UploadStream(input, path, FtpRemoteExists.Overwrite, true);
- return result == FtpStatus.Success;
- }
- catch
- {
- return false;
- }
- }
-
- public bool Remove(string path)
- {
- try
- {
- Client.DeleteFile(path);
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- public bool Has(string path)
- {
- try
- {
- return Client.FileExists(path);
- }
- catch
- {
- return false;
- }
- }
-
- private FtpClient CreateClient()
- {
- var client = new FtpClient(_ftpHost, _username, _password);
- client.Config.EncryptionMode = FtpEncryptionMode.None;
- client.Config.ValidateAnyCertificate = true;
- return client;
- }
-
- public void Dispose()
- {
- Client.Dispose();
- }
-}
\ No newline at end of file
diff --git a/Nebula.Shared/Nebula.Shared.csproj b/Nebula.Shared/Nebula.Shared.csproj
index 51c89f5..4a244a7 100644
--- a/Nebula.Shared/Nebula.Shared.csproj
+++ b/Nebula.Shared/Nebula.Shared.csproj
@@ -11,7 +11,6 @@
Utility.runtime.json
-
diff --git a/Nebula.sln.DotSettings.user b/Nebula.sln.DotSettings.user
index c725730..d5dc88b 100644
--- a/Nebula.sln.DotSettings.user
+++ b/Nebula.sln.DotSettings.user
@@ -1,5 +1,6 @@
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
@@ -20,6 +21,7 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
@@ -27,22 +29,29 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
@@ -51,4 +60,6 @@
<AssemblyExplorer>
<Assembly Path="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.2\ref\net9.0\System.Net.Http.dll" />
<Assembly Path="C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.2\System.Net.Http.dll" />
+ <Assembly Path="C:\Users\Cinka\.nuget\packages\prometheus-net\0.0.2\lib\net40\prometheus-net.dll" />
+ <Assembly Path="C:\Users\Cinka\.nuget\packages\microsoft.extensions.objectpool\7.0.0\lib\net7.0\Microsoft.Extensions.ObjectPool.dll" />
</AssemblyExplorer>
\ No newline at end of file