- shit: Cleanup this mess

This commit is contained in:
2025-01-14 22:10:16 +03:00
parent a0b2cfd677
commit 08e518602b
71 changed files with 1022 additions and 943 deletions

View File

@@ -1,19 +1,16 @@
using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using System.Linq;
using System.Reflection;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.Views;
using Nebula.Shared;
namespace Nebula.Launcher;
public partial class App : Application
public class App : Application
{
private IServiceProvider _serviceProvider = null!;
@@ -26,8 +23,8 @@ public partial class App : Application
{
var services = new ServiceCollection();
services.AddAvaloniaServices();
services.AddViews();
services.AddServices();
services.AddViews();
_serviceProvider = services.BuildServiceProvider();
@@ -52,9 +49,6 @@ public partial class App : Application
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
foreach (var plugin in dataValidationPluginsToRemove) BindingPlugins.DataValidators.Remove(plugin);
}
}

View File

@@ -0,0 +1,2 @@
global using SourceGen;
global using Nebula.Launcher.ViewHelper;

View File

@@ -46,5 +46,6 @@
<ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj"/>
<ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup>
</Project>

View File

@@ -4,13 +4,18 @@ namespace Nebula.Launcher;
public static class Program
{
public static void Main(string[] args) => BuildAvaloniaApp()
public static void Main(string[] args)
{
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
private static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}

View File

@@ -6,10 +6,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.Views;
using Nebula.Launcher.Views.Pages;
namespace Nebula.Launcher;
@@ -18,14 +15,20 @@ public static class ServiceCollectionExtensions
public static void AddAvaloniaServices(this IServiceCollection services)
{
services.AddSingleton<IDispatcher>(_ => Dispatcher.UIThread);
services.AddSingleton(_ => Application.Current?.ApplicationLifetime ?? throw new InvalidOperationException("No application lifetime is set"));
services.AddSingleton(_ =>
Application.Current?.ApplicationLifetime ??
throw new InvalidOperationException("No application lifetime is set"));
services.AddSingleton(sp =>
sp.GetRequiredService<IApplicationLifetime>() switch
{
IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow ?? throw new InvalidOperationException("No main window set"),
ISingleViewApplicationLifetime singleViewPlatform => TopLevel.GetTopLevel(singleViewPlatform.MainView) ?? throw new InvalidOperationException("Could not find top level element for single view"),
_ => throw new InvalidOperationException($"Could not find {nameof(TopLevel)} element"),
IClassicDesktopStyleApplicationLifetime desktop => desktop.MainWindow ??
throw new InvalidOperationException(
"No main window set"),
ISingleViewApplicationLifetime singleViewPlatform =>
TopLevel.GetTopLevel(singleViewPlatform.MainView) ??
throw new InvalidOperationException("Could not find top level element for single view"),
_ => throw new InvalidOperationException($"Could not find {nameof(TopLevel)} element")
}
);
@@ -44,14 +47,12 @@ public static class ServiceCollectionExtensions
}
}
private static IEnumerable<(Type,Type?,bool)> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes())
private static IEnumerable<(Type, Type?, bool)> GetTypesWithHelpAttribute(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var attr = type.GetCustomAttribute<ViewModelRegisterAttribute>();
if (attr is not null) {
yield return (type, attr.Type, attr.IsSingleton);
if (attr is not null) yield return (type, attr.Type, attr.IsSingleton);
}
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using Nebula.Launcher.ViewModels;
using Nebula.Shared;
namespace Nebula.Launcher.Services;
[ServiceRegister, ConstructGenerator]
public sealed partial class ViewHelperService
{
[GenerateProperty] private IServiceProvider ServiceProvider { get; } = default!;
public bool TryGetViewModel(Type type, [NotNullWhen(true)] out ViewModelBase? viewModelBase)
{
viewModelBase = null;
var vm = Design.IsDesignMode
? Activator.CreateInstance(type)
: ServiceProvider.GetService(type);
if (vm is not ViewModelBase vmb) return false;
viewModelBase = vmb;
return true;
}
public bool TryGetViewModel<T>([NotNullWhen(true)] out T? viewModelBase) where T : ViewModelBase
{
var success = TryGetViewModel(typeof(T), out var vmb);
viewModelBase = (T?)vmb;
return success;
}
public T GetViewModel<T>() where T : ViewModelBase
{
TryGetViewModel<T>(out var viewModelBase);
return viewModelBase!;
}
private void Initialise(){}
private void InitialiseInDesignMode(){}
}

View File

@@ -5,12 +5,12 @@ namespace Nebula.Launcher.ViewHelper;
[AttributeUsage(AttributeTargets.Class)]
public class ViewModelRegisterAttribute : Attribute
{
public Type? Type { get; }
public bool IsSingleton { get; }
public ViewModelRegisterAttribute(Type? type = null, bool isSingleton = true)
{
Type = type;
IsSingleton = isSingleton;
}
public Type? Type { get; }
public bool IsSingleton { get; }
}

View File

@@ -2,7 +2,6 @@ using System;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.ViewModels;
namespace Nebula.Launcher;
@@ -16,10 +15,7 @@ public class ViewLocator : IDataTemplate
var type = param.GetType().GetCustomAttribute<ViewModelRegisterAttribute>()?.Type;
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
if (type != null) return (Control)Activator.CreateInstance(type)!;
return new TextBlock { Text = "Not Found: " + param.GetType() };
}

View File

@@ -1,24 +0,0 @@
using System;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Popup;
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(InfoPopupView), false)]
public partial class InfoPopupViewModel : PopupViewModelBase
{
public InfoPopupViewModel()
{
}
public InfoPopupViewModel(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override string Title => "Info";
public override bool IsClosable => true;
[ObservableProperty]
private string _infoText = "Test";
}

View File

@@ -4,8 +4,9 @@ using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.Pages;
using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
@@ -14,34 +15,9 @@ using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(MainView))]
[ConstructGenerator]
public partial class MainViewModel : ViewModelBase
{
public MainViewModel()
{
TryGetViewModel(typeof(AccountInfoViewModel), out var model);
_currentPage = model!;
Items = new ObservableCollection<ListItemTemplate>(_templates);
SelectedListItem = Items.First(vm => vm.ModelType == typeof(AccountInfoViewModel));
}
[UsedImplicitly]
public MainViewModel(AccountInfoViewModel accountInfoViewModel,DebugService debugService, PopupMessageService popupMessageService,
IServiceProvider serviceProvider): base(serviceProvider)
{
_currentPage = accountInfoViewModel;
_debugService = debugService;
Items = new ObservableCollection<ListItemTemplate>(_templates);
popupMessageService.OnPopupRequired += OnPopupRequired;
popupMessageService.OnCloseRequired += OnPopupCloseRequired;
SelectedListItem = Items.First(vm => vm.ModelType == typeof(AccountInfoViewModel));
}
private readonly List<PopupViewModelBase> _viewQueue = new();
private readonly List<ListItemTemplate> _templates =
[
new ListItemTemplate(typeof(AccountInfoViewModel), "Account", "Account"),
@@ -49,41 +25,57 @@ public partial class MainViewModel : ViewModelBase
new ListItemTemplate(typeof(ContentBrowserViewModel), "GridRegular", "Content")
];
[ObservableProperty]
private bool _isPaneOpen;
private readonly List<PopupViewModelBase> _viewQueue = new();
[ObservableProperty]
private ViewModelBase _currentPage;
[ObservableProperty] private ViewModelBase _currentPage;
private readonly DebugService _debugService;
[ObservableProperty] private PopupViewModelBase? _currentPopup;
[ObservableProperty] private string _currentTitle = "Default";
[ObservableProperty] private bool _isEnabled = true;
[ObservableProperty] private bool _popup;
[ObservableProperty]
private PopupViewModelBase? _currentPopup;
[ObservableProperty]
private string _currentTitle = "Default";
[ObservableProperty] private bool _isPaneOpen;
[ObservableProperty] private bool _isPopupClosable = true;
[ObservableProperty] private bool _popup;
[ObservableProperty]
private ListItemTemplate? _selectedListItem;
[ObservableProperty] private ListItemTemplate? _selectedListItem;
[GenerateProperty] private DebugService DebugService { get; } = default!;
[GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!;
[GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!;
public ObservableCollection<ListItemTemplate> Items { get; private set; }
protected override void InitialiseInDesignMode()
{
CurrentPage = ViewHelperService.GetViewModel<AccountInfoViewModel>();
Items = new ObservableCollection<ListItemTemplate>(_templates);
SelectedListItem = Items.First(vm => vm.ModelType == typeof(AccountInfoViewModel));
}
protected override void Initialise()
{
CurrentPage = ViewHelperService.GetViewModel<AccountInfoViewModel>();
Items = new ObservableCollection<ListItemTemplate>(_templates);
PopupMessageService.OnPopupRequired += OnPopupRequired;
PopupMessageService.OnCloseRequired += OnPopupCloseRequired;
SelectedListItem = Items.First(vm => vm.ModelType == typeof(AccountInfoViewModel));
}
partial void OnSelectedListItemChanged(ListItemTemplate? value)
{
if (value is null) return;
if(!TryGetViewModel(value.ModelType, out var vmb))
{
return;
}
if (!ViewHelperService.TryGetViewModel(value.ModelType, out var vmb)) return;
CurrentPage = vmb;
}
public ObservableCollection<ListItemTemplate> Items { get; }
public void PopupMessage(PopupViewModelBase viewModelBase)
{
if (CurrentPopup == null)
@@ -122,7 +114,7 @@ public partial class MainViewModel : ViewModelBase
{
case string str:
{
var view = GetViewModel<InfoPopupViewModel>();
var view = ViewHelperService.GetViewModel<InfoPopupViewModel>();
view.InfoText = str;
PopupMessage(view);
break;
@@ -131,8 +123,8 @@ public partial class MainViewModel : ViewModelBase
PopupMessage(@base);
break;
case Exception error:
var err = GetViewModel<ExceptionViewModel>();
_debugService.Error(error);
var err = ViewHelperService.GetViewModel<ExceptionViewModel>();
DebugService.Error(error);
err.AppendError(error);
PopupMessage(err);
break;
@@ -141,10 +133,7 @@ public partial class MainViewModel : ViewModelBase
private void OnPopupCloseRequired(object obj)
{
if(obj is not PopupViewModelBase viewModelBase)
{
return;
}
if (obj is not PopupViewModelBase viewModelBase) return;
if (obj == CurrentPopup)
ClosePopup();
@@ -164,7 +153,9 @@ public partial class MainViewModel : ViewModelBase
{
var viewModelBase = _viewQueue.FirstOrDefault();
if (viewModelBase is null)
{
OnCloseRequired();
}
else
{
CurrentTitle = viewModelBase.Title;
@@ -174,4 +165,3 @@ public partial class MainViewModel : ViewModelBase
CurrentPopup = viewModelBase;
}
}

View File

@@ -1,46 +1,44 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared;
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Pages;
[ViewModelRegister(typeof(AccountInfoView))]
[ConstructGenerator]
public partial class AccountInfoViewModel : ViewModelBase
{
private readonly PopupMessageService _popupMessageService;
private readonly ConfigurationService _configurationService;
private readonly AuthService _authService;
public ObservableCollection<AuthLoginPasswordModel> Accounts { get; } = new();
public ObservableCollection<string> AuthUrls { get; } = new();
[ObservableProperty]
private string _currentLogin = String.Empty;
[ObservableProperty]
private string _currentPassword = String.Empty;
[ObservableProperty]
private string _currentAuthServer = String.Empty;
[ObservableProperty] private bool _authMenuExpand;
[ObservableProperty] private bool _authUrlConfigExpand;
[ObservableProperty] private int _authViewSpan = 1;
[ObservableProperty] private bool _authMenuExpand;
[ObservableProperty] private string _currentAuthServer = string.Empty;
private bool _isProfilesEmpty;
[ObservableProperty] private string _currentLogin = string.Empty;
[ObservableProperty] private string _currentPassword = string.Empty;
[ObservableProperty] private bool _isLogged;
private bool _isProfilesEmpty;
[GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!;
[GenerateProperty] private ConfigurationService ConfigurationService { get; } = default!;
[GenerateProperty] private AuthService AuthService { get; } = default!;
[GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!;
public ObservableCollection<AuthLoginPasswordModel> Accounts { get; } = new();
public ObservableCollection<string> AuthUrls { get; } = new();
private AuthLoginPassword CurrentAlp
{
get => new(CurrentLogin, CurrentPassword, CurrentAuthServer);
@@ -52,8 +50,13 @@ public partial class AccountInfoViewModel : ViewModelBase
}
}
public string AuthItemSelect
{
set => CurrentAuthServer = value;
}
//Design think
public AccountInfoViewModel()
protected override void InitialiseInDesignMode()
{
AddAccount(new AuthLoginPassword("Binka", "12341", ""));
AuthUrls.Add("https://cinka.ru");
@@ -61,14 +64,8 @@ public partial class AccountInfoViewModel : ViewModelBase
}
//Real think
public AccountInfoViewModel(IServiceProvider serviceProvider, PopupMessageService popupMessageService,
ConfigurationService configurationService, AuthService authService) : base(serviceProvider)
protected override void Initialise()
{
//_popupMessageService = mainViewModel;
_popupMessageService = popupMessageService;
_configurationService = configurationService;
_authService = authService;
ReadAuthConfig();
}
@@ -80,42 +77,37 @@ public partial class AccountInfoViewModel : ViewModelBase
public async void DoAuth()
{
var message = GetViewModel<InfoPopupViewModel>();
var message = ViewHelperService.GetViewModel<InfoPopupViewModel>();
message.InfoText = "Auth think, please wait...";
_popupMessageService.Popup(message);
PopupMessageService.Popup(message);
if(await _authService.Auth(CurrentAlp))
if (await AuthService.Auth(CurrentAlp))
{
message.Dispose();
IsLogged = true;
_configurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp);
ConfigurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp);
}
else
{
message.Dispose();
Logout();
_popupMessageService.Popup("Well, shit is happened: " + _authService.Reason);
PopupMessageService.Popup("Well, shit is happened: " + AuthService.Reason);
}
}
public void Logout()
{
IsLogged = false;
//CurrentAlp = new AuthLoginPassword("", "", "");
_authService.ClearAuth();
AuthService.ClearAuth();
}
private void UpdateAuthMenu()
{
if (AuthMenuExpand || _isProfilesEmpty)
{
AuthViewSpan = 2;
}
else
{
AuthViewSpan = 1;
}
}
private void AddAccount(AuthLoginPassword authLoginPassword)
{
@@ -138,17 +130,12 @@ public partial class AccountInfoViewModel : ViewModelBase
private void ReadAuthConfig()
{
foreach (var profile in
_configurationService.GetConfigValue(CurrentConVar.AuthProfiles)!)
{
ConfigurationService.GetConfigValue(CurrentConVar.AuthProfiles)!)
AddAccount(profile);
}
if (Accounts.Count == 0)
{
UpdateAuthMenu();
}
if (Accounts.Count == 0) UpdateAuthMenu();
var currProfile = _configurationService.GetConfigValue(CurrentConVar.AuthCurrent);
var currProfile = ConfigurationService.GetConfigValue(CurrentConVar.AuthCurrent);
if (currProfile != null)
{
@@ -157,11 +144,8 @@ public partial class AccountInfoViewModel : ViewModelBase
}
AuthUrls.Clear();
var authUrls = _configurationService.GetConfigValue(CurrentConVar.AuthServers)!;
foreach (var url in authUrls)
{
AuthUrls.Add(url);
}
var authUrls = ConfigurationService.GetConfigValue(CurrentConVar.AuthServers)!;
foreach (var url in authUrls) AuthUrls.Add(url);
}
[RelayCommand]
@@ -196,15 +180,15 @@ public partial class AccountInfoViewModel : ViewModelBase
private void DirtyProfile()
{
_configurationService.SetConfigValue(CurrentConVar.AuthProfiles,
ConfigurationService.SetConfigValue(CurrentConVar.AuthProfiles,
Accounts.Select(a => (AuthLoginPassword)a).ToArray());
}
public string AuthItemSelect
{
set => CurrentAuthServer = value;
}
}
public record AuthLoginPasswordModel(string Login, string Password, string AuthServer, ICommand OnSelect = default!, ICommand OnDelete = default!)
public record AuthLoginPasswordModel(
string Login,
string Password,
string AuthServer,
ICommand OnSelect = default!,
ICommand OnDelete = default!)
: AuthLoginPassword(Login, Password, AuthServer);

View File

@@ -11,35 +11,37 @@ using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Pages;
[ViewModelRegister(typeof(ContentBrowserView))]
[ConstructGenerator]
public sealed partial class ContentBrowserViewModel : ViewModelBase
{
private readonly IServiceProvider _provider;
private readonly ContentService _contentService;
private readonly CancellationService _cancellationService;
private readonly FileService _fileService;
private readonly DebugService _debugService;
private readonly PopupMessageService _popupService;
public ObservableCollection<ContentEntry> Entries { get; } = new();
private readonly List<ContentEntry> _root = new();
private List<string> _history = new();
private readonly List<string> _history = new();
[ObservableProperty] private string _message = "";
[ObservableProperty] private string _searchText = "";
[ObservableProperty] private string _serverText = "";
private ContentEntry? _selectedEntry;
[ObservableProperty] private string _serverText = "";
[GenerateProperty] private ContentService ContentService { get; } = default!;
[GenerateProperty] private CancellationService CancellationService { get; } = default!;
[GenerateProperty] private FileService FileService { get; } = default!;
[GenerateProperty] private DebugService DebugService { get; } = default!;
[GenerateProperty] private PopupMessageService PopupService { get; } = default!;
[GenerateProperty] private HubService HubService { get; } = default!;
[GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!;
public ObservableCollection<ContentEntry> Entries { get; } = new();
public ContentEntry? SelectedEntry
{
@@ -48,7 +50,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
{
if (value is { Item: not null })
{
if (_fileService.ContentFileApi.TryOpen(value.Item.Value.Hash, out var stream))
if (FileService.ContentFileApi.TryOpen(value.Item.Value.Hash, out var stream))
{
var ext = Path.GetExtension(value.Item.Value.Path);
@@ -58,6 +60,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
{
stream.CopyTo(sw);
}
stream.Dispose();
var startInfo = new ProcessStartInfo(myTempFile)
@@ -67,6 +70,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
Process.Start(startInfo);
}
return;
}
@@ -75,15 +79,12 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
if (value == null) return;
foreach (var (_, entryCh) in value.Childs)
{
Entries.Add(entryCh);
}
foreach (var (_, entryCh) in value.Childs) Entries.Add(entryCh);
}
}
public ContentBrowserViewModel() : base()
protected override void InitialiseInDesignMode()
{
var a = new ContentEntry(this, "A:", "A", "");
var b = new ContentEntry(this, "B", "B", "");
@@ -91,51 +92,31 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
Entries.Add(a);
}
public ContentBrowserViewModel(IServiceProvider provider, ContentService contentService, CancellationService cancellationService,
FileService fileService, HubService hubService, DebugService debugService, PopupMessageService popupService) : base(provider)
protected override void Initialise()
{
_provider = provider;
_contentService = contentService;
_cancellationService = cancellationService;
_fileService = fileService;
_debugService = debugService;
_popupService = popupService;
FillRoot(HubService.ServerList);
FillRoot(hubService.ServerList);
HubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
HubService.HubServerLoaded += GoHome;
hubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
hubService.HubServerLoaded += GoHome;
if (!hubService.IsUpdating)
{
GoHome();
}
if (!HubService.IsUpdating) GoHome();
}
private void GoHome()
{
SelectedEntry = null;
foreach (var entry in _root)
{
Entries.Add(entry);
}
foreach (var entry in _root) Entries.Add(entry);
}
private void HubServerChangedEventArgs(HubServerChangedEventArgs obj)
{
if (obj.Action == HubServerChangeAction.Clear) _root.Clear();
if (obj.Action == HubServerChangeAction.Add)
{
FillRoot(obj.Items);
};
if (obj.Action == HubServerChangeAction.Add) FillRoot(obj.Items);
}
private void FillRoot(IEnumerable<ServerHubInfo> infos)
{
foreach (var info in infos)
{
_root.Add(new ContentEntry(this, info.StatusData.Name,info.Address,info.Address));
}
foreach (var info in infos) _root.Add(new ContentEntry(this, info.StatusData.Name, info.Address, info.Address));
}
public async void Go(ContentPath path, bool appendHistory = true)
@@ -153,25 +134,17 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
return;
}
if (ServerText != SelectedEntry?.ServerName)
{
SelectedEntry = await CreateEntry(ServerText);
}
if (ServerText != SelectedEntry?.ServerName) SelectedEntry = await CreateEntry(ServerText);
_debugService.Debug("Going to:" + path.Path);
DebugService.Debug("Going to:" + path.Path);
var oriPath = path.Clone();
try
{
if (SelectedEntry == null || !SelectedEntry.GetRoot().TryGetEntry(path, out var centry))
{
throw new Exception("Not found! " + oriPath.Path);
}
if (appendHistory)
{
AppendHistory(SearchText);
}
if (appendHistory) AppendHistory(SearchText);
SearchText = oriPath.Path;
SelectedEntry = centry;
@@ -180,7 +153,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
{
Console.WriteLine(e);
SearchText = oriPath.Path;
_popupService.Popup(e);
PopupService.Popup(e);
}
}
@@ -198,12 +171,12 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
private async Task<ContentEntry> CreateEntry(string serverUrl)
{
var rurl = serverUrl.ToRobustUrl();
var info = await _contentService.GetBuildInfo(rurl, _cancellationService.Token);
var loading = _provider.GetService<LoadingContextViewModel>()!;
var info = await ContentService.GetBuildInfo(rurl, CancellationService.Token);
var loading = ViewHelperService.GetViewModel<LoadingContextViewModel>();
loading.LoadingName = "Loading entry";
_popupService.Popup(loading);
var items = await _contentService.EnsureItems(info.RobustManifestInfo, loading,
_cancellationService.Token);
PopupService.Popup(loading);
var items = await ContentService.EnsureItems(info.RobustManifestInfo, loading,
CancellationService.Token);
var rootEntry = new ContentEntry(this, "", "", serverUrl);
@@ -235,24 +208,32 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
public class ContentEntry
{
private readonly ContentBrowserViewModel _viewModel;
public static IImage DirImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/dir.png")));
public static IImage IconImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/file.png")));
private readonly Dictionary<string, ContentEntry> _childs = new();
private readonly ContentBrowserViewModel _viewModel;
public RobustManifestItem? Item;
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string pathName, string serverName)
{
Name = name;
ServerName = serverName;
PathName = pathName;
_viewModel = viewModel;
}
public bool IsDirectory => Item == null;
public string Name { get; private set; }
public string PathName { get; private set; }
public string ServerName { get; private set; }
public string PathName { get; }
public string ServerName { get; }
public IImage IconPath { get; set; } = DirImage;
public ContentEntry? Parent { get; private set; }
public bool IsRoot => Parent == null;
private readonly Dictionary<string, ContentEntry> _childs = new();
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public bool TryGetChild(string name, [NotNullWhen(true)] out ContentEntry? child)
@@ -271,14 +252,6 @@ public class ContentEntry
return false;
}
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string pathName, string serverName)
{
Name = name;
ServerName = serverName;
PathName = pathName;
_viewModel = viewModel;
}
public ContentPath GetPath()
{
if (Parent != null)
@@ -287,6 +260,7 @@ public class ContentEntry
path.Pathes.Add(PathName);
return path;
}
return new ContentPath([PathName]);
}
@@ -339,10 +313,7 @@ public class ContentEntry
var fName = path.GetNext();
if(!TryGetChild(fName, out var child))
{
return false;
}
if (!TryGetChild(fName, out var child)) return false;
return child.TryGetEntry(path, out entry);
}
@@ -353,14 +324,13 @@ public class ContentEntry
}
}
public struct ContentPath
{
public List<string> Pathes { get; private set; }
public List<string> Pathes { get; }
public ContentPath(List<string> pathes)
{
Pathes = pathes ?? new List<string>();
Pathes = pathes;
}
public ContentPath(string path)

View File

@@ -1,13 +1,17 @@
using System;
using System.Collections.ObjectModel;
using Nebula.Shared.Models;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Pages;
public class FavoriteServerListViewModel : ViewModelBase
{
public FavoriteServerListViewModel() : base(){}
public FavoriteServerListViewModel(IServiceProvider provider) : base(provider){}
public ObservableCollection<ServerHubInfo> Servers = new();
protected override void Initialise()
{
}
protected override void InitialiseInDesignMode()
{
}
}

View File

@@ -4,50 +4,42 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Services;
using Nebula.Launcher.Views.Pages;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Pages;
[ViewModelRegister(typeof(ServerListView))]
[ConstructGenerator]
public partial class ServerListViewModel : ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly HubService _hubService;
public ObservableCollection<ServerEntryModelView> ServerInfos { get; } = new();
[ObservableProperty] private string _searchText = string.Empty;
public Action? OnSearchChange;
[ObservableProperty] private string _searchText;
[GenerateProperty] private HubService HubService { get; } = default!;
[GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!;
public ObservableCollection<ServerEntryModelView> ServerInfos { get; } = new();
private List<ServerHubInfo> UnsortedServers { get; } = new();
//Design think
public ServerListViewModel()
protected override void InitialiseInDesignMode()
{
ServerInfos.Add(CreateServerView(new ServerHubInfo("ss14://localhost",new ServerStatus("Nebula","TestCraft", ["16+","RU"], "super", 12,55,1,false,DateTime.Now, 20),[])));
ServerInfos.Add(CreateServerView(new ServerHubInfo("ss14://localhost",
new ServerStatus("Nebula", "TestCraft", ["16+", "RU"], "super", 12, 55, 1, false, DateTime.Now, 20), [])));
}
//real think
public ServerListViewModel(IServiceProvider serviceProvider, HubService hubService) : base(serviceProvider)
protected override void Initialise()
{
_serviceProvider = serviceProvider;
_hubService = hubService;
foreach (var info in HubService.ServerList) UnsortedServers.Add(info);
foreach (var info in _hubService.ServerList)
{
UnsortedServers.Add(info);
}
hubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
hubService.HubServerLoaded += HubServerLoaded;
HubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
HubService.HubServerLoaded += HubServerLoaded;
OnSearchChange += OnChangeSearch;
if (!hubService.IsUpdating)
{
SortServers();
}
if (!HubService.IsUpdating) SortServers();
}
private void HubServerLoaded()
@@ -63,23 +55,12 @@ public partial class ServerListViewModel : ViewModelBase
private void HubServerChangedEventArgs(HubServerChangedEventArgs obj)
{
if (obj.Action == HubServerChangeAction.Add)
{
foreach (var info in obj.Items)
{
UnsortedServers.Add(info);
}
}
if (obj.Action == HubServerChangeAction.Remove)
{
foreach (var info in obj.Items)
{
UnsortedServers.Remove(info);
}
}
if(obj.Action == HubServerChangeAction.Clear)
{
UnsortedServers.Clear();
}
if (obj.Action == HubServerChangeAction.Clear) UnsortedServers.Clear();
}
private void SortServers()
@@ -88,10 +69,7 @@ public partial class ServerListViewModel : ViewModelBase
{
ServerInfos.Clear();
UnsortedServers.Sort(new ServerComparer());
foreach (var server in UnsortedServers.Where(CheckServerThink))
{
ServerInfos.Add(CreateServerView(server));
}
foreach (var server in UnsortedServers.Where(CheckServerThink)) ServerInfos.Add(CreateServerView(server));
});
}
@@ -103,19 +81,18 @@ public partial class ServerListViewModel : ViewModelBase
private ServerEntryModelView CreateServerView(ServerHubInfo serverHubInfo)
{
var svn = GetViewModel<ServerEntryModelView>();
var svn = ViewHelperService.GetViewModel<ServerEntryModelView>();
svn.ServerHubInfo = serverHubInfo;
return svn;
}
public void FilterRequired()
{
}
public void UpdateRequired()
{
Task.Run(_hubService.UpdateHub);
Task.Run(HubService.UpdateHub);
}
}
@@ -131,6 +108,5 @@ public class ServerComparer : IComparer<ServerHubInfo>
return -1;
return y.StatusData.Players.CompareTo(x.StatusData.Players);
}
}

View File

@@ -1,27 +1,30 @@
using System;
using System.Collections.ObjectModel;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Popup;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Popup;
[ViewModelRegister(typeof(ExceptionView), false)]
public class ExceptionViewModel : PopupViewModelBase
[ConstructGenerator]
public sealed partial class ExceptionViewModel : PopupViewModelBase
{
public ExceptionViewModel() : base()
{
var e = new Exception("TEST");
AppendError(e);
}
public ExceptionViewModel(IServiceProvider serviceProvider) : base(serviceProvider){}
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
public override string Title => "Oopsie! Some shit is happened now!";
public override bool IsClosable => true;
public ObservableCollection<Exception> Errors { get; } = new();
protected override void Initialise()
{
}
protected override void InitialiseInDesignMode()
{
var e = new Exception("TEST");
AppendError(e);
}
public void AppendError(Exception exception)
{
Errors.Add(exception);

View File

@@ -0,0 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.Views.Popup;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels.Popup;
[ViewModelRegister(typeof(InfoPopupView), false)]
[ConstructGenerator]
public partial class InfoPopupViewModel : PopupViewModelBase
{
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
[ObservableProperty] private string _infoText = "Test";
public override string Title => "Info";
public override bool IsClosable => true;
protected override void Initialise()
{
}
protected override void InitialiseInDesignMode()
{
}
}

View File

@@ -1,27 +1,25 @@
using System;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Popup;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.ViewModels.Popup;
[ViewModelRegister(typeof(LoadingContextView), false)]
[ConstructGenerator]
public sealed partial class LoadingContextViewModel : PopupViewModelBase, ILoadingHandler
{
public LoadingContextViewModel() :base(){}
public LoadingContextViewModel(IServiceProvider provider) : base(provider){}
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
[ObservableProperty] private int _currJobs;
[ObservableProperty] private int _resolvedJobs;
public string LoadingName { get; set; } = "Loading...";
public override bool IsClosable => false;
public override string Title => LoadingName;
[ObservableProperty]
private int _currJobs;
[ObservableProperty]
private int _resolvedJobs;
public void SetJobsCount(int count)
{
CurrJobs = count;
@@ -35,11 +33,18 @@ public sealed partial class LoadingContextViewModel : PopupViewModelBase, ILoadi
public void SetResolvedJobsCount(int count)
{
ResolvedJobs = count;
}
public int GetResolvedJobsCount()
{
return ResolvedJobs;
}
protected override void Initialise()
{
}
protected override void InitialiseInDesignMode()
{
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections.ObjectModel;
using Nebula.Launcher.Views.Popup;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels.Popup;
[ViewModelRegister(typeof(LogPopupView), false)]
[ConstructGenerator]
public sealed partial class LogPopupModelView : PopupViewModelBase
{
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
public override string Title => "LOG";
public override bool IsClosable => true;
public ObservableCollection<LogInfo> Logs { get; } = new();
protected override void InitialiseInDesignMode()
{
Logs.Add(new LogInfo
{
Category = "DEBG", Message = "MEOW MEOW TEST"
});
Logs.Add(new LogInfo
{
Category = "ERRO", Message = "MEOW MEOW TEST 11\naaaaa"
});
}
protected override void Initialise()
{
}
public void Append(string str)
{
Logs.Add(LogInfo.FromString(str));
}
}

View File

@@ -0,0 +1,17 @@
using System;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels.Popup;
public abstract class PopupViewModelBase : ViewModelBase, IDisposable
{
public abstract PopupMessageService PopupMessageService { get; }
public abstract string Title { get; }
public abstract bool IsClosable { get; }
public void Dispose()
{
PopupMessageService.ClosePopup(this);
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
public abstract class PopupViewModelBase : ViewModelBase, IDisposable
{
private readonly IServiceProvider _serviceProvider;
public abstract string Title { get; }
public abstract bool IsClosable { get; }
public PopupViewModelBase()
{
}
public PopupViewModelBase(IServiceProvider serviceProvider) : base(serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Dispose()
{
_serviceProvider.GetService<PopupMessageService>()?.ClosePopup(this);
}
}

View File

@@ -1,37 +1,40 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Popup;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.Popup;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(isSingleton: false)]
[ConstructGenerator]
public partial class ServerEntryModelView : ViewModelBase
{
private readonly AuthService _authService = default!;
private readonly ContentService _contentService = default!;
private readonly CancellationService _cancellationService = default!;
private readonly DebugService _debugService = default!;
private readonly RunnerService _runnerService = default!;
private readonly PopupMessageService _popupMessageService;
private Process? _p;
public LogPopupModelView CurrLog;
[GenerateProperty] private AuthService AuthService { get; } = default!;
[GenerateProperty] private ContentService ContentService { 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!;
public bool RunVisible => Process == null;
public ServerHubInfo ServerHubInfo { get; set; } = default!;
private Process? _p;
private Process? Process
{
get { return _p; }
get => _p;
set
{
_p = value;
@@ -39,32 +42,14 @@ public partial class ServerEntryModelView : ViewModelBase
}
}
public LogPopupModelView CurrLog;
public ServerEntryModelView() : base()
protected override void InitialiseInDesignMode()
{
CurrLog = GetViewModel<LogPopupModelView>();
CurrLog = ViewHelperService.GetViewModel<LogPopupModelView>();
}
public ServerEntryModelView(
IServiceProvider serviceProvider,
AuthService authService,
ContentService contentService,
CancellationService cancellationService,
DebugService debugService,
RunnerService runnerService, PopupMessageService popupMessageService
) : base(serviceProvider)
protected override void Initialise()
{
_authService = authService;
_contentService = contentService;
_cancellationService = cancellationService;
_debugService = debugService;
_runnerService = runnerService;
_popupMessageService = popupMessageService;
CurrLog = GetViewModel<LogPopupModelView>();
CurrLog = ViewHelperService.GetViewModel<LogPopupModelView>();
}
public void RunInstance()
@@ -76,22 +61,21 @@ public partial class ServerEntryModelView : ViewModelBase
{
try
{
var authProv = _authService.SelectedAuth;
var authProv = AuthService.SelectedAuth;
var buildInfo =
await _contentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), _cancellationService.Token);
await ContentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), CancellationService.Token);
using (var loadingContext = GetViewModel<LoadingContextViewModel>())
using (var loadingContext = ViewHelperService.GetViewModel<LoadingContextViewModel>())
{
loadingContext.LoadingName = "Loading instance...";
((ILoadingHandler)loadingContext).AppendJob();
_popupMessageService.Popup(loadingContext);
PopupMessageService.Popup(loadingContext);
await RunnerService.PrepareRun(buildInfo, loadingContext, CancellationService.Token);
await _runnerService.PrepareRun(buildInfo, loadingContext, _cancellationService.Token);
Process = Process.Start(new ProcessStartInfo()
Process = Process.Start(new ProcessStartInfo
{
FileName = "dotnet.exe",
Arguments = "./Nebula.Runner.dll",
@@ -102,7 +86,7 @@ public partial class ServerEntryModelView : ViewModelBase
{ "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer },
{ "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey },
{ "GAME_URL", ServerHubInfo.Address },
{ "AUTH_LOGIN", authProv?.AuthLoginPassword.Login },
{ "AUTH_LOGIN", authProv?.AuthLoginPassword.Login }
},
CreateNoWindow = true,
UseShellExecute = false,
@@ -114,10 +98,7 @@ public partial class ServerEntryModelView : ViewModelBase
((ILoadingHandler)loadingContext).AppendResolvedJob();
}
if (Process is null)
{
return;
}
if (Process is null) return;
Process.EnableRaisingEvents = true;
@@ -129,28 +110,25 @@ public partial class ServerEntryModelView : ViewModelBase
Process.Exited += OnExited;
}
catch (TaskCanceledException e)
catch (TaskCanceledException _)
{
_popupMessageService.Popup("Task canceled");
PopupMessageService.Popup("Task canceled");
}
catch (Exception e)
{
_popupMessageService.Popup(e);
PopupMessageService.Popup(e);
}
}
private void OnExited(object? sender, EventArgs e)
{
if (Process is null)
{
return;
}
if (Process is null) return;
Process.OutputDataReceived -= OnOutputDataReceived;
Process.ErrorDataReceived -= OnErrorDataReceived;
Process.Exited -= OnExited;
_debugService.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
DebugService.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
Process.Dispose();
Process = null;
@@ -160,7 +138,7 @@ public partial class ServerEntryModelView : ViewModelBase
{
if (e.Data != null)
{
_debugService.Error(e.Data);
DebugService.Error(e.Data);
CurrLog.Append(e.Data);
}
}
@@ -169,7 +147,7 @@ public partial class ServerEntryModelView : ViewModelBase
{
if (e.Data != null)
{
_debugService.Log(e.Data);
DebugService.Log(e.Data);
CurrLog.Append(e.Data);
}
}
@@ -177,7 +155,7 @@ public partial class ServerEntryModelView : ViewModelBase
public void ReadLog()
{
_popupMessageService.Popup(CurrLog);
PopupMessageService.Popup(CurrLog);
}
public void StopInstance()
@@ -185,20 +163,15 @@ public partial class ServerEntryModelView : ViewModelBase
Process?.CloseMainWindow();
}
static string FindDotnetPath()
private static string FindDotnetPath()
{
var pathEnv = Environment.GetEnvironmentVariable("PATH");
var paths = pathEnv?.Split(System.IO.Path.PathSeparator);
var paths = pathEnv?.Split(Path.PathSeparator);
if (paths != null)
{
foreach (var path in paths)
{
var dotnetPath = System.IO.Path.Combine(path, "dotnet");
if (System.IO.File.Exists(dotnetPath))
{
return dotnetPath;
}
}
var dotnetPath = Path.Combine(path, "dotnet");
if (File.Exists(dotnetPath)) return dotnetPath;
}
throw new Exception("Dotnet not found!");
@@ -214,12 +187,9 @@ public sealed class LogInfo
public static LogInfo FromString(string input)
{
var matches = Regex.Matches(input, @"(\[(?<c>.*)\] (?<m>.*))|(?<m>.*)");
string category = "All";
var category = "All";
if( matches[0].Groups.TryGetValue("c", out var c))
{
category = c.Value;
}
if (matches[0].Groups.TryGetValue("c", out var c)) category = c.Value;
var color = Brush.Parse("#444444");
@@ -237,40 +207,9 @@ public sealed class LogInfo
}
var message = matches[0].Groups["m"].Value;
return new LogInfo()
return new LogInfo
{
Category = category, Message = message, CategoryColor = color
};
}
}
[ViewModelRegister(typeof(LogPopupView), false)]
public sealed class LogPopupModelView : PopupViewModelBase
{
public LogPopupModelView() : base()
{
Logs.Add(new LogInfo()
{
Category = "DEBG", Message = "MEOW MEOW TEST"
});
Logs.Add(new LogInfo()
{
Category = "ERRO", Message = "MEOW MEOW TEST 11\naaaaa"
});
}
public LogPopupModelView(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override string Title => "LOG";
public override bool IsClosable => true;
public ObservableCollection<LogInfo> Logs { get; } = new();
public void Append(string str)
{
Logs.Add(LogInfo.FromString(str));
}
}

View File

@@ -1,51 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Nebula.Launcher.ViewModels;
public abstract class ViewModelBase : ObservableObject
{
private readonly IServiceProvider _serviceProvider;
public ViewModelBase()
{
AssertDesignMode();
_serviceProvider = default!;
}
public ViewModelBase(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public bool TryGetViewModel(Type type,[NotNullWhen(true)] out ViewModelBase? viewModelBase)
{
viewModelBase = null;
var vm = Design.IsDesignMode
? Activator.CreateInstance(type)
: _serviceProvider.GetService(type);
if (vm is not ViewModelBase vmb) return false;
viewModelBase = vmb;
return true;
}
public bool TryGetViewModel<T>([NotNullWhen(true)] out T? viewModelBase) where T: ViewModelBase
{
var success = TryGetViewModel(typeof(T), out var vmb);
viewModelBase = (T?)vmb;
return success;
}
public T GetViewModel<T>() where T: ViewModelBase
{
TryGetViewModel<T>(out var viewModelBase);
return viewModelBase!;
}
public void AssertDesignMode()
{
if (!Design.IsDesignMode) throw new Exception();
}
protected abstract void InitialiseInDesignMode();
protected abstract void Initialise();
}

View File

@@ -85,7 +85,8 @@
Padding="5">
<Label FontSize="10" Foreground="#777777">
<Panel>
<Button Command="{Binding OpenLink}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0" Padding="0" CornerRadius="0" Background="#00000000">
<Button Command="{Binding OpenLink}" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="0" Padding="0" CornerRadius="0" Background="#00000000">
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center">https://cinka.ru/nebula-launcher/</TextBlock>
</Button>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">prototype-product-v0.01</TextBlock>

View File

@@ -1,15 +1,10 @@
using System;
using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.Views;
public partial class MainView : UserControl
{
// This constructor is used when the view is created by the XAML Previewer
public MainView()
{

View File

@@ -3,14 +3,14 @@
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nebula.Launcher.Views.Pages.AccountInfoView"
x:DataType="viewModels:AccountInfoViewModel"
x:DataType="pages:AccountInfoViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages">
<Design.DataContext>
<viewModels:AccountInfoViewModel />
<pages:AccountInfoViewModel />
</Design.DataContext>
<Grid
ColumnDefinitions="*,1.5*"
@@ -29,7 +29,7 @@
ItemsSource="{Binding Accounts}"
Padding="0">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:AuthLoginPasswordModel}">
<DataTemplate DataType="{x:Type pages:AuthLoginPasswordModel}">
<Border
CornerRadius="0,10,0,10"
Margin="5,5,5,0"

View File

@@ -1,9 +1,10 @@
using Avalonia.Controls;
using Nebula.Launcher.ViewModels;
using AccountInfoViewModel = Nebula.Launcher.ViewModels.Pages.AccountInfoViewModel;
namespace Nebula.Launcher.Views.Pages;
public interface ITab;
public partial class AccountInfoView : UserControl
{
public AccountInfoView()

View File

@@ -3,14 +3,14 @@
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nebula.Launcher.Views.Pages.ContentBrowserView"
x:DataType="viewModels:ContentBrowserViewModel"
x:DataType="pages:ContentBrowserViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages">
<Design.DataContext>
<viewModels:ContentBrowserViewModel />
<pages:ContentBrowserViewModel />
</Design.DataContext>
<Grid
@@ -60,7 +60,7 @@
ItemsSource="{Binding Entries}"
Padding="0,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:ContentEntry}">
<DataTemplate DataType="{x:Type pages:ContentEntry}">
<Button
Command="{Binding OnPathGo}"
CornerRadius="0"

View File

@@ -1,7 +1,5 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels;
using ContentBrowserViewModel = Nebula.Launcher.ViewModels.Pages.ContentBrowserViewModel;
namespace Nebula.Launcher.Views.Pages;

View File

@@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Nebula.Launcher.Views.Pages;

View File

@@ -3,16 +3,17 @@
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nebula.Launcher.Views.Pages.ServerListView"
x:DataType="viewModels:ServerListViewModel"
x:DataType="pages:ServerListViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:Nebula.Launcher.ViewModels.Pages">
<Design.DataContext>
<viewModels:ServerListViewModel />
<pages:ServerListViewModel />
</Design.DataContext>
<Grid
@@ -67,7 +68,8 @@
Grid.Row="1"
MinHeight="50">
<Border.Background>
<ImageBrush Stretch="UniformToFill" asyncImageLoader:ImageBrushLoader.Source="https://t4.ftcdn.net/jpg/00/81/55/69/360_F_81556974_8sF8cKszJaRfBGd5sDt1RXE2QbzDtQqs.jpg" />
<ImageBrush Stretch="UniformToFill"
asyncImageLoader:ImageBrushLoader.Source="https://t4.ftcdn.net/jpg/00/81/55/69/360_F_81556974_8sF8cKszJaRfBGd5sDt1RXE2QbzDtQqs.jpg" />
</Border.Background>
<Border
BorderThickness="0,2,2,0"

View File

@@ -1,5 +1,5 @@
using Avalonia.Controls;
using Nebula.Launcher.ViewModels;
using ServerListViewModel = Nebula.Launcher.ViewModels.Pages.ServerListViewModel;
namespace Nebula.Launcher.Views.Pages;

View File

@@ -2,13 +2,13 @@
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:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:popup="clr-namespace:Nebula.Launcher.ViewModels.Popup"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Popup.ExceptionView"
x:DataType="viewModels:ExceptionViewModel">
x:DataType="popup:ExceptionViewModel">
<Design.DataContext>
<viewModels:ExceptionViewModel />
<popup:ExceptionViewModel />
</Design.DataContext>
<ScrollViewer Margin="10" Padding="0,0,8,0">
<ItemsControl
@@ -20,9 +20,13 @@
<Border Background="#333333" CornerRadius="5" Margin="0,0,0,5">
<StackPanel>
<Border Background="#aa2222" CornerRadius="5,5,0,0">
<Label Margin="4"><TextBlock Text="{Binding Message}"/></Label>
<Label Margin="4">
<TextBlock Text="{Binding Message}" />
</Label>
</Border>
<Label Margin="4"><TextBlock Text="{Binding StackTrace}"/></Label>
<Label Margin="4">
<TextBlock Text="{Binding StackTrace}" />
</Label>
</StackPanel>
</Border>
</DataTemplate>

View File

@@ -1,8 +1,5 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Popup;
namespace Nebula.Launcher.Views.Popup;

View File

@@ -3,14 +3,14 @@
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nebula.Launcher.Views.Popup.InfoPopupView"
x:DataType="viewModels:InfoPopupViewModel"
x:DataType="popup:InfoPopupViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:popup="clr-namespace:Nebula.Launcher.ViewModels.Popup">
<Design.DataContext>
<viewModels:InfoPopupViewModel />
<popup:InfoPopupViewModel />
</Design.DataContext>
<Panel HorizontalAlignment="Center" VerticalAlignment="Center">

View File

@@ -1,7 +1,5 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels;
using InfoPopupViewModel = Nebula.Launcher.ViewModels.Popup.InfoPopupViewModel;
namespace Nebula.Launcher.Views.Popup;

View File

@@ -2,12 +2,12 @@
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:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:popup="clr-namespace:Nebula.Launcher.ViewModels.Popup"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Popup.LoadingContextView"
x:DataType="viewModels:LoadingContextViewModel">
x:DataType="popup:LoadingContextViewModel">
<Design.DataContext>
<viewModels:LoadingContextViewModel />
<popup:LoadingContextViewModel />
</Design.DataContext>
<StackPanel Margin="25" Spacing="15">
<ProgressBar Height="40" Maximum="{Binding CurrJobs}" Value="{Binding ResolvedJobs}" />

View File

@@ -1,5 +1,5 @@
using Avalonia.Controls;
using Nebula.Launcher.ViewModels;
using LoadingContextViewModel = Nebula.Launcher.ViewModels.Popup.LoadingContextViewModel;
namespace Nebula.Launcher.Views.Popup;

View File

@@ -3,11 +3,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:popup="clr-namespace:Nebula.Launcher.ViewModels.Popup"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.Popup.LogPopupView"
x:DataType="viewModels:LogPopupModelView">
x:DataType="popup:LogPopupModelView">
<Design.DataContext>
<viewModels:LogPopupModelView />
<popup:LogPopupModelView />
</Design.DataContext>
<ScrollViewer Margin="10" Padding="0,0,8,0">
@@ -24,11 +25,11 @@
CornerRadius="5,0,0,5"
Padding="10,0,12,0" BorderThickness="2,0,2,0">
<Label FontSize="15" VerticalAlignment="Center">
<TextBlock Text="{Binding Category }"></TextBlock>
<TextBlock Text="{Binding Category }" />
</Label>
</Border>
<Label FontSize="12" VerticalAlignment="Center">
<TextBlock Text="{Binding Message }"></TextBlock>
<TextBlock Text="{Binding Message }" />
</Label>
</StackPanel>
</Border>

View File

@@ -1,7 +1,5 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Popup;
namespace Nebula.Launcher.Views.Popup;

View File

@@ -7,8 +7,13 @@ using Robust.LoaderApi;
namespace Nebula.Runner;
[ServiceRegister]
public sealed class App(DebugService debugService, RunnerService runnerService, ContentService contentService) : IRedialApi
public sealed class App(DebugService debugService, RunnerService runnerService, ContentService contentService)
: IRedialApi
{
public void Redial(Uri uri, string text = "")
{
}
public async Task Run(string[] args1)
{
debugService.Log("HELLO!!! ");
@@ -49,19 +54,14 @@ public sealed class App(DebugService debugService, RunnerService runnerService,
await runnerService.Run(args.ToArray(), buildInfo, this, new ConsoleLoadingHandler(), cancelTokenSource.Token);
}
public void Redial(Uri uri, string text = "")
{
}
}
public sealed class ConsoleLoadingHandler : ILoadingHandler
{
private int _currJobs;
private int _resolvedJobs;
private float _percent = 0f;
private float _percent;
private int _resolvedJobs;
public void SetJobsCount(int count)
{
@@ -110,17 +110,10 @@ public sealed class ConsoleLoadingHandler : ILoadingHandler
Console.Write("\r");
for (var i = 0; i < fullCount; i++)
{
Console.Write("#");
}
for (var i = 0; i < fullCount; i++) Console.Write("#");
for (var i = 0; i < emptyCount; i++)
{
Console.Write(" ");
}
for (var i = 0; i < emptyCount; i++) Console.Write(" ");
Console.Write($"\t {_resolvedJobs}/{_currJobs}");
}
}

View File

@@ -1,4 +1,3 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Shared;

View File

@@ -6,20 +6,27 @@ public static class CurrentConVar
{
public static readonly ConVar<string> EngineManifestUrl =
ConVarBuilder.Build("engine.manifestUrl", "https://robust-builds.cdn.spacestation14.com/manifest.json");
public static readonly ConVar<string> EngineModuleManifestUrl =
ConVarBuilder.Build("engine.moduleManifestUrl", "https://robust-builds.cdn.spacestation14.com/modules.json");
public static readonly ConVar<int> ManifestDownloadProtocolVersion =
ConVarBuilder.Build("engine.manifestDownloadProtocolVersion", 1);
public static readonly ConVar<string> RobustAssemblyName =
ConVarBuilder.Build("engine.robustAssemblyName", "Robust.Client");
public static readonly ConVar<string[]> Hub = ConVarBuilder.Build<string[]>("launcher.hub", [
"https://hub.spacestation14.com/api/servers"
]);
public static readonly ConVar<string[]> AuthServers = ConVarBuilder.Build<string[]>("launcher.authServers", [
"https://auth.spacestation14.com/"
]);
public static readonly ConVar<AuthLoginPassword[]> AuthProfiles = ConVarBuilder.Build<AuthLoginPassword[]>("auth.profiles", []);
public static readonly ConVar<AuthLoginPassword> AuthCurrent = ConVarBuilder.Build<AuthLoginPassword>("auth.current");
public static readonly ConVar<AuthLoginPassword[]> AuthProfiles =
ConVarBuilder.Build<AuthLoginPassword[]>("auth.profiles", []);
public static readonly ConVar<AuthLoginPassword> AuthCurrent =
ConVarBuilder.Build<AuthLoginPassword>("auth.current");
}

View File

@@ -15,7 +15,6 @@ public sealed class FileApi : IReadWriteFileApi
{
var fullPath = Path.Join(RootPath, path);
if (File.Exists(fullPath))
{
try
{
stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
@@ -26,7 +25,6 @@ public sealed class FileApi : IReadWriteFileApi
stream = null;
return false;
}
}
stream = null;
return false;
@@ -66,6 +64,7 @@ public sealed class FileApi : IReadWriteFileApi
{
// Log exception if necessary
}
return false;
}

View File

@@ -1,2 +1,3 @@
namespace Nebula.Shared.Models;
public record ListItemTemplate(Type ModelType, string IconKey, string Label);

View File

@@ -4,18 +4,26 @@ namespace Nebula.Shared.Models;
public sealed record AuthInfo(
[property: JsonPropertyName("mode")] string Mode,
[property: JsonPropertyName("public_key")] string PublicKey);
[property: JsonPropertyName("public_key")]
string PublicKey);
public sealed record BuildInfo(
[property: JsonPropertyName("engine_version")] string EngineVersion,
[property: JsonPropertyName("fork_id")] string ForkId,
[property: JsonPropertyName("version")] string Version,
[property: JsonPropertyName("download_url")] string DownloadUrl,
[property: JsonPropertyName("manifest_download_url")] string ManifestDownloadUrl,
[property: JsonPropertyName("manifest_url")] string ManifestUrl,
[property: JsonPropertyName("engine_version")]
string EngineVersion,
[property: JsonPropertyName("fork_id")]
string ForkId,
[property: JsonPropertyName("version")]
string Version,
[property: JsonPropertyName("download_url")]
string DownloadUrl,
[property: JsonPropertyName("manifest_download_url")]
string ManifestDownloadUrl,
[property: JsonPropertyName("manifest_url")]
string ManifestUrl,
[property: JsonPropertyName("acz")] bool Acz,
[property: JsonPropertyName("hash")] string Hash,
[property: JsonPropertyName("manifest_hash")] string ManifestHash);
[property: JsonPropertyName("manifest_hash")]
string ManifestHash);
public sealed record ServerLink(
[property: JsonPropertyName("name")] string Name,
@@ -23,16 +31,20 @@ public sealed record ServerLink(
[property: JsonPropertyName("url")] string Url);
public sealed record ServerInfo(
[property: JsonPropertyName("connect_address")] string ConnectAddress,
[property: JsonPropertyName("connect_address")]
string ConnectAddress,
[property: JsonPropertyName("auth")] AuthInfo Auth,
[property: JsonPropertyName("build")] BuildInfo Build,
[property: JsonPropertyName("desc")] string Desc,
[property: JsonPropertyName("links")] List<ServerLink> Links);
public sealed record EngineVersionInfo(
[property: JsonPropertyName("insecure")] bool Insecure,
[property: JsonPropertyName("redirect")] string? RedirectVersion,
[property: JsonPropertyName("platforms")] Dictionary<string, EngineBuildInfo> Platforms);
[property: JsonPropertyName("insecure")]
bool Insecure,
[property: JsonPropertyName("redirect")]
string? RedirectVersion,
[property: JsonPropertyName("platforms")]
Dictionary<string, EngineBuildInfo> Platforms);
public sealed class EngineBuildInfo
{
@@ -47,27 +59,39 @@ public sealed class EngineBuildInfo
}
public sealed record ServerHubInfo(
[property: JsonPropertyName("address")] string Address,
[property: JsonPropertyName("statusData")] ServerStatus StatusData,
[property: JsonPropertyName("inferredTags")] List<string> InferredTags);
[property: JsonPropertyName("address")]
string Address,
[property: JsonPropertyName("statusData")]
ServerStatus StatusData,
[property: JsonPropertyName("inferredTags")]
List<string> InferredTags);
public sealed record ServerStatus(
[property: JsonPropertyName("map")] string Map,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("tags")] List<string> Tags,
[property: JsonPropertyName("preset")] string Preset,
[property: JsonPropertyName("players")] int Players,
[property: JsonPropertyName("round_id")] int RoundId,
[property: JsonPropertyName("run_level")] int RunLevel,
[property: JsonPropertyName("panic_bunker")] bool PanicBunker,
[property: JsonPropertyName("round_start_time")] DateTime? RoundStartTime,
[property: JsonPropertyName("soft_max_players")] int SoftMaxPlayers);
[property: JsonPropertyName("players")]
int Players,
[property: JsonPropertyName("round_id")]
int RoundId,
[property: JsonPropertyName("run_level")]
int RunLevel,
[property: JsonPropertyName("panic_bunker")]
bool PanicBunker,
[property: JsonPropertyName("round_start_time")]
DateTime? RoundStartTime,
[property: JsonPropertyName("soft_max_players")]
int SoftMaxPlayers);
public sealed record ModulesInfo(
[property: JsonPropertyName("modules")] Dictionary<string, Module> Modules);
[property: JsonPropertyName("modules")]
Dictionary<string, Module> Modules);
public sealed record Module(
[property: JsonPropertyName("versions")] Dictionary<string, ModuleVersionInfo> Versions);
[property: JsonPropertyName("versions")]
Dictionary<string, ModuleVersionInfo> Versions);
public sealed record ModuleVersionInfo(
[property: JsonPropertyName("platforms")] Dictionary<string, EngineBuildInfo> Platforms);
[property: JsonPropertyName("platforms")]
Dictionary<string, EngineBuildInfo> Platforms);

View File

@@ -17,6 +17,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj"/>
</ItemGroup>

View File

@@ -7,10 +7,7 @@ public static class ServiceExt
{
public static void AddServices(this IServiceCollection services)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AddServices(services, assembly);
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) AddServices(services, assembly);
}
public static void AddServices(this IServiceCollection services, Assembly assembly)
@@ -19,35 +16,30 @@ public static class ServiceExt
{
Console.WriteLine("[ServiceMng] ADD SERVICE " + type);
if (inference is null)
{
services.AddSingleton(type);
}
else
{
services.AddSingleton(inference, type);
}
}
}
private static IEnumerable<(Type,Type?)> GetServicesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes())
private static IEnumerable<(Type, Type?)> GetServicesWithHelpAttribute(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var attr = type.GetCustomAttribute<ServiceRegisterAttribute>();
if (attr is not null) {
yield return (type, attr.Inference);
}
if (attr is not null) yield return (type, attr.Inference);
}
}
}
public sealed class ServiceRegisterAttribute : Attribute
{
public Type? Inference { get; }
public bool IsSingleton { get; }
public ServiceRegisterAttribute(Type? inference = null, bool isSingleton = true)
{
IsSingleton = isSingleton;
Inference = inference;
}
public Type? Inference { get; }
public bool IsSingleton { get; }
}

View File

@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Nebula.Shared.FileApis;
using Robust.LoaderApi;
using SharpZstd.Interop;
namespace Nebula.Shared.Services;
@@ -13,23 +14,23 @@ public class AssemblyService
private readonly List<Assembly> _assemblies = new();
private readonly DebugService _debugService;
private readonly HashSet<string> _resolvingAssemblies = new();
public AssemblyService(DebugService debugService)
{
_debugService = debugService;
SharpZstd.Interop.ZstdImportResolver.ResolveLibrary += (name, assembly1, path) =>
ZstdImportResolver.ResolveLibrary += (name, assembly1, path) =>
{
if (name.Equals("SharpZstd.Native"))
{
_debugService.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path);
GetRuntimeInfo(out string platform, out string architecture, out string extension);
string fileName = GetDllName(platform, architecture, extension);
GetRuntimeInfo(out var platform, out var architecture, out var extension);
var fileName = GetDllName(platform, architecture, extension);
if (NativeLibrary.TryLoad(fileName, assembly1, path, out var nativeLibrary))
{
return nativeLibrary;
}
if (NativeLibrary.TryLoad(fileName, assembly1, path, out var nativeLibrary)) return nativeLibrary;
}
return IntPtr.Zero;
};
}
@@ -99,8 +100,6 @@ public class AssemblyService
return true;
}
private readonly HashSet<string> _resolvingAssemblies = new HashSet<string>();
private Assembly? OnAssemblyResolving(AssemblyLoadContext context, AssemblyName name, AssemblyApi assemblyApi)
{
if (_resolvingAssemblies.Contains(name.FullName))
@@ -141,7 +140,7 @@ public class AssemblyService
string architecture,
string extension)
{
string name = $"SharpZstd.Native.{extension}";
var name = $"SharpZstd.Native.{extension}";
return name;
}
@@ -172,24 +171,14 @@ public class AssemblyService
}
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
{
architecture = "x64";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
{
architecture = "x86";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
{
architecture = "arm";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
architecture = "arm64";
}
else
{
throw new PlatformNotSupportedException("Unsupported process architecture.");
}
}
}

View File

@@ -4,15 +4,15 @@ using Nebula.Shared.Models.Auth;
namespace Nebula.Shared.Services;
[ServiceRegister]
public partial class AuthService(
public class AuthService(
RestService restService,
DebugService debugService,
CancellationService cancellationService)
{
private readonly HttpClient _httpClient = new();
public CurrentAuthInfo? SelectedAuth { get; internal set; }
public string Reason = "";
public CurrentAuthInfo? SelectedAuth { get; internal set; }
public async Task<bool> Auth(AuthLoginPassword authLoginPassword)
{
@@ -68,4 +68,5 @@ public partial class AuthService(
}
public sealed record CurrentAuthInfo(Guid UserId, LoginToken Token, AuthLoginPassword AuthLoginPassword);
public record AuthLoginPassword(string Login, string Password, string AuthServer);

View File

@@ -5,15 +5,15 @@ namespace Nebula.Shared.Services;
public class ConVar<T>
{
public string Name { get; }
public Type Type => typeof(T);
public T? DefaultValue { get; }
public ConVar(string name, T? defaultValue = default)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
DefaultValue = defaultValue;
}
public string Name { get; }
public Type Type => typeof(T);
public T? DefaultValue { get; }
}
public static class ConVarBuilder
@@ -30,8 +30,8 @@ public static class ConVarBuilder
[ServiceRegister]
public class ConfigurationService
{
private readonly FileService _fileService;
private readonly DebugService _debugService;
private readonly FileService _fileService;
public ConfigurationService(FileService fileService, DebugService debugService)
{
@@ -46,7 +46,6 @@ public class ConfigurationService
try
{
if (_fileService.ConfigurationApi.TryOpen(GetFileName(conVar), out var stream))
{
using (stream)
{
var obj = JsonSerializer.Deserialize<T>(stream);
@@ -57,7 +56,6 @@ public class ConfigurationService
}
}
}
}
catch (Exception e)
{
_debugService.Error($"Error loading config for {conVar.Name}: {e.Message}");
@@ -74,7 +72,8 @@ public class ConfigurationService
if (!conVar.Type.IsInstanceOfType(value))
{
_debugService.Error($"Type mismatch for config {conVar.Name}. Expected {conVar.Type}, got {value.GetType()}.");
_debugService.Error(
$"Type mismatch for config {conVar.Name}. Expected {conVar.Type}, got {value.GetType()}.");
return;
}
@@ -105,7 +104,8 @@ public class ConfigurationService
public static class ConfigExtensions
{
public static bool TryGetConfigValue<T>(this ConfigurationService configurationService, ConVar<T> conVar, [NotNullWhen(true)] out T? value)
public static bool TryGetConfigValue<T>(this ConfigurationService configurationService, ConVar<T> conVar,
[NotNullWhen(true)] out T? value)
{
ArgumentNullException.ThrowIfNull(configurationService);
ArgumentNullException.ThrowIfNull(conVar);

View File

@@ -15,7 +15,8 @@ public partial class ContentService
return fileService.ContentFileApi.Has(item.Hash);
}
public async Task<List<RobustManifestItem>> EnsureItems(ManifestReader manifestReader, Uri downloadUri, ILoadingHandler loadingHandler,
public async Task<List<RobustManifestItem>> EnsureItems(ManifestReader manifestReader, Uri downloadUri,
ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
List<RobustManifestItem> allItems = [];
@@ -66,7 +67,8 @@ public partial class ContentService
return await EnsureItems(manifestReader, info.DownloadUri, loadingHandler, cancellationToken);
}
public async Task Unpack(RobustManifestInfo info, IWriteFileApi otherApi, ILoadingHandler loadingHandler, CancellationToken cancellationToken)
public async Task Unpack(RobustManifestInfo info, IWriteFileApi otherApi, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
debugService.Log("Unpack manifest files");
var items = await EnsureItems(info, loadingHandler, cancellationToken);
@@ -83,11 +85,13 @@ public partial class ContentService
{
debugService.Error("OH FUCK!! " + item.Path);
}
loadingHandler.AppendResolvedJob();
}
}
public async Task Download(Uri contentCdn, List<RobustManifestItem> toDownload, ILoadingHandler loadingHandler, CancellationToken cancellationToken)
public async Task Download(Uri contentCdn, List<RobustManifestItem> toDownload, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
{
@@ -110,7 +114,8 @@ public partial class ContentService
var request = new HttpRequestMessage(HttpMethod.Post, contentCdn);
request.Headers.Add(
"X-Robust-Download-Protocol",
varService.GetConfigValue(CurrentConVar.ManifestDownloadProtocolVersion).ToString(CultureInfo.InvariantCulture));
varService.GetConfigValue(CurrentConVar.ManifestDownloadProtocolVersion)
.ToString(CultureInfo.InvariantCulture));
request.Content = new ByteArrayContent(requestBody);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

View File

@@ -5,10 +5,9 @@ namespace Nebula.Shared.Services;
[ServiceRegister]
public class DebugService : IDisposable
{
public ILogger Logger;
private static string LogPath = Path.Combine(FileService.RootPath, "log");
public DateTime LogDate = DateTime.Now;
public ILogger Logger;
private FileStream LogStream;
private StreamWriter LogWriter;
@@ -26,6 +25,12 @@ public class DebugService : IDisposable
//LogWriter = new StreamWriter(LogStream);
}
public void Dispose()
{
LogWriter.Dispose();
LogStream.Dispose();
}
public void Debug(string message)
{
Log(LoggerCategory.Debug, message);
@@ -48,12 +53,6 @@ public class DebugService : IDisposable
Error(e.InnerException);
}
public void Dispose()
{
LogWriter.Dispose();
LogStream.Dispose();
}
private void Log(LoggerCategory category, string message)
{
Logger.Log(category, message);

View File

@@ -15,11 +15,11 @@ public sealed class EngineService
private readonly IServiceProvider _serviceProvider;
private readonly ConfigurationService _varService;
private readonly Task _currInfoTask;
public Dictionary<string, Module> ModuleInfos = default!;
public Dictionary<string, EngineVersionInfo> VersionInfos = default!;
private Task _currInfoTask;
public EngineService(RestService restService, DebugService debugService, ConfigurationService varService,
FileService fileService, IServiceProvider serviceProvider, AssemblyService assemblyService)
{

View File

@@ -14,11 +14,11 @@ public class FileService
Environment.SpecialFolder.ApplicationData), "Datum");
private readonly DebugService _debugService;
public readonly IReadWriteFileApi ConfigurationApi;
public readonly IReadWriteFileApi ContentFileApi;
public readonly IReadWriteFileApi EngineFileApi;
public readonly IReadWriteFileApi ManifestFileApi;
public readonly IReadWriteFileApi ConfigurationApi;
private HashApi? _hashApi;

View File

@@ -8,14 +8,11 @@ public class HubService
private readonly ConfigurationService _configurationService;
private readonly RestService _restService;
private readonly List<ServerHubInfo> _serverList = new();
public Action<HubServerChangedEventArgs>? HubServerChangedEventArgs;
public Action? HubServerLoaded;
private readonly List<ServerHubInfo> _serverList = new();
public IReadOnlyList<ServerHubInfo> ServerList => _serverList;
private bool _isUpdating = false;
public bool IsUpdating => _isUpdating;
public HubService(ConfigurationService configurationService, RestService restService)
{
_configurationService = configurationService;
@@ -24,27 +21,30 @@ public class HubService
UpdateHub();
}
public IReadOnlyList<ServerHubInfo> ServerList => _serverList;
public bool IsUpdating { get; private set; }
public async void UpdateHub()
{
if(_isUpdating) return;
if (IsUpdating) return;
_serverList.Clear();
_isUpdating = true;
IsUpdating = true;
HubServerChangedEventArgs?.Invoke(new HubServerChangedEventArgs([], HubServerChangeAction.Clear));
foreach (var urlStr in _configurationService.GetConfigValue(CurrentConVar.Hub)!)
{
var servers = await _restService.GetAsyncDefault<List<ServerHubInfo>>(new Uri(urlStr), [], CancellationToken.None);
var servers =
await _restService.GetAsyncDefault<List<ServerHubInfo>>(new Uri(urlStr), [], CancellationToken.None);
_serverList.AddRange(servers);
HubServerChangedEventArgs?.Invoke(new HubServerChangedEventArgs(servers, HubServerChangeAction.Add));
}
_isUpdating = false;
IsUpdating = false;
HubServerLoaded?.Invoke();
}
}
public class HubServerChangedEventArgs : EventArgs
@@ -61,5 +61,7 @@ public class HubServerChangedEventArgs : EventArgs
public enum HubServerChangeAction
{
Add, Remove, Clear,
Add,
Remove,
Clear
}

View File

@@ -3,12 +3,14 @@ namespace Nebula.Shared.Services;
[ServiceRegister]
public class PopupMessageService
{
public Action<object>? OnPopupRequired;
public Action<object>? OnCloseRequired;
public Action<object>? OnPopupRequired;
public void Popup(object obj)
{
OnPopupRequired?.Invoke(obj);
}
public void ClosePopup(object obj)
{
OnCloseRequired?.Invoke(obj);

View File

@@ -12,7 +12,8 @@ public sealed class RunnerService(
EngineService engineService,
AssemblyService assemblyService)
{
public async Task PrepareRun(RobustBuildInfo buildInfo, ILoadingHandler loadingHandler, CancellationToken cancellationToken)
public async Task PrepareRun(RobustBuildInfo buildInfo, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
debugService.Log("Prepare Content!");
@@ -25,7 +26,8 @@ public sealed class RunnerService(
await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion);
}
public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi, ILoadingHandler loadingHandler,
public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi,
ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
debugService.Log("Start Content!");
@@ -49,7 +51,8 @@ public sealed class RunnerService(
var args = new MainArgs(runArgs, engine, redialApi, extraMounts);
if (!assemblyService.TryOpenAssembly(varService.GetConfigValue(CurrentConVar.RobustAssemblyName)!, engine, out var clientAssembly))
if (!assemblyService.TryOpenAssembly(varService.GetConfigValue(CurrentConVar.RobustAssemblyName)!, engine,
out var clientAssembly))
throw new Exception("Unable to locate Robust.Client.dll in engine build!");
if (!assemblyService.TryGetLoader(clientAssembly, out var loader))

View File

@@ -22,10 +22,6 @@ public static class Helper
{
Process.Start("open", url);
}
else
{
}
}
public static async Task<T> AsJson<T>(this HttpContent content) where T : notnull

View File

@@ -0,0 +1,132 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Nebula.SourceGenerators;
[Generator]
public class DependencyAutoGenerator : IIncrementalGenerator
{
private static readonly string ConstructGeneratorAttributeName = "ConstructGeneratorAttribute";
private static readonly string GeneratePropertyAttributeName = "GeneratePropertyAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
GenerateAttribute(ConstructGeneratorAttributeName, "Class", context);
GenerateAttribute(GeneratePropertyAttributeName, "Property", context);
var provider = context.SyntaxProvider
.CreateSyntaxProvider(
(s, _) => s is ClassDeclarationSyntax,
(ctx, _) => GetClassDeclarationForSourceGen(ctx))
.Where(t => t.reportAttributeFound)
.Select((t, _) => t.Item1);
context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()),
(ctx, t) => GenerateCode(ctx, t.Left, t.Right));
}
private static void GenerateAttribute(string attributeName, string usage,
IncrementalGeneratorInitializationContext context)
{
var attributeSourceCode = $@"// <auto-generated/>
namespace SourceGen
{{
[System.AttributeUsage(System.AttributeTargets.{usage})]
public class {attributeName} : System.Attribute
{{
}}
}}";
context.RegisterPostInitializationOutput(ctx => ctx.AddSource(
$"{attributeName}.g.cs",
SourceText.From(attributeSourceCode, Encoding.UTF8)));
}
private static (ClassDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen(
GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol)
continue;
var attributeName = attributeSymbol.ContainingType.ToDisplayString();
if (attributeName == $"SourceGen.{ConstructGeneratorAttributeName}")
return (classDeclarationSyntax, true);
}
return (classDeclarationSyntax, false);
}
private void GenerateCode(SourceProductionContext context, Compilation compilation,
ImmutableArray<ClassDeclarationSyntax> classDeclarations)
{
foreach (var classDeclarationSyntax in classDeclarations)
{
var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol)
continue;
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
var className = classDeclarationSyntax.Identifier.Text;
var defaultConstruct = $@"public {className}(){{}}";
var propertiesGenerated = GetProperties(classSymbol).ToList();
var constr = propertiesGenerated.Select(a => $"{a.Type.ToDisplayString()} g{a.Name}");
var body = propertiesGenerated.Select(a => $"this.{a.Name} = g{a.Name};");
if (!constr.Any()) defaultConstruct = "";
var code = $@"// <auto-generated/>
using System;
using System.Collections.Generic;
namespace {namespaceName};
partial class {className}
{{
{defaultConstruct}
public {className}(
{string.Join(",\n\t\t", constr)}
) : base(){{
{string.Join("\n\t\t", body)}
if (Avalonia.Controls.Design.IsDesignMode) InitialiseInDesignMode();
else Initialise();
}}
}}
";
// Add the source code to the compilation.
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
}
}
private static IEnumerable<IPropertySymbol> GetProperties(INamedTypeSymbol classSymbol)
{
return classSymbol.GetMembers().OfType<IPropertySymbol>().Where(a =>
HasAttribute(a, $"SourceGen.{GeneratePropertyAttributeName}"));
}
private static bool HasAttribute(ISymbol type, string attributeName)
{
foreach (var attribute in type.GetAttributes())
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
return true;
return false;
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<RootNamespace>Nebula.SourceGenerators</RootNamespace>
<PackageId>Nebula.SourceGenerators</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"DebugRoslynSourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../Nebula.Shared/Nebula.Shared.csproj"
}
}
}

View File

@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Shared", "Nebula.Sha
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Runner", "Nebula.Runner\Nebula.Runner.csproj", "{82D96367-44B0-4F25-A094-CBE73B052B73}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.SourceGenerators", "Nebula.SourceGenerators\Nebula.SourceGenerators.csproj", "{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,5 +32,9 @@ Global
{82D96367-44B0-4F25-A094-CBE73B052B73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82D96367-44B0-4F25-A094-CBE73B052B73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82D96367-44B0-4F25-A094-CBE73B052B73}.Release|Any CPU.Build.0 = Release|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal