3 Commits

Author SHA1 Message Date
c304ac94fe - tweak: dotnet 10 update 2026-01-16 18:32:37 +03:00
e0a16f7fb6 - fix: memory leak from canalisation part 2 2025-12-12 22:23:45 +03:00
f7cec5d093 - fix: memory leak part 1 2025-12-11 21:47:54 +03:00
53 changed files with 782 additions and 534 deletions

View File

@@ -17,7 +17,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 10.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
- name: Set version - name: Set version

View File

@@ -17,7 +17,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0 uses: actions/setup-dotnet@v3.2.0
with: with:
dotnet-version: 9.0.x dotnet-version: 10.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
- name: Create build - name: Create build

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@ obj/
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
release/ release/
publish/ publish/
/.vs

6
Directory.Build.props Normal file
View File

@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>

31
Directory.Packages.props Normal file
View File

@@ -0,0 +1,31 @@
<Project>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.11" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.11" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.3.11" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.3.11" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.11" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.3.0" />
<PackageVersion Include="AsyncImageLoader.Avalonia" Version="3.5.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="Fluent.Net" Version="1.0.63" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" />
<PackageVersion Include="libsodium" Version="1.0.20" />
<PackageVersion Include="Robust.Natives" Version="0.2.3" />
<PackageVersion Include="Avalonia.Controls.ItemsRepeater" Version="11.1.5" />
<PackageVersion Include="Lib.Harmony" Version="2.3.6" />
<PackageVersion Include="SharpZstd.Interop" Version="1.5.6" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="NUnit" Version="3.14.0" />
<PackageVersion Include="NUnit.Analyzers" Version="3.9.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/>
</ItemGroup>
</Project>

View File

@@ -70,4 +70,5 @@ popup-login-credentials-warning-cancel = Cancel
popup-login-credentials-warning-proceed = Proceed popup-login-credentials-warning-proceed = Proceed
goto-path-home = Root folder goto-path-home = Root folder
tab-favorite = Favorite tab-favorite = Favorite
server-list-loading = Loading server list.. Please wait

View File

@@ -70,4 +70,5 @@ popup-login-credentials-warning-cancel = Отмена
popup-login-credentials-warning-proceed = Продолжить popup-login-credentials-warning-proceed = Продолжить
goto-path-home = Корн. папка goto-path-home = Корн. папка
tab-favorite = Избранное tab-favorite = Избранное
server-list-loading = Загрузка списка серверов. Пожалуйста, подождите...

View File

@@ -1,20 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Controls.ServerListView">
<ScrollViewer
Margin="5,0,0,10"
Padding="0,0,10,0">
<StackPanel Margin="0,0,0,30">
<Label x:Name="LoadingLabel" Margin="10" HorizontalAlignment="Center">Loading... Please wait</Label>
<ItemsControl
x:Name="ErrorList"
Margin="10,0,10,0" />
<ItemsControl
x:Name="ServerList"
Padding="0" />
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -1,118 +0,0 @@
using Avalonia.Controls;
using Nebula.Launcher.Models;
using Nebula.Launcher.ServerListProviders;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.Controls;
public partial class ServerListView : UserControl
{
private IServerListProvider _provider = default!;
private ServerFilter? _currentFilter;
public bool IsLoading { get; private set; }
public ServerListView()
{
InitializeComponent();
}
public static ServerListView TakeFrom(IServerListProvider provider)
{
var serverListView = new ServerListView();
if (provider is IServerListDirtyInvoker invoker)
{
invoker.Dirty += serverListView.OnDirty;
}
serverListView._provider = provider;
serverListView.RefreshFromProvider();
return serverListView;
}
public void RefreshFromProvider()
{
if (IsLoading)
return;
Clear();
StartLoading();
_provider.LoadServerList();
if (_provider.IsLoaded) PasteServersFromList();
else _provider.OnLoaded += RefreshRequired;
}
public void RequireStatusUpdate()
{
foreach (var rawView in ServerList.Items)
{
if (rawView is ServerEntryModelView serverEntryModelView)
{
//serverEntryModelView.UpdateStatusIfNecessary();
}
}
}
public void ApplyFilter(ServerFilter? filter)
{
_currentFilter = filter;
if(IsLoading)
return;
foreach (var serverView in ServerList.Items)
{
if(serverView is IFilterConsumer filterConsumer)
filterConsumer.ProcessFilter(filter);
}
}
private void OnDirty()
{
RefreshFromProvider();
}
private void Clear()
{
ErrorList.Items.Clear();
ServerList.Items.Clear();
}
private void PasteServersFromList()
{
foreach (var serverEntry in _provider.GetServers())
{
ServerList.Items.Add(serverEntry);
if(serverEntry is IFilterConsumer serverFilter)
serverFilter.ProcessFilter(_currentFilter);
}
foreach (var error in _provider.GetErrors())
{
ErrorList.Items.Add(error);
}
EndLoading();
}
private void RefreshRequired()
{
PasteServersFromList();
_provider.OnLoaded -= RefreshRequired;
}
private void StartLoading()
{
Clear();
IsLoading = true;
LoadingLabel.IsVisible = true;
}
private void EndLoading()
{
IsLoading = false;
LoadingLabel.IsVisible = false;
}
}

View File

@@ -2,6 +2,7 @@ using System;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewModels.Pages; using Nebula.Launcher.ViewModels.Pages;
using Color = System.Drawing.Color; using Color = System.Drawing.Color;

View File

@@ -54,5 +54,7 @@ public static class LauncherConVar
public static readonly ConVar<string> CurrentLang = ConVarBuilder.Build<string>("launcher.language", CultureInfo.CurrentCulture.Name); public static readonly ConVar<string> CurrentLang = ConVarBuilder.Build<string>("launcher.language", CultureInfo.CurrentCulture.Name);
public static readonly ConVar<string> ILSpyUrl = ConVarBuilder.Build<string>("decompiler.url", public static readonly ConVar<string> ILSpyUrl = ConVarBuilder.Build<string>("decompiler.url",
"https://github.com/icsharpcode/ILSpy/releases/download/v9.0/ILSpy_binaries_9.0.0.7889-x64.zip"); "https://github.com/icsharpcode/ILSpy/releases/download/v10.0-preview2/ILSpy_selfcontained_10.0.0.8282-preview2-x64.zip");
public static readonly ConVar<string> ILSpyVersion = ConVarBuilder.Build<string>("dotnet.version", "10");
} }

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Nebula.Launcher.ProcessHelper; using Nebula.Launcher.ProcessHelper;
using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.ViewModels.Popup;
using Nebula.Shared.Services; using Nebula.Shared.Services;
@@ -6,27 +8,53 @@ namespace Nebula.Launcher.Models;
public sealed class ContentLogConsumer : IProcessLogConsumer public sealed class ContentLogConsumer : IProcessLogConsumer
{ {
private readonly LogPopupModelView _currLog; private readonly List<string> _outMessages = [];
private readonly PopupMessageService _popupMessageService;
private LogPopupModelView? _currentLogPopup;
public int MaxMessages { get; set; } = 100;
public ContentLogConsumer(LogPopupModelView currLog, PopupMessageService popupMessageService) public void Popup(PopupMessageService popupMessageService)
{ {
_currLog = currLog; if(_currentLogPopup is not null)
_popupMessageService = popupMessageService; return;
_currentLogPopup = new LogPopupModelView(popupMessageService);
_currentLogPopup.OnDisposing += OnLogPopupDisposing;
foreach (var message in _outMessages.ToArray())
{
_currentLogPopup.Append(message);
}
popupMessageService.Popup(_currentLogPopup);
}
private void OnLogPopupDisposing(PopupViewModelBase obj)
{
if(_currentLogPopup == null)
return;
_currentLogPopup.OnDisposing -= OnLogPopupDisposing;
_currentLogPopup = null;
} }
public void Out(string text) public void Out(string text)
{ {
_currLog.Append(text); _outMessages.Add(text);
if(_outMessages.Count >= MaxMessages)
_outMessages.RemoveAt(0);
_currentLogPopup?.Append(text);
} }
public void Error(string text) public void Error(string text)
{ {
_currLog.Append(text); Out(text);
} }
public void Fatal(string text) public void Fatal(string text)
{ {
_popupMessageService.Popup("Fatal error while stop instance:" + text); throw new Exception("Error while running programm: " + text);
} }
} }

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
@@ -15,38 +14,24 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0"/> <PackageReference Include="AsyncImageLoader.Avalonia"/>
<PackageReference Include="Avalonia" Version="11.2.1"/> <PackageReference Include="Avalonia"/>
<PackageReference Include="Avalonia.Desktop" Version="11.2.1"/> <PackageReference Include="Avalonia.Desktop"/>
<PackageReference Include="Avalonia.Svg.Skia" Version="11.2.0.2" /> <PackageReference Include="Avalonia.Svg.Skia"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1"/> <PackageReference Include="Avalonia.Themes.Fluent"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1"/> <PackageReference Include="Avalonia.Fonts.Inter"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1"> <PackageReference Include="Avalonia.Diagnostics">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1"/> <PackageReference Include="CommunityToolkit.Mvvm"/>
<PackageReference Include="Fluent.Net" Version="1.0.63" /> <PackageReference Include="Fluent.Net"/>
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0"/> <PackageReference Include="JetBrains.Annotations"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0"/> <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
<PackageReference Include="libsodium" Version="1.0.20"/> <PackageReference Include="libsodium"/>
<PackageReference Include="Robust.Natives" Version="0.2.3" /> <PackageReference Include="Robust.Natives"/>
</ItemGroup> <PackageReference Include="Avalonia.Controls.ItemsRepeater"/>
<ItemGroup>
<Compile Update="Views\Tabs\ServerListTab.axaml.cs">
<DependentUpon>ServerListTab.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Popup\AddFavoriteView.axaml.cs">
<DependentUpon>AddFavoriteView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\ServerListView.axaml.cs">
<DependentUpon>ServerListView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup> </ItemGroup>
<Target Name="BuildCheck" AfterTargets="AfterBuild"> <Target Name="BuildCheck" AfterTargets="AfterBuild">
@@ -78,8 +63,4 @@
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj"/> <ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj"/>
<ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> <ProjectReference Include="..\Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Controls\ServerListView.axaml" />
</ItemGroup>
</Project> </Project>

View File

@@ -13,7 +13,7 @@ namespace Nebula.Launcher.ProcessHelper;
[ServiceRegister] [ServiceRegister]
public sealed class GameRunnerPreparer(IServiceProvider provider, ContentService contentService, EngineService engineService) public sealed class GameRunnerPreparer(IServiceProvider provider, ContentService contentService, EngineService engineService)
{ {
public async Task<ProcessRunHandler<GameProcessStartInfoProvider>> GetGameProcessStartInfoProvider(RobustUrl address, ILoadingHandlerFactory loadingHandlerFactory, CancellationToken cancellationToken = default) public async Task<GameProcessStartInfoProvider> GetGameProcessStartInfoProvider(RobustUrl address, ILoadingHandlerFactory loadingHandlerFactory, CancellationToken cancellationToken = default)
{ {
var buildInfo = await contentService.GetBuildInfo(address, cancellationToken); var buildInfo = await contentService.GetBuildInfo(address, cancellationToken);
@@ -39,11 +39,9 @@ public sealed class GameRunnerPreparer(IServiceProvider provider, ContentService
await stream.DisposeAsync(); await stream.DisposeAsync();
} }
var gameInfo = return
provider.GetService<GameProcessStartInfoProvider>()!.WithBuildInfo(buildInfo.BuildInfo.Auth.PublicKey, provider.GetService<GameProcessStartInfoProvider>()!.WithBuildInfo(buildInfo.BuildInfo.Auth.PublicKey,
address); address);
var gameProcessRunHandler = new ProcessRunHandler<GameProcessStartInfoProvider>(gameInfo);
return gameProcessRunHandler;
} }
} }

View File

@@ -6,29 +6,27 @@ using Nebula.Shared.Services.Logging;
namespace Nebula.Launcher.ProcessHelper; namespace Nebula.Launcher.ProcessHelper;
public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable where T: IProcessStartInfoProvider public class ProcessRunHandler : IDisposable
{ {
private ProcessStartInfo? _processInfo; private ProcessStartInfo? _processInfo;
private Task<ProcessStartInfo>? _processInfoTask; private Task<ProcessStartInfo>? _processInfoTask;
private Process? _process; private Process? _process;
private ProcessLogConsumerCollection _consumerCollection = new(); private readonly IProcessLogConsumer _logConsumer;
private string _lastError = string.Empty; private string _lastError = string.Empty;
private readonly T _currentProcessStartInfoProvider; private readonly IProcessStartInfoProvider _currentProcessStartInfoProvider;
public T GetCurrentProcessStartInfo() => _currentProcessStartInfoProvider; public IProcessStartInfoProvider GetCurrentProcessStartInfo() => _currentProcessStartInfoProvider;
public bool IsRunning => _processInfo is not null; public bool IsRunning => _processInfo is not null;
public Action<ProcessRunHandler<T>>? OnProcessExited; public Action<ProcessRunHandler>? OnProcessExited;
public void RegisterLogger(IProcessLogConsumer consumer)
{
_consumerCollection.RegisterLogger(consumer);
}
public ProcessRunHandler(T processStartInfoProvider) public bool Disposed { get; private set; }
public ProcessRunHandler(IProcessStartInfoProvider processStartInfoProvider, IProcessLogConsumer logConsumer)
{ {
_currentProcessStartInfoProvider = processStartInfoProvider; _currentProcessStartInfoProvider = processStartInfoProvider;
_logConsumer = logConsumer;
_processInfoTask = _currentProcessStartInfoProvider.GetProcessStartInfo(); _processInfoTask = _currentProcessStartInfoProvider.GetProcessStartInfo();
_processInfoTask.GetAwaiter().OnCompleted(OnInfoProvided); _processInfoTask.GetAwaiter().OnCompleted(OnInfoProvided);
} }
@@ -42,8 +40,18 @@ public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable wher
_processInfoTask = null; _processInfoTask = null;
} }
private void CheckIfDisposed()
{
if (!Disposed) return;
throw new ObjectDisposedException(nameof(ProcessRunHandler));
}
public void Start() public void Start()
{ {
CheckIfDisposed();
if(_process is not null)
throw new InvalidOperationException("Already running");
if (_processInfoTask != null) if (_processInfoTask != null)
{ {
_processInfoTask.Wait(); _processInfoTask.Wait();
@@ -66,7 +74,8 @@ public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable wher
public void Stop() public void Stop()
{ {
_process?.CloseMainWindow(); CheckIfDisposed();
Dispose();
} }
private void OnExited(object? sender, EventArgs e) private void OnExited(object? sender, EventArgs e)
@@ -79,12 +88,13 @@ public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable wher
if (_process.ExitCode != 0) if (_process.ExitCode != 0)
_consumerCollection.Fatal(_lastError); _logConsumer.Fatal(_lastError);
_process.Dispose(); _process.Dispose();
_process = null; _process = null;
OnProcessExited?.Invoke(this); OnProcessExited?.Invoke(this);
Dispose();
} }
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
@@ -92,7 +102,7 @@ public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable wher
if (e.Data != null) if (e.Data != null)
{ {
_lastError = e.Data; _lastError = e.Data;
_consumerCollection.Error(e.Data); _logConsumer.Error(e.Data);
} }
} }
@@ -100,14 +110,22 @@ public class ProcessRunHandler<T> : IProcessConsumerCollection, IDisposable wher
{ {
if (e.Data != null) if (e.Data != null)
{ {
_consumerCollection.Out(e.Data); _logConsumer.Out(e.Data);
} }
} }
public void Dispose() public void Dispose()
{ {
if (_process is not null)
{
_process.CloseMainWindow();
return;
}
CheckIfDisposed();
_processInfoTask?.Dispose(); _processInfoTask?.Dispose();
_process?.Dispose(); Disposed = true;
} }
} }

View File

@@ -28,6 +28,7 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
public bool IsLoaded { get; private set; } public bool IsLoaded { get; private set; }
public Action? OnLoaded { get; set; } public Action? OnLoaded { get; set; }
public Action? OnDisposed { get; set; }
public Action? Dirty { get; set; } public Action? Dirty { get; set; }
public IEnumerable<IListEntryModelView> GetServers() public IEnumerable<IListEntryModelView> GetServers()
{ {
@@ -108,9 +109,14 @@ public sealed partial class FavoriteServerListProvider : IServerListProvider, IS
} }
private void InitialiseInDesignMode(){} private void InitialiseInDesignMode(){}
public void Dispose()
{
OnDisposed?.Invoke();
}
} }
public class AddFavoriteButton: Border, IListEntryModelView{ public sealed class AddFavoriteButton: Border, IListEntryModelView{
private Button _addFavoriteButton = new Button(); private Button _addFavoriteButton = new Button();
public AddFavoriteButton(IServiceProvider serviceProvider) public AddFavoriteButton(IServiceProvider serviceProvider)
@@ -128,4 +134,9 @@ public class AddFavoriteButton: Border, IListEntryModelView{
Child = _addFavoriteButton; Child = _addFavoriteButton;
} }
public bool IsFavorite { get; set; } public bool IsFavorite { get; set; }
public void Dispose()
{
}
} }

View File

@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Pages; using Nebula.Launcher.ViewModels.Pages;
using Nebula.Shared; using Nebula.Shared;
using Nebula.Shared.Models; using Nebula.Shared.Models;
@@ -22,6 +20,7 @@ public sealed partial class HubServerListProvider : IServerListProvider
public bool IsLoaded { get; private set; } public bool IsLoaded { get; private set; }
public Action? OnLoaded { get; set; } public Action? OnLoaded { get; set; }
public Action? OnDisposed { get; set; }
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
private readonly List<IListEntryModelView> _servers = []; private readonly List<IListEntryModelView> _servers = [];
@@ -83,4 +82,10 @@ public sealed partial class HubServerListProvider : IServerListProvider
private void Initialise(){} private void Initialise(){}
private void InitialiseInDesignMode(){} private void InitialiseInDesignMode(){}
public void Dispose()
{
OnDisposed?.Invoke();
_cts?.Dispose();
}
} }

View File

@@ -5,10 +5,11 @@ using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.ServerListProviders; namespace Nebula.Launcher.ServerListProviders;
public interface IServerListProvider public interface IServerListProvider : IDisposable
{ {
public bool IsLoaded { get; } public bool IsLoaded { get; }
public Action? OnLoaded { get; set; } public Action? OnLoaded { get; set; }
public Action? OnDisposed { get; set; }
public IEnumerable<IListEntryModelView> GetServers(); public IEnumerable<IListEntryModelView> GetServers();
public IEnumerable<Exception> GetErrors(); public IEnumerable<Exception> GetErrors();

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Nebula.Launcher.Controls;
using Nebula.Launcher.ViewModels; using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Pages; using Nebula.Launcher.ViewModels.Pages;
@@ -10,6 +9,8 @@ public sealed class TestServerList : IServerListProvider
{ {
public bool IsLoaded => true; public bool IsLoaded => true;
public Action? OnLoaded { get; set; } public Action? OnLoaded { get; set; }
public Action? OnDisposed { get; set; }
public IEnumerable<IListEntryModelView> GetServers() public IEnumerable<IListEntryModelView> GetServers()
{ {
return [new ServerEntryModelView(),new ServerEntryModelView()]; return [new ServerEntryModelView(),new ServerEntryModelView()];
@@ -24,4 +25,9 @@ public sealed class TestServerList : IServerListProvider
{ {
} }
public void Dispose()
{
OnDisposed?.Invoke();
}
} }

View File

@@ -29,16 +29,16 @@ public sealed partial class DecompilerService
[GenerateProperty] private EngineService EngineService {get;} [GenerateProperty] private EngineService EngineService {get;}
[GenerateProperty] private DebugService DebugService {get;} [GenerateProperty] private DebugService DebugService {get;}
private HttpClient _httpClient = new HttpClient(); private readonly HttpClient _httpClient = new();
private ILogger _logger; private ILogger _logger;
private static string fullPath = Path.Join(FileService.RootPath,"ILSpy"); private string FullPath => Path.Join(FileService.RootPath,$"ILSpy.{ConfigurationService.GetConfigValue(LauncherConVar.ILSpyVersion)}");
private static string executePath = Path.Join(fullPath, "ILSpy.exe"); private string ExecutePath => Path.Join(FullPath, "ILSpy.exe");
public async void OpenDecompiler(string arguments){ public async void OpenDecompiler(string arguments){
await EnsureILSpy(); await EnsureILSpy();
var startInfo = new ProcessStartInfo(){ var startInfo = new ProcessStartInfo(){
FileName = executePath, FileName = ExecutePath,
Arguments = arguments Arguments = arguments
}; };
Process.Start(startInfo); Process.Start(startInfo);
@@ -84,7 +84,7 @@ public sealed partial class DecompilerService
private void InitialiseInDesignMode(){} private void InitialiseInDesignMode(){}
private async Task EnsureILSpy(){ private async Task EnsureILSpy(){
if(!Directory.Exists(fullPath)) if(!Directory.Exists(FullPath))
await Download(); await Download();
} }
@@ -95,7 +95,7 @@ public sealed partial class DecompilerService
PopupMessageService.Popup(loading); PopupMessageService.Popup(loading);
using var response = await _httpClient.GetAsync(ConfigurationService.GetConfigValue(LauncherConVar.ILSpyUrl)); using var response = await _httpClient.GetAsync(ConfigurationService.GetConfigValue(LauncherConVar.ILSpyUrl));
using var zipArchive = new ZipArchive(await response.Content.ReadAsStreamAsync()); using var zipArchive = new ZipArchive(await response.Content.ReadAsStreamAsync());
Directory.CreateDirectory(fullPath); Directory.CreateDirectory(FullPath);
zipArchive.ExtractToDirectory(fullPath); zipArchive.ExtractToDirectory(FullPath);
} }
} }

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Nebula.Launcher.Models;
using Nebula.Launcher.ProcessHelper;
using Nebula.Launcher.ViewModels;
using Nebula.Shared;
using Nebula.Shared.Services;
namespace Nebula.Launcher.Services;
[ServiceRegister]
public sealed class InstanceRunningContainer(PopupMessageService popupMessageService, DebugService debugService)
{
private readonly InstanceKeyPool _keyPool = new();
private readonly Dictionary<InstanceKey, ProcessRunHandler> _processCache = new();
private readonly Dictionary<InstanceKey, ContentLogConsumer> _contentLoggerCache = new();
private readonly Dictionary<ProcessRunHandler, InstanceKey> _keyCache = new();
public Action<InstanceKey, bool>? IsRunningChanged;
public InstanceKey RegisterInstance(IProcessStartInfoProvider provider)
{
var id = _keyPool.Take();
var currentContentLogConsumer = new ContentLogConsumer();
var logBridge = new DebugLoggerBridge(debugService.GetLogger("PROCESS_"+id.Id));
var logContainer = new ProcessLogConsumerCollection();
logContainer.RegisterLogger(currentContentLogConsumer);
logContainer.RegisterLogger(logBridge);
var handler = new ProcessRunHandler(provider, logContainer);
handler.OnProcessExited += OnProcessExited;
_processCache[id] = handler;
_contentLoggerCache[id] = currentContentLogConsumer;
_keyCache[handler] = id;
return id;
}
public void Popup(InstanceKey instanceKey)
{
if(!_contentLoggerCache.TryGetValue(instanceKey, out var handler))
return;
handler.Popup(popupMessageService);
}
public void Run(InstanceKey instanceKey)
{
if(!_processCache.TryGetValue(instanceKey, out var process))
return;
process.Start();
IsRunningChanged?.Invoke(instanceKey, true);
}
public void Stop(InstanceKey instanceKey)
{
if(!_processCache.TryGetValue(instanceKey, out var process))
return;
process.Stop();
}
public bool IsRunning(InstanceKey instanceKey)
{
return _processCache.ContainsKey(instanceKey);
}
private void RemoveProcess(ProcessRunHandler handler)
{
if(handler.Disposed) return;
var key = _keyCache[handler];
IsRunningChanged?.Invoke(key, false);
_processCache.Remove(key);
_keyCache.Remove(handler);
_contentLoggerCache.Remove(key);
}
private void OnProcessExited(ProcessRunHandler obj)
{
obj.OnProcessExited -= OnProcessExited;
RemoveProcess(obj);
}
}

View File

@@ -11,7 +11,7 @@ using Nebula.Shared.Services;
namespace Nebula.Launcher.Services; namespace Nebula.Launcher.Services;
[ConstructGenerator, ServiceRegister] [ConstructGenerator, ServiceRegister]
public partial class LocalizationService public sealed partial class LocalizationService
{ {
[GenerateProperty] private ConfigurationService ConfigurationService { get; } [GenerateProperty] private ConfigurationService ConfigurationService { get; }
[GenerateProperty] private DebugService DebugService { get; } [GenerateProperty] private DebugService DebugService { get; }
@@ -40,7 +40,6 @@ public partial class LocalizationService
Console.WriteLine(error); Console.WriteLine(error);
} }
_currentMessageContext = mc; _currentMessageContext = mc;
} catch (Exception e) { } catch (Exception e) {
DebugService.GetLogger("localisationService").Error(e); DebugService.GetLogger("localisationService").Error(e);

View File

@@ -3,7 +3,7 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using Avalonia.Media; using Avalonia.Media;
namespace Nebula.Launcher.ViewModels.Pages; namespace Nebula.Launcher.Utils;
public static class ColorUtils public static class ColorUtils
{ {

View File

@@ -1,12 +1,11 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Nebula.Shared;
namespace Nebula.Launcher.Services; namespace Nebula.Launcher.Utils;
public static class ExplorerHelper public static class ExplorerUtils
{ {
public static void OpenFolder(string path) public static void OpenFolder(string path)
{ {

View File

@@ -0,0 +1,29 @@
using System;
using System.IO;
namespace Nebula.Launcher.Utils;
public static class VCRuntimeDllChecker
{
public static bool AreVCRuntimeDllsPresent()
{
if (!OperatingSystem.IsWindows()) return true;
string systemDir = Environment.SystemDirectory;
string[] requiredDlls = {
"msvcp140.dll",
"vcruntime140.dll"
};
foreach (var dll in requiredDlls)
{
var path = Path.Combine(systemDir, dll);
if (!File.Exists(path))
{
return false;
}
}
return true;
}
}

View File

@@ -8,6 +8,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Nebula.Launcher.Models; using Nebula.Launcher.Models;
using Nebula.Launcher.Services; using Nebula.Launcher.Services;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewModels.Pages; using Nebula.Launcher.ViewModels.Pages;
using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views; using Nebula.Launcher.Views;
@@ -208,7 +209,7 @@ public partial class MainViewModel : ViewModelBase
public void OpenRootPath() public void OpenRootPath()
{ {
ExplorerHelper.OpenFolder(FileService.RootPath); ExplorerUtils.OpenFolder(FileService.RootPath);
} }
public void OpenLink() public void OpenLink()
@@ -248,16 +249,18 @@ public partial class MainViewModel : ViewModelBase
else else
_viewQueue.Remove(viewModelBase); _viewQueue.Remove(viewModelBase);
} }
public void TriggerPane()
[RelayCommand]
private void TriggerPane()
{ {
IsPaneOpen = !IsPaneOpen; IsPaneOpen = !IsPaneOpen;
} }
[RelayCommand] public void CloseCurrentPopup()
public void ClosePopup() {
CurrentPopup?.Dispose();
}
private void ClosePopup()
{ {
var viewModelBase = _viewQueue.FirstOrDefault(); var viewModelBase = _viewQueue.FirstOrDefault();
if (viewModelBase is null) if (viewModelBase is null)
@@ -272,29 +275,4 @@ public partial class MainViewModel : ViewModelBase
CurrentPopup = viewModelBase; CurrentPopup = viewModelBase;
} }
}
public static class VCRuntimeDllChecker
{
public static bool AreVCRuntimeDllsPresent()
{
if (!OperatingSystem.IsWindows()) return true;
string systemDir = Environment.SystemDirectory;
string[] requiredDlls = {
"msvcp140.dll",
"vcruntime140.dll"
};
foreach (var dll in requiredDlls)
{
var path = Path.Combine(systemDir, dll);
if (!File.Exists(path))
{
return false;
}
}
return true;
}
} }

View File

@@ -5,6 +5,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Nebula.Launcher.Services; using Nebula.Launcher.Services;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views.Pages; using Nebula.Launcher.Views.Pages;
using Nebula.Shared; using Nebula.Shared;
@@ -69,7 +70,7 @@ public partial class ConfigurationViewModel : ViewModelBase
public void OpenDataFolder() public void OpenDataFolder()
{ {
ExplorerHelper.OpenFolder(FileService.RootPath); ExplorerUtils.OpenFolder(FileService.RootPath);
} }
public void ExportLogs() public void ExportLogs()
@@ -79,7 +80,7 @@ public partial class ConfigurationViewModel : ViewModelBase
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
ZipFile.CreateFromDirectory(logPath, Path.Join(path, DateTime.Now.ToString("yyyy-MM-dd") + ".zip")); ZipFile.CreateFromDirectory(logPath, Path.Join(path, DateTime.Now.ToString("yyyy-MM-dd") + ".zip"));
ExplorerHelper.OpenFolder(path); ExplorerUtils.OpenFolder(path);
} }
public void RemoveAllContent() public void RemoveAllContent()

View File

@@ -11,6 +11,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.Models; using Nebula.Launcher.Models;
using Nebula.Launcher.Services; using Nebula.Launcher.Services;
using Nebula.Launcher.Utils;
using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views; using Nebula.Launcher.Views;
using Nebula.Launcher.Views.Pages; using Nebula.Launcher.Views.Pages;
@@ -63,7 +64,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase, IContentHol
ContentService.Unpack(serverEntry.FileApi, myTempDir, loading.CreateLoadingContext()); ContentService.Unpack(serverEntry.FileApi, myTempDir, loading.CreateLoadingContext());
loading.Dispose(); loading.Dispose();
}); });
ExplorerHelper.OpenFolder(tmpDir); ExplorerUtils.OpenFolder(tmpDir);
} }
public void OnGoEnter() public void OnGoEnter()
@@ -80,10 +81,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase, IContentHol
var cur = ServiceProvider.GetService<ServerFolderContentEntry>()!; var cur = ServiceProvider.GetService<ServerFolderContentEntry>()!;
cur.Init(this, ServerText.ToRobustUrl()); cur.Init(this, ServerText.ToRobustUrl());
var curContent = cur.Go(new ContentPath(SearchText), CancellationService.Token); var curContent = cur.Go(new ContentPath(SearchText), CancellationService.Token);
if(curContent == null) CurrentEntry = curContent ?? throw new NullReferenceException($"{SearchText} not found in {ServerText}");
throw new NullReferenceException($"{SearchText} not found in {ServerText}");
CurrentEntry = curContent;
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -23,22 +23,16 @@ namespace Nebula.Launcher.ViewModels.Pages;
public partial class ServerOverviewModel : ViewModelBase public partial class ServerOverviewModel : ViewModelBase
{ {
[ObservableProperty] private string _searchText = string.Empty; [ObservableProperty] private string _searchText = string.Empty;
[ObservableProperty] private bool _isFilterVisible; [ObservableProperty] private bool _isFilterVisible;
[ObservableProperty] private ServerListView _currentServerList = new();
public readonly ServerFilter CurrentFilter = new(); public readonly ServerFilter CurrentFilter = new();
[GenerateProperty] private IServiceProvider ServiceProvider { get; } [GenerateProperty] private IServiceProvider ServiceProvider { get; }
[GenerateProperty] private ConfigurationService ConfigurationService { get; } [GenerateProperty] private ConfigurationService ConfigurationService { get; }
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } [GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; }
public ObservableCollection<ServerListTabTemplate> Items { get; private set; } public ObservableCollection<ServerListTabTemplate> Items { get; private set; }
[ObservableProperty] private ServerListTabTemplate _selectedItem; [ObservableProperty] private ServerListTabTemplate _selectedItem;
[GenerateProperty, DesignConstruct] private ServerViewContainer ServerViewContainer { get; } [GenerateProperty, DesignConstruct] private ServerViewContainer ServerViewContainer { get; }
[GenerateProperty, DesignConstruct] public ServerListViewModel CurrentServerList { get; }
private Dictionary<string, ServerListView> _viewCache = [];
//Design think //Design think
@@ -106,31 +100,32 @@ public partial class ServerOverviewModel : ViewModelBase
{ {
ServerViewContainer.Clear(); ServerViewContainer.Clear();
CurrentServerList.RefreshFromProvider(); CurrentServerList.RefreshFromProvider();
CurrentServerList.RequireStatusUpdate();
CurrentServerList.ApplyFilter(CurrentFilter); CurrentServerList.ApplyFilter(CurrentFilter);
} }
partial void OnSelectedItemChanged(ServerListTabTemplate value) partial void OnSelectedItemChanged(ServerListTabTemplate value)
{ {
if (!_viewCache.TryGetValue(value.TabName, out var view)) CurrentServerList.Provider = value.ServerListProvider;
{
view = ServerListView.TakeFrom(value.ServerListProvider);
_viewCache[value.TabName] = view;
}
CurrentServerList = view;
ApplyFilter(); ApplyFilter();
} }
} }
[ServiceRegister] [ServiceRegister]
public class ServerViewContainer public sealed class ServerViewContainer
{ {
private readonly ViewHelperService _viewHelperService; private readonly ViewHelperService _viewHelperService;
private readonly List<string> _favorites = []; private readonly List<string> _favorites = [];
private readonly Dictionary<string, string> _customNames = []; private readonly Dictionary<string, string> _customNames = [];
private readonly Dictionary<string, WeakReference<IListEntryModelView>> _entries = new();
public ICollection<IListEntryModelView> Items =>
_entries.Values
.Select(wr => wr.TryGetTarget(out var target) ? target : null)
.Where(t => t != null)
.ToList()!;
public ServerViewContainer() public ServerViewContainer()
{ {
_viewHelperService = new ViewHelperService(); _viewHelperService = new ViewHelperService();
@@ -144,107 +139,127 @@ public class ServerViewContainer
configurationService.SubscribeVarChanged(LauncherConVar.ServerCustomNames, OnCustomNamesChanged, true); configurationService.SubscribeVarChanged(LauncherConVar.ServerCustomNames, OnCustomNamesChanged, true);
} }
private void OnCustomNamesChanged(Dictionary<string,string>? value)
{
var oldNames =
_customNames.ToDictionary(k => k.Key, v => v.Value); //Clone think
_customNames.Clear();
if(value == null)
{
foreach (var (ip,_) in oldNames)
{
if(!_entries.TryGetValue(ip, out var listEntry) || listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = null;
}
return;
}
foreach (var (oldIp, oldName) in oldNames)
{
if(value.TryGetValue(oldIp, out var newName))
{
if (oldName == newName)
value.Remove(newName);
continue;
}
if(!_entries.TryGetValue(oldIp, out var listEntry) ||
listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = null;
}
foreach (var (ip, name) in value)
{
_customNames.Add(ip, name);
if(!_entries.TryGetValue(ip, out var listEntry) || listEntry is not IEntryNameHolder entryNameHolder)
continue;
entryNameHolder.Name = name;
}
}
private void OnFavoritesChange(string[]? value)
{
_favorites.Clear();
if(value == null) return;
foreach (var favorite in value)
{
_favorites.Add(favorite);
if (_entries.TryGetValue(favorite, out var entry) && entry is IFavoriteEntryModelView favoriteView)
{
favoriteView.IsFavorite = true;
}
}
}
private readonly Dictionary<string, IListEntryModelView> _entries = new();
public ICollection<IListEntryModelView> Items => _entries.Values;
public void Clear() public void Clear()
{ {
foreach (var (_, weakRef) in _entries)
{
if (weakRef.TryGetTarget(out var value))
value.Dispose();
}
_entries.Clear(); _entries.Clear();
} }
public IListEntryModelView Get(RobustUrl url, ServerStatus? serverStatus = null) public IListEntryModelView Get(RobustUrl url, ServerStatus? serverStatus = null)
{ {
var key = url.ToString();
IListEntryModelView? entry; IListEntryModelView? entry;
lock (_entries) lock (_entries)
{ {
_customNames.TryGetValue(url.ToString(), out var customName); _customNames.TryGetValue(key, out var customName);
if (_entries.TryGetValue(url.ToString(), out entry)) if (_entries.TryGetValue(key, out var weakEntry)
&& weakEntry.TryGetTarget(out entry))
{ {
return entry; return entry;
} }
if (serverStatus is not null) if (serverStatus is not null)
entry = _viewHelperService.GetViewModel<ServerEntryModelView>().WithData(url, customName, serverStatus); {
entry = _viewHelperService
.GetViewModel<ServerEntryModelView>()
.WithData(url, customName, serverStatus);
}
else else
entry = _viewHelperService.GetViewModel<ServerCompoundEntryViewModel>().LoadServerEntry(url, customName, CancellationToken.None); {
entry = _viewHelperService
if(_favorites.Contains(url.ToString()) && .GetViewModel<ServerCompoundEntryViewModel>()
entry is IFavoriteEntryModelView favoriteEntryModelView) .LoadServerEntry(url, customName, CancellationToken.None);
favoriteEntryModelView.IsFavorite = true; }
_entries.Add(url.ToString(), entry); if (_favorites.Contains(key)
&& entry is IFavoriteEntryModelView fav)
{
fav.IsFavorite = true;
}
_entries[key] = new WeakReference<IListEntryModelView>(entry);
} }
return entry; return entry;
} }
private void OnFavoritesChange(string[]? value)
{
_favorites.Clear();
if (value == null) return;
foreach (var favorite in value)
{
_favorites.Add(favorite);
if (_entries.TryGetValue(favorite, out var weak)
&& weak.TryGetTarget(out var entry)
&& entry is IFavoriteEntryModelView fav)
{
fav.IsFavorite = true;
}
}
}
private void OnCustomNamesChanged(Dictionary<string, string>? value)
{
var oldNames = _customNames.ToDictionary(x => x.Key, x => x.Value);
_customNames.Clear();
if (value == null)
{
foreach (var (ip, _) in oldNames)
{
ResetName(ip);
}
return;
}
foreach (var (oldIp, oldName) in oldNames)
{
if (value.TryGetValue(oldIp, out var newName))
{
if (oldName == newName)
value.Remove(newName);
continue;
}
ResetName(oldIp);
}
foreach (var (ip, name) in value)
{
_customNames.Add(ip, name);
if (_entries.TryGetValue(ip, out var weak)
&& weak.TryGetTarget(out var entry)
&& entry is IEntryNameHolder holder)
{
holder.Name = name;
}
}
}
private void ResetName(string ip)
{
if (_entries.TryGetValue(ip, out var weak)
&& weak.TryGetTarget(out var entry)
&& entry is IEntryNameHolder holder)
{
holder.Name = null;
}
}
} }
public interface IListEntryModelView public interface IListEntryModelView : IDisposable
{ {
} }

View File

@@ -9,10 +9,12 @@ public abstract class PopupViewModelBase : ViewModelBase, IDisposable
public abstract string Title { get; } public abstract string Title { get; }
public abstract bool IsClosable { get; } public abstract bool IsClosable { get; }
public Action<PopupViewModelBase>? OnDisposing;
public void Dispose() public void Dispose()
{ {
OnDispose(); OnDispose();
OnDisposing?.Invoke(this);
PopupMessageService.ClosePopup(this); PopupMessageService.ClosePopup(this);
} }

View File

@@ -1,9 +1,6 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.Models; using Nebula.Launcher.Models;
@@ -13,7 +10,6 @@ using Nebula.Launcher.Views;
using Nebula.Shared.Models; using Nebula.Shared.Models;
using Nebula.Shared.Services; using Nebula.Shared.Services;
using Nebula.Shared.ViewHelper; using Nebula.Shared.ViewHelper;
using BindingFlags = System.Reflection.BindingFlags;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
@@ -22,8 +18,7 @@ namespace Nebula.Launcher.ViewModels;
public sealed partial class ServerCompoundEntryViewModel : public sealed partial class ServerCompoundEntryViewModel :
ViewModelBase, IFavoriteEntryModelView, IFilterConsumer, IListEntryModelView, IEntryNameHolder ViewModelBase, IFavoriteEntryModelView, IFilterConsumer, IListEntryModelView, IEntryNameHolder
{ {
[ObservableProperty] private ServerEntryModelView? _currentEntry; private ServerEntryModelView? _currentEntry;
[ObservableProperty] private Control? _entryControl;
[ObservableProperty] private string _message = "Loading server entry..."; [ObservableProperty] private string _message = "Loading server entry...";
[ObservableProperty] private bool _isFavorite; [ObservableProperty] private bool _isFavorite;
[ObservableProperty] private bool _loading = true; [ObservableProperty] private bool _loading = true;
@@ -32,6 +27,28 @@ public sealed partial class ServerCompoundEntryViewModel :
private RobustUrl? _url; private RobustUrl? _url;
private ServerFilter? _currentFilter; private ServerFilter? _currentFilter;
public ServerEntryModelView? CurrentEntry
{
get => _currentEntry;
set
{
if (value == _currentEntry) return;
_currentEntry = value;
if (_currentEntry != null)
{
_currentEntry.IsFavorite = IsFavorite;
_currentEntry.Name = Name;
_currentEntry.ProcessFilter(_currentFilter);
}
Loading = _currentEntry == null;
OnPropertyChanged();
}
}
public string? Name public string? Name
{ {
get => _name; get => _name;
@@ -58,31 +75,43 @@ public sealed partial class ServerCompoundEntryViewModel :
{ {
} }
public ServerCompoundEntryViewModel LoadServerEntry(RobustUrl url,string? name, CancellationToken cancellationToken) public ServerCompoundEntryViewModel LoadWithEntry(ServerEntryModelView? entry)
{ {
Task.Run(async () => CurrentEntry = entry;
{
_url = url;
try
{
Message = "Loading server entry...";
var status = await RestService.GetAsync<ServerStatus>(_url.StatusUri, cancellationToken);
CurrentEntry = ServiceProvider.GetService<ServerEntryModelView>()!.WithData(_url,name, status);
CurrentEntry.IsFavorite = IsFavorite;
CurrentEntry.Loading = false;
CurrentEntry.ProcessFilter(_currentFilter);
Loading = false;
}
catch (Exception e)
{
Message = e.Message;
}
}, cancellationToken);
return this; return this;
} }
public ServerCompoundEntryViewModel LoadServerEntry(RobustUrl url, string? name, CancellationToken cancellationToken)
{
_url = url;
_name = name;
Task.Run(LoadServer, cancellationToken);
return this;
}
private async Task LoadServer()
{
if (_url is null)
{
Message = "Url is not set";
return;
}
try
{
Message = "Loading server entry...";
var status = await RestService.GetAsync<ServerStatus>(_url.StatusUri, CancellationToken.None);
CurrentEntry = ServiceProvider.GetService<ServerEntryModelView>()!.WithData(_url, null, status);
Loading = false;
}
catch (Exception e)
{
Message = "Error while fetching data from " + _url + " : " + e.Message;
}
}
public void ToggleFavorites() public void ToggleFavorites()
{ {
if (CurrentEntry is null && _url is not null) if (CurrentEntry is null && _url is not null)
@@ -102,4 +131,9 @@ public sealed partial class ServerCompoundEntryViewModel :
if(CurrentEntry is IFilterConsumer filterConsumer) if(CurrentEntry is IFilterConsumer filterConsumer)
filterConsumer.ProcessFilter(serverFilter); filterConsumer.ProcessFilter(serverFilter);
} }
public void Dispose()
{
CurrentEntry?.Dispose();
}
} }

View File

@@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls; using Avalonia.Controls;
@@ -23,15 +21,13 @@ namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ServerEntryView), false)] [ViewModelRegister(typeof(ServerEntryView), false)]
[ConstructGenerator] [ConstructGenerator]
public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView, IEntryNameHolder public sealed partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView, IEntryNameHolder
{ {
[ObservableProperty] private string _description = "Fetching info..."; [ObservableProperty] private string _description = "Fetching info...";
[ObservableProperty] private bool _expandInfo; [ObservableProperty] private bool _expandInfo;
[ObservableProperty] private bool _isFavorite; [ObservableProperty] private bool _isFavorite;
[ObservableProperty] private bool _isVisible; [ObservableProperty] private bool _isVisible;
[ObservableProperty] private bool _runVisible = true; [ObservableProperty] private bool _runVisible = true;
[ObservableProperty] private bool _tagDataVisible;
[ObservableProperty] private bool _loading;
[ObservableProperty] private string _realName; [ObservableProperty] private string _realName;
public string? Name public string? Name
@@ -42,10 +38,7 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
private ILogger _logger; private ILogger _logger;
private ServerInfo? _serverInfo; private ServerInfo? _serverInfo;
private ContentLogConsumer _currentContentLogConsumer; private InstanceKey _instanceKey;
private ProcessRunHandler<GameProcessStartInfoProvider>? _currentInstance;
public LogPopupModelView CurrLog;
public RobustUrl Address { get; private set; } public RobustUrl Address { get; private set; }
[GenerateProperty] private AccountInfoViewModel AccountInfoViewModel { get; } [GenerateProperty] private AccountInfoViewModel AccountInfoViewModel { get; }
[GenerateProperty] private CancellationService CancellationService { get; } = default!; [GenerateProperty] private CancellationService CancellationService { get; } = default!;
@@ -56,6 +49,7 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
[GenerateProperty] private MainViewModel MainViewModel { get; } = default!; [GenerateProperty] private MainViewModel MainViewModel { get; } = default!;
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } = default!; [GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } = default!;
[GenerateProperty] private GameRunnerPreparer GameRunnerPreparer { get; } = default!; [GenerateProperty] private GameRunnerPreparer GameRunnerPreparer { get; } = default!;
[GenerateProperty] private InstanceRunningContainer InstanceRunningContainer { get; } = default!;
public ServerStatus Status { get; private set; } = public ServerStatus Status { get; private set; } =
new( new(
@@ -101,14 +95,19 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
["rp:hrp", "18+"], ["rp:hrp", "18+"],
"Antag", 15, 5, 1, false "Antag", 15, 5, 1, false
, DateTime.Now, 100); , DateTime.Now, 100);
Address = "ss14://localhost".ToRobustUrl(); Address = "ss14://localhost";
} }
protected override void Initialise() protected override void Initialise()
{ {
_logger = DebugService.GetLogger(this); _logger = DebugService.GetLogger(this);
CurrLog = ViewHelperService.GetViewModel<LogPopupModelView>(); InstanceRunningContainer.IsRunningChanged += IsRunningChanged;
_currentContentLogConsumer = new(CurrLog, PopupMessageService); }
private void IsRunningChanged(InstanceKey arg1, bool isRunning)
{
if(arg1.Equals(_instanceKey))
RunVisible = !isRunning;
} }
public void ProcessFilter(ServerFilter? serverFilter) public void ProcessFilter(ServerFilter? serverFilter)
@@ -130,7 +129,7 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
OnPropertyChanged(nameof(Status)); OnPropertyChanged(nameof(Status));
} }
public ServerEntryModelView WithData(RobustUrl url, string? name,ServerStatus serverStatus) public ServerEntryModelView WithData(RobustUrl url, string? name, ServerStatus serverStatus)
{ {
Address = url; Address = url;
SetStatus(serverStatus); SetStatus(serverStatus);
@@ -162,13 +161,11 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
public void RunInstance() public void RunInstance()
{ {
CurrLog.Clear();
Task.Run(async ()=> await RunInstanceAsync()); Task.Run(async ()=> await RunInstanceAsync());
} }
public void RunInstanceIgnoreAuth() public void RunInstanceIgnoreAuth()
{ {
CurrLog.Clear();
Task.Run(async ()=> await RunInstanceAsync(true)); Task.Run(async ()=> await RunInstanceAsync(true));
} }
@@ -190,14 +187,11 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
viewModelLoading.LoadingName = "Loading instance..."; viewModelLoading.LoadingName = "Loading instance...";
PopupMessageService.Popup(viewModelLoading); PopupMessageService.Popup(viewModelLoading);
_currentInstance = var currProcessStartProvider =
await GameRunnerPreparer.GetGameProcessStartInfoProvider(Address, viewModelLoading, CancellationService.Token); await GameRunnerPreparer.GetGameProcessStartInfoProvider(Address, viewModelLoading, CancellationService.Token);
_logger.Log("Preparing instance..."); _logger.Log("Preparing instance...");
_currentInstance.RegisterLogger(_currentContentLogConsumer); _instanceKey = InstanceRunningContainer.RegisterInstance(currProcessStartProvider);
_currentInstance.RegisterLogger(new DebugLoggerBridge(DebugService.GetLogger($"PROCESS_{Random.Shared.Next(65535)}"))); InstanceRunningContainer.Run(_instanceKey);
_currentInstance.OnProcessExited += OnProcessExited;
RunVisible = false;
_currentInstance.Start();
_logger.Log("Starting instance..." + RealName); _logger.Log("Starting instance..." + RealName);
} }
catch (Exception e) catch (Exception e)
@@ -205,28 +199,17 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
var error = new Exception("Error while attempt run instance", e); var error = new Exception("Error while attempt run instance", e);
_logger.Error(error); _logger.Error(error);
PopupMessageService.Popup(error); PopupMessageService.Popup(error);
RunVisible = true;
} }
} }
private void OnProcessExited(ProcessRunHandler<GameProcessStartInfoProvider> obj)
{
RunVisible = true;
if (_currentInstance == null) return;
_currentInstance.OnProcessExited -= OnProcessExited;
_currentInstance.Dispose();
_currentInstance = null;
}
public void StopInstance() public void StopInstance()
{ {
_currentInstance?.Stop(); InstanceRunningContainer.Stop(_instanceKey);
} }
public void ReadLog() public void ReadLog()
{ {
PopupMessageService.Popup(CurrLog); InstanceRunningContainer.Popup(_instanceKey);
} }
public async void ExpandInfoRequired() public async void ExpandInfoRequired()
@@ -243,9 +226,39 @@ public partial class ServerEntryModelView : ViewModelBase, IFilterConsumer, ILis
if (info.Links is null) return; if (info.Links is null) return;
foreach (var link in info.Links) Links.Add(link); foreach (var link in info.Links) Links.Add(link);
} }
public void Dispose()
{
_logger.Dispose();
}
} }
public class LinkGoCommand : ICommand public sealed class InstanceKeyPool
{
private int _nextId = 1;
public InstanceKey Take()
{
return new InstanceKey(_nextId++);
}
public void Free(InstanceKey id)
{
// TODO: make some free logic later
}
}
public record struct InstanceKey(int Id):
IEquatable<int>,
IComparable<InstanceKey>
{
public static implicit operator InstanceKey(int id) => new InstanceKey(id);
public static implicit operator int(InstanceKey id) => id.Id;
public bool Equals(int other) => Id == other;
public int CompareTo(InstanceKey other) => Id.CompareTo(other.Id);
};
public sealed class LinkGoCommand : ICommand
{ {
public LinkGoCommand() public LinkGoCommand()
{ {

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.ObjectModel;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.Models;
using Nebula.Launcher.ServerListProviders;
using Nebula.Launcher.ViewModels.Pages;
using Nebula.Launcher.Views;
using Nebula.Shared.ViewHelper;
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ServerListView), false)]
public partial class ServerListViewModel : ViewModelBase
{
[ObservableProperty] private bool _isLoading;
public ServerListViewModel()
{
if (Design.IsDesignMode)
{
Provider = new TestServerList();
}
}
private IServerListProvider? _provider;
public ObservableCollection<IListEntryModelView> ServerList { get; } = new();
public ObservableCollection<Exception> ErrorList { get; } = new();
public IServerListProvider Provider
{
get => _provider ?? throw new Exception();
set
{
_provider = value;
_provider.OnDisposed += OnProviderDisposed;
if (_provider is IServerListDirtyInvoker invoker)
{
invoker.Dirty += OnDirty;
}
if(!_provider.IsLoaded)
RefreshFromProvider();
else
{
Clear();
PasteServersFromList();
}
}
}
private void OnProviderDisposed()
{
Provider.OnLoaded -= RefreshRequired;
Provider.OnDisposed -= OnProviderDisposed;
if (Provider is IServerListDirtyInvoker invoker)
{
invoker.Dirty -= OnDirty;
}
_provider = null;
}
private ServerFilter? _currentFilter;
public void RefreshFromProvider()
{
if (IsLoading)
return;
Clear();
StartLoading();
Provider.LoadServerList();
if (Provider.IsLoaded) PasteServersFromList();
else Provider.OnLoaded += RefreshRequired;
}
public void ApplyFilter(ServerFilter? filter)
{
_currentFilter = filter;
if(IsLoading)
return;
foreach (var serverView in ServerList)
{
if(serverView is IFilterConsumer filterConsumer)
filterConsumer.ProcessFilter(filter);
}
}
private void OnDirty()
{
RefreshFromProvider();
}
private void Clear()
{
ErrorList.Clear();
ServerList.Clear();
}
private void PasteServersFromList()
{
foreach (var serverEntry in Provider.GetServers())
{
ServerList.Add(serverEntry);
if(serverEntry is IFilterConsumer serverFilter)
serverFilter.ProcessFilter(_currentFilter);
}
foreach (var error in Provider.GetErrors())
{
ErrorList.Add(error);
}
EndLoading();
}
private void RefreshRequired()
{
PasteServersFromList();
Provider.OnLoaded -= RefreshRequired;
}
private void StartLoading()
{
Clear();
IsLoading = true;
}
private void EndLoading()
{
IsLoading = false;
}
protected override void InitialiseInDesignMode()
{
}
protected override void Initialise()
{
}
}

View File

@@ -77,7 +77,7 @@
</ListBox> </ListBox>
<Button <Button
Classes="ViewSelectButton" Classes="ViewSelectButton"
Command="{Binding TriggerPaneCommand}" Command="{Binding TriggerPane}"
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Padding="5,0,5,0" Padding="5,0,5,0"
@@ -178,7 +178,7 @@
<Label Content="{Binding CurrentTitle}" VerticalAlignment="Center" /> <Label Content="{Binding CurrentTitle}" VerticalAlignment="Center" />
</StackPanel> </StackPanel>
<Button <Button
Command="{Binding ClosePopupCommand}" Command="{Binding CloseCurrentPopup}"
Content="X" Content="X"
CornerRadius="0,10,0,0" CornerRadius="0,10,0,0"
HorizontalAlignment="Right" HorizontalAlignment="Right"

View File

@@ -42,9 +42,10 @@
</ListBox> </ListBox>
<Border <Border
Child="{Binding CurrentServerList}"
Grid.Row="1" Grid.Row="1"
Grid.RowSpan="2" /> Grid.RowSpan="2" >
<ContentControl Content="{Binding CurrentServerList}"></ContentControl>
</Border>
<Border Grid.Row="1" <Border Grid.Row="1"
Background="{StaticResource DefaultGrad}" Background="{StaticResource DefaultGrad}"

View File

@@ -70,9 +70,9 @@
</Grid> </Grid>
</Border> </Border>
<Panel IsVisible="{Binding !Loading}"> <ContentControl
<views:ServerEntryView IsVisible="{Binding !Loading}" DataContext="{Binding CurrentEntry}"/> IsVisible="{Binding !Loading}"
</Panel> Content="{Binding CurrentEntry}"/>
</Panel> </Panel>
</UserControl> </UserControl>

View File

@@ -0,0 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:services="clr-namespace:Nebula.Launcher.Services"
xmlns:viewModels1="clr-namespace:Nebula.Launcher.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Nebula.Launcher.Views.ServerListView"
x:DataType="viewModels1:ServerListViewModel">
<Design.DataContext>
<viewModels1:ServerListViewModel />
</Design.DataContext>
<ScrollViewer
Margin="5,0,0,10"
Padding="0,0,10,0">
<StackPanel Margin="0,0,0,30">
<Label IsVisible="{Binding IsLoading}"
x:Name="LoadingLabel"
Margin="10" HorizontalAlignment="Center"
Content="{services:LocaledText 'server-list-loading'}"/>
<ItemsControl
ItemsSource="{Binding ErrorList}"
Margin="10,0,10,0" />
<ItemsControl
ItemsSource="{Binding ServerList}"
Padding="0" />
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System;
using Avalonia.Controls;
using Nebula.Launcher.Models;
using Nebula.Launcher.ServerListProviders;
using Nebula.Launcher.ViewModels;
using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.Views;
public partial class ServerListView : UserControl
{
public ServerListView()
{
InitializeComponent();
}
}

View File

@@ -1,10 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,8 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -12,8 +10,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.3.6" /> <PackageReference Include="Lib.Harmony"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0"/> <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
<PackageReference Include="SharpZstd.Interop" Version="1.5.6"/> <PackageReference Include="SharpZstd.Interop"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -34,8 +34,10 @@ public static class CurrentConVar
public static readonly ConVar<Dictionary<string,string>> DotnetUrl = ConVarBuilder.Build<Dictionary<string,string>>("dotnet.url", public static readonly ConVar<Dictionary<string,string>> DotnetUrl = ConVarBuilder.Build<Dictionary<string,string>>("dotnet.url",
new(){ new(){
{"win-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-x64.zip"}, {"win-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-win-x64.exe"},
{"win-x86", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-x86.zip"}, {"win-x86", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-win-x86.exe"},
{"linux-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-linux-x64.tar.gz"} {"linux-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-linux-x64.tar.gz"}
}); });
public static readonly ConVar<string> DotnetVersion = ConVarBuilder.Build<string>("dotnet.version", "10.0.2");
} }

View File

@@ -29,7 +29,7 @@ public class RobustUrl
return url.HttpUri; return url.HttpUri;
} }
public static explicit operator RobustUrl(string url) public static implicit operator RobustUrl(string url)
{ {
return new RobustUrl(url); return new RobustUrl(url);
} }

View File

@@ -1,7 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -11,9 +9,9 @@
<EmbeddedResource Include="Utils\runtime.json"> <EmbeddedResource Include="Utils\runtime.json">
<LogicalName>Utility.runtime.json</LogicalName> <LogicalName>Utility.runtime.json</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
<PackageReference Include="Robust.Natives" Version="0.2.3" /> <PackageReference Include="Robust.Natives"/>
<PackageReference Include="SharpZstd.Interop" Version="1.5.6" /> <PackageReference Include="SharpZstd.Interop"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -8,10 +8,10 @@ namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class DotnetResolverService(DebugService debugService, ConfigurationService configurationService) public class DotnetResolverService(DebugService debugService, ConfigurationService configurationService)
{ {
private static readonly string FullPath = private string FullPath =>
Path.Join(FileService.RootPath, "dotnet", DotnetUrlHelper.GetRuntimeIdentifier()); Path.Join(FileService.RootPath, $"dotnet.{configurationService.GetConfigValue(CurrentConVar.DotnetVersion)}", DotnetUrlHelper.GetRuntimeIdentifier());
private static readonly string ExecutePath = Path.Join(FullPath, "dotnet" + DotnetUrlHelper.GetExtension()); private string ExecutePath => Path.Join(FullPath, "dotnet" + DotnetUrlHelper.GetExtension());
private readonly HttpClient _httpClient = new(); private readonly HttpClient _httpClient = new();
public async Task<string> EnsureDotnet() public async Task<string> EnsureDotnet()

View File

@@ -14,13 +14,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/> <PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -64,18 +64,18 @@ namespace {viewNamespace}
}}"; }}";
// Add the source code to the compilation. // Add the source code to the compilation.
context.AddSource($"{viewName}_viewConstructAuto.g.cs", SourceText.From(code, Encoding.UTF8)); context.AddSource($"{viewModelName}_{viewName}_viewConstructAuto.g.cs", SourceText.From(code, Encoding.UTF8));
} }
catch (Exception e) catch (Exception e)
{ {
var coder1 = $@" var coder1 = $@"
// <auto-generated/> // <auto-generated/>
// Error! // Error! {e.Message}
namespace {viewModelNamespace} namespace {viewModelNamespace}
{{ {{
public partial class {viewModelName} public partial class {viewModelName}
{{ {{
// {e.Message} // ERROR: {e.Message}
}} }}
}}"; }}";
@@ -84,6 +84,4 @@ namespace {viewModelNamespace}
} }
} }
} }
} }

View File

@@ -1,22 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/> <PackageReference Include="coverlet.collector"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq"/>
<PackageReference Include="NUnit" Version="3.14.0"/> <PackageReference Include="NUnit"/>
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/> <PackageReference Include="NUnit.Analyzers"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -16,7 +16,8 @@ public static class DotnetStandalone
private static readonly HttpClient HttpClient = new(); private static readonly HttpClient HttpClient = new();
private static readonly string FullPath = private static readonly string FullPath =
Path.Join(MainWindow.RootPath, "dotnet", DotnetUrlHelper.GetRuntimeIdentifier()); Path.Join(MainWindow.RootPath, $"dotnet.{ConfigurationStandalone.GetConfigValue(UpdateConVars.DotnetVersion)}",
DotnetUrlHelper.GetRuntimeIdentifier());
private static readonly string ExecutePath = Path.Join(FullPath, "dotnet" + DotnetUrlHelper.GetExtension()); private static readonly string ExecutePath = Path.Join(FullPath, "dotnet" + DotnetUrlHelper.GetExtension());

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@@ -17,12 +16,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.1"/> <PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.1"/> <PackageReference Include="Avalonia.Desktop"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1"/> <PackageReference Include="Avalonia.Themes.Fluent"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1"/> <PackageReference Include="Avalonia.Fonts.Inter"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1"> <PackageReference Include="Avalonia.Diagnostics">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets> <IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>

View File

@@ -12,8 +12,10 @@ public static class UpdateConVars
public static readonly ConVar<Dictionary<string,string>> DotnetUrl = ConVarBuilder.Build<Dictionary<string,string>>("dotnet.url", public static readonly ConVar<Dictionary<string,string>> DotnetUrl = ConVarBuilder.Build<Dictionary<string,string>>("dotnet.url",
new(){ new(){
{"win-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-x64.zip"}, {"win-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-win-x64.exe"},
{"win-x86", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-win-x86.zip"}, {"win-x86", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-win-x86.exe"},
{"linux-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/9.0.6/dotnet-runtime-9.0.6-linux-x64.tar.gz"} {"linux-x64", "https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.2/dotnet-runtime-10.0.2-linux-x64.tar.gz"}
}); });
public static readonly ConVar<string> DotnetVersion = ConVarBuilder.Build<string>("dotnet.version", "10.0.2");
} }

View File

@@ -1,58 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Launcher", "Nebula.Launcher\Nebula.Launcher.csproj", "{D8F9728D-6153-4351-8BE2-52F7D54C299D}"
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.SourceGenerators", "Nebula.SourceGenerators\Nebula.SourceGenerators.csproj", "{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.UpdateResolver", "Nebula.UpdateResolver\Nebula.UpdateResolver.csproj", "{90CB754D-00A1-493D-A630-02BDA0AFF31A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.Packager", "Nebula.Packager\Nebula.Packager.csproj", "{D444A5F9-4549-467F-9398-97DD6DB1E263}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebula.UnitTest", "Nebula.UnitTest\Nebula.UnitTest.csproj", "{735691F8-949C-4476-B9E4-5DF6FF8D3D0B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D8F9728D-6153-4351-8BE2-52F7D54C299D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8F9728D-6153-4351-8BE2-52F7D54C299D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F9728D-6153-4351-8BE2-52F7D54C299D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8F9728D-6153-4351-8BE2-52F7D54C299D}.Release|Any CPU.Build.0 = Release|Any CPU
{8AE91631-DE96-4A97-A255-058E27A7C3EA}.Debug|Any CPU.ActiveCfg = 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.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
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{985A8F36-AFEB-4E85-8EDB-7C9DDEC698DC}.Release|Any CPU.Build.0 = Release|Any CPU
{90CB754D-00A1-493D-A630-02BDA0AFF31A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90CB754D-00A1-493D-A630-02BDA0AFF31A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90CB754D-00A1-493D-A630-02BDA0AFF31A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90CB754D-00A1-493D-A630-02BDA0AFF31A}.Release|Any CPU.Build.0 = Release|Any CPU
{D444A5F9-4549-467F-9398-97DD6DB1E263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D444A5F9-4549-467F-9398-97DD6DB1E263}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D444A5F9-4549-467F-9398-97DD6DB1E263}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D444A5F9-4549-467F-9398-97DD6DB1E263}.Release|Any CPU.Build.0 = Release|Any CPU
{735691F8-949C-4476-B9E4-5DF6FF8D3D0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{735691F8-949C-4476-B9E4-5DF6FF8D3D0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{735691F8-949C-4476-B9E4-5DF6FF8D3D0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{735691F8-949C-4476-B9E4-5DF6FF8D3D0B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -2,6 +2,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArchiving_002EUtils_002EWindows_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F27e9f12ad1e4318b9b02849ec3e6a502fa3ee761c4f0522ba756ab30cde1c_003FArchiving_002EUtils_002EWindows_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArchiving_002EUtils_002EWindows_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F27e9f12ad1e4318b9b02849ec3e6a502fa3ee761c4f0522ba756ab30cde1c_003FArchiving_002EUtils_002EWindows_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssembly_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F501151723a8d43558c75acbd334f26322066fa4b1c82b1297291314bf92ff_003FAssembly_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssembly_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F501151723a8d43558c75acbd334f26322066fa4b1c82b1297291314bf92ff_003FAssembly_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAuthenticationHeaderValue_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F88b338246f59cffdb6f3dc3d8dbcfc169599dc71d6f44a8f2732983db7f73a_003FAuthenticationHeaderValue_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAuthenticationHeaderValue_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F88b338246f59cffdb6f3dc3d8dbcfc169599dc71d6f44a8f2732983db7f73a_003FAuthenticationHeaderValue_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAvaloniaList_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3cc366334cc52275393f9def48cfcbccc8382175579fbd4f75b8c0e4bf33_003FAvaloniaList_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAvaloniaXamlLoader_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F80462644bd1cc7e0b229dc4f5752b48c01cb67b46ae563b1b5078cc2556b98_003FAvaloniaXamlLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAvaloniaXamlLoader_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F80462644bd1cc7e0b229dc4f5752b48c01cb67b46ae563b1b5078cc2556b98_003FAvaloniaXamlLoader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABorder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5fda7f1253ea19edc15f91b94a33322b857f1a9319fbffea8d26e9d304178_003FBorder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABorder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5fda7f1253ea19edc15f91b94a33322b857f1a9319fbffea8d26e9d304178_003FBorder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABrushes_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F86e7f7d5cebacb8f8e37f52cb9a1f6a4b8933239631e3d969a4bc881ae92f9_003FBrushes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABrushes_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F86e7f7d5cebacb8f8e37f52cb9a1f6a4b8933239631e3d969a4bc881ae92f9_003FBrushes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
@@ -43,6 +44,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2b95745d8f2ddf7b8ad6130e01c5b2782e253ff11247a9aeefcef47277b1ab_003FImage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2b95745d8f2ddf7b8ad6130e01c5b2782e253ff11247a9aeefcef47277b1ab_003FImage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIndex_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2a1a813823579c69832f1304f97761e7be433bd6aa928f351d138050b56a38_003FIndex_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIndex_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2a1a813823579c69832f1304f97761e7be433bd6aa928f351d138050b56a38_003FIndex_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInt32_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa882d183338544fdbcbdfc7b6d3dcb78916630765551644a221b5be9c45a121b_003FInt32_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInt32_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa882d183338544fdbcbdfc7b6d3dcb78916630765551644a221b5be9c45a121b_003FInt32_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInt64_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2f657497243c260bae22d6d2c67ab907371015db851628463cc13adfaf325_003FInt64_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInterop_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc4d71b51722245ae8cde97bfd996e68386928_003F3a_003F004a1338_003FInterop_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AInterop_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc4d71b51722245ae8cde97bfd996e68386928_003F3a_003F004a1338_003FInterop_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIObservable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F36_003Fae70bbb9_003FIObservable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIObservable_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F36_003Fae70bbb9_003FIObservable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemsControl_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9b99b33b61e064a95d985c50edb17a9ee889e36b9ae2381866346ee68ced8bd9_003FItemsControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItemsControl_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FUsers_003FCinka_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9b99b33b61e064a95d985c50edb17a9ee889e36b9ae2381866346ee68ced8bd9_003FItemsControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

10
Nebula.slnx Normal file
View File

@@ -0,0 +1,10 @@
<Solution>
<Project Path="Nebula.Launcher\Nebula.Launcher.csproj" Type="Classic C#" />
<Project Path="Nebula.Packager\Nebula.Packager.csproj" Type="Classic C#" />
<Project Path="Nebula.Runner\Nebula.Runner.csproj" Type="Classic C#" />
<Project Path="Nebula.Shared\Nebula.Shared.csproj" Type="Classic C#" />
<Project Path="Nebula.SourceGenerators\Nebula.SourceGenerators.csproj" Type="Classic C#" />
<Project Path="Nebula.UnitTest\Nebula.UnitTest.csproj" Type="Classic C#" />
<Project Path="Nebula.UpdateResolver\Nebula.UpdateResolver.csproj" Type="Classic C#" />
<Project Path="Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" Type="Classic C#" />
</Solution>