diff --git a/Nebula.Launcher/Controls/ServerListView.axaml.cs b/Nebula.Launcher/Controls/ServerListView.axaml.cs index fd11a68..4eaf9b8 100644 --- a/Nebula.Launcher/Controls/ServerListView.axaml.cs +++ b/Nebula.Launcher/Controls/ServerListView.axaml.cs @@ -43,6 +43,17 @@ public partial class ServerListView : UserControl else _provider.OnLoaded += RefreshRequired; } + public void RequireStatusUpdate() + { + foreach (var rawView in ServerList.Items) + { + if (rawView is ServerEntryModelView serverEntryModelView) + { + serverEntryModelView.UpdateStatusIfNecessary(); + } + } + } + public void ApplyFilter(ServerFilter? filter) { _currentFilter = filter; diff --git a/Nebula.Launcher/ProcessHelper/DotnetProcessStartInfoProviderBase.cs b/Nebula.Launcher/ProcessHelper/DotnetProcessStartInfoProviderBase.cs new file mode 100644 index 0000000..b342261 --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/DotnetProcessStartInfoProviderBase.cs @@ -0,0 +1,25 @@ +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; +using Nebula.Shared.Services; + +namespace Nebula.Launcher.ProcessHelper; + +public abstract class DotnetProcessStartInfoProviderBase(DotnetResolverService resolverService) : IProcessStartInfoProvider +{ + protected abstract string GetDllPath(); + + public virtual async Task GetProcessStartInfo() + { + return new ProcessStartInfo + { + FileName = await resolverService.EnsureDotnet(), + Arguments = GetDllPath(), + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + StandardOutputEncoding = Encoding.UTF8 + }; + } +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/GameProcessStartInfoProvider.cs b/Nebula.Launcher/ProcessHelper/GameProcessStartInfoProvider.cs new file mode 100644 index 0000000..65ed509 --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/GameProcessStartInfoProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Nebula.Shared; +using Nebula.Shared.Models; +using Nebula.Shared.Services; + +namespace Nebula.Launcher.ProcessHelper; + +[ServiceRegister(isSingleton:false)] +public sealed class GameProcessStartInfoProvider(DotnetResolverService resolverService, AuthService authService) : + DotnetProcessStartInfoProviderBase(resolverService) +{ + private string? _publicKey; + private RobustUrl _address = default!; + + protected override string GetDllPath() + { + var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + return Path.Join(path, "Nebula.Runner.dll"); + } + + public GameProcessStartInfoProvider WithBuildInfo(string publicKey, RobustUrl address) + { + _publicKey = publicKey; + _address = address; + + return this; + } + + public override async Task GetProcessStartInfo() + { + var baseStart = await base.GetProcessStartInfo(); + + var authProv = authService.SelectedAuth; + if(authProv is null) + throw new Exception("Client is without selected auth"); + + baseStart.EnvironmentVariables["ROBUST_AUTH_USERID"] = authProv.UserId.ToString(); + baseStart.EnvironmentVariables["ROBUST_AUTH_TOKEN"] = authProv.Token.Token; + baseStart.EnvironmentVariables["ROBUST_AUTH_SERVER"] = authProv.AuthServer; + baseStart.EnvironmentVariables["AUTH_LOGIN"] = authProv.Login; + baseStart.EnvironmentVariables["ROBUST_AUTH_PUBKEY"] = _publicKey; + baseStart.EnvironmentVariables["GAME_URL"] = _address.ToString(); + + return baseStart; + } +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/GameRunnerPreparer.cs b/Nebula.Launcher/ProcessHelper/GameRunnerPreparer.cs new file mode 100644 index 0000000..73c423f --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/GameRunnerPreparer.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Nebula.Shared; +using Nebula.Shared.Models; +using Nebula.Shared.Services; + +namespace Nebula.Launcher.ProcessHelper; + +[ServiceRegister] +public sealed class GameRunnerPreparer(IServiceProvider provider, ContentService contentService, EngineService engineService, DebugService debugService) +{ + public async Task> GetGameProcessStartInfoProvider(RobustUrl address, ILoadingHandler loadingHandler, CancellationToken cancellationToken = default) + { + var buildInfo = await contentService.GetBuildInfo(address, cancellationToken); + + var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); + + if (engine is null) + throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion); + + await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken); + await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion); + + var gameInfo = + provider.GetService()!.WithBuildInfo(buildInfo.BuildInfo.Auth.PublicKey, + address); + var gameProcessRunHandler = new ProcessRunHandler(gameInfo); + + return gameProcessRunHandler; + } +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/IProcessConsumerCollection.cs b/Nebula.Launcher/ProcessHelper/IProcessConsumerCollection.cs new file mode 100644 index 0000000..8f322e5 --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/IProcessConsumerCollection.cs @@ -0,0 +1,6 @@ +namespace Nebula.Launcher.ProcessHelper; + +public interface IProcessConsumerCollection +{ + public void RegisterLogger(IProcessLogConsumer consumer); +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/IProcessLogConsumer.cs b/Nebula.Launcher/ProcessHelper/IProcessLogConsumer.cs new file mode 100644 index 0000000..0800f5a --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/IProcessLogConsumer.cs @@ -0,0 +1,8 @@ +namespace Nebula.Launcher.ProcessHelper; + +public interface IProcessLogConsumer +{ + public void Out(string text); + public void Error(string text); + public void Fatal(string text); +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/IProcessRunner.cs b/Nebula.Launcher/ProcessHelper/IProcessRunner.cs new file mode 100644 index 0000000..fb30a68 --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/IProcessRunner.cs @@ -0,0 +1,9 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Nebula.Launcher.ProcessHelper; + +public interface IProcessStartInfoProvider +{ + public Task GetProcessStartInfo(); +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/ProcessLogConsumerCollection.cs b/Nebula.Launcher/ProcessHelper/ProcessLogConsumerCollection.cs new file mode 100644 index 0000000..d97f14d --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/ProcessLogConsumerCollection.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace Nebula.Launcher.ProcessHelper; + +public sealed class ProcessLogConsumerCollection: IProcessLogConsumer, IProcessConsumerCollection +{ + private readonly List _consumers = []; + + public void RegisterLogger(IProcessLogConsumer consumer) + { + _consumers.Add(consumer); + } + + public void Out(string text) + { + foreach (var consumer in _consumers) + { + consumer.Out(text); + } + } + + public void Error(string text) + { + foreach (var consumer in _consumers) + { + consumer.Error(text); + } + } + + public void Fatal(string text) + { + foreach (var consumer in _consumers) + { + consumer.Fatal(text); + } + } +} \ No newline at end of file diff --git a/Nebula.Launcher/ProcessHelper/ProcessRunHandler.cs b/Nebula.Launcher/ProcessHelper/ProcessRunHandler.cs new file mode 100644 index 0000000..35eeab8 --- /dev/null +++ b/Nebula.Launcher/ProcessHelper/ProcessRunHandler.cs @@ -0,0 +1,137 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; + +namespace Nebula.Launcher.ProcessHelper; + +public class ProcessRunHandler : IProcessConsumerCollection, IDisposable where T: IProcessStartInfoProvider +{ + private ProcessStartInfo? _processInfo; + private Task? _processInfoTask; + + private Process? _process; + private ProcessLogConsumerCollection _consumerCollection = new(); + + private string _lastError = string.Empty; + private readonly T _currentProcessStartInfoProvider; + + public T GetCurrentProcessStartInfo() => _currentProcessStartInfoProvider; + public bool IsRunning => _processInfo is not null; + public Action>? OnProcessExited; + + public void RegisterLogger(IProcessLogConsumer consumer) + { + _consumerCollection.RegisterLogger(consumer); + } + + public ProcessRunHandler(T processStartInfoProvider) + { + _currentProcessStartInfoProvider = processStartInfoProvider; + _processInfoTask = _currentProcessStartInfoProvider.GetProcessStartInfo(); + _processInfoTask.GetAwaiter().OnCompleted(OnInfoProvided); + } + + private void OnInfoProvided() + { + if (_processInfoTask == null) + return; + + _processInfo = _processInfoTask.GetAwaiter().GetResult(); + _processInfoTask = null; + } + + public void Start() + { + if (_processInfoTask != null) + { + _processInfoTask.Wait(); + } + + _process = Process.Start(_processInfo!); + + if (_process is null) return; + + _process.EnableRaisingEvents = true; + + _process.BeginOutputReadLine(); + _process.BeginErrorReadLine(); + + _process.OutputDataReceived += OnOutputDataReceived; + _process.ErrorDataReceived += OnErrorDataReceived; + + _process.Exited += OnExited; + } + + public void Stop() + { + _process?.CloseMainWindow(); + } + + private void OnExited(object? sender, EventArgs e) + { + if (_process is null) return; + + _process.OutputDataReceived -= OnOutputDataReceived; + _process.ErrorDataReceived -= OnErrorDataReceived; + _process.Exited -= OnExited; + + + if (_process.ExitCode != 0) + _consumerCollection.Fatal(_lastError); + + _process.Dispose(); + _process = null; + + OnProcessExited?.Invoke(this); + } + + private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data != null) + { + _lastError = e.Data; + _consumerCollection.Error(e.Data); + } + } + + private void OnOutputDataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data != null) + { + _consumerCollection.Out(e.Data); + } + } + + public void Dispose() + { + _processInfoTask?.Dispose(); + _process?.Dispose(); + } +} + +public sealed class DebugLoggerBridge : IProcessLogConsumer +{ + private ILogger _logger; + + public DebugLoggerBridge(ILogger logger) + { + _logger = logger; + } + + public void Out(string text) + { + _logger.Log(LoggerCategory.Log, text); + } + + public void Error(string text) + { + _logger.Log(LoggerCategory.Error, text); + } + + public void Fatal(string text) + { + _logger.Log(LoggerCategory.Error, text); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs b/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs index 43f5fa8..f8791b5 100644 --- a/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs +++ b/Nebula.Launcher/ServerListProviders/FavoriteServerListProvider.cs @@ -16,7 +16,7 @@ using Nebula.Shared.Utils; namespace Nebula.Launcher.ServerListProviders; -[ServiceRegister(), ConstructGenerator] +[ServiceRegister, ConstructGenerator] public sealed partial class FavoriteServerListProvider : IServerListProvider, IServerListDirtyInvoker { [GenerateProperty] private ConfigurationService ConfigurationService { get; } diff --git a/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs b/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs index cc5d268..19ffd10 100644 --- a/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs +++ b/Nebula.Launcher/ViewModels/Pages/ServerOverviewModel.cs @@ -104,6 +104,8 @@ public partial class ServerOverviewModel : ViewModelBase public void UpdateRequired() { CurrentServerList.RefreshFromProvider(); + CurrentServerList.RequireStatusUpdate(); + CurrentServerList.ApplyFilter(CurrentFilter); } partial void OnSelectedItemChanged(ServerListTabTemplate value) diff --git a/Nebula.Launcher/ViewModels/Popup/LogPopupModelView.cs b/Nebula.Launcher/ViewModels/Popup/LogPopupModelView.cs index bbc9cf7..c397f77 100644 --- a/Nebula.Launcher/ViewModels/Popup/LogPopupModelView.cs +++ b/Nebula.Launcher/ViewModels/Popup/LogPopupModelView.cs @@ -1,4 +1,6 @@ using System.Collections.ObjectModel; +using System.Text.RegularExpressions; +using Avalonia.Media; using Nebula.Launcher.Views.Popup; using Nebula.Shared.Services; @@ -35,4 +37,45 @@ public sealed partial class LogPopupModelView : PopupViewModelBase { Logs.Add(LogInfo.FromString(str)); } + + public void Clear() + { + Logs.Clear(); + } +} + +public sealed class LogInfo +{ + public string Category { get; set; } = "LOG"; + public IBrush CategoryColor { get; set; } = Brush.Parse("#424242"); + public string Message { get; set; } = ""; + + public static LogInfo FromString(string input) + { + var matches = Regex.Matches(input, @"(\[(?.*)\] (?.*))|(?.*)"); + var category = "All"; + + if (matches[0].Groups.TryGetValue("c", out var c)) category = c.Value; + + var color = Brush.Parse("#444444"); + + switch (category) + { + case "DEBG": + color = Brush.Parse("#2436d4"); + break; + case "ERRO": + color = Brush.Parse("#d42436"); + break; + case "INFO": + color = Brush.Parse("#0ab3c9"); + break; + } + + var message = matches[0].Groups["m"].Value; + return new LogInfo + { + Category = category, Message = message, CategoryColor = color + }; + } } \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs index 061a26b..4d9accc 100644 --- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs +++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs @@ -1,17 +1,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Input; using Avalonia.Controls; -using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; +using Nebula.Launcher.ProcessHelper; using Nebula.Launcher.ServerListProviders; using Nebula.Launcher.Services; using Nebula.Launcher.ViewModels.Pages; @@ -32,30 +27,27 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer [ObservableProperty] private bool _expandInfo; [ObservableProperty] private bool _isFavorite; [ObservableProperty] private bool _isVisible; - - private string _lastError = ""; - private Process? _p; + [ObservableProperty] private bool _runVisible = true; + private ILogger _logger; - private ILogger? _processLogger; - + private bool _isStatusFromHub; private ServerInfo? _serverInfo; + private ContentLogConsumer _currentContentLogConsumer; + private ProcessRunHandler? _currentInstance; + [ObservableProperty] private bool _tagDataVisible; public LogPopupModelView CurrLog; public RobustUrl Address { get; private set; } - - [GenerateProperty] private AuthService AuthService { get; } = default!; - [GenerateProperty] private ContentService ContentService { get; } = default!; [GenerateProperty] private ConfigurationService ConfigurationService { get; } = default!; [GenerateProperty] private CancellationService CancellationService { get; } = default!; [GenerateProperty] private DebugService DebugService { get; } = default!; - [GenerateProperty] private RunnerService RunnerService { get; } = default!; [GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!; [GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!; [GenerateProperty] private RestService RestService { get; } = default!; [GenerateProperty] private MainViewModel MainViewModel { get; } = default!; [GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } = default!; - [GenerateProperty] private DotnetResolverService DotnetResolverService { get; } = default!; + [GenerateProperty] private GameRunnerPreparer GameRunnerPreparer { get; } = default!; public ServerStatus Status { get; private set; } = new( @@ -71,22 +63,9 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer ); public ObservableCollection Links { get; } = new(); - public bool RunVisible => Process == null; - public ObservableCollection Tags { get; } = []; - public ICommand OnLinkGo { get; } = new LinkGoCommand(); - private Process? Process - { - get => _p; - set - { - _p = value; - OnPropertyChanged(nameof(RunVisible)); - } - } - public async Task GetServerInfo() { if (_serverInfo == null) @@ -119,6 +98,7 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer { _logger = DebugService.GetLogger(this); CurrLog = ViewHelperService.GetViewModel(); + _currentContentLogConsumer = new(CurrLog, PopupMessageService); } public void ProcessFilter(ServerFilter? serverFilter) @@ -140,11 +120,18 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer OnPropertyChanged(nameof(Status)); } + public void UpdateStatusIfNecessary() + { + if(_isStatusFromHub) return; + FetchStatus(); + } + public ServerEntryModelView WithData(RobustUrl url, ServerStatus? serverStatus) { Address = url; - if (serverStatus is not null) - SetStatus(serverStatus); + _isStatusFromHub = serverStatus is not null; + if (_isStatusFromHub) + SetStatus(serverStatus!); else FetchStatus(); @@ -188,125 +175,46 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer } public void RunInstance() - { - Task.Run(RunAsync); + { + CurrLog.Clear(); + Task.Run(RunInstanceAsync); } - public async Task RunAsync() + private async void RunInstanceAsync() { - try - { - var authProv = AuthService.SelectedAuth; + using var loadingContext = ViewHelperService.GetViewModel(); + loadingContext.LoadingName = "Loading instance..."; + ((ILoadingHandler)loadingContext).AppendJob(); - var buildInfo = - await ContentService.GetBuildInfo(Address, CancellationService.Token); - - using (var loadingContext = ViewHelperService.GetViewModel()) - { - loadingContext.LoadingName = "Loading instance..."; - ((ILoadingHandler)loadingContext).AppendJob(); - - PopupMessageService.Popup(loadingContext); - - await RunnerService.PrepareRun(buildInfo, loadingContext, CancellationService.Token); - - var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - - Process = Process.Start(new ProcessStartInfo - { - FileName = await DotnetResolverService.EnsureDotnet(), - Arguments = Path.Join(path, "Nebula.Runner.dll"), - Environment = - { - { "ROBUST_AUTH_USERID", authProv?.UserId.ToString() }, - { "ROBUST_AUTH_TOKEN", authProv?.Token.Token }, - { "ROBUST_AUTH_SERVER", authProv?.AuthServer }, - { "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey }, - { "GAME_URL", Address.ToString() }, - { "AUTH_LOGIN", authProv?.Login } - }, - CreateNoWindow = true, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - StandardOutputEncoding = Encoding.UTF8 - }); - - ((ILoadingHandler)loadingContext).AppendResolvedJob(); - } - - if (Process is null) return; + PopupMessageService.Popup(loadingContext); + _currentInstance = + await GameRunnerPreparer.GetGameProcessStartInfoProvider(Address, loadingContext, CancellationService.Token); - _processLogger = DebugService.GetLogger($"PROCESS_{Process.Id}"); - - Process.EnableRaisingEvents = true; - - Process.BeginOutputReadLine(); - Process.BeginErrorReadLine(); - - Process.OutputDataReceived += OnOutputDataReceived; - Process.ErrorDataReceived += OnErrorDataReceived; - - Process.Exited += OnExited; - } - catch (TaskCanceledException e) - { - PopupMessageService.Popup("Task canceled: " + e.Message); - _logger.Error("Task canceled"); - _logger.Error(e); - } - catch (Exception e) - { - PopupMessageService.Popup(e); - } + _currentInstance.RegisterLogger(_currentContentLogConsumer); + _currentInstance.RegisterLogger(new DebugLoggerBridge(DebugService.GetLogger($"PROCESS_{Random.Shared.Next(65535)}"))); + _currentInstance.OnProcessExited += OnProcessExited; + RunVisible = false; + _currentInstance.Start(); } - private void OnExited(object? sender, EventArgs e) + private void OnProcessExited(ProcessRunHandler obj) { - if (Process is null) return; - - Process.OutputDataReceived -= OnOutputDataReceived; - Process.ErrorDataReceived -= OnErrorDataReceived; - Process.Exited -= OnExited; - - _processLogger?.Log("PROCESS EXIT WITH CODE " + Process.ExitCode); - - if (Process.ExitCode != 0) - PopupMessageService.Popup($"Game exit with code {Process.ExitCode}.\nReason: {_lastError}"); - - _processLogger?.Dispose(); + RunVisible = true; + if (_currentInstance == null) return; - Process.Dispose(); - Process = null; - } - - private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data != null) - { - _lastError = e.Data; - _processLogger?.Error(e.Data); - CurrLog.Append(e.Data); - } - } - - private void OnOutputDataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data != null) - { - _processLogger?.Log(e.Data); - CurrLog.Append(e.Data); - } - } - - public void ReadLog() - { - PopupMessageService.Popup(CurrLog); + _currentInstance.OnProcessExited -= OnProcessExited; + _currentInstance.Dispose(); + _currentInstance = null; } public void StopInstance() { - Process?.CloseMainWindow(); + _currentInstance?.Stop(); + } + + public void ReadLog() + { + PopupMessageService.Popup(CurrLog); } public async void ExpandInfoRequired() @@ -323,58 +231,37 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer if (info.Links is null) return; foreach (var link in info.Links) Links.Add(link); } - - private static string FindDotnetPath() - { - var pathEnv = Environment.GetEnvironmentVariable("PATH"); - var paths = pathEnv?.Split(Path.PathSeparator); - if (paths != null) - foreach (var path in paths) - { - var dotnetPath = Path.Combine(path, "dotnet"); - if (File.Exists(dotnetPath)) return dotnetPath; - } - - return "dotnet"; - } } -public sealed class LogInfo +public sealed class ContentLogConsumer : IProcessLogConsumer { - public string Category { get; set; } = "LOG"; - public IBrush CategoryColor { get; set; } = Brush.Parse("#424242"); - public string Message { get; set; } = ""; + private readonly LogPopupModelView _currLog; + private readonly PopupMessageService _popupMessageService; - public static LogInfo FromString(string input) + public ContentLogConsumer(LogPopupModelView currLog, PopupMessageService popupMessageService) { - var matches = Regex.Matches(input, @"(\[(?.*)\] (?.*))|(?.*)"); - var category = "All"; + _currLog = currLog; + _popupMessageService = popupMessageService; + } - if (matches[0].Groups.TryGetValue("c", out var c)) category = c.Value; + public void Out(string text) + { + _currLog.Append(text); + } - var color = Brush.Parse("#444444"); + public void Error(string text) + { + _currLog.Append(text); + } - switch (category) - { - case "DEBG": - color = Brush.Parse("#2436d4"); - break; - case "ERRO": - color = Brush.Parse("#d42436"); - break; - case "INFO": - color = Brush.Parse("#0ab3c9"); - break; - } - - var message = matches[0].Groups["m"].Value; - return new LogInfo - { - Category = category, Message = message, CategoryColor = color - }; + public void Fatal(string text) + { + _popupMessageService.Popup("Fatal error while stop instance:" + text); } } + + public class LinkGoCommand : ICommand { public LinkGoCommand() diff --git a/Nebula.Launcher/Views/Popup/LogPopupView.axaml b/Nebula.Launcher/Views/Popup/LogPopupView.axaml index 6e35d81..b8ec6f0 100644 --- a/Nebula.Launcher/Views/Popup/LogPopupView.axaml +++ b/Nebula.Launcher/Views/Popup/LogPopupView.axaml @@ -17,7 +17,7 @@ ItemsSource="{Binding Logs}" Padding="0"> - + ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -21,6 +22,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded