- rework: Logging logic and file logging

This commit is contained in:
2025-05-05 20:43:28 +03:00
parent cd6b4d3ad4
commit 5c53976cfe
25 changed files with 332 additions and 138 deletions

View File

@@ -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)

View File

@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Width="600"
Height="400"
x:Class="Nebula.Launcher.MessageBox.MessageView">
<Grid RowDefinitions="50,*" ColumnDefinitions="*">
<Border Grid.Column="0" Background="#222222" Padding="10" BorderBrush="#444444" BorderThickness="0,0,0,3">
<Label VerticalAlignment="Center" x:Name="Title">Text</Label>
</Border>
<Panel Margin="5" Grid.Row="1">
<Label x:Name="Message">Message</Label>
</Panel>
</Grid>
</UserControl>

View File

@@ -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);
}

View File

@@ -0,0 +1,12 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:messageBox="clr-namespace:Nebula.Launcher.MessageBox"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Width="600"
Height="400"
x:Class="Nebula.Launcher.MessageBox.MessageWindow"
Title="MessageWindow">
</Window>

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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(){

View File

@@ -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<ListItemTemplate> 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<ExceptionListViewModel>();
DebugService.Error(error);
_logger.Error(error);
err.AppendError(error);
PopupMessage(err);
break;

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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<LogPopupModelView>();
}
@@ -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);
}
}

View File

@@ -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<Assembly> _assemblies = new();
private readonly DebugService _debugService;
private readonly ILogger _logger;
private readonly HashSet<string> _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;
}

View File

@@ -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");

View File

@@ -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<T>(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<T>(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}");
}
}

View File

@@ -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<HashApi> 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;
}

View File

@@ -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;
}

View File

@@ -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<RobustBuildInfo> GetBuildInfo(RobustUrl url, CancellationToken cancellationToken)
{

View File

@@ -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<string, ServiceLogger> 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);
}
}

View File

@@ -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<string, EngineVersionInfo> _versionsInfo = default!;
private ModulesInfo? _modulesInfo;
private Dictionary<string, EngineVersionInfo>? _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<string, EngineVersionInfo> 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<T> LoadExacManifest<T>(ConVar<string[]> conVar,ConVar<T> 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<T>(
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<AssemblyApi?> 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();
}
}

View File

@@ -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<ServerHubInfo> _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;
}
}

View File

@@ -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);
}
}

View File

@@ -1,6 +1,6 @@
namespace Nebula.Shared.Services.Logging;
public interface ILogger
public interface ILogger : IDisposable
{
public void Log(LoggerCategory loggerCategory, string message);
}

View File

@@ -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<T> GetAsync<T>(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;
}
}

View File

@@ -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);

View File

@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAvaloniaXamlLoader_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F80462644bd1cc7e0b229dc4f5752b48c01cb67b46ae563b1b5078cc2556b98_003FAvaloniaXamlLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AButton_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fcc84c38d8785b88e166e6741b6a4c0dfa09eaf6e41eb151b255817e11f27570_003FButton_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACancellationToken_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2565b9d99fdde488bc7801b84387b2cc864959cfb63212e1ff576fc9c6bb7e_003FCancellationToken_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACollection_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F50341a469131fa51e5443b9bd96c4ca1c96bfa709f7f41fd15941ff6296a8dc_003FCollection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>