- 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

@@ -10,31 +10,31 @@
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0" />
<PackageReference Include="Avalonia" Version="11.2.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1" />
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0"/>
<PackageReference Include="Avalonia" Version="11.2.1"/>
<PackageReference Include="Avalonia.Desktop" Version="11.2.1"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="libsodium" Version="1.0.20" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/>
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0"/>
<PackageReference Include="libsodium" Version="1.0.20"/>
</ItemGroup>
<ItemGroup>
<Compile Update="Views\Tabs\ServerListTab.axaml.cs">
<DependentUpon>ServerListTab.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Tabs\ServerListTab.axaml.cs">
<DependentUpon>ServerListTab.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Target Name="BuildCheck" AfterTargets="AfterBuild">
@@ -45,6 +45,7 @@
</Target>
<ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" />
<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()
.StartWithClassicDesktopLifetime(args);
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")
}
);
@@ -38,20 +41,18 @@ public static class ServiceCollectionExtensions
foreach (var (viewModel, view, isSingleton) in GetTypesWithHelpAttribute(Assembly.GetExecutingAssembly()))
{
if(isSingleton)services.AddSingleton(viewModel);
if (isSingleton) services.AddSingleton(viewModel);
else services.AddTransient(viewModel);
if (view != null) services.AddTransient(view);
}
}
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,12 +15,9 @@ 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()};
return new TextBlock { Text = "Not Found: " + param.GetType() };
}
public bool Match(object? data)

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,23 +50,22 @@ public partial class AccountInfoViewModel : ViewModelBase
}
}
//Design think
public AccountInfoViewModel()
public string AuthItemSelect
{
AddAccount(new AuthLoginPassword("Binka","12341",""));
set => CurrentAuthServer = value;
}
//Design think
protected override void InitialiseInDesignMode()
{
AddAccount(new AuthLoginPassword("Binka", "12341", ""));
AuthUrls.Add("https://cinka.ru");
AuthUrls.Add("https://cinka.ru");
}
//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,41 +77,36 @@ 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,
Accounts.Select(a => (AuthLoginPassword) a).ToArray());
}
public string AuthItemSelect
{
set => CurrentAuthServer = value;
ConfigurationService.SetConfigValue(CurrentConVar.AuthProfiles,
Accounts.Select(a => (AuthLoginPassword)a).ToArray());
}
}
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,25 +50,27 @@ 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);
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie"+ ext);
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie" + ext);
using(var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None))
using (var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(sw);
}
stream.Dispose();
var startInfo = new ProcessStartInfo(myTempFile)
{
UseShellExecute = true
UseShellExecute = true
};
Process.Start(startInfo);
}
return;
}
@@ -75,67 +79,44 @@ 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", "");
var a = new ContentEntry(this, "A:", "A", "");
var b = new ContentEntry(this, "B", "B", "");
a.TryAddChild(b);
Entries.Add(a);
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.Clear) _root.Clear();
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,14 +171,14 @@ 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);
var rootEntry = new ContentEntry(this, "", "", serverUrl);
foreach (var item in items)
{
@@ -220,7 +193,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase
private void AppendHistory(string str)
{
if(_history.Count >= 10) _history.RemoveAt(9);
if (_history.Count >= 10) _history.RemoveAt(9);
_history.Insert(0, str);
}
@@ -235,41 +208,13 @@ 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")));
public RobustManifestItem? Item;
public bool IsDirectory => Item == null;
public string Name { get; private set; }
public string PathName { get; private set; }
public string ServerName { get; private set; }
public IImage IconPath { get; set; } = DirImage;
public ContentEntry? Parent { get; private set; }
public bool IsRoot => Parent == null;
private readonly Dictionary<string, ContentEntry> _childs = new();
private readonly ContentBrowserViewModel _viewModel;
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public bool TryGetChild(string name,[NotNullWhen(true)] out ContentEntry? child)
{
return _childs.TryGetValue(name, out child);
}
public bool TryAddChild(ContentEntry contentEntry)
{
if(_childs.TryAdd(contentEntry.PathName, contentEntry))
{
contentEntry.Parent = this;
return true;
}
return false;
}
public RobustManifestItem? Item;
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string pathName, string serverName)
{
@@ -279,6 +224,34 @@ public class ContentEntry
_viewModel = viewModel;
}
public bool IsDirectory => Item == null;
public string Name { 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;
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public bool TryGetChild(string name, [NotNullWhen(true)] out ContentEntry? child)
{
return _childs.TryGetValue(name, out child);
}
public bool TryAddChild(ContentEntry contentEntry)
{
if (_childs.TryAdd(contentEntry.PathName, contentEntry))
{
contentEntry.Parent = this;
return true;
}
return false;
}
public ContentPath GetPath()
{
if (Parent != null)
@@ -287,6 +260,7 @@ public class ContentEntry
path.Pathes.Add(PathName);
return path;
}
return new ContentPath([PathName]);
}
@@ -296,7 +270,7 @@ public class ContentEntry
var fName = rootPath.GetNext();
if(!TryGetChild(fName, out var child))
if (!TryGetChild(fName, out var child))
{
child = new ContentEntry(_viewModel, fName, fName, ServerName);
TryAddChild(child);
@@ -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)
{
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,31 +1,34 @@
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);
if(exception.InnerException != null)
if (exception.InnerException != null)
AppendError(exception.InnerException);
}
}

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)]
[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()
@@ -74,83 +59,76 @@ public partial class ServerEntryModelView : ViewModelBase
public async Task RunAsync()
{
try
{
var authProv = _authService.SelectedAuth;
try
{
var authProv = AuthService.SelectedAuth;
var buildInfo =
await _contentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), _cancellationService.Token);
var buildInfo =
await ContentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), CancellationService.Token);
using (var loadingContext = GetViewModel<LoadingContextViewModel>())
{
loadingContext.LoadingName = "Loading instance...";
((ILoadingHandler)loadingContext).AppendJob();
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
{
FileName = "dotnet.exe",
Arguments = "./Nebula.Runner.dll",
Environment =
{
{ "ROBUST_AUTH_USERID", authProv?.UserId.ToString() },
{ "ROBUST_AUTH_TOKEN", authProv?.Token.Token },
{ "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer },
{ "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey },
{ "GAME_URL", ServerHubInfo.Address },
{ "AUTH_LOGIN", authProv?.AuthLoginPassword.Login }
},
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8
});
Process = Process.Start(new ProcessStartInfo()
{
FileName = "dotnet.exe",
Arguments = "./Nebula.Runner.dll",
Environment =
{
{ "ROBUST_AUTH_USERID", authProv?.UserId.ToString() },
{ "ROBUST_AUTH_TOKEN", authProv?.Token.Token },
{ "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer },
{ "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey },
{ "GAME_URL", ServerHubInfo.Address },
{ "AUTH_LOGIN", authProv?.AuthLoginPassword.Login },
},
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8
});
((ILoadingHandler)loadingContext).AppendResolvedJob();
}
((ILoadingHandler)loadingContext).AppendResolvedJob();
}
if (Process is null) return;
if (Process is null)
{
return;
}
Process.EnableRaisingEvents = true;
Process.EnableRaisingEvents = true;
Process.BeginOutputReadLine();
Process.BeginErrorReadLine();
Process.BeginOutputReadLine();
Process.BeginErrorReadLine();
Process.OutputDataReceived += OnOutputDataReceived;
Process.ErrorDataReceived += OnErrorDataReceived;
Process.OutputDataReceived += OnOutputDataReceived;
Process.ErrorDataReceived += OnErrorDataReceived;
Process.Exited += OnExited;
}
catch (TaskCanceledException e)
{
_popupMessageService.Popup("Task canceled");
}
catch (Exception e)
{
_popupMessageService.Popup(e);
}
Process.Exited += OnExited;
}
catch (TaskCanceledException _)
{
PopupMessageService.Popup("Task canceled");
}
catch (Exception 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,21 +163,16 @@ 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!");
}
@@ -207,19 +180,16 @@ public partial class ServerEntryModelView : ViewModelBase
public sealed class LogInfo
{
public string Category { get; set; } = "LOG";
public string Category { get; set; } = "LOG";
public IBrush CategoryColor { get; set; } = Brush.Parse("#424242");
public string Message { get; set; } = "";
public static LogInfo FromString(string input)
{
var matches = Regex.Matches(input, @"(\[(?<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,25 +2,25 @@
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}"/>
<ProgressBar Height="40" Maximum="{Binding CurrJobs}" Value="{Binding ResolvedJobs}" />
<Panel>
<StackPanel Orientation="Horizontal" Spacing="5" HorizontalAlignment="Left" VerticalAlignment="Center">
<Label>
<TextBlock Text="{Binding ResolvedJobs}"/>
<TextBlock Text="{Binding ResolvedJobs}" />
</Label>
<Label>
/
</Label>
<Label>
<TextBlock Text="{Binding CurrJobs}"/>
<TextBlock Text="{Binding CurrJobs}" />
</Label>
</StackPanel>

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;
@@ -10,7 +10,7 @@ public partial class LoadingContextView : UserControl
InitializeComponent();
}
public LoadingContextView(LoadingContextViewModel viewModel): this()
public LoadingContextView(LoadingContextViewModel viewModel) : this()
{
DataContext = viewModel;
}

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">
@@ -20,20 +21,20 @@
<Border CornerRadius="5" Margin="0,0,0,5">
<StackPanel Orientation="Horizontal" Spacing="5" Margin="0">
<Border MinWidth="100"
Background="{Binding CategoryColor}"
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>
Background="{Binding CategoryColor}"
CornerRadius="5,0,0,5"
Padding="10,0,12,0" BorderThickness="2,0,2,0">
<Label FontSize="15" VerticalAlignment="Center">
<TextBlock Text="{Binding Category }" />
</Label>
</Border>
<Label FontSize="12" VerticalAlignment="Center">
<TextBlock Text="{Binding Message }"></TextBlock>
<TextBlock Text="{Binding Message }" />
</Label>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>

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

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="Nebula.Launcher.Desktop"/>
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="Nebula.Launcher.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>

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)
{
@@ -91,15 +91,15 @@ public sealed class ConsoleLoadingHandler : ILoadingHandler
private void UpdatePercent()
{
if(_currJobs == 0)
if (_currJobs == 0)
{
_percent = 0;
return;
}
if(_resolvedJobs > _currJobs) return;
if (_resolvedJobs > _currJobs) return;
_percent = _resolvedJobs /(float) _currJobs;
_percent = _resolvedJobs / (float)_currJobs;
}
private void Draw()
@@ -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

@@ -8,11 +8,11 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" />
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0"/>
<PackageReference Include="SharpZstd.Interop" Version="1.5.6"/>
</ItemGroup>
</Project>

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

@@ -30,7 +30,7 @@ public interface ILoadingHandler
}
}
public sealed class QueryJob: IDisposable
public sealed class QueryJob : IDisposable
{
private readonly ILoadingHandler _handler;

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

@@ -11,13 +11,14 @@
<EmbeddedResource Include="Utils\runtime.json">
<LogicalName>Utility.runtime.json</LogicalName>
</EmbeddedResource>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="Robust.Natives" Version="0.1.1" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0"/>
<PackageReference Include="Robust.Natives" Version="0.1.1"/>
<PackageReference Include="SharpZstd.Interop" Version="1.5.6"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" />
<ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj"/>
</ItemGroup>
</Project>

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);
@@ -56,7 +55,6 @@ public class ConfigurationService
return obj;
}
}
}
}
catch (Exception e)
{
@@ -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;
@@ -17,7 +16,7 @@ public class DebugService : IDisposable
Logger = logger;
//if (!Directory.Exists(LogPath))
// Directory.CreateDirectory(LogPath);
// Directory.CreateDirectory(LogPath);
//var filename = String.Format("{0:yyyy-MM-dd}.txt", DateTime.Now);
@@ -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);
@@ -44,16 +49,10 @@ public class DebugService : IDisposable
public void Error(Exception e)
{
Error(e.Message + "\r\n" + e.StackTrace);
if(e.InnerException != null)
if (e.InnerException != null)
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