- tweak: refactor funny

This commit is contained in:
2025-01-05 17:05:23 +03:00
parent 5b24f915a2
commit 8619e248fd
67 changed files with 485 additions and 492 deletions

View File

@@ -8,6 +8,7 @@ using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewModels; using Nebula.Launcher.ViewModels;
using Nebula.Launcher.Views; using Nebula.Launcher.Views;
using Nebula.Shared;
namespace Nebula.Launcher; namespace Nebula.Launcher;

View File

@@ -1,11 +0,0 @@
using Nebula.Launcher.Services;
namespace Nebula.Launcher;
public class AppNoUi(RunnerService runnerService, AuthService authService)
{
public void Run(string[] args)
{
}
}

View File

@@ -26,13 +26,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="libsodium" Version="1.0.20" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Robust.Natives" Version="0.1.1" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.6" />
<EmbeddedResource Include="Utils\runtime.json">
<LogicalName>Utility.runtime.json</LogicalName>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -43,6 +37,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" /> <ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,40 +1,11 @@
using Avalonia; using Avalonia;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Nebula.Launcher; namespace Nebula.Launcher;
sealed class Program public static class Program
{ {
// Initialization code. Don't use any Avalonia, third-party APIs or any public static void Main(string[] args) => BuildAvaloniaApp()
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized .StartWithClassicDesktopLifetime(args);
// 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<AppNoUi>(); //Separated because no ui
var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetService<AppNoUi>()!.Run(args);
}
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
private static AppBuilder BuildAvaloniaApp() private static AppBuilder BuildAvaloniaApp()

View File

@@ -36,58 +36,22 @@ public static class ServiceCollectionExtensions
{ {
services.AddTransient<MainWindow>(); services.AddTransient<MainWindow>();
foreach (var (viewModel, view) in GetTypesWithHelpAttribute(Assembly.GetExecutingAssembly())) foreach (var (viewModel, view, isSingleton) in GetTypesWithHelpAttribute(Assembly.GetExecutingAssembly()))
{ {
services.AddSingleton(viewModel); if(isSingleton)services.AddSingleton(viewModel);
services.AddTransient(view); else services.AddTransient(viewModel);
} if (view != null) 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);
}
} }
} }
private static IEnumerable<(Type,Type)> GetTypesWithHelpAttribute(Assembly assembly) { private static IEnumerable<(Type,Type?,bool)> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) foreach(Type type in assembly.GetTypes())
{ {
var attr = type.GetCustomAttribute<ViewRegisterAttribute>(); var attr = type.GetCustomAttribute<ViewModelRegisterAttribute>();
if (attr is not null) { if (attr is not null) {
yield return (type, attr.Type); yield return (type, attr.Type, attr.IsSingleton);
}
}
}
private static IEnumerable<(Type,Type?)> GetServicesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes())
{
var attr = type.GetCustomAttribute<ServiceRegisterAttribute>();
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;
}
}

View File

@@ -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<RobustBuildInfo> GetBuildInfo(RobustUrl url, CancellationToken cancellationToken)
{
var info = new RobustBuildInfo();
info.Url = url;
var bi = await _restService.GetAsync<ServerInfo>(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;
}
}

View File

@@ -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<PopupViewModelBase?>? OnPopupRequired;
public PopupMessageService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void PopupInfo(string info)
{
var message = _serviceProvider.GetService<InfoPopupViewModel>();
message.InfoText = info;
PopupMessage(message);
}
public void PopupMessage(PopupViewModelBase viewModelBase)
{
OnPopupRequired?.Invoke(viewModelBase);
}
public void ClosePopup()
{
OnPopupRequired?.Invoke(null);
}
}

View File

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

View File

@@ -14,7 +14,7 @@ public class ViewLocator : IDataTemplate
if (param is null) if (param is null)
return null; return null;
var type = param.GetType().GetCustomAttribute<ViewRegisterAttribute>()?.Type; var type = param.GetType().GetCustomAttribute<ViewModelRegisterAttribute>()?.Type;
if (type != null) if (type != null)
{ {

View File

@@ -4,14 +4,15 @@ using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Nebula.Launcher.Services;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewHelper; using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Pages; using Nebula.Launcher.Views.Pages;
using Nebula.Shared;
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewRegister(typeof(AccountInfoView))] [ViewModelRegister(typeof(AccountInfoView))]
public partial class AccountInfoViewModel : ViewModelBase public partial class AccountInfoViewModel : ViewModelBase
{ {
private readonly PopupMessageService _popupMessageService; private readonly PopupMessageService _popupMessageService;
@@ -79,12 +80,12 @@ public partial class AccountInfoViewModel : ViewModelBase
public async void DoAuth() public async void DoAuth()
{ {
_popupMessageService.PopupInfo("Auth think, please wait..."); _popupMessageService.Popup("Auth think, please wait...");
if(await _authService.Auth(CurrentAlp)) if(await _authService.Auth(CurrentAlp))
{ {
_popupMessageService.ClosePopup(); _popupMessageService.ClosePopup();
_popupMessageService.PopupInfo("Hello, " + _authService.SelectedAuth!.AuthLoginPassword.Login); _popupMessageService.Popup("Hello, " + _authService.SelectedAuth!.AuthLoginPassword.Login);
IsLogged = true; IsLogged = true;
_configurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp); _configurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp);
} }
@@ -92,7 +93,7 @@ public partial class AccountInfoViewModel : ViewModelBase
{ {
_popupMessageService.ClosePopup(); _popupMessageService.ClosePopup();
Logout(); 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; IsLogged = false;
CurrentAlp = new AuthLoginPassword("", "", ""); CurrentAlp = new AuthLoginPassword("", "", "");
_authService.SelectedAuth = null; _authService.ClearAuth();
} }
private void UpdateAuthMenu() private void UpdateAuthMenu()

View File

@@ -5,7 +5,7 @@ using Nebula.Launcher.Views.Popup;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewRegister(typeof(InfoPopupView), false)] [ViewModelRegister(typeof(InfoPopupView), false)]
public partial class InfoPopupViewModel : PopupViewModelBase public partial class InfoPopupViewModel : PopupViewModelBase
{ {
public InfoPopupViewModel() public InfoPopupViewModel()

View File

@@ -5,15 +5,15 @@ using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using JetBrains.Annotations; using JetBrains.Annotations;
using Nebula.Launcher.Models;
using Nebula.Launcher.Services;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewHelper; using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views; using Nebula.Launcher.Views;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewRegister(typeof(MainView))] [ViewModelRegister(typeof(MainView))]
public partial class MainViewModel : ViewModelBase public partial class MainViewModel : ViewModelBase
{ {
public MainViewModel() public MainViewModel()
@@ -111,15 +111,23 @@ public partial class MainViewModel : ViewModelBase
Helper.OpenBrowser("https://cinka.ru/nebula-launcher/"); Helper.OpenBrowser("https://cinka.ru/nebula-launcher/");
} }
private void OnPopupRequired(PopupViewModelBase? viewModelBase) private void OnPopupRequired(object? viewModelBase)
{ {
if (viewModelBase is null) switch (viewModelBase)
{ {
ClosePopup(); case null:
} ClosePopup();
else break;
{ case string str:
PopupMessage(viewModelBase); {
var view = GetViewModel<InfoPopupViewModel>();
view.InfoText = str;
PopupMessage(view);
break;
}
case PopupViewModelBase @base:
PopupMessage(@base);
break;
} }
} }

View File

@@ -1,33 +1,41 @@
using System; using System;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection; using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Models; using Nebula.Shared.Models;
using Nebula.Launcher.Services;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(isSingleton:false)]
public partial class ServerEntryModelView : ViewModelBase public partial class ServerEntryModelView : ViewModelBase
{ {
[ObservableProperty] private bool _runVisible = true;
private readonly IServiceProvider _serviceProvider;
private readonly RunnerService _runnerService;
private readonly PopupMessageService _popupMessageService;
private readonly RestService _restService;
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<RunnerService>()!;
_popupMessageService = serviceProvider.GetService<PopupMessageService>()!;
_restService = serviceProvider.GetService<RestService>()!;
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()
{
} }
} }

View File

@@ -3,14 +3,14 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.Models;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewHelper; using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Pages; using Nebula.Launcher.Views.Pages;
using Nebula.Shared.Models;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewRegister(typeof(ServerListView))] [ViewModelRegister(typeof(ServerListView))]
public partial class ServerListViewModel : ViewModelBase public partial class ServerListViewModel : ViewModelBase
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
@@ -84,7 +84,9 @@ public partial class ServerListViewModel : ViewModelBase
private ServerEntryModelView CreateServerView(ServerHubInfo serverHubInfo) private ServerEntryModelView CreateServerView(ServerHubInfo serverHubInfo)
{ {
return new ServerEntryModelView(_serviceProvider, serverHubInfo); var svn = GetViewModel<ServerEntryModelView>();
svn.ServerHubInfo = serverHubInfo;
return svn;
} }
public void FilterRequired() public void FilterRequired()

View File

@@ -8,7 +8,7 @@
xmlns:converters="clr-namespace:Nebula.Launcher.Converters" xmlns:converters="clr-namespace:Nebula.Launcher.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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:viewModels="clr-namespace:Nebula.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext> <Design.DataContext>

View File

@@ -8,7 +8,6 @@
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:Nebula.Launcher.Models"
xmlns:viewModels="clr-namespace:Nebula.Launcher.ViewModels" 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">
@@ -25,7 +24,7 @@
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:ServerEntryModelView}"> <DataTemplate DataType="{x:Type viewModels:ServerEntryModelView}">
<Grid <Grid
ColumnDefinitions="*,90" ColumnDefinitions="*,120"
Margin="0,5,0,5" Margin="0,5,0,5"
RowDefinitions="30,*"> RowDefinitions="30,*">
<Border <Border
@@ -38,7 +37,7 @@
</Label> </Label>
</Border> </Border>
<Border <Border
BorderThickness="2,0,0,0" BorderThickness="2,0,2,0"
CornerRadius="0" CornerRadius="0"
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
@@ -69,9 +68,9 @@
</Border.Background> </Border.Background>
<Border <Border
BorderThickness="0,2,2,0" BorderThickness="0,2,2,0"
CornerRadius="0,10,10,10" CornerRadius="0,0,10,10"
IsVisible="True" IsVisible="True"
Margin="0,0,5,0" Margin="0,0,0,0"
Opacity="40" Opacity="40"
Padding="0"> Padding="0">
<UniformGrid Margin="15,5,15,0"> <UniformGrid Margin="15,5,15,0">
@@ -121,18 +120,36 @@
</Border> </Border>
<Panel Grid.Column="1" Grid.Row="1"> <Panel Grid.Column="1" Grid.Row="1">
<Border Classes="ButtonBack" CornerRadius="0,0,10,0"> <Button
<Button Command="{Binding RunInstance}"
Command="{Binding OnConnectRequired}" IsVisible="{Binding RunVisible}"
CornerRadius="0,0,10,0" HorizontalAlignment="Stretch"
HorizontalAlignment="Stretch" CornerRadius="10,0,10,10"
Padding="0" VerticalAlignment="Stretch">
VerticalAlignment="Stretch"> <Label HorizontalAlignment="Center" VerticalAlignment="Center">
Play
</Label>
</Button>
<Grid Grid.ColumnDefinitions="*,*" IsVisible="{Binding !RunVisible}">
<Button Command="{Binding ReadLog}"
CornerRadius="10,0,0,10"
Margin="0,0,1,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Label HorizontalAlignment="Center" VerticalAlignment="Center"> <Label HorizontalAlignment="Center" VerticalAlignment="Center">
Play Log
</Label> </Label>
</Button> </Button>
</Border> <Button Grid.Column="1" HorizontalAlignment="Stretch"
CornerRadius="0,0,10,0"
Margin="1,0,0,0"
VerticalAlignment="Stretch"
Command="{Binding StopInstance}">
<Label HorizontalAlignment="Center" VerticalAlignment="Center">
Stop
</Label>
</Button>
</Grid>
</Panel> </Panel>
</Grid> </Grid>
</DataTemplate> </DataTemplate>

14
Nebula.Runner/App.cs Normal file
View File

@@ -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));
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
</ItemGroup>
</Project>

16
Nebula.Runner/Program.cs Normal file
View File

@@ -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<App>()!.Run(args);
}
}

View File

@@ -1,6 +1,6 @@
using Nebula.Launcher.Services; using Nebula.Shared.Services;
namespace Nebula.Launcher; namespace Nebula.Shared;
public static class CurrentConVar public static class CurrentConVar
{ {

View File

@@ -1,8 +1,6 @@
using System.Collections.Generic; using Robust.LoaderApi;
using System.IO;
using Robust.LoaderApi;
namespace Nebula.Launcher.FileApis; namespace Nebula.Shared.FileApis;
public class AssemblyApi : IFileApi public class AssemblyApi : IFileApi
{ {
@@ -12,7 +10,7 @@ public class AssemblyApi : IFileApi
{ {
_root = root; _root = root;
} }
public bool TryOpen(string path, out Stream? stream) public bool TryOpen(string path, out Stream? stream)
{ {
return _root.TryOpen(path, out stream); return _root.TryOpen(path, out stream);

View File

@@ -1,9 +1,6 @@
using System; using Nebula.Shared.FileApis.Interfaces;
using System.Collections.Generic;
using System.IO;
using Nebula.Launcher.FileApis.Interfaces;
namespace Nebula.Launcher.FileApis; namespace Nebula.Shared.FileApis;
public class FileApi : IReadWriteFileApi public class FileApi : IReadWriteFileApi
{ {

View File

@@ -1,10 +1,7 @@
using System.Collections.Generic; using Nebula.Shared.Models;
using System.IO;
using Nebula.Launcher.Models;
using Nebula.Launcher.Utils;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.FileApis; namespace Nebula.Shared.FileApis;
public class HashApi : IFileApi public class HashApi : IFileApi
{ {

View File

@@ -1,6 +1,6 @@
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.FileApis.Interfaces; namespace Nebula.Shared.FileApis.Interfaces;
public interface IReadWriteFileApi : IFileApi, IWriteFileApi public interface IReadWriteFileApi : IFileApi, IWriteFileApi
{ {

View File

@@ -1,6 +1,4 @@
using System.IO; namespace Nebula.Shared.FileApis.Interfaces;
namespace Nebula.Launcher.FileApis.Interfaces;
public interface IWriteFileApi public interface IWriteFileApi
{ {

View File

@@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.FileApis; namespace Nebula.Shared.FileApis;
public sealed class ZipFileApi : IFileApi public sealed class ZipFileApi : IFileApi
{ {

View File

@@ -1,6 +1,4 @@
using System; namespace Nebula.Shared.Models.Auth;
namespace Nebula.Launcher.Models.Auth;
public sealed record AuthenticateRequest(string? Username, Guid? UserId, string Password, string? TfaCode = null) public sealed record AuthenticateRequest(string? Username, Guid? UserId, string Password, string? TfaCode = null)
{ {

View File

@@ -1,5 +1,3 @@
using System; namespace Nebula.Shared.Models.Auth;
namespace Nebula.Launcher.Models.Auth;
public sealed record AuthenticateResponse(string Token, string Username, Guid UserId, DateTimeOffset ExpireTime); public sealed record AuthenticateResponse(string Token, string Username, Guid UserId, DateTimeOffset ExpireTime);

View File

@@ -1,6 +1,4 @@
using System; namespace Nebula.Shared.Models.Auth;
namespace Nebula.Launcher.Models.Auth;
public class LoginInfo public class LoginInfo
{ {

View File

@@ -1,6 +1,4 @@
using System; namespace Nebula.Shared.Models.Auth;
namespace Nebula.Launcher.Models.Auth;
public readonly struct LoginToken public readonly struct LoginToken
{ {

View File

@@ -1,4 +1,4 @@
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public enum ContentCompressionScheme public enum ContentCompressionScheme
{ {

View File

@@ -1,6 +1,4 @@
using System; namespace Nebula.Shared.Models;
namespace Nebula.Launcher.Models;
[Flags] [Flags]
public enum DownloadStreamHeaderFlags public enum DownloadStreamHeaderFlags

View File

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

View File

@@ -1,7 +1,6 @@
using System.Collections.Generic;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public sealed class MainArgs : IMainArgs public sealed class MainArgs : IMainArgs
{ {

View File

@@ -1,4 +1,4 @@
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public class RobustBuildInfo public class RobustBuildInfo
{ {

View File

@@ -1,5 +1,3 @@
using System; namespace Nebula.Shared.Models;
namespace Nebula.Launcher.Models;
public record struct RobustManifestInfo(Uri ManifestUri, Uri DownloadUri, string Hash); public record struct RobustManifestInfo(Uri ManifestUri, Uri DownloadUri, string Hash);

View File

@@ -1,3 +1,3 @@
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public record struct RobustManifestItem(string Hash, string Path, int Id); public record struct RobustManifestItem(string Hash, string Path, int Id);

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public sealed record AuthInfo( public sealed record AuthInfo(
[property: JsonPropertyName("mode")] string Mode, [property: JsonPropertyName("mode")] string Mode,

View File

@@ -1,7 +1,6 @@
using System; using Nebula.Shared.Utils;
using Nebula.Launcher.Utils;
namespace Nebula.Launcher.Models; namespace Nebula.Shared.Models;
public class RobustUrl public class RobustUrl
{ {

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Utils\runtime.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="libsodium" Version="1.0.20" />
<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" />
</ItemGroup>
</Project>

View File

@@ -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<ServiceRegisterAttribute>();
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;
}
}

View File

@@ -1,19 +1,16 @@
using System; using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Loader; using System.Runtime.Loader;
using Nebula.Launcher.FileApis; using Nebula.Shared.FileApis;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class AssemblyService public class AssemblyService
{ {
private readonly List<Assembly> _assemblies = new(); private readonly Dictionary<string,Assembly> _assemblies = new();
private readonly DebugService _debugService; private readonly DebugService _debugService;
public AssemblyService(DebugService debugService) public AssemblyService(DebugService debugService)
@@ -21,12 +18,12 @@ public class AssemblyService
_debugService = debugService; _debugService = debugService;
} }
public IReadOnlyList<Assembly> Assemblies => _assemblies; //public IReadOnlyList<Assembly> Assemblies => _assemblies;
public AssemblyApi Mount(IFileApi fileApi) public AssemblyApi Mount(IFileApi fileApi, string apiName = "")
{ {
var asmApi = new AssemblyApi(fileApi); 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; AssemblyLoadContext.Default.ResolvingUnmanagedDll += LoadContextOnResolvingUnmanaged;
return asmApi; return asmApi;
@@ -56,6 +53,11 @@ public class AssemblyService
public bool TryOpenAssembly(string name, AssemblyApi assemblyApi, [NotNullWhen(true)] out Assembly? assembly) 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)) if (!TryOpenAssemblyStream(name, assemblyApi, out var asm, out var pdb))
{ {
assembly = null; assembly = null;
@@ -64,9 +66,8 @@ public class AssemblyService
assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb); assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb);
_debugService.Log("LOADED ASSEMBLY " + name); _debugService.Log("LOADED ASSEMBLY " + name);
_assemblies.Add(name, assembly);
if (!_assemblies.Contains(assembly)) _assemblies.Add(assembly);
asm.Dispose(); asm.Dispose();
pdb?.Dispose(); pdb?.Dispose();
@@ -86,9 +87,11 @@ public class AssemblyService
return true; 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; return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null;
} }

View File

@@ -1,45 +1,32 @@
using System; using System.Net.Http.Headers;
using System.Net.Http; using Nebula.Shared.Models.Auth;
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;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public partial class AuthService : ObservableObject public partial class AuthService(
RestService restService,
DebugService debugService,
CancellationService cancellationService)
{ {
private readonly HttpClient _httpClient = new(); private readonly HttpClient _httpClient = new();
private readonly RestService _restService; public CurrentAuthInfo? SelectedAuth { get; internal set; }
private readonly DebugService _debugService;
[ObservableProperty]
private CurrentAuthInfo? _selectedAuth;
public string Reason = ""; public string Reason = "";
public AuthService(RestService restService, DebugService debugService)
{
_restService = restService;
_debugService = debugService;
}
public async Task<bool> Auth(AuthLoginPassword authLoginPassword) public async Task<bool> Auth(AuthLoginPassword authLoginPassword)
{ {
var authServer = authLoginPassword.AuthServer; var authServer = authLoginPassword.AuthServer;
var login = authLoginPassword.Login; var login = authLoginPassword.Login;
var password = authLoginPassword.Password; 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 authUrl = new Uri($"{authServer}api/auth/authenticate");
var result = var result =
await _restService.PostAsync<AuthenticateResponse, AuthenticateRequest>( await restService.PostAsync<AuthenticateResponse, AuthenticateRequest>(
new AuthenticateRequest(login, password), authUrl, CancellationToken.None); new AuthenticateRequest(login, password), authUrl, cancellationService.Token);
if (result.Value is null) if (result.Value is null)
{ {
@@ -53,6 +40,11 @@ public partial class AuthService : ObservableObject
return true; return true;
} }
public void ClearAuth()
{
SelectedAuth = null;
}
public async Task<bool> EnsureToken() public async Task<bool> EnsureToken()
{ {
if (SelectedAuth is null) return false; if (SelectedAuth is null) return false;
@@ -61,7 +53,7 @@ public partial class AuthService : ObservableObject
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", SelectedAuth.Token.Token); 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; if (!resp.IsSuccessStatusCode) SelectedAuth = null;

View File

@@ -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();
}
}

View File

@@ -1,9 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.Json; using System.Text.Json;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
public class ConVar<T> public class ConVar<T>
{ {

View File

@@ -1,24 +1,18 @@
using System; using System.Buffers.Binary;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Numerics; using System.Numerics;
using System.Threading; using Nebula.Shared.FileApis.Interfaces;
using System.Threading.Tasks; using Nebula.Shared.Models;
using Nebula.Launcher.FileApis.Interfaces; using Nebula.Shared.Utils;
using Nebula.Launcher.Models;
using Nebula.Launcher.Utils;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
public partial class ContentService public partial class ContentService
{ {
public bool CheckManifestExist(RobustManifestItem item) public bool CheckManifestExist(RobustManifestItem item)
{ {
return _fileService.ContentFileApi.Has(item.Hash); return fileService.ContentFileApi.Has(item.Hash);
} }
public async Task<List<RobustManifestItem>> EnsureItems(ManifestReader manifestReader, Uri downloadUri, public async Task<List<RobustManifestItem>> EnsureItems(ManifestReader manifestReader, Uri downloadUri,
@@ -31,7 +25,7 @@ public partial class ContentService
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
_debugService.Log("ensuring is cancelled!"); debugService.Log("ensuring is cancelled!");
return []; return [];
} }
@@ -40,11 +34,11 @@ public partial class ContentService
allItems.Add(item.Value); allItems.Add(item.Value);
} }
_debugService.Log("Download Count:" + items.Count); debugService.Log("Download Count:" + items.Count);
await Download(downloadUri, items, cancellationToken); await Download(downloadUri, items, cancellationToken);
_fileService.ManifestItems = allItems; fileService.ManifestItems = allItems;
return allItems; return allItems;
} }
@@ -52,21 +46,21 @@ public partial class ContentService
public async Task<List<RobustManifestItem>> EnsureItems(RobustManifestInfo info, public async Task<List<RobustManifestItem>> EnsureItems(RobustManifestInfo info,
CancellationToken cancellationToken) 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); 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); var response = await _http.GetAsync(info.ManifestUri, cancellationToken);
if (!response.IsSuccessStatusCode) throw new Exception(); if (!response.IsSuccessStatusCode) throw new Exception();
await using var streamContent = await response.Content.ReadAsStreamAsync(cancellationToken); 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); streamContent.Seek(0, SeekOrigin.Begin);
using var manifestReader = new ManifestReader(streamContent); using var manifestReader = new ManifestReader(streamContent);
return await EnsureItems(manifestReader, info.DownloadUri, cancellationToken); 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) 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); var items = await EnsureItems(info, cancellationToken);
foreach (var item in items) 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); otherApi.Save(item.Path, stream);
stream.Close(); stream.Close();
} }
else 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) if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
{ {
_debugService.Log("Nothing to download! Fuck this!"); debugService.Log("Nothing to download! Fuck this!");
return; return;
} }
_debugService.Log("Downloading from: " + contentCdn); debugService.Log("Downloading from: " + contentCdn);
var requestBody = new byte[toDownload.Count * 4]; var requestBody = new byte[toDownload.Count * 4];
var reqI = 0; var reqI = 0;
@@ -110,7 +104,7 @@ public partial class ContentService
var request = new HttpRequestMessage(HttpMethod.Post, contentCdn); var request = new HttpRequestMessage(HttpMethod.Post, contentCdn);
request.Headers.Add( request.Headers.Add(
"X-Robust-Download-Protocol", "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 = new ByteArrayContent(requestBody);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
@@ -120,7 +114,7 @@ public partial class ContentService
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
_debugService.Log("Downloading is cancelled!"); debugService.Log("Downloading is cancelled!");
return; return;
} }
@@ -165,7 +159,7 @@ public partial class ContentService
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
_debugService.Log("Downloading is cancelled!"); debugService.Log("Downloading is cancelled!");
decompressContext?.Dispose(); decompressContext?.Dispose();
compressContext?.Dispose(); compressContext?.Dispose();
return; return;
@@ -233,9 +227,9 @@ public partial class ContentService
} }
using var fileStream = new MemoryStream(data.ToArray()); 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; i += 1;
} }
} }

View File

@@ -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<RobustBuildInfo> GetBuildInfo(RobustUrl url, CancellationToken cancellationToken)
{
var info = new RobustBuildInfo();
info.Url = url;
var bi = await restService.GetAsync<ServerInfo>(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;
}
}

View File

@@ -1,8 +1,6 @@
using System; using Nebula.Shared.Services.Logging;
using System.IO;
using Nebula.Launcher.Services.Logging;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class DebugService : IDisposable public class DebugService : IDisposable
@@ -18,14 +16,14 @@ public class DebugService : IDisposable
{ {
Logger = logger; Logger = logger;
if (!Directory.Exists(LogPath)) //if (!Directory.Exists(LogPath))
Directory.CreateDirectory(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), //LogStream = File.Open(Path.Combine(LogPath, filename),
FileMode.Append, FileAccess.Write); // FileMode.Append, FileAccess.Write);
LogWriter = new StreamWriter(LogStream); //LogWriter = new StreamWriter(LogStream);
} }
public void Debug(string message) public void Debug(string message)
@@ -59,7 +57,7 @@ public class DebugService : IDisposable
private void Log(LoggerCategory category, string message) private void Log(LoggerCategory category, string message)
{ {
Logger.Log(category, message); Logger.Log(category, message);
SaveToLog(category, message); //SaveToLog(category, message);
} }
private void SaveToLog(LoggerCategory category, string message) private void SaveToLog(LoggerCategory category, string message)

View File

@@ -1,19 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using Nebula.Shared.FileApis;
using System.Linq; using Nebula.Shared.Models;
using System.Net.Http; using Nebula.Shared.Utils;
using System.Threading;
using System.Threading.Tasks;
using Nebula.Launcher.FileApis;
using Nebula.Launcher.Models;
using Nebula.Launcher.Utils;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class EngineService public sealed class EngineService
{ {
private readonly AssemblyService _assemblyService; private readonly AssemblyService _assemblyService;
private readonly DebugService _debugService; private readonly DebugService _debugService;
@@ -21,9 +14,11 @@ public class EngineService
private readonly RestService _restService; private readonly RestService _restService;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ConfigurationService _varService; private readonly ConfigurationService _varService;
public Dictionary<string, Module> ModuleInfos;
public Dictionary<string, Module> ModuleInfos = default!;
public Dictionary<string, EngineVersionInfo> VersionInfos = default!;
public Dictionary<string, EngineVersionInfo> VersionInfos; private Task _currInfoTask;
public EngineService(RestService restService, DebugService debugService, ConfigurationService varService, public EngineService(RestService restService, DebugService debugService, ConfigurationService varService,
FileService fileService, IServiceProvider serviceProvider, AssemblyService assemblyService) FileService fileService, IServiceProvider serviceProvider, AssemblyService assemblyService)
@@ -35,8 +30,7 @@ public class EngineService
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_assemblyService = assemblyService; _assemblyService = assemblyService;
var loadTask = Task.Run(() => LoadEngineManifest(CancellationToken.None)); _currInfoTask = Task.Run(() => LoadEngineManifest(CancellationToken.None));
loadTask.Wait();
} }
public async Task LoadEngineManifest(CancellationToken cancellationToken) public async Task LoadEngineManifest(CancellationToken cancellationToken)
@@ -57,6 +51,8 @@ public class EngineService
public EngineBuildInfo? GetVersionInfo(string version) public EngineBuildInfo? GetVersionInfo(string version)
{ {
CheckAndWaitValidation();
if (!VersionInfos.TryGetValue(version, out var foundVersion)) if (!VersionInfos.TryGetValue(version, out var foundVersion))
return null; return null;
@@ -85,7 +81,7 @@ public class EngineService
try try
{ {
return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi)); return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi),$"Engine.Ensure-{version}");
} }
catch (Exception e) catch (Exception e)
{ {
@@ -120,6 +116,8 @@ public class EngineService
public EngineBuildInfo? GetModuleBuildInfo(string moduleName, string version) public EngineBuildInfo? GetModuleBuildInfo(string moduleName, string version)
{ {
CheckAndWaitValidation();
if (!ModuleInfos.TryGetValue(moduleName, out var module) || if (!ModuleInfos.TryGetValue(moduleName, out var module) ||
!module.Versions.TryGetValue(version, out var value)) !module.Versions.TryGetValue(version, out var value))
return null; return null;
@@ -138,6 +136,8 @@ public class EngineService
public string ResolveModuleVersion(string moduleName, string engineVersion) public string ResolveModuleVersion(string moduleName, string engineVersion)
{ {
CheckAndWaitValidation();
var engineVersionObj = Version.Parse(engineVersion); var engineVersionObj = Version.Parse(engineVersion);
var module = ModuleInfos[moduleName]; var module = ModuleInfos[moduleName];
var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv.Value }) var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv.Value })
@@ -161,7 +161,7 @@ public class EngineService
try try
{ {
return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi)); return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi),"Engine.EnsureModule");
} }
catch (Exception e) catch (Exception e)
{ {
@@ -186,4 +186,13 @@ public class EngineService
{ {
return moduleName + "" + moduleVersion; return moduleName + "" + moduleVersion;
} }
private void CheckAndWaitValidation()
{
if (_currInfoTask.IsCompleted)
return;
_debugService.Debug("thinks is not done yet, please wait");
_currInfoTask.Wait();
}
} }

View File

@@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Nebula.Launcher.FileApis; using Nebula.Shared.FileApis;
using Nebula.Launcher.FileApis.Interfaces; using Nebula.Shared.FileApis.Interfaces;
using Nebula.Launcher.Models; using Nebula.Shared.Models;
using Nebula.Launcher.Utils;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class FileService public class FileService

View File

@@ -1,9 +1,6 @@
using System; using Nebula.Shared.Models;
using System.Collections.Generic;
using System.Threading;
using Nebula.Launcher.Models;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class HubService public class HubService

View File

@@ -1,6 +1,4 @@
using System; namespace Nebula.Shared.Services.Logging;
namespace Nebula.Launcher.Services.Logging;
[ServiceRegister(typeof(ILogger))] [ServiceRegister(typeof(ILogger))]
public class ConsoleLogger : ILogger public class ConsoleLogger : ILogger

View File

@@ -1,4 +1,4 @@
namespace Nebula.Launcher.Services.Logging; namespace Nebula.Shared.Services.Logging;
public interface ILogger public interface ILogger
{ {

View File

@@ -0,0 +1,15 @@
namespace Nebula.Shared.Services;
[ServiceRegister]
public class PopupMessageService
{
public Action<object?>? OnPopupRequired;
public void Popup(object obj)
{
OnPopupRequired?.Invoke(obj);
}
public void ClosePopup()
{
OnPopupRequired?.Invoke(null);
}
}

View File

@@ -1,16 +1,10 @@
using System;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using Nebula.Shared.Utils;
using System.Threading.Tasks;
using Nebula.Launcher.Utils;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class RestService public class RestService

View File

@@ -1,66 +1,68 @@
using System; using Nebula.Shared.Models;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Nebula.Launcher.Models;
using Robust.LoaderApi; using Robust.LoaderApi;
namespace Nebula.Launcher.Services; namespace Nebula.Shared.Services;
[ServiceRegister] [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; public async Task PrepareRun(RobustUrl url)
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)
{ {
_contentService = contentService; var buildInfo = await contentService.GetBuildInfo(url, cancellationService.Token);
_debugService = debugService; await PrepareRun(buildInfo, cancellationService.Token);
_varService = varService; }
_fileService = fileService;
_engineService = engineService; public async Task PrepareRun(RobustBuildInfo buildInfo, CancellationToken cancellationToken)
_assemblyService = assemblyService; {
_authService = authService; debugService.Log("Prepare Content!");
_popupMessageService = popupMessageService;
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, public async Task Run(string[] runArgs, RobustBuildInfo buildInfo, IRedialApi redialApi,
CancellationToken cancellationToken) 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) if (engine is null)
throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion); 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<ApiMount> var extraMounts = new List<ApiMount>
{ {
new(_fileService.HashApi, "/") new(fileService.HashApi, "/")
}; };
var module = 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) if (module is not null)
extraMounts.Add(new ApiMount(module, "/")); extraMounts.Add(new ApiMount(module, "/"));
var args = new MainArgs(runArgs, engine, redialApi, extraMounts); 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!"); 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; return;
await Task.Run(() => loader.Main(args), cancellationToken); await Task.Run(() => loader.Main(args), cancellationToken);
@@ -71,12 +73,12 @@ public class RunnerService: IRedialApi
var url = new RobustUrl(urlraw); var url = new RobustUrl(urlraw);
using var cancelTokenSource = new CancellationTokenSource(); 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) if (account is null)
{ {
_popupMessageService.PopupInfo("Error! Auth is required!"); popupMessageService.Popup("Error! Auth is required!");
return; return;
} }
@@ -112,13 +114,13 @@ public class RunnerService: IRedialApi
args.Add("--ss14-address"); args.Add("--ss14-address");
args.Add(url.ToString()); 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); await Run(args.ToArray(), buildInfo, this, cancelTokenSource.Token);
} }
public async void Redial(Uri uri, string text = "") public async void Redial(Uri uri, string text = "")
{ {
await RunGame(uri.ToString()); //await RunGame(uri.ToString());
} }
} }

View File

@@ -1,10 +1,6 @@
using System;
using System.Diagnostics; 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 public sealed class BandwidthStream : Stream
{ {

View File

@@ -1,8 +1,6 @@
using System;
using System.Windows.Input; using System.Windows.Input;
using Nebula.Launcher.ViewModels;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public class DelegateCommand<T> : ICommand public class DelegateCommand<T> : ICommand
{ {

View File

@@ -1,10 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Net.Http;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public static class Helper public static class Helper
{ {

View File

@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text; using System.Text;
using Nebula.Launcher.Models; using Nebula.Shared.Models;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public class ManifestReader : StreamReader public class ManifestReader : StreamReader
{ {

View File

@@ -1,4 +1,4 @@
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public class Ref<T> public class Ref<T>
{ {

View File

@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public static class RidUtility public static class RidUtility
{ {

View File

@@ -1,10 +1,6 @@
using System;
using System.Buffers; 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 public static class StreamHelper
{ {

View File

@@ -1,9 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Web; using System.Web;
using Nebula.Shared.Models;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public static class UriHelper public static class UriHelper
{ {
@@ -132,4 +132,9 @@ public static class UriHelper
return uriBuilder.Uri; return uriBuilder.Uri;
} }
public static RobustUrl ToRobustUrl(this string str)
{
return new RobustUrl(str);
}
} }

View File

@@ -1,15 +1,11 @@
using System;
using System.Buffers; using System.Buffers;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using SharpZstd.Interop; using SharpZstd.Interop;
using static SharpZstd.Interop.Zstd; using static SharpZstd.Interop.Zstd;
namespace Nebula.Launcher.Utils; namespace Nebula.Shared.Utils;
public static class ZStd public static class ZStd
{ {

View File

@@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Launcher", "Nebula.L
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{8AE91631-DE96-4A97-A255-058E27A7C3EA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.LoaderApi", "Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj", "{8AE91631-DE96-4A97-A255-058E27A7C3EA}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{8AE91631-DE96-4A97-A255-058E27A7C3EA}.Release|Any CPU.Build.0 = 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 EndGlobalSection
EndGlobal EndGlobal