diff --git a/Nebula.Launcher/App.axaml.cs b/Nebula.Launcher/App.axaml.cs
index 7450309..47c203a 100644
--- a/Nebula.Launcher/App.axaml.cs
+++ b/Nebula.Launcher/App.axaml.cs
@@ -7,6 +7,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
+using Nebula.Launcher.MessageBox;
using Nebula.Launcher.ViewModels.ContentView;
using Nebula.Launcher.Views;
using Nebula.Shared;
@@ -22,6 +23,25 @@ public class App : Application
public override void OnFrameworkInitializationCompleted()
{
+ if (!Program.IsNewInstance)
+ {
+ IMessageContainerProvider? provider = null;
+ switch (ApplicationLifetime)
+ {
+ case IClassicDesktopStyleApplicationLifetime desktop:
+ DisableAvaloniaDataAnnotationValidation();
+ desktop.MainWindow = new MessageWindow(out provider);
+ break;
+ case ISingleViewApplicationLifetime singleViewPlatform:
+ singleViewPlatform.MainView = new MessageView(out provider);
+ break;
+ }
+
+ provider?.ShowMessage("Launcher is already running.","hey shithead!");
+
+ return;
+ }
+
if (Design.IsDesignMode)
{
switch (ApplicationLifetime)
diff --git a/Nebula.Launcher/MessageBox/MessageView.axaml b/Nebula.Launcher/MessageBox/MessageView.axaml
new file mode 100644
index 0000000..5c39b61
--- /dev/null
+++ b/Nebula.Launcher/MessageBox/MessageView.axaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Nebula.Launcher/MessageBox/MessageView.axaml.cs b/Nebula.Launcher/MessageBox/MessageView.axaml.cs
new file mode 100644
index 0000000..85d80ae
--- /dev/null
+++ b/Nebula.Launcher/MessageBox/MessageView.axaml.cs
@@ -0,0 +1,25 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Nebula.Launcher.MessageBox;
+
+public partial class MessageView : UserControl, IMessageContainerProvider
+{
+ public MessageView(out IMessageContainerProvider provider)
+ {
+ InitializeComponent();
+ provider = this;
+ }
+
+ public void ShowMessage(string message, string title)
+ {
+ Title.Content = title;
+ Message.Content = message;
+ }
+}
+
+public interface IMessageContainerProvider
+{
+ public void ShowMessage(string message, string title);
+}
\ No newline at end of file
diff --git a/Nebula.Launcher/MessageBox/MessageWindow.axaml b/Nebula.Launcher/MessageBox/MessageWindow.axaml
new file mode 100644
index 0000000..581b1f1
--- /dev/null
+++ b/Nebula.Launcher/MessageBox/MessageWindow.axaml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs b/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs
new file mode 100644
index 0000000..d7c2b3b
--- /dev/null
+++ b/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace Nebula.Launcher.MessageBox;
+
+public partial class MessageWindow : Window
+{
+ public MessageWindow(out IMessageContainerProvider provider)
+ {
+ InitializeComponent();
+ Content = new MessageView(out provider);
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Launcher/Program.cs b/Nebula.Launcher/Program.cs
index db6eff8..92ebd9e 100644
--- a/Nebula.Launcher/Program.cs
+++ b/Nebula.Launcher/Program.cs
@@ -1,13 +1,22 @@
-using Avalonia;
+using System;
+using System.Threading;
+using Avalonia;
namespace Nebula.Launcher;
public static class Program
{
+ private static Mutex? _mutex;
+ public static bool IsNewInstance;
+ [STAThread]
public static void Main(string[] args)
{
+ _mutex = new Mutex(true, $"Global\\Nebula.Launcher", out IsNewInstance);
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
+
+ if (IsNewInstance)
+ _mutex.ReleaseMutex();
}
// Avalonia configuration, don't remove; also used by visual designer.
@@ -18,4 +27,5 @@ public static class Program
.WithInterFont()
.LogToTrace();
}
-}
\ No newline at end of file
+}
+
diff --git a/Nebula.Launcher/Services/DecompilerService.cs b/Nebula.Launcher/Services/DecompilerService.cs
index b921eac..c6ea2d2 100644
--- a/Nebula.Launcher/Services/DecompilerService.cs
+++ b/Nebula.Launcher/Services/DecompilerService.cs
@@ -13,6 +13,7 @@ using Nebula.Shared.FileApis;
using Nebula.Shared.FileApis.Interfaces;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
+using Nebula.Shared.Services.Logging;
namespace Nebula.Launcher.Services;
@@ -25,10 +26,11 @@ public sealed partial class DecompilerService
[GenerateProperty] private ContentService ContentService {get;}
[GenerateProperty] private FileService FileService {get;}
[GenerateProperty] private CancellationService CancellationService {get;}
- [GenerateProperty] private EngineService engineService {get;}
- [GenerateProperty] private DebugService debugService {get;}
+ [GenerateProperty] private EngineService EngineService {get;}
+ [GenerateProperty] private DebugService DebugService {get;}
private HttpClient _httpClient = new HttpClient();
+ private ILogger _logger;
private static string fullPath = Path.Join(FileService.RootPath,"ILSpy");
private static string executePath = Path.Join(fullPath, "ILSpy.exe");
@@ -50,7 +52,7 @@ public sealed partial class DecompilerService
var buildInfo =
await ContentService.GetBuildInfo(url, CancellationService.Token);
- var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
+ var engine = await EngineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
if (engine is null)
throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion);
@@ -73,12 +75,15 @@ public sealed partial class DecompilerService
((IDisposable)loadingHandler).Dispose();
- debugService.Log("File extracted. " + tmpDir);
+ _logger.Log("File extracted. " + tmpDir);
OpenDecompiler(string.Join(' ', myTempDir.AllFiles.Select(f=>Path.Join(tmpDir, f))) + " --newinstance");
}
- private void Initialise(){}
+ private void Initialise()
+ {
+ _logger = DebugService.GetLogger(this);
+ }
private void InitialiseInDesignMode(){}
private async Task EnsureILSpy(){
diff --git a/Nebula.Launcher/ViewModels/MainViewModel.cs b/Nebula.Launcher/ViewModels/MainViewModel.cs
index 4c907b8..3c9d6c2 100644
--- a/Nebula.Launcher/ViewModels/MainViewModel.cs
+++ b/Nebula.Launcher/ViewModels/MainViewModel.cs
@@ -10,6 +10,7 @@ using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
@@ -43,6 +44,8 @@ public partial class MainViewModel : ViewModelBase
[GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!;
[GenerateProperty] private FileService FileService { get; } = default!;
+ private ILogger _logger;
+
public ObservableCollection Items { get; private set; }
protected override void InitialiseInDesignMode()
@@ -53,6 +56,7 @@ public partial class MainViewModel : ViewModelBase
protected override void Initialise()
{
+ _logger = DebugService.GetLogger(this);
InitialiseInDesignMode();
PopupMessageService.OnPopupRequired += OnPopupRequired;
@@ -160,7 +164,7 @@ public partial class MainViewModel : ViewModelBase
break;
case Exception error:
var err = ViewHelperService.GetViewModel();
- DebugService.Error(error);
+ _logger.Error(error);
err.AppendError(error);
PopupMessage(err);
break;
diff --git a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
index ec6f532..62007c7 100644
--- a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
+++ b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs
@@ -15,6 +15,7 @@ using Nebula.Launcher.Views.Pages;
using Nebula.Shared.FileApis;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels.Pages;
@@ -31,6 +32,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
[ObservableProperty] private string _searchText = "";
private ContentEntry? _selectedEntry;
+ private ILogger _logger;
[ObservableProperty] private string _serverText = "";
[ObservableProperty] private ContentViewBase? _contentView;
public bool IsCustomContenView => ContentView != null;
@@ -69,7 +71,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie" + ext);
if(TryGetContentViewer(ext, out var contentViewBase)){
- DebugService.Debug($"Opening custom context:{item.Value.Path}");
+ _logger.Debug($"Opening custom context:{item.Value.Path}");
contentViewBase.InitialiseWithData(value.GetPath(), stream, value);
stream.Dispose();
ContentView = contentViewBase;
@@ -88,7 +90,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
};
- DebugService.Log("Opening " + myTempFile);
+ _logger.Log("Opening " + myTempFile);
Process.Start(startInfo);
return;
@@ -122,6 +124,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
protected override void Initialise()
{
+ _logger = DebugService.GetLogger(this);
FillRoot(HubService.ServerList);
HubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
@@ -173,7 +176,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
if (ServerText != SelectedEntry?.ServerName) SelectedEntry = await CreateEntry(ServerText);
- DebugService.Debug("Going to:" + path.Path);
+ _logger.Debug("Going to:" + path.Path);
var oriPath = path.Clone();
try
@@ -214,7 +217,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
FileName = "explorer.exe",
Arguments = tmpDir,
};
- DebugService.Log("Opening " + tmpDir);
+ _logger.Log("Opening " + tmpDir);
Process.Start(startInfo);
}
diff --git a/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs b/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs
index ec05790..e1824e7 100644
--- a/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs
+++ b/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs
@@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewModels.Pages;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared.Services;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
using AddFavoriteView = Nebula.Launcher.Views.Popup.AddFavoriteView;
@@ -12,12 +13,15 @@ namespace Nebula.Launcher.ViewModels.Popup;
[ConstructGenerator]
public partial class AddFavoriteViewModel : PopupViewModelBase
{
+ private ILogger _logger;
+
protected override void InitialiseInDesignMode()
{
}
protected override void Initialise()
{
+ _logger = DebugService.GetLogger(this);
}
[GenerateProperty]
@@ -41,7 +45,7 @@ public partial class AddFavoriteViewModel : PopupViewModelBase
catch (Exception e)
{
Error = e.Message;
- DebugService.Error(e);
+ _logger.Error(e);
}
}
}
\ No newline at end of file
diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
index 3ab1a2e..f654f65 100644
--- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
+++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
@@ -16,6 +16,7 @@ using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
@@ -31,6 +32,8 @@ public partial class ServerEntryModelView : ViewModelBase
private string _lastError = "";
private Process? _p;
+ private ILogger _logger;
+ private ILogger? _processLogger;
private ServerInfo? _serverInfo;
[ObservableProperty] private bool _tagDataVisible;
@@ -89,7 +92,7 @@ public partial class ServerEntryModelView : ViewModelBase
catch (Exception e)
{
Description = e.Message;
- DebugService.Error(e);
+ _logger.Error(e);
}
return _serverInfo;
@@ -109,6 +112,7 @@ public partial class ServerEntryModelView : ViewModelBase
protected override void Initialise()
{
+ _logger = DebugService.GetLogger(this);
CurrLog = ViewHelperService.GetViewModel();
}
@@ -144,7 +148,7 @@ public partial class ServerEntryModelView : ViewModelBase
}
catch (Exception e)
{
- DebugService.Error(e);
+ _logger.Error(e);
Status = new ServerStatus("ErrorLand", $"ERROR: {e.Message}", [], "", -1, -1, -1, false,
DateTime.Now,
-1);
@@ -211,6 +215,8 @@ public partial class ServerEntryModelView : ViewModelBase
}
if (Process is null) return;
+
+ _processLogger = DebugService.GetLogger(buildInfo.BuildInfo.Build.Hash);
Process.EnableRaisingEvents = true;
@@ -240,11 +246,13 @@ public partial class ServerEntryModelView : ViewModelBase
Process.ErrorDataReceived -= OnErrorDataReceived;
Process.Exited -= OnExited;
- DebugService.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
+ _processLogger?.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
if (Process.ExitCode != 0)
PopupMessageService.Popup($"Game exit with code {Process.ExitCode}.\nReason: {_lastError}");
+ _processLogger?.Dispose();
+
Process.Dispose();
Process = null;
}
@@ -254,7 +262,7 @@ public partial class ServerEntryModelView : ViewModelBase
if (e.Data != null)
{
_lastError = e.Data;
- DebugService.Error(e.Data);
+ _processLogger?.Error(e.Data);
CurrLog.Append(e.Data);
}
}
@@ -263,7 +271,7 @@ public partial class ServerEntryModelView : ViewModelBase
{
if (e.Data != null)
{
- DebugService.Log(e.Data);
+ _processLogger?.Log(e.Data);
CurrLog.Append(e.Data);
}
}
diff --git a/Nebula.Shared/Services/AssemblyService.cs b/Nebula.Shared/Services/AssemblyService.cs
index 4c77946..db534e9 100644
--- a/Nebula.Shared/Services/AssemblyService.cs
+++ b/Nebula.Shared/Services/AssemblyService.cs
@@ -3,6 +3,7 @@ using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Nebula.Shared.FileApis;
+using Nebula.Shared.Services.Logging;
using Robust.LoaderApi;
using SharpZstd.Interop;
@@ -12,19 +13,19 @@ namespace Nebula.Shared.Services;
public class AssemblyService
{
private readonly List _assemblies = new();
- private readonly DebugService _debugService;
+ private readonly ILogger _logger;
private readonly HashSet _resolvingAssemblies = new();
public AssemblyService(DebugService debugService)
{
- _debugService = debugService;
-
+ _logger = debugService.GetLogger(this);
+
ZstdImportResolver.ResolveLibrary += (name, assembly1, path) =>
{
if (name.Equals("SharpZstd.Native"))
{
- _debugService.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path);
+ _logger.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path);
GetRuntimeInfo(out var platform, out var architecture, out var extension);
var fileName = GetDllName(platform, architecture, extension);
@@ -77,7 +78,7 @@ public class AssemblyService
}
assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb);
- _debugService.Log("LOADED ASSEMBLY " + name);
+ _logger.Log("LOADED ASSEMBLY " + name);
if (!_assemblies.Contains(assembly)) _assemblies.Add(assembly);
@@ -104,14 +105,14 @@ public class AssemblyService
{
if (_resolvingAssemblies.Contains(name.FullName))
{
- _debugService.Debug($"Already resolving {name.Name}, skipping.");
+ _logger.Debug($"Already resolving {name.Name}, skipping.");
return null; // Prevent recursive resolution
}
try
{
_resolvingAssemblies.Add(name.FullName);
- _debugService.Debug($"Resolving assembly from FileAPI: {name.Name}");
+ _logger.Debug($"Resolving assembly from FileAPI: {name.Name}");
return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null;
}
finally
@@ -125,12 +126,12 @@ public class AssemblyService
var ourDir = Path.GetDirectoryName(typeof(AssemblyApi).Assembly.Location);
var a = Path.Combine(ourDir!, unmanaged);
- _debugService.Debug($"Loading dll lib: {a}");
+ _logger.Debug($"Loading dll lib: {a}");
if (NativeLibrary.TryLoad(a, out var handle))
return handle;
- _debugService.Error("Loading dll error! Not found");
+ _logger.Error("Loading dll error! Not found");
return IntPtr.Zero;
}
diff --git a/Nebula.Shared/Services/AuthService.cs b/Nebula.Shared/Services/AuthService.cs
index d6e2b7b..fb2b012 100644
--- a/Nebula.Shared/Services/AuthService.cs
+++ b/Nebula.Shared/Services/AuthService.cs
@@ -3,6 +3,7 @@ using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nebula.Shared.Models.Auth;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Shared.Services;
@@ -15,6 +16,7 @@ public class AuthService(
{
private readonly HttpClient _httpClient = new();
public CurrentAuthInfo? SelectedAuth { get; private set; }
+ private readonly ILogger _logger = debugService.GetLogger("AuthService");
public async Task Auth(AuthLoginPassword authLoginPassword, string? code = null)
{
@@ -22,7 +24,7 @@ public class AuthService(
var login = authLoginPassword.Login;
var password = authLoginPassword.Password;
- debugService.Debug($"Auth to {authServer}api/auth/authenticate {login}");
+ _logger.Debug($"Auth to {authServer}api/auth/authenticate {login}");
var authUrl = new Uri($"{authServer}api/auth/authenticate");
diff --git a/Nebula.Shared/Services/ConfigurationService.cs b/Nebula.Shared/Services/ConfigurationService.cs
index 6bd4db6..7bbd5c8 100644
--- a/Nebula.Shared/Services/ConfigurationService.cs
+++ b/Nebula.Shared/Services/ConfigurationService.cs
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Nebula.Shared.FileApis.Interfaces;
+using Nebula.Shared.Services.Logging;
using Robust.LoaderApi;
namespace Nebula.Shared.Services;
@@ -32,14 +33,12 @@ public static class ConVarBuilder
[ServiceRegister]
public class ConfigurationService
{
- private readonly DebugService _debugService;
-
public IReadWriteFileApi ConfigurationApi { get; init; }
+ private readonly ILogger _logger;
public ConfigurationService(FileService fileService, DebugService debugService)
{
- _debugService = debugService ?? throw new ArgumentNullException(nameof(debugService));
-
+ _logger = debugService.GetLogger(this);
ConfigurationApi = fileService.CreateFileApi("config");
}
@@ -56,17 +55,17 @@ public class ConfigurationService
var obj = JsonSerializer.Deserialize(stream);
if (obj != null)
{
- _debugService.Log($"Successfully loaded config: {conVar.Name}");
+ _logger.Log($"Successfully loaded config: {conVar.Name}");
return obj;
}
}
}
catch (Exception e)
{
- _debugService.Error($"Error loading config for {conVar.Name}: {e.Message}");
+ _logger.Error($"Error loading config for {conVar.Name}: {e.Message}");
}
- _debugService.Log($"Using default value for config: {conVar.Name}");
+ _logger.Log($"Using default value for config: {conVar.Name}");
return conVar.DefaultValue;
}
@@ -83,7 +82,7 @@ public class ConfigurationService
var obj = JsonSerializer.Deserialize(stream);
if (obj != null)
{
- _debugService.Log($"Successfully loaded config: {conVar.Name}");
+ _logger.Log($"Successfully loaded config: {conVar.Name}");
value = obj;
return true;
}
@@ -91,10 +90,10 @@ public class ConfigurationService
}
catch (Exception e)
{
- _debugService.Error($"Error loading config for {conVar.Name}: {e.Message}");
+ _logger.Error($"Error loading config for {conVar.Name}: {e.Message}");
}
- _debugService.Log($"Using default value for config: {conVar.Name}");
+ _logger.Log($"Using default value for config: {conVar.Name}");
return false;
}
@@ -105,14 +104,14 @@ public class ConfigurationService
if (!conVar.Type.IsInstanceOfType(value))
{
- _debugService.Error(
+ _logger.Error(
$"Type mismatch for config {conVar.Name}. Expected {conVar.Type}, got {value.GetType()}.");
return;
}
try
{
- _debugService.Log($"Saving config: {conVar.Name}");
+ _logger.Log($"Saving config: {conVar.Name}");
var serializedData = JsonSerializer.Serialize(value);
using var stream = new MemoryStream();
@@ -125,7 +124,7 @@ public class ConfigurationService
}
catch (Exception e)
{
- _debugService.Error($"Error saving config for {conVar.Name}: {e.Message}");
+ _logger.Error($"Error saving config for {conVar.Name}: {e.Message}");
}
}
diff --git a/Nebula.Shared/Services/ContentService.Download.cs b/Nebula.Shared/Services/ContentService.Download.cs
index 9d32d2a..e2fabb3 100644
--- a/Nebula.Shared/Services/ContentService.Download.cs
+++ b/Nebula.Shared/Services/ContentService.Download.cs
@@ -38,7 +38,7 @@ public partial class ContentService
items = allItems.Where(a=> !hashApi.Has(a)).ToList();
- debugService.Log("Download Count:" + items.Count);
+ _logger.Log("Download Count:" + items.Count);
await Download(downloadUri, items, hashApi, loadingHandler, cancellationToken);
return hashApi;
@@ -47,15 +47,15 @@ public partial class ContentService
public async Task EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
- debugService.Log("Getting manifest: " + info.Hash);
+ _logger.Log("Getting manifest: " + info.Hash);
if (ManifestFileApi.TryOpen(info.Hash, out var stream))
{
- debugService.Log("Loading manifest from: " + info.Hash);
+ _logger.Log("Loading manifest from: " + info.Hash);
return await EnsureItems(new ManifestReader(stream), info.DownloadUri, loadingHandler, cancellationToken);
}
- debugService.Log("Fetching manifest from: " + info.ManifestUri);
+ _logger.Log("Fetching manifest from: " + info.ManifestUri);
var response = await _http.GetAsync(info.ManifestUri, cancellationToken);
if (!response.IsSuccessStatusCode) throw new Exception();
@@ -69,7 +69,7 @@ public partial class ContentService
public void Unpack(HashApi hashApi, IWriteFileApi otherApi, ILoadingHandler loadingHandler)
{
- debugService.Log("Unpack manifest files");
+ _logger.Log("Unpack manifest files");
var items = hashApi.Manifest.Values.ToList();
loadingHandler.AppendJob(items.Count);
@@ -82,13 +82,13 @@ public partial class ContentService
{
if (hashApi.TryOpen(item, out var stream))
{
- debugService.Log($"Unpack {item.Hash} to: {item.Path}");
+ _logger.Log($"Unpack {item.Hash} to: {item.Path}");
otherApi.Save(item.Hash, stream);
stream.Close();
}
else
{
- debugService.Error("OH FUCK!! " + item.Path);
+ _logger.Error("OH FUCK!! " + item.Path);
}
loadingHandler.AppendResolvedJob();
@@ -105,13 +105,13 @@ public partial class ContentService
{
if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
{
- debugService.Log("Nothing to download! Fuck this!");
+ _logger.Log("Nothing to download! Fuck this!");
return;
}
var downloadJobWatch = loadingHandler.GetQueryJob();
- debugService.Log("Downloading from: " + contentCdn);
+ _logger.Log("Downloading from: " + contentCdn);
var requestBody = new byte[toDownload.Count * 4];
var reqI = 0;
@@ -135,7 +135,7 @@ public partial class ContentService
if (cancellationToken.IsCancellationRequested)
{
- debugService.Log("Downloading is cancelled!");
+ _logger.Log("Downloading is cancelled!");
return;
}
@@ -185,7 +185,7 @@ public partial class ContentService
{
if (cancellationToken.IsCancellationRequested)
{
- debugService.Log("Downloading is cancelled!");
+ _logger.Log("Downloading is cancelled!");
decompressContext?.Dispose();
compressContext?.Dispose();
return;
@@ -230,7 +230,7 @@ public partial class ContentService
using var fileStream = new MemoryStream(data.ToArray());
hashApi.Save(item, fileStream);
- debugService.Log("file saved:" + item.Path);
+ _logger.Log("file saved:" + item.Path);
loadingHandler.AppendResolvedJob();
i += 1;
}
diff --git a/Nebula.Shared/Services/ContentService.Migration.cs b/Nebula.Shared/Services/ContentService.Migration.cs
index a20547a..b62013c 100644
--- a/Nebula.Shared/Services/ContentService.Migration.cs
+++ b/Nebula.Shared/Services/ContentService.Migration.cs
@@ -7,12 +7,12 @@ public partial class ContentService
{
public bool CheckMigration(ILoadingHandler loadingHandler)
{
- debugService.Log("Checking migration...");
+ _logger.Log("Checking migration...");
var migrationList = ContentFileApi.AllFiles.Where(f => !f.Contains("\\")).ToList();
if(migrationList.Count == 0) return false;
- debugService.Log($"Found {migrationList.Count} migration files. Starting migration...");
+ _logger.Log($"Found {migrationList.Count} migration files. Starting migration...");
Task.Run(() => DoMigration(loadingHandler, migrationList));
return true;
}
diff --git a/Nebula.Shared/Services/ContentService.cs b/Nebula.Shared/Services/ContentService.cs
index b5223af..b4a6447 100644
--- a/Nebula.Shared/Services/ContentService.cs
+++ b/Nebula.Shared/Services/ContentService.cs
@@ -1,5 +1,6 @@
using System.Data;
using Nebula.Shared.Models;
+using Nebula.Shared.Services.Logging;
namespace Nebula.Shared.Services;
@@ -11,6 +12,7 @@ public partial class ContentService(
FileService fileService)
{
private readonly HttpClient _http = new();
+ private readonly ILogger _logger = debugService.GetLogger("ContentService");
public async Task GetBuildInfo(RobustUrl url, CancellationToken cancellationToken)
{
diff --git a/Nebula.Shared/Services/DebugService.cs b/Nebula.Shared/Services/DebugService.cs
index 742dfa3..00a7186 100644
--- a/Nebula.Shared/Services/DebugService.cs
+++ b/Nebula.Shared/Services/DebugService.cs
@@ -1,47 +1,33 @@
+using System.Reflection;
+using Nebula.Shared.FileApis;
using Nebula.Shared.Services.Logging;
+using Robust.LoaderApi;
namespace Nebula.Shared.Services;
[ServiceRegister]
public class DebugService : IDisposable
{
- public ILogger Logger;
-
- public DebugService(ILogger logger)
+ private ServiceLogger Root {get; set;}
+
+ public DebugService()
{
- Logger = logger;
+ Root = new ServiceLogger("Root");
+ }
+
+ public ILogger GetLogger(string loggerName)
+ {
+ return Root.GetLogger(loggerName);
+ }
+
+ public ILogger GetLogger(object objectToLog)
+ {
+ return Root.GetLogger(objectToLog.GetType().Name);
}
public void Dispose()
{
-
- }
-
- public void Debug(string message)
- {
- Log(LoggerCategory.Debug, message);
- }
-
- public void Error(string message)
- {
- Log(LoggerCategory.Error, message);
- }
-
- public void Log(string message)
- {
- Log(LoggerCategory.Log, message);
- }
-
- public void Error(Exception e)
- {
- Error(e.Message + "\r\n" + e.StackTrace);
- if (e.InnerException != null)
- Error(e.InnerException);
- }
-
- private void Log(LoggerCategory category, string message)
- {
- Logger.Log(category, message);
+ Root.Dispose();
}
}
@@ -50,4 +36,89 @@ public enum LoggerCategory
Log,
Debug,
Error
+}
+
+internal class ServiceLogger : ILogger
+{
+ public ServiceLogger? Root { get; private set; }
+ public ServiceLogger(string category)
+ {
+ Category = category;
+
+ var directory = Path.Combine(FileService.RootPath,"log", Assembly.GetEntryAssembly()?.GetName().Name ?? "App");
+ if(!Directory.Exists(directory)) Directory.CreateDirectory(directory);
+
+ _fileStream = File.Open(Path.Combine(directory,$"{Category}.log"), FileMode.Create, FileAccess.Write, FileShare.Read);
+ _streamWriter = new StreamWriter(_fileStream);
+ }
+
+ public string Category { get; init; }
+
+ private Dictionary Childs { get; init; } = new();
+
+ private readonly FileStream _fileStream;
+ private readonly StreamWriter _streamWriter;
+
+ public ServiceLogger GetLogger(string category)
+ {
+ if (Childs.TryGetValue(category, out var logger))
+ return logger;
+
+ logger = new ServiceLogger(category);
+ logger.Root = this;
+ Childs.Add(category, logger);
+ return logger;
+ }
+
+ public void Log(LoggerCategory loggerCategory, string message)
+ {
+ var output =
+ $"[{DateTime.Now.ToUniversalTime():yyyy-MM-dd HH:mm:ss}][{Enum.GetName(loggerCategory)}][{Category}]: {message}";
+ Console.WriteLine(output);
+
+ LogToFile(output);
+ }
+
+ private void LogToFile(string output)
+ {
+ Root?.LogToFile(output);
+ _streamWriter.WriteLine(output);
+ _streamWriter.Flush();
+ }
+
+ public void Dispose()
+ {
+ _fileStream.Dispose();
+ _streamWriter.Dispose();
+ foreach (var (_, child) in Childs)
+ {
+ child.Dispose();
+ }
+ Childs.Clear();
+ }
+}
+
+public static class LoggerExtensions
+{
+ public static void Debug(this ILogger logger,string message)
+ {
+ logger.Log(LoggerCategory.Debug, message);
+ }
+
+ public static void Error(this ILogger logger,string message)
+ {
+ logger.Log(LoggerCategory.Error, message);
+ }
+
+ public static void Log(this ILogger logger,string message)
+ {
+ logger.Log(LoggerCategory.Log, message);
+ }
+
+ public static void Error(this ILogger logger,Exception e)
+ {
+ Error(logger,e.Message + "\r\n" + e.StackTrace);
+ if (e.InnerException != null)
+ Error(logger, e.InnerException);
+ }
}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/EngineService.cs b/Nebula.Shared/Services/EngineService.cs
index f2622c2..8c1b61a 100644
--- a/Nebula.Shared/Services/EngineService.cs
+++ b/Nebula.Shared/Services/EngineService.cs
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using Nebula.Shared.FileApis;
using Nebula.Shared.FileApis.Interfaces;
using Nebula.Shared.Models;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Shared.Services;
@@ -10,34 +11,46 @@ namespace Nebula.Shared.Services;
public sealed class EngineService
{
private readonly AssemblyService _assemblyService;
- private readonly DebugService _debugService;
private readonly FileService _fileService;
private readonly RestService _restService;
private readonly ConfigurationService _varService;
private readonly Task _currInfoTask;
private readonly IReadWriteFileApi _engineFileApi;
+ private readonly ILogger _logger;
- private ModulesInfo _modulesInfo = default!;
- private Dictionary _versionsInfo = default!;
+ private ModulesInfo? _modulesInfo;
+ private Dictionary? _versionsInfo;
public EngineService(RestService restService, DebugService debugService, ConfigurationService varService,
FileService fileService, AssemblyService assemblyService)
{
_restService = restService;
- _debugService = debugService;
+ _logger = debugService.GetLogger(this);
_varService = varService;
_fileService = fileService;
_assemblyService = assemblyService;
_engineFileApi = fileService.CreateFileApi("engine");
-
_currInfoTask = Task.Run(() => LoadEngineManifest(CancellationToken.None));
}
+ public void GetEngineInfo(out ModulesInfo modulesInfo, out Dictionary versionsInfo)
+ {
+ if(!_currInfoTask.IsCompleted) _currInfoTask.Wait();
+ if(_currInfoTask.Exception != null) throw new Exception("Error while loading engine manifest:",_currInfoTask.Exception);
+
+ if(_modulesInfo == null || _versionsInfo == null) throw new NullReferenceException("Engine manifest is null");
+
+ modulesInfo = _modulesInfo;
+ versionsInfo = _versionsInfo;
+ }
+
public async Task LoadEngineManifest(CancellationToken cancellationToken)
{
+ _logger.Log("start fetching engine manifest");
_versionsInfo = await LoadExacManifest(CurrentConVar.EngineManifestUrl, CurrentConVar.EngineManifestBackup, cancellationToken);
_modulesInfo = await LoadExacManifest(CurrentConVar.EngineModuleManifestUrl, CurrentConVar.ModuleManifestBackup, cancellationToken);
+ _logger.Log("fetched engine manifest successfully");
}
private async Task LoadExacManifest(ConVar conVar,ConVar backup,CancellationToken cancellationToken)
@@ -48,7 +61,7 @@ public sealed class EngineService
{
try
{
- _debugService.Log("Fetching engine manifest from: " + manifestUrl);
+ _logger.Log("Fetching engine manifest from: " + manifestUrl);
var info = await _restService.GetAsync(
new Uri(manifestUrl), cancellationToken);
@@ -57,11 +70,11 @@ public sealed class EngineService
}
catch (Exception e)
{
- _debugService.Error($"error while attempt fetch engine manifest: {e.Message}");
+ _logger.Error($"error while attempt fetch engine manifest: {e.Message}");
}
}
- _debugService.Debug("Trying fallback module manifest...");
+ _logger.Debug("Trying fallback module manifest...");
if (!_varService.TryGetConfigValue(backup, out var moduleInfo))
{
throw new Exception("No module info data available");
@@ -72,9 +85,9 @@ public sealed class EngineService
public EngineBuildInfo? GetVersionInfo(string version)
{
- CheckAndWaitValidation();
+ GetEngineInfo(out var modulesInfo, out var engineVersionInfo);
- if (!_versionsInfo.TryGetValue(version, out var foundVersion))
+ if (!engineVersionInfo.TryGetValue(version, out var foundVersion))
return null;
if (foundVersion.RedirectVersion != null)
@@ -83,7 +96,7 @@ public sealed class EngineService
var bestRid = RidUtility.FindBestRid(foundVersion.Platforms.Keys);
if (bestRid == null) bestRid = "linux-x64";
- _debugService.Log("Selecting RID" + bestRid);
+ _logger.Log("Selecting RID" + bestRid);
return foundVersion.Platforms[bestRid];
}
@@ -96,7 +109,7 @@ public sealed class EngineService
public async Task EnsureEngine(string version)
{
- _debugService.Log("Ensure engine " + version);
+ _logger.Log("Ensure engine " + version);
if (!TryOpen(version)) await DownloadEngine(version);
@@ -119,7 +132,7 @@ public sealed class EngineService
if (!TryGetVersionInfo(version, out var info))
return;
- _debugService.Log("Downloading engine version " + version);
+ _logger.Log("Downloading engine version " + version);
using var client = new HttpClient();
var s = await client.GetStreamAsync(info.Url);
_engineFileApi.Save(version, s);
@@ -140,9 +153,9 @@ public sealed class EngineService
public EngineBuildInfo? GetModuleBuildInfo(string moduleName, string version)
{
- CheckAndWaitValidation();
+ GetEngineInfo(out var modulesInfo, out var engineVersionInfo);
- if (!_modulesInfo.Modules.TryGetValue(moduleName, out var module) ||
+ if (!modulesInfo.Modules.TryGetValue(moduleName, out var module) ||
!module.Versions.TryGetValue(version, out var value))
return null;
@@ -160,10 +173,10 @@ public sealed class EngineService
public string ResolveModuleVersion(string moduleName, string engineVersion)
{
- CheckAndWaitValidation();
+ GetEngineInfo(out var modulesInfo, out var engineVersionInfo);
var engineVersionObj = Version.Parse(engineVersion);
- var module = _modulesInfo.Modules[moduleName];
+ var module = modulesInfo.Modules[moduleName];
var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv })
.Where(kv => engineVersionObj >= kv.Version)
.MaxBy(kv => kv.Version);
@@ -185,7 +198,9 @@ public sealed class EngineService
try
{
- return _assemblyService.Mount(_fileService.OpenZip(fileName, _engineFileApi) ?? throw new InvalidOperationException($"{fileName} is not exist!"));
+ return _assemblyService.Mount(
+ _fileService.OpenZip(fileName, _engineFileApi) ??
+ throw new InvalidOperationException($"{fileName} is not exist!"));
}
catch (Exception)
{
@@ -199,7 +214,7 @@ public sealed class EngineService
if (!TryGetModuleBuildInfo(moduleName, moduleVersion, out var info))
return;
- _debugService.Log("Downloading engine module version " + moduleVersion);
+ _logger.Log("Downloading engine module version " + moduleVersion);
using var client = new HttpClient();
var s = await client.GetStreamAsync(info.Url);
_engineFileApi.Save(ConcatName(moduleName, moduleVersion), s);
@@ -210,13 +225,4 @@ public sealed class EngineService
{
return moduleName + "" + moduleVersion;
}
-
- private void CheckAndWaitValidation()
- {
- if (_currInfoTask.IsCompleted)
- return;
-
- _debugService.Debug("thinks is not done yet, please wait");
- _currInfoTask.Wait();
- }
}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/HubService.cs b/Nebula.Shared/Services/HubService.cs
index 63c756b..25c3ce1 100644
--- a/Nebula.Shared/Services/HubService.cs
+++ b/Nebula.Shared/Services/HubService.cs
@@ -1,4 +1,5 @@
using Nebula.Shared.Models;
+using Nebula.Shared.Services.Logging;
namespace Nebula.Shared.Services;
@@ -7,7 +8,7 @@ public class HubService
{
private readonly ConfigurationService _configurationService;
private readonly RestService _restService;
- private readonly DebugService _debugService;
+ private readonly ILogger _logger;
private readonly List _serverList = new();
@@ -19,7 +20,7 @@ public class HubService
{
_configurationService = configurationService;
_restService = restService;
- _debugService = debugService;
+ _logger = debugService.GetLogger(this);
UpdateHub();
}
@@ -54,8 +55,8 @@ public class HubService
}
catch (Exception e)
{
- _debugService.Error($"Failed to get servers for {uri}");
- _debugService.Error(e);
+ _logger.Error($"Failed to get servers for {uri}");
+ _logger.Error(e);
exception = e;
}
}
diff --git a/Nebula.Shared/Services/Logging/ConsoleLogger.cs b/Nebula.Shared/Services/Logging/ConsoleLogger.cs
deleted file mode 100644
index 7a35728..0000000
--- a/Nebula.Shared/Services/Logging/ConsoleLogger.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Nebula.Shared.Services.Logging;
-
-[ServiceRegister(typeof(ILogger))]
-public class ConsoleLogger : ILogger
-{
- public void Log(LoggerCategory loggerCategory, string message)
- {
- Console.ForegroundColor = ConsoleColor.DarkCyan;
- Console.Write($"[{Enum.GetName(loggerCategory)}] ");
- Console.ResetColor();
- Console.WriteLine(message);
- }
-}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/Logging/ILogger.cs b/Nebula.Shared/Services/Logging/ILogger.cs
index 51bb543..27d7e22 100644
--- a/Nebula.Shared/Services/Logging/ILogger.cs
+++ b/Nebula.Shared/Services/Logging/ILogger.cs
@@ -1,6 +1,6 @@
namespace Nebula.Shared.Services.Logging;
-public interface ILogger
+public interface ILogger : IDisposable
{
public void Log(LoggerCategory loggerCategory, string message);
}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/RestService.cs b/Nebula.Shared/Services/RestService.cs
index 24bccdb..5cc2c25 100644
--- a/Nebula.Shared/Services/RestService.cs
+++ b/Nebula.Shared/Services/RestService.cs
@@ -2,6 +2,7 @@ using System.Globalization;
using System.Net;
using System.Text;
using System.Text.Json;
+using Nebula.Shared.Services.Logging;
using Nebula.Shared.Utils;
namespace Nebula.Shared.Services;
@@ -10,7 +11,7 @@ namespace Nebula.Shared.Services;
public class RestService
{
private readonly HttpClient _client = new();
- private readonly DebugService _debug;
+ private readonly ILogger _logger;
private readonly JsonSerializerOptions _serializerOptions = new()
{
@@ -20,7 +21,7 @@ public class RestService
public RestService(DebugService debug)
{
- _debug = debug;
+ _logger = debug.GetLogger(this);
}
public async Task GetAsync(Uri uri, CancellationToken cancellationToken) where T : notnull
@@ -37,7 +38,7 @@ public class RestService
}
catch (Exception e)
{
- _debug.Error(e);
+ _logger.Error(e);
return defaultValue;
}
}
diff --git a/Nebula.Shared/Services/RunnerService.cs b/Nebula.Shared/Services/RunnerService.cs
index 9f8465e..90c1343 100644
--- a/Nebula.Shared/Services/RunnerService.cs
+++ b/Nebula.Shared/Services/RunnerService.cs
@@ -1,4 +1,5 @@
using Nebula.Shared.Models;
+using Nebula.Shared.Services.Logging;
using Robust.LoaderApi;
namespace Nebula.Shared.Services;
@@ -11,10 +12,12 @@ public sealed class RunnerService(
EngineService engineService,
AssemblyService assemblyService)
{
+ private ILogger _logger = debugService.GetLogger("RunnerService");
+
public async Task PrepareRun(RobustBuildInfo buildInfo, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
- debugService.Log("Prepare Content!");
+ _logger.Log("Prepare Content!");
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
@@ -29,7 +32,7 @@ public sealed class RunnerService(
ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
- debugService.Log("Start Content!");
+ _logger.Log("Start Content!");
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
diff --git a/Nebula.sln.DotSettings.user b/Nebula.sln.DotSettings.user
index 244caef..ab59f99 100644
--- a/Nebula.sln.DotSettings.user
+++ b/Nebula.sln.DotSettings.user
@@ -1,4 +1,5 @@
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded