2024-12-27 19:15:33 +03:00
|
|
|
using System;
|
2025-06-14 22:33:03 +03:00
|
|
|
using System.Collections.Generic;
|
2025-01-17 16:23:50 +03:00
|
|
|
using System.Collections.ObjectModel;
|
2025-01-05 17:05:23 +03:00
|
|
|
using System.Diagnostics;
|
2025-01-14 22:10:16 +03:00
|
|
|
using System.IO;
|
2025-06-14 22:33:03 +03:00
|
|
|
using System.Linq;
|
2025-05-02 20:06:33 +03:00
|
|
|
using System.Reflection;
|
2025-01-07 19:14:42 +03:00
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
2025-01-08 18:00:06 +03:00
|
|
|
using System.Threading.Tasks;
|
2025-01-27 15:55:30 +03:00
|
|
|
using System.Windows.Input;
|
2025-05-02 20:06:33 +03:00
|
|
|
using Avalonia.Controls;
|
2025-01-07 19:14:42 +03:00
|
|
|
using Avalonia.Media;
|
2025-01-17 16:23:50 +03:00
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
2025-06-14 22:33:03 +03:00
|
|
|
using Nebula.Launcher.ServerListProviders;
|
2025-01-14 22:10:16 +03:00
|
|
|
using Nebula.Launcher.Services;
|
2025-05-02 20:06:33 +03:00
|
|
|
using Nebula.Launcher.ViewModels.Pages;
|
2025-01-14 22:10:16 +03:00
|
|
|
using Nebula.Launcher.ViewModels.Popup;
|
2025-01-16 21:07:50 +03:00
|
|
|
using Nebula.Launcher.Views;
|
2025-01-05 17:05:23 +03:00
|
|
|
using Nebula.Shared.Models;
|
2025-01-07 17:01:00 +03:00
|
|
|
using Nebula.Shared.Services;
|
2025-05-05 20:43:28 +03:00
|
|
|
using Nebula.Shared.Services.Logging;
|
2025-01-17 16:23:50 +03:00
|
|
|
using Nebula.Shared.Utils;
|
2024-12-27 19:15:33 +03:00
|
|
|
|
|
|
|
|
namespace Nebula.Launcher.ViewModels;
|
|
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
[ViewModelRegister(typeof(ServerEntryView), false)]
|
2025-01-14 22:10:16 +03:00
|
|
|
[ConstructGenerator]
|
2025-06-14 22:33:03 +03:00
|
|
|
public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer
|
2024-12-27 19:15:33 +03:00
|
|
|
{
|
2025-05-02 20:06:33 +03:00
|
|
|
[ObservableProperty] private string _description = "Fetching info...";
|
|
|
|
|
[ObservableProperty] private bool _expandInfo;
|
|
|
|
|
[ObservableProperty] private bool _isFavorite;
|
|
|
|
|
[ObservableProperty] private bool _isVisible;
|
|
|
|
|
|
|
|
|
|
private string _lastError = "";
|
2025-01-14 22:10:16 +03:00
|
|
|
private Process? _p;
|
2025-05-05 20:43:28 +03:00
|
|
|
private ILogger _logger;
|
|
|
|
|
private ILogger? _processLogger;
|
2025-05-02 20:06:33 +03:00
|
|
|
|
|
|
|
|
private ServerInfo? _serverInfo;
|
|
|
|
|
[ObservableProperty] private bool _tagDataVisible;
|
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
public LogPopupModelView CurrLog;
|
2025-05-02 20:06:33 +03:00
|
|
|
public RobustUrl Address { get; private set; }
|
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
[GenerateProperty] private AuthService AuthService { get; } = default!;
|
|
|
|
|
[GenerateProperty] private ContentService ContentService { get; } = default!;
|
2025-06-14 22:33:03 +03:00
|
|
|
[GenerateProperty] private ConfigurationService ConfigurationService { get; } = default!;
|
2025-01-14 22:10:16 +03:00
|
|
|
[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!;
|
2025-01-17 16:23:50 +03:00
|
|
|
[GenerateProperty] private RestService RestService { get; } = default!;
|
2025-05-02 20:06:33 +03:00
|
|
|
[GenerateProperty] private MainViewModel MainViewModel { get; } = default!;
|
2025-06-14 22:33:03 +03:00
|
|
|
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } = default!;
|
2025-06-15 13:48:56 +03:00
|
|
|
[GenerateProperty] private DotnetResolverService DotnetResolverService { get; } = default!;
|
2025-05-02 20:06:33 +03:00
|
|
|
|
|
|
|
|
public ServerStatus Status { get; private set; } =
|
|
|
|
|
new(
|
|
|
|
|
"Fetching data...",
|
|
|
|
|
"Loading...", [],
|
|
|
|
|
"",
|
|
|
|
|
-1,
|
|
|
|
|
-1,
|
|
|
|
|
-1,
|
2025-02-01 13:13:49 +03:00
|
|
|
false,
|
|
|
|
|
DateTime.Now,
|
|
|
|
|
-1
|
|
|
|
|
);
|
2025-05-02 20:06:33 +03:00
|
|
|
|
2025-01-28 19:59:35 +03:00
|
|
|
public ObservableCollection<ServerLink> Links { get; } = new();
|
2025-01-08 18:00:06 +03:00
|
|
|
public bool RunVisible => Process == null;
|
2025-01-05 17:05:23 +03:00
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
public ObservableCollection<string> Tags { get; } = [];
|
2025-01-17 16:23:50 +03:00
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
public ICommand OnLinkGo { get; } = new LinkGoCommand();
|
|
|
|
|
|
|
|
|
|
private Process? Process
|
|
|
|
|
{
|
|
|
|
|
get => _p;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_p = value;
|
|
|
|
|
OnPropertyChanged(nameof(RunVisible));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-12 14:51:47 +03:00
|
|
|
|
2025-01-17 16:23:50 +03:00
|
|
|
public async Task<ServerInfo?> GetServerInfo()
|
|
|
|
|
{
|
|
|
|
|
if (_serverInfo == null)
|
2025-01-29 12:32:42 +03:00
|
|
|
try
|
|
|
|
|
{
|
2025-01-30 20:18:40 +03:00
|
|
|
_serverInfo = await RestService.GetAsync<ServerInfo>(Address.InfoUri, CancellationService.Token);
|
2025-01-29 12:32:42 +03:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Description = e.Message;
|
2025-05-05 20:43:28 +03:00
|
|
|
_logger.Error(e);
|
2025-01-29 12:32:42 +03:00
|
|
|
}
|
2025-01-17 16:23:50 +03:00
|
|
|
|
|
|
|
|
return _serverInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
protected override void InitialiseInDesignMode()
|
2025-01-05 17:05:23 +03:00
|
|
|
{
|
2025-01-17 16:23:50 +03:00
|
|
|
Description = "Server of meow girls! Nya~ \nNyaMeow\nOOOINK!!";
|
2025-05-02 20:06:33 +03:00
|
|
|
Links.Add(new ServerLink("Discord", "discord", "https://cinka.ru"));
|
2025-01-28 19:59:35 +03:00
|
|
|
Status = new ServerStatus("Ameba",
|
|
|
|
|
"Locala meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow meow ",
|
|
|
|
|
["rp:hrp", "18+"],
|
|
|
|
|
"Antag", 15, 5, 1, false
|
|
|
|
|
, DateTime.Now, 100);
|
|
|
|
|
Address = "ss14://localhost".ToRobustUrl();
|
2025-01-05 17:05:23 +03:00
|
|
|
}
|
2024-12-27 19:15:33 +03:00
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
protected override void Initialise()
|
2025-01-05 17:05:23 +03:00
|
|
|
{
|
2025-05-05 20:43:28 +03:00
|
|
|
_logger = DebugService.GetLogger(this);
|
2025-01-14 22:10:16 +03:00
|
|
|
CurrLog = ViewHelperService.GetViewModel<LogPopupModelView>();
|
2025-01-07 19:14:42 +03:00
|
|
|
}
|
2025-01-07 17:01:00 +03:00
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
public void ProcessFilter(ServerFilter? serverFilter)
|
2025-05-02 20:06:33 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
if (serverFilter == null)
|
|
|
|
|
{
|
|
|
|
|
IsVisible = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
IsVisible = serverFilter.IsMatch(Status.Name, Tags);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-01 13:13:49 +03:00
|
|
|
public void SetStatus(ServerStatus serverStatus)
|
2025-01-28 19:59:35 +03:00
|
|
|
{
|
|
|
|
|
Status = serverStatus;
|
|
|
|
|
Tags.Clear();
|
2025-05-02 20:06:33 +03:00
|
|
|
foreach (var tag in Status.Tags) Tags.Add(tag);
|
2025-02-01 13:13:49 +03:00
|
|
|
OnPropertyChanged(nameof(Status));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ServerEntryModelView WithData(RobustUrl url, ServerStatus? serverStatus)
|
|
|
|
|
{
|
|
|
|
|
Address = url;
|
|
|
|
|
if (serverStatus is not null)
|
|
|
|
|
SetStatus(serverStatus);
|
|
|
|
|
else
|
|
|
|
|
FetchStatus();
|
2025-06-14 22:33:03 +03:00
|
|
|
|
|
|
|
|
IsFavorite = GetFavoriteEntries().Contains(Address.ToString());
|
2025-01-28 19:59:35 +03:00
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
}
|
2025-06-14 22:33:03 +03:00
|
|
|
|
|
|
|
|
private List<string> GetFavoriteEntries()
|
|
|
|
|
{
|
|
|
|
|
return ConfigurationService.GetConfigValue(LauncherConVar.Favorites)?.ToList() ?? [];
|
|
|
|
|
}
|
2025-02-01 13:13:49 +03:00
|
|
|
|
|
|
|
|
private async void FetchStatus()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
SetStatus(await RestService.GetAsync<ServerStatus>(Address.StatusUri, CancellationService.Token));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
2025-05-05 20:43:28 +03:00
|
|
|
_logger.Error(e);
|
2025-02-01 13:13:49 +03:00
|
|
|
Status = new ServerStatus("ErrorLand", $"ERROR: {e.Message}", [], "", -1, -1, -1, false,
|
|
|
|
|
DateTime.Now,
|
|
|
|
|
-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
|
|
|
|
|
public void OpenContentViewer()
|
|
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
MainViewModel.RequirePage<ContentBrowserViewModel>().Go(Address, ContentPath.Empty);
|
2025-05-02 20:06:33 +03:00
|
|
|
}
|
|
|
|
|
|
2025-01-28 19:59:35 +03:00
|
|
|
public void ToggleFavorites()
|
|
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
IsFavorite = !IsFavorite;
|
|
|
|
|
if(IsFavorite)
|
|
|
|
|
FavoriteServerListProvider.AddFavorite(this);
|
|
|
|
|
else
|
|
|
|
|
FavoriteServerListProvider.RemoveFavorite(this);
|
2025-01-28 19:59:35 +03:00
|
|
|
}
|
|
|
|
|
|
2025-01-08 18:00:06 +03:00
|
|
|
public void RunInstance()
|
2025-01-05 17:05:23 +03:00
|
|
|
{
|
2025-01-08 18:00:06 +03:00
|
|
|
Task.Run(RunAsync);
|
|
|
|
|
}
|
2025-01-07 17:01:00 +03:00
|
|
|
|
2025-01-08 18:00:06 +03:00
|
|
|
public async Task RunAsync()
|
|
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var authProv = AuthService.SelectedAuth;
|
|
|
|
|
|
|
|
|
|
var buildInfo =
|
2025-01-28 19:59:35 +03:00
|
|
|
await ContentService.GetBuildInfo(Address, CancellationService.Token);
|
2025-01-14 22:10:16 +03:00
|
|
|
|
|
|
|
|
using (var loadingContext = ViewHelperService.GetViewModel<LoadingContextViewModel>())
|
|
|
|
|
{
|
|
|
|
|
loadingContext.LoadingName = "Loading instance...";
|
|
|
|
|
((ILoadingHandler)loadingContext).AppendJob();
|
|
|
|
|
|
|
|
|
|
PopupMessageService.Popup(loadingContext);
|
|
|
|
|
|
|
|
|
|
await RunnerService.PrepareRun(buildInfo, loadingContext, CancellationService.Token);
|
|
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
2025-06-15 13:48:56 +03:00
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
Process = Process.Start(new ProcessStartInfo
|
|
|
|
|
{
|
2025-06-15 13:48:56 +03:00
|
|
|
FileName = await DotnetResolverService.EnsureDotnet(),
|
2025-05-01 20:58:43 +03:00
|
|
|
Arguments = Path.Join(path, "Nebula.Runner.dll"),
|
2025-01-14 22:10:16 +03:00
|
|
|
Environment =
|
|
|
|
|
{
|
|
|
|
|
{ "ROBUST_AUTH_USERID", authProv?.UserId.ToString() },
|
|
|
|
|
{ "ROBUST_AUTH_TOKEN", authProv?.Token.Token },
|
2025-02-01 18:19:18 +03:00
|
|
|
{ "ROBUST_AUTH_SERVER", authProv?.AuthServer },
|
2025-01-14 22:10:16 +03:00
|
|
|
{ "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey },
|
2025-01-28 19:59:35 +03:00
|
|
|
{ "GAME_URL", Address.ToString() },
|
2025-02-01 18:19:18 +03:00
|
|
|
{ "AUTH_LOGIN", authProv?.Login }
|
2025-01-14 22:10:16 +03:00
|
|
|
},
|
|
|
|
|
CreateNoWindow = true,
|
|
|
|
|
UseShellExecute = false,
|
|
|
|
|
RedirectStandardOutput = true,
|
|
|
|
|
RedirectStandardError = true,
|
|
|
|
|
StandardOutputEncoding = Encoding.UTF8
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
((ILoadingHandler)loadingContext).AppendResolvedJob();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Process is null) return;
|
2025-05-05 20:43:28 +03:00
|
|
|
|
2025-05-05 21:28:09 +03:00
|
|
|
_processLogger = DebugService.GetLogger($"PROCESS_{Process.Id}");
|
2025-01-14 22:10:16 +03:00
|
|
|
|
|
|
|
|
Process.EnableRaisingEvents = true;
|
|
|
|
|
|
|
|
|
|
Process.BeginOutputReadLine();
|
|
|
|
|
Process.BeginErrorReadLine();
|
|
|
|
|
|
|
|
|
|
Process.OutputDataReceived += OnOutputDataReceived;
|
|
|
|
|
Process.ErrorDataReceived += OnErrorDataReceived;
|
|
|
|
|
|
|
|
|
|
Process.Exited += OnExited;
|
|
|
|
|
}
|
2025-05-05 22:01:59 +03:00
|
|
|
catch (TaskCanceledException e)
|
2025-01-14 22:10:16 +03:00
|
|
|
{
|
2025-05-05 22:01:59 +03:00
|
|
|
PopupMessageService.Popup("Task canceled: " + e.Message);
|
|
|
|
|
_logger.Error("Task canceled");
|
|
|
|
|
_logger.Error(e);
|
2025-01-14 22:10:16 +03:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
PopupMessageService.Popup(e);
|
|
|
|
|
}
|
2025-01-05 17:05:23 +03:00
|
|
|
}
|
2025-01-07 17:01:00 +03:00
|
|
|
|
|
|
|
|
private void OnExited(object? sender, EventArgs e)
|
|
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
if (Process is null) return;
|
|
|
|
|
|
2025-01-08 18:00:06 +03:00
|
|
|
Process.OutputDataReceived -= OnOutputDataReceived;
|
|
|
|
|
Process.ErrorDataReceived -= OnErrorDataReceived;
|
|
|
|
|
Process.Exited -= OnExited;
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2025-05-05 20:43:28 +03:00
|
|
|
_processLogger?.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
if (Process.ExitCode != 0)
|
2025-03-12 14:51:47 +03:00
|
|
|
PopupMessageService.Popup($"Game exit with code {Process.ExitCode}.\nReason: {_lastError}");
|
|
|
|
|
|
2025-05-05 20:43:28 +03:00
|
|
|
_processLogger?.Dispose();
|
|
|
|
|
|
2025-01-08 18:00:06 +03:00
|
|
|
Process.Dispose();
|
|
|
|
|
Process = null;
|
2025-01-07 17:01:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
|
|
|
|
|
{
|
2025-01-07 19:14:42 +03:00
|
|
|
if (e.Data != null)
|
|
|
|
|
{
|
2025-03-12 14:51:47 +03:00
|
|
|
_lastError = e.Data;
|
2025-05-05 20:43:28 +03:00
|
|
|
_processLogger?.Error(e.Data);
|
2025-01-07 19:14:42 +03:00
|
|
|
CurrLog.Append(e.Data);
|
|
|
|
|
}
|
2025-01-07 17:01:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
|
|
|
|
{
|
2025-01-07 19:14:42 +03:00
|
|
|
if (e.Data != null)
|
|
|
|
|
{
|
2025-05-05 20:43:28 +03:00
|
|
|
_processLogger?.Log(e.Data);
|
2025-01-07 19:14:42 +03:00
|
|
|
CurrLog.Append(e.Data);
|
|
|
|
|
}
|
2025-01-07 17:01:00 +03:00
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
|
2025-01-05 17:05:23 +03:00
|
|
|
public void ReadLog()
|
2024-12-27 19:15:33 +03:00
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
PopupMessageService.Popup(CurrLog);
|
2024-12-27 19:15:33 +03:00
|
|
|
}
|
|
|
|
|
|
2025-01-05 17:05:23 +03:00
|
|
|
public void StopInstance()
|
2024-12-27 19:15:33 +03:00
|
|
|
{
|
2025-01-08 18:00:06 +03:00
|
|
|
Process?.CloseMainWindow();
|
2025-01-07 17:01:00 +03:00
|
|
|
}
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2025-01-17 16:23:50 +03:00
|
|
|
public async void ExpandInfoRequired()
|
|
|
|
|
{
|
|
|
|
|
ExpandInfo = !ExpandInfo;
|
2025-05-02 20:06:33 +03:00
|
|
|
if (Design.IsDesignMode) return;
|
|
|
|
|
|
2025-01-17 16:23:50 +03:00
|
|
|
var info = await GetServerInfo();
|
2025-05-02 20:06:33 +03:00
|
|
|
if (info == null) return;
|
|
|
|
|
|
2025-01-27 15:55:30 +03:00
|
|
|
Description = info.Desc;
|
|
|
|
|
|
|
|
|
|
Links.Clear();
|
2025-05-02 20:06:33 +03:00
|
|
|
if (info.Links is null) return;
|
|
|
|
|
foreach (var link in info.Links) Links.Add(link);
|
2025-01-17 16:23:50 +03:00
|
|
|
}
|
|
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
private static string FindDotnetPath()
|
2025-01-07 17:01:00 +03:00
|
|
|
{
|
|
|
|
|
var pathEnv = Environment.GetEnvironmentVariable("PATH");
|
2025-01-14 22:10:16 +03:00
|
|
|
var paths = pathEnv?.Split(Path.PathSeparator);
|
2025-01-07 17:01:00 +03:00
|
|
|
if (paths != null)
|
|
|
|
|
foreach (var path in paths)
|
|
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
var dotnetPath = Path.Combine(path, "dotnet");
|
|
|
|
|
if (File.Exists(dotnetPath)) return dotnetPath;
|
2025-01-07 17:01:00 +03:00
|
|
|
}
|
|
|
|
|
|
2025-03-13 14:58:47 +03:00
|
|
|
return "dotnet";
|
2024-12-27 19:15:33 +03:00
|
|
|
}
|
2025-01-07 19:14:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sealed class LogInfo
|
|
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
public string Category { get; set; } = "LOG";
|
2025-01-07 19:14:42 +03:00
|
|
|
public IBrush CategoryColor { get; set; } = Brush.Parse("#424242");
|
|
|
|
|
public string Message { get; set; } = "";
|
|
|
|
|
|
|
|
|
|
public static LogInfo FromString(string input)
|
|
|
|
|
{
|
|
|
|
|
var matches = Regex.Matches(input, @"(\[(?<c>.*)\] (?<m>.*))|(?<m>.*)");
|
2025-01-14 22:10:16 +03:00
|
|
|
var category = "All";
|
2025-01-07 19:14:42 +03:00
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
if (matches[0].Groups.TryGetValue("c", out var c)) category = c.Value;
|
2025-01-07 19:14:42 +03:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2025-01-07 19:14:42 +03:00
|
|
|
var message = matches[0].Groups["m"].Value;
|
2025-01-14 22:10:16 +03:00
|
|
|
return new LogInfo
|
2025-01-07 19:14:42 +03:00
|
|
|
{
|
|
|
|
|
Category = category, Message = message, CategoryColor = color
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-01-27 15:55:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class LinkGoCommand : ICommand
|
|
|
|
|
{
|
|
|
|
|
public LinkGoCommand()
|
|
|
|
|
{
|
|
|
|
|
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
|
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
|
2025-01-27 15:55:30 +03:00
|
|
|
public bool CanExecute(object? parameter)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Execute(object? parameter)
|
|
|
|
|
{
|
2025-05-02 20:06:33 +03:00
|
|
|
if (parameter is not string str) return;
|
2025-01-27 15:55:30 +03:00
|
|
|
Helper.SafeOpenBrowser(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event EventHandler? CanExecuteChanged;
|
2025-06-14 22:33:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IFilterConsumer
|
|
|
|
|
{
|
|
|
|
|
public void ProcessFilter(ServerFilter? serverFilter);
|
2024-12-27 19:15:33 +03:00
|
|
|
}
|