diff --git a/Nebula.Launcher/App.axaml.cs b/Nebula.Launcher/App.axaml.cs index e045caa..885f647 100644 --- a/Nebula.Launcher/App.axaml.cs +++ b/Nebula.Launcher/App.axaml.cs @@ -8,6 +8,7 @@ using Avalonia.Markup.Xaml; using Microsoft.Extensions.DependencyInjection; using Nebula.Launcher.ViewModels; using Nebula.Launcher.Views; +using Nebula.Shared; namespace Nebula.Launcher; diff --git a/Nebula.Launcher/AppNoUi.cs b/Nebula.Launcher/AppNoUi.cs deleted file mode 100644 index 9020e5c..0000000 --- a/Nebula.Launcher/AppNoUi.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Nebula.Launcher.Services; - -namespace Nebula.Launcher; - -public class AppNoUi(RunnerService runnerService, AuthService authService) -{ - public void Run(string[] args) - { - - } -} \ No newline at end of file diff --git a/Nebula.Launcher/Nebula.Launcher.csproj b/Nebula.Launcher/Nebula.Launcher.csproj index 161a840..a6943fb 100644 --- a/Nebula.Launcher/Nebula.Launcher.csproj +++ b/Nebula.Launcher/Nebula.Launcher.csproj @@ -26,13 +26,7 @@ - - - - - Utility.runtime.json - @@ -43,6 +37,6 @@ - + diff --git a/Nebula.Launcher/Program.cs b/Nebula.Launcher/Program.cs index 123d1b9..987e1bb 100644 --- a/Nebula.Launcher/Program.cs +++ b/Nebula.Launcher/Program.cs @@ -1,40 +1,11 @@ using Avalonia; -using System; -using Microsoft.Extensions.DependencyInjection; namespace Nebula.Launcher; -sealed class Program +public static class Program { - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) - { - var uiMode = false; - - if (uiMode) - { - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - } - else - { - RunNoUI(args); - } - } - - private static void RunNoUI(string[] args) - { - var services = new ServiceCollection(); - services.AddAvaloniaServices(); - services.AddServices(); - services.AddSingleton(); //Separated because no ui - - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService()!.Run(args); - } + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); // Avalonia configuration, don't remove; also used by visual designer. private static AppBuilder BuildAvaloniaApp() diff --git a/Nebula.Launcher/ServiceCollectionExtensions.cs b/Nebula.Launcher/ServiceCollectionExtensions.cs index a2b0daf..e15a463 100644 --- a/Nebula.Launcher/ServiceCollectionExtensions.cs +++ b/Nebula.Launcher/ServiceCollectionExtensions.cs @@ -36,58 +36,22 @@ public static class ServiceCollectionExtensions { services.AddTransient(); - foreach (var (viewModel, view) in GetTypesWithHelpAttribute(Assembly.GetExecutingAssembly())) + foreach (var (viewModel, view, isSingleton) in GetTypesWithHelpAttribute(Assembly.GetExecutingAssembly())) { - services.AddSingleton(viewModel); - services.AddTransient(view); - } - - } - - public static void AddServices(this IServiceCollection services) - { - foreach (var (type, inference) in GetServicesWithHelpAttribute(Assembly.GetExecutingAssembly())) - { - if (inference is null) - { - services.AddSingleton(type); - } - else - { - services.AddSingleton(inference, type); - } + if(isSingleton)services.AddSingleton(viewModel); + else services.AddTransient(viewModel); + if (view != null) services.AddTransient(view); } } - private static IEnumerable<(Type,Type)> GetTypesWithHelpAttribute(Assembly assembly) { + private static IEnumerable<(Type,Type?,bool)> GetTypesWithHelpAttribute(Assembly assembly) { foreach(Type type in assembly.GetTypes()) { - var attr = type.GetCustomAttribute(); + var attr = type.GetCustomAttribute(); if (attr is not null) { - yield return (type, attr.Type); - } - } - } - - private static IEnumerable<(Type,Type?)> GetServicesWithHelpAttribute(Assembly assembly) { - foreach(Type type in assembly.GetTypes()) - { - var attr = type.GetCustomAttribute(); - if (attr is not null) { - yield return (type, attr.Inference); + yield return (type, attr.Type, attr.IsSingleton); } } } } -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; - } -} \ No newline at end of file diff --git a/Nebula.Launcher/Services/ContentService.cs b/Nebula.Launcher/Services/ContentService.cs deleted file mode 100644 index 77824bf..0000000 --- a/Nebula.Launcher/Services/ContentService.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Data; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Nebula.Launcher.Models; - -namespace Nebula.Launcher.Services; - -[ServiceRegister] -public partial class ContentService -{ - private readonly AssemblyService _assemblyService; - private readonly DebugService _debugService; - private readonly EngineService _engineService; - private readonly FileService _fileService; - private readonly HttpClient _http = new(); - private readonly RestService _restService; - private readonly ConfigurationService _varService; - - public ContentService(RestService restService, DebugService debugService, ConfigurationService varService, - FileService fileService, EngineService engineService, AssemblyService assemblyService) - { - _restService = restService; - _debugService = debugService; - _varService = varService; - _fileService = fileService; - _engineService = engineService; - _assemblyService = assemblyService; - } - - public async Task GetBuildInfo(RobustUrl url, CancellationToken cancellationToken) - { - var info = new RobustBuildInfo(); - info.Url = url; - var bi = await _restService.GetAsync(url.InfoUri, cancellationToken); - if (bi.Value is null) throw new NoNullAllowedException(); - info.BuildInfo = bi.Value; - Console.WriteLine(info.BuildInfo); - info.RobustManifestInfo = info.BuildInfo.Build.Acz - ? new RobustManifestInfo(new RobustPath(info.Url, "manifest.txt"), new RobustPath(info.Url, "download"), - bi.Value.Build.ManifestHash) - : new RobustManifestInfo(new Uri(info.BuildInfo.Build.ManifestUrl), - new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Value.Build.ManifestHash); - - return info; - } -} \ No newline at end of file diff --git a/Nebula.Launcher/Services/PopupMessageService.cs b/Nebula.Launcher/Services/PopupMessageService.cs deleted file mode 100644 index 84ad41e..0000000 --- a/Nebula.Launcher/Services/PopupMessageService.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Nebula.Launcher.ViewModels; - -namespace Nebula.Launcher.Services; - -[ServiceRegister] -public class PopupMessageService -{ - private readonly IServiceProvider _serviceProvider; - - public Action? OnPopupRequired; - - public PopupMessageService(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - - } - - public void PopupInfo(string info) - { - var message = _serviceProvider.GetService(); - message.InfoText = info; - PopupMessage(message); - } - - public void PopupMessage(PopupViewModelBase viewModelBase) - { - OnPopupRequired?.Invoke(viewModelBase); - } - - public void ClosePopup() - { - OnPopupRequired?.Invoke(null); - } -} \ No newline at end of file diff --git a/Nebula.Launcher/ViewHelper/ViewRegisterAttribute.cs b/Nebula.Launcher/ViewHelper/ViewModelRegisterAttribute.cs similarity index 55% rename from Nebula.Launcher/ViewHelper/ViewRegisterAttribute.cs rename to Nebula.Launcher/ViewHelper/ViewModelRegisterAttribute.cs index b15cb4c..f5e2774 100644 --- a/Nebula.Launcher/ViewHelper/ViewRegisterAttribute.cs +++ b/Nebula.Launcher/ViewHelper/ViewModelRegisterAttribute.cs @@ -3,12 +3,12 @@ using System; namespace Nebula.Launcher.ViewHelper; [AttributeUsage(AttributeTargets.Class)] -public class ViewRegisterAttribute : Attribute +public class ViewModelRegisterAttribute : Attribute { - public Type Type { get; } + public Type? Type { get; } public bool IsSingleton { get; } - public ViewRegisterAttribute(Type type, bool isSingleton = true) + public ViewModelRegisterAttribute(Type? type = null, bool isSingleton = true) { Type = type; IsSingleton = isSingleton; diff --git a/Nebula.Launcher/ViewLocator.cs b/Nebula.Launcher/ViewLocator.cs index 3d9999c..471747e 100644 --- a/Nebula.Launcher/ViewLocator.cs +++ b/Nebula.Launcher/ViewLocator.cs @@ -14,7 +14,7 @@ public class ViewLocator : IDataTemplate if (param is null) return null; - var type = param.GetType().GetCustomAttribute()?.Type; + var type = param.GetType().GetCustomAttribute()?.Type; if (type != null) { diff --git a/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs b/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs index 558d7e9..e93868f 100644 --- a/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs +++ b/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs @@ -4,14 +4,15 @@ using System.Linq; using System.Windows.Input; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Nebula.Launcher.Services; -using Nebula.Launcher.Utils; using Nebula.Launcher.ViewHelper; using Nebula.Launcher.Views.Pages; +using Nebula.Shared; +using Nebula.Shared.Services; +using Nebula.Shared.Utils; namespace Nebula.Launcher.ViewModels; -[ViewRegister(typeof(AccountInfoView))] +[ViewModelRegister(typeof(AccountInfoView))] public partial class AccountInfoViewModel : ViewModelBase { private readonly PopupMessageService _popupMessageService; @@ -79,12 +80,12 @@ public partial class AccountInfoViewModel : ViewModelBase public async void DoAuth() { - _popupMessageService.PopupInfo("Auth think, please wait..."); + _popupMessageService.Popup("Auth think, please wait..."); if(await _authService.Auth(CurrentAlp)) { _popupMessageService.ClosePopup(); - _popupMessageService.PopupInfo("Hello, " + _authService.SelectedAuth!.AuthLoginPassword.Login); + _popupMessageService.Popup("Hello, " + _authService.SelectedAuth!.AuthLoginPassword.Login); IsLogged = true; _configurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp); } @@ -92,7 +93,7 @@ public partial class AccountInfoViewModel : ViewModelBase { _popupMessageService.ClosePopup(); Logout(); - _popupMessageService.PopupInfo("Well, shit is happened: " + _authService.Reason); + _popupMessageService.Popup("Well, shit is happened: " + _authService.Reason); } } @@ -100,7 +101,7 @@ public partial class AccountInfoViewModel : ViewModelBase { IsLogged = false; CurrentAlp = new AuthLoginPassword("", "", ""); - _authService.SelectedAuth = null; + _authService.ClearAuth(); } private void UpdateAuthMenu() diff --git a/Nebula.Launcher/ViewModels/InfoPopupViewModel.cs b/Nebula.Launcher/ViewModels/InfoPopupViewModel.cs index 59d80a3..6e36b5e 100644 --- a/Nebula.Launcher/ViewModels/InfoPopupViewModel.cs +++ b/Nebula.Launcher/ViewModels/InfoPopupViewModel.cs @@ -5,7 +5,7 @@ using Nebula.Launcher.Views.Popup; namespace Nebula.Launcher.ViewModels; -[ViewRegister(typeof(InfoPopupView), false)] +[ViewModelRegister(typeof(InfoPopupView), false)] public partial class InfoPopupViewModel : PopupViewModelBase { public InfoPopupViewModel() diff --git a/Nebula.Launcher/ViewModels/MainViewModel.cs b/Nebula.Launcher/ViewModels/MainViewModel.cs index 910c2ca..03b12c7 100644 --- a/Nebula.Launcher/ViewModels/MainViewModel.cs +++ b/Nebula.Launcher/ViewModels/MainViewModel.cs @@ -5,15 +5,15 @@ using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using JetBrains.Annotations; -using Nebula.Launcher.Models; -using Nebula.Launcher.Services; -using Nebula.Launcher.Utils; using Nebula.Launcher.ViewHelper; using Nebula.Launcher.Views; +using Nebula.Shared.Models; +using Nebula.Shared.Services; +using Nebula.Shared.Utils; namespace Nebula.Launcher.ViewModels; -[ViewRegister(typeof(MainView))] +[ViewModelRegister(typeof(MainView))] public partial class MainViewModel : ViewModelBase { public MainViewModel() @@ -111,15 +111,23 @@ public partial class MainViewModel : ViewModelBase Helper.OpenBrowser("https://cinka.ru/nebula-launcher/"); } - private void OnPopupRequired(PopupViewModelBase? viewModelBase) + private void OnPopupRequired(object? viewModelBase) { - if (viewModelBase is null) + switch (viewModelBase) { - ClosePopup(); - } - else - { - PopupMessage(viewModelBase); + case null: + ClosePopup(); + break; + case string str: + { + var view = GetViewModel(); + view.InfoText = str; + PopupMessage(view); + break; + } + case PopupViewModelBase @base: + PopupMessage(@base); + break; } } diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs index 12ceb7e..be2bfb5 100644 --- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs +++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs @@ -1,33 +1,41 @@ using System; +using System.Diagnostics; using CommunityToolkit.Mvvm.ComponentModel; -using Microsoft.Extensions.DependencyInjection; -using Nebula.Launcher.Models; -using Nebula.Launcher.Services; +using Nebula.Launcher.ViewHelper; +using Nebula.Shared.Models; namespace Nebula.Launcher.ViewModels; +[ViewModelRegister(isSingleton:false)] public partial class ServerEntryModelView : ViewModelBase { - - private readonly IServiceProvider _serviceProvider; - private readonly RunnerService _runnerService; - private readonly PopupMessageService _popupMessageService; - private readonly RestService _restService; + [ObservableProperty] private bool _runVisible = true; - public ServerHubInfo ServerHubInfo { get; } + public ServerHubInfo ServerHubInfo { get; set; } - public ServerEntryModelView(IServiceProvider serviceProvider, ServerHubInfo serverHubInfo) : base(serviceProvider) + public ServerEntryModelView() : base() { - _serviceProvider = serviceProvider; - _runnerService = serviceProvider.GetService()!; - _popupMessageService = serviceProvider.GetService()!; - _restService = serviceProvider.GetService()!; - ServerHubInfo = serverHubInfo; } - public async void OnConnectRequired() + public ServerEntryModelView(IServiceProvider serviceProvider) : base(serviceProvider) { - _popupMessageService.PopupInfo("Running server: " + ServerHubInfo.StatusData.Name); - await _runnerService.RunGame(ServerHubInfo.Address); + } + + public void RunInstance() + { + var p = Process.Start("./Nebula.Runner", "a b c"); + p.BeginOutputReadLine(); + p.BeginErrorReadLine(); + } + + + public void ReadLog() + { + + } + + public void StopInstance() + { + } } \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/ServerListViewModel.cs b/Nebula.Launcher/ViewModels/ServerListViewModel.cs index fa36ebb..0e07e15 100644 --- a/Nebula.Launcher/ViewModels/ServerListViewModel.cs +++ b/Nebula.Launcher/ViewModels/ServerListViewModel.cs @@ -3,14 +3,14 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; -using Nebula.Launcher.Models; -using Nebula.Launcher.Services; using Nebula.Launcher.ViewHelper; using Nebula.Launcher.Views.Pages; +using Nebula.Shared.Models; +using Nebula.Shared.Services; namespace Nebula.Launcher.ViewModels; -[ViewRegister(typeof(ServerListView))] +[ViewModelRegister(typeof(ServerListView))] public partial class ServerListViewModel : ViewModelBase { private readonly IServiceProvider _serviceProvider; @@ -84,7 +84,9 @@ public partial class ServerListViewModel : ViewModelBase private ServerEntryModelView CreateServerView(ServerHubInfo serverHubInfo) { - return new ServerEntryModelView(_serviceProvider, serverHubInfo); + var svn = GetViewModel(); + svn.ServerHubInfo = serverHubInfo; + return svn; } public void FilterRequired() diff --git a/Nebula.Launcher/Views/MainView.axaml b/Nebula.Launcher/Views/MainView.axaml index c95ddcc..ceb414a 100644 --- a/Nebula.Launcher/Views/MainView.axaml +++ b/Nebula.Launcher/Views/MainView.axaml @@ -8,7 +8,7 @@ xmlns:converters="clr-namespace:Nebula.Launcher.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:models="clr-namespace:Nebula.Launcher.Models" + xmlns:models="clr-namespace:Nebula.Shared.Models;assembly=Nebula.Shared" xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/Nebula.Launcher/Views/Pages/ServerListView.axaml b/Nebula.Launcher/Views/Pages/ServerListView.axaml index dae53af..c4ad766 100644 --- a/Nebula.Launcher/Views/Pages/ServerListView.axaml +++ b/Nebula.Launcher/Views/Pages/ServerListView.axaml @@ -8,7 +8,6 @@ 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:models="clr-namespace:Nebula.Launcher.Models" xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> @@ -25,7 +24,7 @@ @@ -121,18 +120,36 @@ - - + + - + + diff --git a/Nebula.Runner/App.cs b/Nebula.Runner/App.cs new file mode 100644 index 0000000..9a1688e --- /dev/null +++ b/Nebula.Runner/App.cs @@ -0,0 +1,14 @@ +using Nebula.Shared; +using Nebula.Shared.Services; + +namespace Nebula.Runner; + +[ServiceRegister] +public class App(DebugService debugService) +{ + + public void Run(string[] args) + { + debugService.Log("HELLO!!! " + string.Join(" ",args)); + } +} \ No newline at end of file diff --git a/Nebula.Runner/Nebula.Runner.csproj b/Nebula.Runner/Nebula.Runner.csproj new file mode 100644 index 0000000..63b96af --- /dev/null +++ b/Nebula.Runner/Nebula.Runner.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/Nebula.Runner/Program.cs b/Nebula.Runner/Program.cs new file mode 100644 index 0000000..ad79ca3 --- /dev/null +++ b/Nebula.Runner/Program.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Nebula.Shared; + +namespace Nebula.Runner; + +public static class Program +{ + public static void Main(string[] args) + { + var services = new ServiceCollection(); + services.AddServices(); + + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService()!.Run(args); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/CurrentConVar.cs b/Nebula.Shared/CurrentConVar.cs similarity index 95% rename from Nebula.Launcher/CurrentConVar.cs rename to Nebula.Shared/CurrentConVar.cs index e528fac..e35e9a6 100644 --- a/Nebula.Launcher/CurrentConVar.cs +++ b/Nebula.Shared/CurrentConVar.cs @@ -1,6 +1,6 @@ -using Nebula.Launcher.Services; +using Nebula.Shared.Services; -namespace Nebula.Launcher; +namespace Nebula.Shared; public static class CurrentConVar { diff --git a/Nebula.Launcher/FileApis/AssemblyApi.cs b/Nebula.Shared/FileApis/AssemblyApi.cs similarity index 74% rename from Nebula.Launcher/FileApis/AssemblyApi.cs rename to Nebula.Shared/FileApis/AssemblyApi.cs index be55084..b6d2f57 100644 --- a/Nebula.Launcher/FileApis/AssemblyApi.cs +++ b/Nebula.Shared/FileApis/AssemblyApi.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; -using System.IO; -using Robust.LoaderApi; +using Robust.LoaderApi; -namespace Nebula.Launcher.FileApis; +namespace Nebula.Shared.FileApis; public class AssemblyApi : IFileApi { @@ -12,7 +10,7 @@ public class AssemblyApi : IFileApi { _root = root; } - + public bool TryOpen(string path, out Stream? stream) { return _root.TryOpen(path, out stream); diff --git a/Nebula.Launcher/FileApis/FileApi.cs b/Nebula.Shared/FileApis/FileApi.cs similarity index 89% rename from Nebula.Launcher/FileApis/FileApi.cs rename to Nebula.Shared/FileApis/FileApi.cs index f2a7c45..08187a0 100644 --- a/Nebula.Launcher/FileApis/FileApi.cs +++ b/Nebula.Shared/FileApis/FileApi.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Nebula.Launcher.FileApis.Interfaces; +using Nebula.Shared.FileApis.Interfaces; -namespace Nebula.Launcher.FileApis; +namespace Nebula.Shared.FileApis; public class FileApi : IReadWriteFileApi { diff --git a/Nebula.Launcher/FileApis/HashApi.cs b/Nebula.Shared/FileApis/HashApi.cs similarity index 83% rename from Nebula.Launcher/FileApis/HashApi.cs rename to Nebula.Shared/FileApis/HashApi.cs index e5ae500..321a6e4 100644 --- a/Nebula.Launcher/FileApis/HashApi.cs +++ b/Nebula.Shared/FileApis/HashApi.cs @@ -1,10 +1,7 @@ -using System.Collections.Generic; -using System.IO; -using Nebula.Launcher.Models; -using Nebula.Launcher.Utils; +using Nebula.Shared.Models; using Robust.LoaderApi; -namespace Nebula.Launcher.FileApis; +namespace Nebula.Shared.FileApis; public class HashApi : IFileApi { diff --git a/Nebula.Launcher/FileApis/Interfaces/IReadWriteFileApi.cs b/Nebula.Shared/FileApis/Interfaces/IReadWriteFileApi.cs similarity index 66% rename from Nebula.Launcher/FileApis/Interfaces/IReadWriteFileApi.cs rename to Nebula.Shared/FileApis/Interfaces/IReadWriteFileApi.cs index 487c719..3dd15a1 100644 --- a/Nebula.Launcher/FileApis/Interfaces/IReadWriteFileApi.cs +++ b/Nebula.Shared/FileApis/Interfaces/IReadWriteFileApi.cs @@ -1,6 +1,6 @@ using Robust.LoaderApi; -namespace Nebula.Launcher.FileApis.Interfaces; +namespace Nebula.Shared.FileApis.Interfaces; public interface IReadWriteFileApi : IFileApi, IWriteFileApi { diff --git a/Nebula.Launcher/FileApis/Interfaces/IWriteFileApi.cs b/Nebula.Shared/FileApis/Interfaces/IWriteFileApi.cs similarity index 69% rename from Nebula.Launcher/FileApis/Interfaces/IWriteFileApi.cs rename to Nebula.Shared/FileApis/Interfaces/IWriteFileApi.cs index c4883c9..801ec33 100644 --- a/Nebula.Launcher/FileApis/Interfaces/IWriteFileApi.cs +++ b/Nebula.Shared/FileApis/Interfaces/IWriteFileApi.cs @@ -1,6 +1,4 @@ -using System.IO; - -namespace Nebula.Launcher.FileApis.Interfaces; +namespace Nebula.Shared.FileApis.Interfaces; public interface IWriteFileApi { diff --git a/Nebula.Launcher/FileApis/ZipFileApi.cs b/Nebula.Shared/FileApis/ZipFileApi.cs similarity index 93% rename from Nebula.Launcher/FileApis/ZipFileApi.cs rename to Nebula.Shared/FileApis/ZipFileApi.cs index 241ed26..1579779 100644 --- a/Nebula.Launcher/FileApis/ZipFileApi.cs +++ b/Nebula.Shared/FileApis/ZipFileApi.cs @@ -1,12 +1,9 @@ -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.IO.Compression; -using System.Linq; using System.Runtime.InteropServices; using Robust.LoaderApi; -namespace Nebula.Launcher.FileApis; +namespace Nebula.Shared.FileApis; public sealed class ZipFileApi : IFileApi { diff --git a/Nebula.Launcher/Models/Auth/AuthenticateRequest.cs b/Nebula.Shared/Models/Auth/AuthenticateRequest.cs similarity index 80% rename from Nebula.Launcher/Models/Auth/AuthenticateRequest.cs rename to Nebula.Shared/Models/Auth/AuthenticateRequest.cs index 565663e..e3d05d0 100644 --- a/Nebula.Launcher/Models/Auth/AuthenticateRequest.cs +++ b/Nebula.Shared/Models/Auth/AuthenticateRequest.cs @@ -1,6 +1,4 @@ -using System; - -namespace Nebula.Launcher.Models.Auth; +namespace Nebula.Shared.Models.Auth; public sealed record AuthenticateRequest(string? Username, Guid? UserId, string Password, string? TfaCode = null) { diff --git a/Nebula.Launcher/Models/Auth/AuthenticateResponse.cs b/Nebula.Shared/Models/Auth/AuthenticateResponse.cs similarity index 66% rename from Nebula.Launcher/Models/Auth/AuthenticateResponse.cs rename to Nebula.Shared/Models/Auth/AuthenticateResponse.cs index 9894b57..4a9d57a 100644 --- a/Nebula.Launcher/Models/Auth/AuthenticateResponse.cs +++ b/Nebula.Shared/Models/Auth/AuthenticateResponse.cs @@ -1,5 +1,3 @@ -using System; - -namespace Nebula.Launcher.Models.Auth; +namespace Nebula.Shared.Models.Auth; public sealed record AuthenticateResponse(string Token, string Username, Guid UserId, DateTimeOffset ExpireTime); \ No newline at end of file diff --git a/Nebula.Launcher/Models/Auth/LoginInfo.cs b/Nebula.Shared/Models/Auth/LoginInfo.cs similarity index 81% rename from Nebula.Launcher/Models/Auth/LoginInfo.cs rename to Nebula.Shared/Models/Auth/LoginInfo.cs index d861086..800f013 100644 --- a/Nebula.Launcher/Models/Auth/LoginInfo.cs +++ b/Nebula.Shared/Models/Auth/LoginInfo.cs @@ -1,6 +1,4 @@ -using System; - -namespace Nebula.Launcher.Models.Auth; +namespace Nebula.Shared.Models.Auth; public class LoginInfo { diff --git a/Nebula.Launcher/Models/Auth/LoginToken.cs b/Nebula.Shared/Models/Auth/LoginToken.cs similarity index 81% rename from Nebula.Launcher/Models/Auth/LoginToken.cs rename to Nebula.Shared/Models/Auth/LoginToken.cs index 4d4c3b6..09abe04 100644 --- a/Nebula.Launcher/Models/Auth/LoginToken.cs +++ b/Nebula.Shared/Models/Auth/LoginToken.cs @@ -1,6 +1,4 @@ -using System; - -namespace Nebula.Launcher.Models.Auth; +namespace Nebula.Shared.Models.Auth; public readonly struct LoginToken { diff --git a/Nebula.Launcher/Models/ContentCompressionScheme.cs b/Nebula.Shared/Models/ContentCompressionScheme.cs similarity index 87% rename from Nebula.Launcher/Models/ContentCompressionScheme.cs rename to Nebula.Shared/Models/ContentCompressionScheme.cs index 22fd911..b90e9ea 100644 --- a/Nebula.Launcher/Models/ContentCompressionScheme.cs +++ b/Nebula.Shared/Models/ContentCompressionScheme.cs @@ -1,4 +1,4 @@ -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public enum ContentCompressionScheme { diff --git a/Nebula.Launcher/Models/DownloadStreamHeaderFlags.cs b/Nebula.Shared/Models/DownloadStreamHeaderFlags.cs similarity index 88% rename from Nebula.Launcher/Models/DownloadStreamHeaderFlags.cs rename to Nebula.Shared/Models/DownloadStreamHeaderFlags.cs index c043e71..a5a00cd 100644 --- a/Nebula.Launcher/Models/DownloadStreamHeaderFlags.cs +++ b/Nebula.Shared/Models/DownloadStreamHeaderFlags.cs @@ -1,6 +1,4 @@ -using System; - -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; [Flags] public enum DownloadStreamHeaderFlags diff --git a/Nebula.Launcher/Models/ListItemTemplate.cs b/Nebula.Shared/Models/ListItemTemplate.cs similarity index 61% rename from Nebula.Launcher/Models/ListItemTemplate.cs rename to Nebula.Shared/Models/ListItemTemplate.cs index 33d0d11..8dfdec4 100644 --- a/Nebula.Launcher/Models/ListItemTemplate.cs +++ b/Nebula.Shared/Models/ListItemTemplate.cs @@ -1,4 +1,2 @@ -using System; - -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public record ListItemTemplate(Type ModelType, string IconKey, string Label); \ No newline at end of file diff --git a/Nebula.Launcher/Models/MainArgs.cs b/Nebula.Shared/Models/MainArgs.cs similarity index 87% rename from Nebula.Launcher/Models/MainArgs.cs rename to Nebula.Shared/Models/MainArgs.cs index 845f283..d8c6539 100644 --- a/Nebula.Launcher/Models/MainArgs.cs +++ b/Nebula.Shared/Models/MainArgs.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; using Robust.LoaderApi; -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public sealed class MainArgs : IMainArgs { diff --git a/Nebula.Launcher/Models/RobustBuildInfo.cs b/Nebula.Shared/Models/RobustBuildInfo.cs similarity index 80% rename from Nebula.Launcher/Models/RobustBuildInfo.cs rename to Nebula.Shared/Models/RobustBuildInfo.cs index 98c4e8f..6e66a4f 100644 --- a/Nebula.Launcher/Models/RobustBuildInfo.cs +++ b/Nebula.Shared/Models/RobustBuildInfo.cs @@ -1,4 +1,4 @@ -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public class RobustBuildInfo { diff --git a/Nebula.Launcher/Models/RobustManifestInfo.cs b/Nebula.Shared/Models/RobustManifestInfo.cs similarity index 64% rename from Nebula.Launcher/Models/RobustManifestInfo.cs rename to Nebula.Shared/Models/RobustManifestInfo.cs index 1cb7b12..69ff327 100644 --- a/Nebula.Launcher/Models/RobustManifestInfo.cs +++ b/Nebula.Shared/Models/RobustManifestInfo.cs @@ -1,5 +1,3 @@ -using System; - -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public record struct RobustManifestInfo(Uri ManifestUri, Uri DownloadUri, string Hash); \ No newline at end of file diff --git a/Nebula.Launcher/Models/RobustManifestItem.cs b/Nebula.Shared/Models/RobustManifestItem.cs similarity index 68% rename from Nebula.Launcher/Models/RobustManifestItem.cs rename to Nebula.Shared/Models/RobustManifestItem.cs index bcde69f..48d6fde 100644 --- a/Nebula.Launcher/Models/RobustManifestItem.cs +++ b/Nebula.Shared/Models/RobustManifestItem.cs @@ -1,3 +1,3 @@ -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public record struct RobustManifestItem(string Hash, string Path, int Id); \ No newline at end of file diff --git a/Nebula.Launcher/Models/RobustServerEntry.cs b/Nebula.Shared/Models/RobustServerEntry.cs similarity index 97% rename from Nebula.Launcher/Models/RobustServerEntry.cs rename to Nebula.Shared/Models/RobustServerEntry.cs index 7aa94a8..dc2ef32 100644 --- a/Nebula.Launcher/Models/RobustServerEntry.cs +++ b/Nebula.Shared/Models/RobustServerEntry.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public sealed record AuthInfo( [property: JsonPropertyName("mode")] string Mode, diff --git a/Nebula.Launcher/Models/RobustUrl.cs b/Nebula.Shared/Models/RobustUrl.cs similarity index 94% rename from Nebula.Launcher/Models/RobustUrl.cs rename to Nebula.Shared/Models/RobustUrl.cs index 1c4a31d..c1326a2 100644 --- a/Nebula.Launcher/Models/RobustUrl.cs +++ b/Nebula.Shared/Models/RobustUrl.cs @@ -1,7 +1,6 @@ -using System; -using Nebula.Launcher.Utils; +using Nebula.Shared.Utils; -namespace Nebula.Launcher.Models; +namespace Nebula.Shared.Models; public class RobustUrl { diff --git a/Nebula.Shared/Nebula.Shared.csproj b/Nebula.Shared/Nebula.Shared.csproj new file mode 100644 index 0000000..8a8daa2 --- /dev/null +++ b/Nebula.Shared/Nebula.Shared.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + + + + diff --git a/Nebula.Shared/ServiceManager.cs b/Nebula.Shared/ServiceManager.cs new file mode 100644 index 0000000..9e1dfc9 --- /dev/null +++ b/Nebula.Shared/ServiceManager.cs @@ -0,0 +1,44 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Nebula.Shared; + +public static class ServiceExt +{ + public static void AddServices(this IServiceCollection services) + { + foreach (var (type, inference) in GetServicesWithHelpAttribute(Assembly.GetExecutingAssembly())) + { + 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()) + { + var attr = type.GetCustomAttribute(); + 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; + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Services/AssemblyService.cs b/Nebula.Shared/Services/AssemblyService.cs similarity index 80% rename from Nebula.Launcher/Services/AssemblyService.cs rename to Nebula.Shared/Services/AssemblyService.cs index 331a1ec..432d115 100644 --- a/Nebula.Launcher/Services/AssemblyService.cs +++ b/Nebula.Shared/Services/AssemblyService.cs @@ -1,19 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; -using Nebula.Launcher.FileApis; +using Nebula.Shared.FileApis; using Robust.LoaderApi; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] public class AssemblyService { - private readonly List _assemblies = new(); + private readonly Dictionary _assemblies = new(); private readonly DebugService _debugService; public AssemblyService(DebugService debugService) @@ -21,12 +18,12 @@ public class AssemblyService _debugService = debugService; } - public IReadOnlyList Assemblies => _assemblies; + //public IReadOnlyList Assemblies => _assemblies; - public AssemblyApi Mount(IFileApi fileApi) + public AssemblyApi Mount(IFileApi fileApi, string apiName = "") { var asmApi = new AssemblyApi(fileApi); - AssemblyLoadContext.Default.Resolving += (context, name) => OnAssemblyResolving(context, name, asmApi); + AssemblyLoadContext.Default.Resolving += (context, name) => OnAssemblyResolving(context, name, asmApi, apiName); AssemblyLoadContext.Default.ResolvingUnmanagedDll += LoadContextOnResolvingUnmanaged; return asmApi; @@ -56,6 +53,11 @@ public class AssemblyService public bool TryOpenAssembly(string name, AssemblyApi assemblyApi, [NotNullWhen(true)] out Assembly? assembly) { + if (_assemblies.TryGetValue(name, out assembly)) + { + return true; + } + if (!TryOpenAssemblyStream(name, assemblyApi, out var asm, out var pdb)) { assembly = null; @@ -64,9 +66,8 @@ public class AssemblyService assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb); _debugService.Log("LOADED ASSEMBLY " + name); - - - if (!_assemblies.Contains(assembly)) _assemblies.Add(assembly); + + _assemblies.Add(name, assembly); asm.Dispose(); pdb?.Dispose(); @@ -86,9 +87,11 @@ public class AssemblyService return true; } - private Assembly? OnAssemblyResolving(AssemblyLoadContext context, AssemblyName name, AssemblyApi assemblyApi) + private Assembly? OnAssemblyResolving(AssemblyLoadContext context, AssemblyName name, AssemblyApi assemblyApi, + string apiName) { - _debugService.Debug("Resolving assembly from FileAPI: " + name.Name); + + _debugService.Debug($"Resolving assembly from {apiName}: {name.Name}"); return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null; } diff --git a/Nebula.Launcher/Services/AuthService.cs b/Nebula.Shared/Services/AuthService.cs similarity index 65% rename from Nebula.Launcher/Services/AuthService.cs rename to Nebula.Shared/Services/AuthService.cs index 56c603a..28f5987 100644 --- a/Nebula.Launcher/Services/AuthService.cs +++ b/Nebula.Shared/Services/AuthService.cs @@ -1,45 +1,32 @@ -using System; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Input; -using CommunityToolkit.Mvvm.ComponentModel; -using Nebula.Launcher.Models.Auth; +using System.Net.Http.Headers; +using Nebula.Shared.Models.Auth; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] -public partial class AuthService : ObservableObject +public partial class AuthService( + RestService restService, + DebugService debugService, + CancellationService cancellationService) { private readonly HttpClient _httpClient = new(); - private readonly RestService _restService; - private readonly DebugService _debugService; - - [ObservableProperty] - private CurrentAuthInfo? _selectedAuth; + public CurrentAuthInfo? SelectedAuth { get; internal set; } public string Reason = ""; - public AuthService(RestService restService, DebugService debugService) - { - _restService = restService; - _debugService = debugService; - } - public async Task Auth(AuthLoginPassword authLoginPassword) { var authServer = authLoginPassword.AuthServer; var login = authLoginPassword.Login; var password = authLoginPassword.Password; - _debugService.Debug($"Auth to {authServer}api/auth/authenticate {login}"); + debugService.Debug($"Auth to {authServer}api/auth/authenticate {login}"); var authUrl = new Uri($"{authServer}api/auth/authenticate"); var result = - await _restService.PostAsync( - new AuthenticateRequest(login, password), authUrl, CancellationToken.None); + await restService.PostAsync( + new AuthenticateRequest(login, password), authUrl, cancellationService.Token); if (result.Value is null) { @@ -53,6 +40,11 @@ public partial class AuthService : ObservableObject return true; } + public void ClearAuth() + { + SelectedAuth = null; + } + public async Task EnsureToken() { if (SelectedAuth is null) return false; @@ -61,7 +53,7 @@ public partial class AuthService : ObservableObject using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", SelectedAuth.Token.Token); - using var resp = await _httpClient.SendAsync(requestMessage); + using var resp = await _httpClient.SendAsync(requestMessage, cancellationService.Token); if (!resp.IsSuccessStatusCode) SelectedAuth = null; diff --git a/Nebula.Shared/Services/CancellationService.cs b/Nebula.Shared/Services/CancellationService.cs new file mode 100644 index 0000000..0a68a64 --- /dev/null +++ b/Nebula.Shared/Services/CancellationService.cs @@ -0,0 +1,14 @@ +namespace Nebula.Shared.Services; + +[ServiceRegister] +public class CancellationService +{ + private CancellationTokenSource _cancellationTokenSource = new(); + public CancellationToken Token => _cancellationTokenSource.Token; + + public void Cancel() + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Services/ConfigurationService.cs b/Nebula.Shared/Services/ConfigurationService.cs similarity index 98% rename from Nebula.Launcher/Services/ConfigurationService.cs rename to Nebula.Shared/Services/ConfigurationService.cs index fbd7f46..83ff526 100644 --- a/Nebula.Launcher/Services/ConfigurationService.cs +++ b/Nebula.Shared/Services/ConfigurationService.cs @@ -1,9 +1,7 @@ -using System; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Text.Json; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; public class ConVar { diff --git a/Nebula.Launcher/Services/ContentService.Download.cs b/Nebula.Shared/Services/ContentService.Download.cs similarity index 82% rename from Nebula.Launcher/Services/ContentService.Download.cs rename to Nebula.Shared/Services/ContentService.Download.cs index 2834483..5700dfb 100644 --- a/Nebula.Launcher/Services/ContentService.Download.cs +++ b/Nebula.Shared/Services/ContentService.Download.cs @@ -1,24 +1,18 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; +using System.Buffers.Binary; using System.Globalization; -using System.IO; -using System.Net.Http; using System.Net.Http.Headers; using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using Nebula.Launcher.FileApis.Interfaces; -using Nebula.Launcher.Models; -using Nebula.Launcher.Utils; +using Nebula.Shared.FileApis.Interfaces; +using Nebula.Shared.Models; +using Nebula.Shared.Utils; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; public partial class ContentService { public bool CheckManifestExist(RobustManifestItem item) { - return _fileService.ContentFileApi.Has(item.Hash); + return fileService.ContentFileApi.Has(item.Hash); } public async Task> EnsureItems(ManifestReader manifestReader, Uri downloadUri, @@ -31,7 +25,7 @@ public partial class ContentService { if (cancellationToken.IsCancellationRequested) { - _debugService.Log("ensuring is cancelled!"); + debugService.Log("ensuring is cancelled!"); return []; } @@ -40,11 +34,11 @@ public partial class ContentService allItems.Add(item.Value); } - _debugService.Log("Download Count:" + items.Count); + debugService.Log("Download Count:" + items.Count); await Download(downloadUri, items, cancellationToken); - _fileService.ManifestItems = allItems; + fileService.ManifestItems = allItems; return allItems; } @@ -52,21 +46,21 @@ public partial class ContentService public async Task> EnsureItems(RobustManifestInfo info, CancellationToken cancellationToken) { - _debugService.Log("Getting manifest: " + info.Hash); + debugService.Log("Getting manifest: " + info.Hash); - if (_fileService.ManifestFileApi.TryOpen(info.Hash, out var stream)) + if (fileService.ManifestFileApi.TryOpen(info.Hash, out var stream)) { - _debugService.Log("Loading manifest from: " + info.Hash); + debugService.Log("Loading manifest from: " + info.Hash); return await EnsureItems(new ManifestReader(stream), info.DownloadUri, cancellationToken); } - _debugService.Log("Fetching manifest from: " + info.ManifestUri); + debugService.Log("Fetching manifest from: " + info.ManifestUri); var response = await _http.GetAsync(info.ManifestUri, cancellationToken); if (!response.IsSuccessStatusCode) throw new Exception(); await using var streamContent = await response.Content.ReadAsStreamAsync(cancellationToken); - _fileService.ManifestFileApi.Save(info.Hash, streamContent); + fileService.ManifestFileApi.Save(info.Hash, streamContent); streamContent.Seek(0, SeekOrigin.Begin); using var manifestReader = new ManifestReader(streamContent); return await EnsureItems(manifestReader, info.DownloadUri, cancellationToken); @@ -74,18 +68,18 @@ public partial class ContentService public async Task Unpack(RobustManifestInfo info, IWriteFileApi otherApi, CancellationToken cancellationToken) { - _debugService.Log("Unpack manifest files"); + debugService.Log("Unpack manifest files"); var items = await EnsureItems(info, cancellationToken); foreach (var item in items) - if (_fileService.ContentFileApi.TryOpen(item.Hash, out var stream)) + if (fileService.ContentFileApi.TryOpen(item.Hash, out var stream)) { - _debugService.Log($"Unpack {item.Hash} to: {item.Path}"); + debugService.Log($"Unpack {item.Hash} to: {item.Path}"); otherApi.Save(item.Path, stream); stream.Close(); } else { - _debugService.Error("OH FUCK!! " + item.Path); + debugService.Error("OH FUCK!! " + item.Path); } } @@ -93,11 +87,11 @@ public partial class ContentService { if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested) { - _debugService.Log("Nothing to download! Fuck this!"); + debugService.Log("Nothing to download! Fuck this!"); return; } - _debugService.Log("Downloading from: " + contentCdn); + debugService.Log("Downloading from: " + contentCdn); var requestBody = new byte[toDownload.Count * 4]; var reqI = 0; @@ -110,7 +104,7 @@ 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"); @@ -120,7 +114,7 @@ public partial class ContentService if (cancellationToken.IsCancellationRequested) { - _debugService.Log("Downloading is cancelled!"); + debugService.Log("Downloading is cancelled!"); return; } @@ -165,7 +159,7 @@ public partial class ContentService { if (cancellationToken.IsCancellationRequested) { - _debugService.Log("Downloading is cancelled!"); + debugService.Log("Downloading is cancelled!"); decompressContext?.Dispose(); compressContext?.Dispose(); return; @@ -233,9 +227,9 @@ public partial class ContentService } using var fileStream = new MemoryStream(data.ToArray()); - _fileService.ContentFileApi.Save(item.Hash, fileStream); + fileService.ContentFileApi.Save(item.Hash, fileStream); - _debugService.Log("file saved:" + item.Path); + debugService.Log("file saved:" + item.Path); i += 1; } } diff --git a/Nebula.Shared/Services/ContentService.cs b/Nebula.Shared/Services/ContentService.cs new file mode 100644 index 0000000..c29cfe3 --- /dev/null +++ b/Nebula.Shared/Services/ContentService.cs @@ -0,0 +1,30 @@ +using System.Data; +using Nebula.Shared.Models; + +namespace Nebula.Shared.Services; + +[ServiceRegister] +public partial class ContentService( + RestService restService, + DebugService debugService, + ConfigurationService varService, + FileService fileService) +{ + private readonly HttpClient _http = new(); + + public async Task GetBuildInfo(RobustUrl url, CancellationToken cancellationToken) + { + var info = new RobustBuildInfo(); + info.Url = url; + var bi = await restService.GetAsync(url.InfoUri, cancellationToken); + if (bi.Value is null) throw new NoNullAllowedException(); + info.BuildInfo = bi.Value; + info.RobustManifestInfo = info.BuildInfo.Build.Acz + ? new RobustManifestInfo(new RobustPath(info.Url, "manifest.txt"), new RobustPath(info.Url, "download"), + bi.Value.Build.ManifestHash) + : new RobustManifestInfo(new Uri(info.BuildInfo.Build.ManifestUrl), + new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Value.Build.ManifestHash); + + return info; + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Services/DebugService.cs b/Nebula.Shared/Services/DebugService.cs similarity index 73% rename from Nebula.Launcher/Services/DebugService.cs rename to Nebula.Shared/Services/DebugService.cs index b903d82..40bfac8 100644 --- a/Nebula.Launcher/Services/DebugService.cs +++ b/Nebula.Shared/Services/DebugService.cs @@ -1,8 +1,6 @@ -using System; -using System.IO; -using Nebula.Launcher.Services.Logging; +using Nebula.Shared.Services.Logging; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] public class DebugService : IDisposable @@ -18,14 +16,14 @@ public class DebugService : IDisposable { Logger = logger; - if (!Directory.Exists(LogPath)) - Directory.CreateDirectory(LogPath); + //if (!Directory.Exists(LogPath)) + // Directory.CreateDirectory(LogPath); - var filename = String.Format("{0:yyyy-MM-dd}.txt", DateTime.Now); + //var filename = String.Format("{0:yyyy-MM-dd}.txt", DateTime.Now); - LogStream = File.Open(Path.Combine(LogPath, filename), - FileMode.Append, FileAccess.Write); - LogWriter = new StreamWriter(LogStream); + //LogStream = File.Open(Path.Combine(LogPath, filename), + // FileMode.Append, FileAccess.Write); + //LogWriter = new StreamWriter(LogStream); } public void Debug(string message) @@ -59,7 +57,7 @@ public class DebugService : IDisposable private void Log(LoggerCategory category, string message) { Logger.Log(category, message); - SaveToLog(category, message); + //SaveToLog(category, message); } private void SaveToLog(LoggerCategory category, string message) diff --git a/Nebula.Launcher/Services/EngineService.cs b/Nebula.Shared/Services/EngineService.cs similarity index 86% rename from Nebula.Launcher/Services/EngineService.cs rename to Nebula.Shared/Services/EngineService.cs index 1b7590d..f0fc197 100644 --- a/Nebula.Launcher/Services/EngineService.cs +++ b/Nebula.Shared/Services/EngineService.cs @@ -1,19 +1,12 @@ -using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Nebula.Launcher.FileApis; -using Nebula.Launcher.Models; -using Nebula.Launcher.Utils; +using Nebula.Shared.FileApis; +using Nebula.Shared.Models; +using Nebula.Shared.Utils; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] -public class EngineService +public sealed class EngineService { private readonly AssemblyService _assemblyService; private readonly DebugService _debugService; @@ -21,9 +14,11 @@ public class EngineService private readonly RestService _restService; private readonly IServiceProvider _serviceProvider; private readonly ConfigurationService _varService; - public Dictionary ModuleInfos; + + public Dictionary ModuleInfos = default!; + public Dictionary VersionInfos = default!; - public Dictionary VersionInfos; + private Task _currInfoTask; public EngineService(RestService restService, DebugService debugService, ConfigurationService varService, FileService fileService, IServiceProvider serviceProvider, AssemblyService assemblyService) @@ -35,8 +30,7 @@ public class EngineService _serviceProvider = serviceProvider; _assemblyService = assemblyService; - var loadTask = Task.Run(() => LoadEngineManifest(CancellationToken.None)); - loadTask.Wait(); + _currInfoTask = Task.Run(() => LoadEngineManifest(CancellationToken.None)); } public async Task LoadEngineManifest(CancellationToken cancellationToken) @@ -57,6 +51,8 @@ public class EngineService public EngineBuildInfo? GetVersionInfo(string version) { + CheckAndWaitValidation(); + if (!VersionInfos.TryGetValue(version, out var foundVersion)) return null; @@ -85,7 +81,7 @@ public class EngineService try { - return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi)); + return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi),$"Engine.Ensure-{version}"); } catch (Exception e) { @@ -120,6 +116,8 @@ public class EngineService public EngineBuildInfo? GetModuleBuildInfo(string moduleName, string version) { + CheckAndWaitValidation(); + if (!ModuleInfos.TryGetValue(moduleName, out var module) || !module.Versions.TryGetValue(version, out var value)) return null; @@ -138,6 +136,8 @@ public class EngineService public string ResolveModuleVersion(string moduleName, string engineVersion) { + CheckAndWaitValidation(); + var engineVersionObj = Version.Parse(engineVersion); var module = ModuleInfos[moduleName]; var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv.Value }) @@ -161,7 +161,7 @@ public class EngineService try { - return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi)); + return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi),"Engine.EnsureModule"); } catch (Exception e) { @@ -186,4 +186,13 @@ public class EngineService { return moduleName + "" + moduleVersion; } + + private void CheckAndWaitValidation() + { + if (_currInfoTask.IsCompleted) + return; + + _debugService.Debug("thinks is not done yet, please wait"); + _currInfoTask.Wait(); + } } \ No newline at end of file diff --git a/Nebula.Launcher/Services/FileService.cs b/Nebula.Shared/Services/FileService.cs similarity index 88% rename from Nebula.Launcher/Services/FileService.cs rename to Nebula.Shared/Services/FileService.cs index 178283f..922f026 100644 --- a/Nebula.Launcher/Services/FileService.cs +++ b/Nebula.Shared/Services/FileService.cs @@ -1,15 +1,11 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; -using Nebula.Launcher.FileApis; -using Nebula.Launcher.FileApis.Interfaces; -using Nebula.Launcher.Models; -using Nebula.Launcher.Utils; +using Nebula.Shared.FileApis; +using Nebula.Shared.FileApis.Interfaces; +using Nebula.Shared.Models; using Robust.LoaderApi; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] public class FileService diff --git a/Nebula.Launcher/Services/HubService.cs b/Nebula.Shared/Services/HubService.cs similarity index 91% rename from Nebula.Launcher/Services/HubService.cs rename to Nebula.Shared/Services/HubService.cs index 0374b07..a3a0550 100644 --- a/Nebula.Launcher/Services/HubService.cs +++ b/Nebula.Shared/Services/HubService.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using Nebula.Launcher.Models; +using Nebula.Shared.Models; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] public class HubService diff --git a/Nebula.Launcher/Services/Logging/ConsoleLogger.cs b/Nebula.Shared/Services/Logging/ConsoleLogger.cs similarity index 85% rename from Nebula.Launcher/Services/Logging/ConsoleLogger.cs rename to Nebula.Shared/Services/Logging/ConsoleLogger.cs index e5f3e45..7a35728 100644 --- a/Nebula.Launcher/Services/Logging/ConsoleLogger.cs +++ b/Nebula.Shared/Services/Logging/ConsoleLogger.cs @@ -1,6 +1,4 @@ -using System; - -namespace Nebula.Launcher.Services.Logging; +namespace Nebula.Shared.Services.Logging; [ServiceRegister(typeof(ILogger))] public class ConsoleLogger : ILogger diff --git a/Nebula.Launcher/Services/Logging/ILogger.cs b/Nebula.Shared/Services/Logging/ILogger.cs similarity index 68% rename from Nebula.Launcher/Services/Logging/ILogger.cs rename to Nebula.Shared/Services/Logging/ILogger.cs index 47d805e..51bb543 100644 --- a/Nebula.Launcher/Services/Logging/ILogger.cs +++ b/Nebula.Shared/Services/Logging/ILogger.cs @@ -1,4 +1,4 @@ -namespace Nebula.Launcher.Services.Logging; +namespace Nebula.Shared.Services.Logging; public interface ILogger { diff --git a/Nebula.Shared/Services/PopupMessageService.cs b/Nebula.Shared/Services/PopupMessageService.cs new file mode 100644 index 0000000..ab4c8d1 --- /dev/null +++ b/Nebula.Shared/Services/PopupMessageService.cs @@ -0,0 +1,15 @@ +namespace Nebula.Shared.Services; + +[ServiceRegister] +public class PopupMessageService +{ + public Action? OnPopupRequired; + public void Popup(object obj) + { + OnPopupRequired?.Invoke(obj); + } + public void ClosePopup() + { + OnPopupRequired?.Invoke(null); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Services/RestService.cs b/Nebula.Shared/Services/RestService.cs similarity index 95% rename from Nebula.Launcher/Services/RestService.cs rename to Nebula.Shared/Services/RestService.cs index 87b5d24..af10388 100644 --- a/Nebula.Launcher/Services/RestService.cs +++ b/Nebula.Shared/Services/RestService.cs @@ -1,16 +1,10 @@ -using System; using System.Globalization; -using System.IO; using System.Net; -using System.Net.Http; -using System.Net.Http.Json; using System.Text; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Nebula.Launcher.Utils; +using Nebula.Shared.Utils; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] public class RestService diff --git a/Nebula.Launcher/Services/RunnerService.cs b/Nebula.Shared/Services/RunnerService.cs similarity index 53% rename from Nebula.Launcher/Services/RunnerService.cs rename to Nebula.Shared/Services/RunnerService.cs index 711ae35..a0e0ee2 100644 --- a/Nebula.Launcher/Services/RunnerService.cs +++ b/Nebula.Shared/Services/RunnerService.cs @@ -1,66 +1,68 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Nebula.Launcher.Models; +using Nebula.Shared.Models; using Robust.LoaderApi; -namespace Nebula.Launcher.Services; +namespace Nebula.Shared.Services; [ServiceRegister] -public class RunnerService: IRedialApi +public sealed class RunnerService( + ContentService contentService, + DebugService debugService, + ConfigurationService varService, + FileService fileService, + EngineService engineService, + AssemblyService assemblyService, + AuthService authService, + PopupMessageService popupMessageService, + CancellationService cancellationService) + : IRedialApi { - private readonly AssemblyService _assemblyService; - private readonly AuthService _authService; - private readonly PopupMessageService _popupMessageService; - private readonly ContentService _contentService; - private readonly DebugService _debugService; - private readonly EngineService _engineService; - private readonly FileService _fileService; - private readonly ConfigurationService _varService; - - public RunnerService(ContentService contentService, DebugService debugService, ConfigurationService varService, - FileService fileService, EngineService engineService, AssemblyService assemblyService, AuthService authService, - PopupMessageService popupMessageService) + public async Task PrepareRun(RobustUrl url) { - _contentService = contentService; - _debugService = debugService; - _varService = varService; - _fileService = fileService; - _engineService = engineService; - _assemblyService = assemblyService; - _authService = authService; - _popupMessageService = popupMessageService; + var buildInfo = await contentService.GetBuildInfo(url, cancellationService.Token); + await PrepareRun(buildInfo, cancellationService.Token); + } + + public async Task PrepareRun(RobustBuildInfo buildInfo, CancellationToken cancellationToken) + { + debugService.Log("Prepare Content!"); + + var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); + + if (engine is null) + throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion); + + await contentService.EnsureItems(buildInfo.RobustManifestInfo, cancellationToken); + await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion); } public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi, CancellationToken cancellationToken) { - _debugService.Log("Start Content!"); + debugService.Log("Start Content!"); - var engine = await _engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); + var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); if (engine is null) throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion); - await _contentService.EnsureItems(buildInfo.RobustManifestInfo, cancellationToken); + await contentService.EnsureItems(buildInfo.RobustManifestInfo, cancellationToken); var extraMounts = new List { - new(_fileService.HashApi, "/") + new(fileService.HashApi, "/") }; var module = - await _engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion); + await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion); if (module is not null) extraMounts.Add(new ApiMount(module, "/")); 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)) + if (!assemblyService.TryGetLoader(clientAssembly, out var loader)) return; await Task.Run(() => loader.Main(args), cancellationToken); @@ -71,12 +73,12 @@ public class RunnerService: IRedialApi var url = new RobustUrl(urlraw); using var cancelTokenSource = new CancellationTokenSource(); - var buildInfo = await _contentService.GetBuildInfo(url, cancelTokenSource.Token); + var buildInfo = await contentService.GetBuildInfo(url, cancelTokenSource.Token); - var account = _authService.SelectedAuth; + var account = authService.SelectedAuth; if (account is null) { - _popupMessageService.PopupInfo("Error! Auth is required!"); + popupMessageService.Popup("Error! Auth is required!"); return; } @@ -112,13 +114,13 @@ public class RunnerService: IRedialApi args.Add("--ss14-address"); args.Add(url.ToString()); - _debugService.Debug("Connect to " + url.ToString() + " " + account.AuthLoginPassword.AuthServer); + debugService.Debug("Connect to " + url.ToString() + " " + account.AuthLoginPassword.AuthServer); await Run(args.ToArray(), buildInfo, this, cancelTokenSource.Token); } public async void Redial(Uri uri, string text = "") { - await RunGame(uri.ToString()); + //await RunGame(uri.ToString()); } } \ No newline at end of file diff --git a/Nebula.Launcher/Utils/BandwidthStream.cs b/Nebula.Shared/Utils/BandwidthStream.cs similarity index 96% rename from Nebula.Launcher/Utils/BandwidthStream.cs rename to Nebula.Shared/Utils/BandwidthStream.cs index c77cbbe..4778cd8 100644 --- a/Nebula.Launcher/Utils/BandwidthStream.cs +++ b/Nebula.Shared/Utils/BandwidthStream.cs @@ -1,10 +1,6 @@ -using System; using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public sealed class BandwidthStream : Stream { diff --git a/Nebula.Launcher/Utils/DelegateCommand.cs b/Nebula.Shared/Utils/DelegateCommand.cs similarity index 84% rename from Nebula.Launcher/Utils/DelegateCommand.cs rename to Nebula.Shared/Utils/DelegateCommand.cs index be0530a..ede602c 100644 --- a/Nebula.Launcher/Utils/DelegateCommand.cs +++ b/Nebula.Shared/Utils/DelegateCommand.cs @@ -1,8 +1,6 @@ -using System; using System.Windows.Input; -using Nebula.Launcher.ViewModels; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public class DelegateCommand : ICommand { diff --git a/Nebula.Launcher/Utils/Helper.cs b/Nebula.Shared/Utils/Helper.cs similarity index 92% rename from Nebula.Launcher/Utils/Helper.cs rename to Nebula.Shared/Utils/Helper.cs index 4568cf6..a0df568 100644 --- a/Nebula.Launcher/Utils/Helper.cs +++ b/Nebula.Shared/Utils/Helper.cs @@ -1,10 +1,8 @@ using System.Diagnostics; -using System.Net.Http; using System.Runtime.InteropServices; using System.Text.Json; -using System.Threading.Tasks; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public static class Helper { diff --git a/Nebula.Launcher/Utils/Manifest.cs b/Nebula.Shared/Utils/Manifest.cs similarity index 97% rename from Nebula.Launcher/Utils/Manifest.cs rename to Nebula.Shared/Utils/Manifest.cs index 5f6d9eb..91f3048 100644 --- a/Nebula.Launcher/Utils/Manifest.cs +++ b/Nebula.Shared/Utils/Manifest.cs @@ -1,9 +1,8 @@ using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Text; -using Nebula.Launcher.Models; +using Nebula.Shared.Models; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public class ManifestReader : StreamReader { diff --git a/Nebula.Launcher/Utils/Ref.cs b/Nebula.Shared/Utils/Ref.cs similarity index 62% rename from Nebula.Launcher/Utils/Ref.cs rename to Nebula.Shared/Utils/Ref.cs index b350d03..d33d626 100644 --- a/Nebula.Launcher/Utils/Ref.cs +++ b/Nebula.Shared/Utils/Ref.cs @@ -1,4 +1,4 @@ -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public class Ref { diff --git a/Nebula.Launcher/Utils/RidUtility.cs b/Nebula.Shared/Utils/RidUtility.cs similarity index 97% rename from Nebula.Launcher/Utils/RidUtility.cs rename to Nebula.Shared/Utils/RidUtility.cs index 73bb866..dea9684 100644 --- a/Nebula.Launcher/Utils/RidUtility.cs +++ b/Nebula.Shared/Utils/RidUtility.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Runtime.InteropServices; using System.Text.Json; using System.Text.Json.Serialization; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public static class RidUtility { diff --git a/Nebula.Launcher/Utils/StreamHelper.cs b/Nebula.Shared/Utils/StreamHelper.cs similarity index 92% rename from Nebula.Launcher/Utils/StreamHelper.cs rename to Nebula.Shared/Utils/StreamHelper.cs index 45ddeed..1693f95 100644 --- a/Nebula.Launcher/Utils/StreamHelper.cs +++ b/Nebula.Shared/Utils/StreamHelper.cs @@ -1,10 +1,6 @@ -using System; using System.Buffers; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public static class StreamHelper { diff --git a/Nebula.Launcher/Utils/UriHelper.cs b/Nebula.Shared/Utils/UriHelper.cs similarity index 96% rename from Nebula.Launcher/Utils/UriHelper.cs rename to Nebula.Shared/Utils/UriHelper.cs index 8e0a259..cf674f0 100644 --- a/Nebula.Launcher/Utils/UriHelper.cs +++ b/Nebula.Shared/Utils/UriHelper.cs @@ -1,9 +1,9 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Web; +using Nebula.Shared.Models; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public static class UriHelper { @@ -132,4 +132,9 @@ public static class UriHelper return uriBuilder.Uri; } + + public static RobustUrl ToRobustUrl(this string str) + { + return new RobustUrl(str); + } } \ No newline at end of file diff --git a/Nebula.Launcher/Utils/ZStd.cs b/Nebula.Shared/Utils/ZStd.cs similarity index 99% rename from Nebula.Launcher/Utils/ZStd.cs rename to Nebula.Shared/Utils/ZStd.cs index 4b49da6..254b622 100644 --- a/Nebula.Launcher/Utils/ZStd.cs +++ b/Nebula.Shared/Utils/ZStd.cs @@ -1,15 +1,11 @@ -using System; using System.Buffers; -using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; using SharpZstd.Interop; using static SharpZstd.Interop.Zstd; -namespace Nebula.Launcher.Utils; +namespace Nebula.Shared.Utils; public static class ZStd { diff --git a/Nebula.Launcher/Utils/runtime.json b/Nebula.Shared/Utils/runtime.json similarity index 100% rename from Nebula.Launcher/Utils/runtime.json rename to Nebula.Shared/Utils/runtime.json diff --git a/Nebula.sln b/Nebula.sln index 634edbc..65b1fc5 100644 --- a/Nebula.sln +++ b/Nebula.sln @@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Launcher", "Nebula.L EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{8AE91631-DE96-4A97-A255-058E27A7C3EA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Shared", "Nebula.Shared\Nebula.Shared.csproj", "{47519EA2-03C0-49D8-86CA-418F6B7267A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Runner", "Nebula.Runner\Nebula.Runner.csproj", "{82D96367-44B0-4F25-A094-CBE73B052B73}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +22,13 @@ Global {8AE91631-DE96-4A97-A255-058E27A7C3EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {8AE91631-DE96-4A97-A255-058E27A7C3EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {8AE91631-DE96-4A97-A255-058E27A7C3EA}.Release|Any CPU.Build.0 = Release|Any CPU + {47519EA2-03C0-49D8-86CA-418F6B7267A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47519EA2-03C0-49D8-86CA-418F6B7267A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47519EA2-03C0-49D8-86CA-418F6B7267A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47519EA2-03C0-49D8-86CA-418F6B7267A4}.Release|Any CPU.Build.0 = Release|Any CPU + {82D96367-44B0-4F25-A094-CBE73B052B73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 EndGlobalSection EndGlobal