diff --git a/.idea/.idea.Nebula/.idea/avalonia.xml b/.idea/.idea.Nebula/.idea/avalonia.xml
index 21eb961..99693f9 100644
--- a/.idea/.idea.Nebula/.idea/avalonia.xml
+++ b/.idea/.idea.Nebula/.idea/avalonia.xml
@@ -15,7 +15,9 @@
+
+
diff --git a/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs b/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs
index e93868f..c771660 100644
--- a/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs
+++ b/Nebula.Launcher/ViewModels/AccountInfoViewModel.cs
@@ -80,18 +80,19 @@ public partial class AccountInfoViewModel : ViewModelBase
public async void DoAuth()
{
- _popupMessageService.Popup("Auth think, please wait...");
+ var message = GetViewModel();
+ message.InfoText = "Auth think, please wait...";
+ _popupMessageService.Popup(message);
if(await _authService.Auth(CurrentAlp))
{
- _popupMessageService.ClosePopup();
- _popupMessageService.Popup("Hello, " + _authService.SelectedAuth!.AuthLoginPassword.Login);
+ message.Dispose();
IsLogged = true;
_configurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp);
}
else
{
- _popupMessageService.ClosePopup();
+ message.Dispose();
Logout();
_popupMessageService.Popup("Well, shit is happened: " + _authService.Reason);
}
@@ -100,7 +101,7 @@ public partial class AccountInfoViewModel : ViewModelBase
public void Logout()
{
IsLogged = false;
- CurrentAlp = new AuthLoginPassword("", "", "");
+ //CurrentAlp = new AuthLoginPassword("", "", "");
_authService.ClearAuth();
}
diff --git a/Nebula.Launcher/ViewModels/ExceptionViewModel.cs b/Nebula.Launcher/ViewModels/ExceptionViewModel.cs
new file mode 100644
index 0000000..eed2337
--- /dev/null
+++ b/Nebula.Launcher/ViewModels/ExceptionViewModel.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.ObjectModel;
+using Nebula.Launcher.ViewHelper;
+using Nebula.Launcher.Views.Popup;
+
+namespace Nebula.Launcher.ViewModels;
+
+[ViewModelRegister(typeof(ExceptionView), false)]
+public class ExceptionViewModel : PopupViewModelBase
+{
+ public ExceptionViewModel() : base()
+ {
+ var e = new Exception("TEST");
+
+ AppendError(e);
+ }
+
+ public ExceptionViewModel(IServiceProvider serviceProvider) : base(serviceProvider){}
+
+ public override string Title => "Oopsie! Some shit is happened now!";
+
+ public ObservableCollection Errors { get; } = new();
+
+ public void AppendError(Exception exception)
+ {
+ Errors.Add(exception);
+ if(exception.InnerException != null)
+ AppendError(exception.InnerException);
+ }
+}
diff --git a/Nebula.Launcher/ViewModels/LoadingContextViewModel.cs b/Nebula.Launcher/ViewModels/LoadingContextViewModel.cs
new file mode 100644
index 0000000..69a4fdd
--- /dev/null
+++ b/Nebula.Launcher/ViewModels/LoadingContextViewModel.cs
@@ -0,0 +1,44 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Nebula.Launcher.ViewHelper;
+using Nebula.Launcher.Views.Popup;
+using Nebula.Shared.Models;
+
+namespace Nebula.Launcher.ViewModels;
+
+[ViewModelRegister(typeof(LoadingContextView), false)]
+public sealed partial class LoadingContextViewModel : PopupViewModelBase, ILoadingHandler
+{
+ public LoadingContextViewModel() :base(){}
+ public LoadingContextViewModel(IServiceProvider provider) : base(provider){}
+
+ public string LoadingName { get; set; } = "Loading...";
+
+ public override string Title => LoadingName;
+
+ [ObservableProperty]
+ private int _currJobs;
+ [ObservableProperty]
+ private int _resolvedJobs;
+
+ public void SetJobsCount(int count)
+ {
+ CurrJobs = count;
+ }
+
+ public int GetJobsCount()
+ {
+ return CurrJobs;
+ }
+
+ public void SetResolvedJobsCount(int count)
+ {
+ ResolvedJobs = count;
+
+ }
+
+ public int GetResolvedJobsCount()
+ {
+ return ResolvedJobs;
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Launcher/ViewModels/MainViewModel.cs b/Nebula.Launcher/ViewModels/MainViewModel.cs
index 03b12c7..90684a9 100644
--- a/Nebula.Launcher/ViewModels/MainViewModel.cs
+++ b/Nebula.Launcher/ViewModels/MainViewModel.cs
@@ -27,19 +27,20 @@ public partial class MainViewModel : ViewModelBase
}
[UsedImplicitly]
- public MainViewModel(AccountInfoViewModel accountInfoViewModel, PopupMessageService popupMessageService,
+ public MainViewModel(AccountInfoViewModel accountInfoViewModel,DebugService debugService, PopupMessageService popupMessageService,
IServiceProvider serviceProvider): base(serviceProvider)
{
_currentPage = accountInfoViewModel;
- _popupMessageService = popupMessageService;
+ _debugService = debugService;
Items = new ObservableCollection(_templates);
- _popupMessageService.OnPopupRequired += OnPopupRequired;
+ popupMessageService.OnPopupRequired += OnPopupRequired;
+ popupMessageService.OnCloseRequired += OnPopupCloseRequired;
SelectedListItem = Items.First(vm => vm.ModelType == typeof(AccountInfoViewModel));
}
-
- private readonly Queue _viewQueue = new();
+
+ private readonly List _viewQueue = new();
private readonly List _templates =
[
@@ -53,7 +54,7 @@ public partial class MainViewModel : ViewModelBase
[ObservableProperty]
private ViewModelBase _currentPage;
- private readonly PopupMessageService _popupMessageService;
+ private readonly DebugService _debugService;
[ObservableProperty] private bool _isEnabled = true;
[ObservableProperty] private bool _popup;
@@ -90,7 +91,7 @@ public partial class MainViewModel : ViewModelBase
}
else
{
- _viewQueue.Enqueue(viewModelBase);
+ _viewQueue.Add(viewModelBase);
}
}
@@ -111,13 +112,10 @@ public partial class MainViewModel : ViewModelBase
Helper.OpenBrowser("https://cinka.ru/nebula-launcher/");
}
- private void OnPopupRequired(object? viewModelBase)
+ private void OnPopupRequired(object viewModelBase)
{
switch (viewModelBase)
{
- case null:
- ClosePopup();
- break;
case string str:
{
var view = GetViewModel();
@@ -128,8 +126,28 @@ public partial class MainViewModel : ViewModelBase
case PopupViewModelBase @base:
PopupMessage(@base);
break;
+ case Exception error:
+ var err = GetViewModel();
+ _debugService.Error(error);
+ err.AppendError(error);
+ PopupMessage(err);
+ break;
}
}
+
+ private void OnPopupCloseRequired(object obj)
+ {
+ if(obj is not PopupViewModelBase viewModelBase)
+ {
+ return;
+ }
+
+ if (obj == CurrentPopup)
+ ClosePopup();
+ else
+ _viewQueue.Remove(viewModelBase);
+ }
+
[RelayCommand]
private void TriggerPane()
@@ -140,10 +158,14 @@ public partial class MainViewModel : ViewModelBase
[RelayCommand]
public void ClosePopup()
{
- if (!_viewQueue.TryDequeue(out var viewModelBase))
+ var viewModelBase = _viewQueue.FirstOrDefault();
+ if (viewModelBase is null)
OnCloseRequired();
else
+ {
CurrentTitle = viewModelBase.Title;
+ _viewQueue.RemoveAt(0);
+ }
CurrentPopup = viewModelBase;
}
diff --git a/Nebula.Launcher/ViewModels/PopupViewModelBase.cs b/Nebula.Launcher/ViewModels/PopupViewModelBase.cs
index 32239f6..cac33a6 100644
--- a/Nebula.Launcher/ViewModels/PopupViewModelBase.cs
+++ b/Nebula.Launcher/ViewModels/PopupViewModelBase.cs
@@ -1,16 +1,25 @@
using System;
+using Microsoft.Extensions.DependencyInjection;
+using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels;
-public abstract class PopupViewModelBase : ViewModelBase
+public abstract class PopupViewModelBase : ViewModelBase, IDisposable
{
+ private readonly IServiceProvider _serviceProvider;
+
public PopupViewModelBase()
{
}
public PopupViewModelBase(IServiceProvider serviceProvider) : base(serviceProvider)
{
+ _serviceProvider = serviceProvider;
}
- public abstract string Title { get; }
+ public abstract string Title { get; }
+ public void Dispose()
+ {
+ _serviceProvider.GetService()?.ClosePopup(this);
+ }
}
\ No newline at end of file
diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
index 650d13a..5419db8 100644
--- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
+++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs
@@ -3,6 +3,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper;
@@ -22,12 +23,23 @@ public partial class ServerEntryModelView : ViewModelBase
private readonly RunnerService _runnerService = default!;
private readonly PopupMessageService _popupMessageService;
- [ObservableProperty] private bool _runVisible = true;
+ public bool RunVisible => Process == null;
public ServerHubInfo ServerHubInfo { get; set; } = default!;
-
-
- private Process? _process;
+
+
+ private Process? _p;
+ private Process? Process
+ {
+ get { return _p; }
+ set
+ {
+ _p = value;
+ OnPropertyChanged(nameof(RunVisible));
+ }
+ }
+
+
public LogPopupModelView CurrLog;
@@ -55,65 +67,93 @@ public partial class ServerEntryModelView : ViewModelBase
CurrLog = GetViewModel();
}
- public async void RunInstance()
+ public void RunInstance()
{
- var authProv = _authService.SelectedAuth;
-
- var buildInfo = await _contentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), _cancellationService.Token);
+ Task.Run(RunAsync);
+ }
- await _runnerService.PrepareRun(buildInfo, _cancellationService.Token);
-
- _process = Process.Start(new ProcessStartInfo()
- {
- FileName = "dotnet.exe",
- Arguments = "./Nebula.Runner.dll",
- Environment = {
- { "ROBUST_AUTH_USERID", authProv?.UserId.ToString() } ,
- { "ROBUST_AUTH_TOKEN", authProv?.Token.Token } ,
- { "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer } ,
- { "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey } ,
- { "GAME_URL", ServerHubInfo.Address } ,
- { "AUTH_LOGIN", authProv?.AuthLoginPassword.Login } ,
- },
- CreateNoWindow = true,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8
- });
-
-
- if (_process is null)
- {
- return;
- }
-
- _process.BeginOutputReadLine();
- _process.BeginErrorReadLine();
-
- RunVisible = false;
-
- _process.OutputDataReceived += OnOutputDataReceived;
- _process.ErrorDataReceived += OnErrorDataReceived;
-
- _process.Exited += OnExited;
+ public async Task RunAsync()
+ {
+ try
+ {
+ var authProv = _authService.SelectedAuth;
+
+ var buildInfo =
+ await _contentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), _cancellationService.Token);
+
+ using (var loadingContext = GetViewModel())
+ {
+ loadingContext.LoadingName = "Loading instance...";
+ ((ILoadingHandler)loadingContext).AppendJob();
+
+ _popupMessageService.Popup(loadingContext);
+
+
+ await _runnerService.PrepareRun(buildInfo, loadingContext, _cancellationService.Token);
+
+ Process = Process.Start(new ProcessStartInfo()
+ {
+ FileName = "dotnet.exe",
+ Arguments = "./Nebula.Runner.dll",
+ Environment =
+ {
+ { "ROBUST_AUTH_USERID", authProv?.UserId.ToString() },
+ { "ROBUST_AUTH_TOKEN", authProv?.Token.Token },
+ { "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer },
+ { "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey },
+ { "GAME_URL", ServerHubInfo.Address },
+ { "AUTH_LOGIN", authProv?.AuthLoginPassword.Login },
+ },
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ StandardOutputEncoding = Encoding.UTF8
+ });
+
+ ((ILoadingHandler)loadingContext).AppendResolvedJob();
+ }
+
+ if (Process is null)
+ {
+ return;
+ }
+
+ Process.EnableRaisingEvents = true;
+
+ Process.BeginOutputReadLine();
+ Process.BeginErrorReadLine();
+
+ Process.OutputDataReceived += OnOutputDataReceived;
+ Process.ErrorDataReceived += OnErrorDataReceived;
+
+ Process.Exited += OnExited;
+ }
+ catch (TaskCanceledException e)
+ {
+ _popupMessageService.Popup("Task canceled");
+ }
+ catch (Exception e)
+ {
+ _popupMessageService.Popup(e);
+ }
}
private void OnExited(object? sender, EventArgs e)
{
- if (_process is null)
+ if (Process is null)
{
return;
}
- _process.OutputDataReceived -= OnOutputDataReceived;
- _process.ErrorDataReceived -= OnErrorDataReceived;
- _process.Exited -= OnExited;
+ Process.OutputDataReceived -= OnOutputDataReceived;
+ Process.ErrorDataReceived -= OnErrorDataReceived;
+ Process.Exited -= OnExited;
- _debugService.Log("PROCESS EXIT WITH CODE " + _process.ExitCode);
+ _debugService.Log("PROCESS EXIT WITH CODE " + Process.ExitCode);
- _process.Dispose();
- _process = null;
- RunVisible = true;
+ Process.Dispose();
+ Process = null;
}
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
@@ -142,7 +182,7 @@ public partial class ServerEntryModelView : ViewModelBase
public void StopInstance()
{
- _process?.Close();
+ Process?.CloseMainWindow();
}
static string FindDotnetPath()
diff --git a/Nebula.Launcher/Views/Popup/ExceptionView.axaml b/Nebula.Launcher/Views/Popup/ExceptionView.axaml
new file mode 100644
index 0000000..3ef54ec
--- /dev/null
+++ b/Nebula.Launcher/Views/Popup/ExceptionView.axaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nebula.Launcher/Views/Popup/ExceptionView.axaml.cs b/Nebula.Launcher/Views/Popup/ExceptionView.axaml.cs
new file mode 100644
index 0000000..2b64a54
--- /dev/null
+++ b/Nebula.Launcher/Views/Popup/ExceptionView.axaml.cs
@@ -0,0 +1,20 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Nebula.Launcher.ViewModels;
+
+namespace Nebula.Launcher.Views.Popup;
+
+public partial class ExceptionView : UserControl
+{
+ public ExceptionView()
+ {
+ InitializeComponent();
+ }
+
+ public ExceptionView(ExceptionViewModel viewModel) : this()
+ {
+ DataContext = viewModel;
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Launcher/Views/Popup/LoadingContextView.axaml b/Nebula.Launcher/Views/Popup/LoadingContextView.axaml
new file mode 100644
index 0000000..6aac85c
--- /dev/null
+++ b/Nebula.Launcher/Views/Popup/LoadingContextView.axaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nebula.Launcher/Views/Popup/LoadingContextView.axaml.cs b/Nebula.Launcher/Views/Popup/LoadingContextView.axaml.cs
new file mode 100644
index 0000000..a4a3231
--- /dev/null
+++ b/Nebula.Launcher/Views/Popup/LoadingContextView.axaml.cs
@@ -0,0 +1,17 @@
+using Avalonia.Controls;
+using Nebula.Launcher.ViewModels;
+
+namespace Nebula.Launcher.Views.Popup;
+
+public partial class LoadingContextView : UserControl
+{
+ public LoadingContextView()
+ {
+ InitializeComponent();
+ }
+
+ public LoadingContextView(LoadingContextViewModel viewModel): this()
+ {
+ DataContext = viewModel;
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Runner/App.cs b/Nebula.Runner/App.cs
index 888cf41..908d4d3 100644
--- a/Nebula.Runner/App.cs
+++ b/Nebula.Runner/App.cs
@@ -47,11 +47,80 @@ public sealed class App(DebugService debugService, RunnerService runnerService,
args.Add("--ss14-address");
args.Add(url.ToString());
- await runnerService.Run(args.ToArray(), buildInfo, this, cancelTokenSource.Token);
+ await runnerService.Run(args.ToArray(), buildInfo, this, new ConsoleLoadingHandler(), cancelTokenSource.Token);
}
public void Redial(Uri uri, string text = "")
{
+ }
+}
+
+public sealed class ConsoleLoadingHandler : ILoadingHandler
+{
+ private int _currJobs;
+ private int _resolvedJobs;
+
+ private float _percent = 0f;
+
+ public void SetJobsCount(int count)
+ {
+ _currJobs = count;
+
+ UpdatePercent();
+ Draw();
+ }
+
+ public int GetJobsCount()
+ {
+ return _currJobs;
+ }
+
+ public void SetResolvedJobsCount(int count)
+ {
+ _resolvedJobs = count;
+
+ UpdatePercent();
+ Draw();
+ }
+
+ public int GetResolvedJobsCount()
+ {
+ return _resolvedJobs;
+ }
+
+ private void UpdatePercent()
+ {
+ if(_currJobs == 0)
+ {
+ _percent = 0;
+ return;
+ }
+
+ if(_resolvedJobs > _currJobs) return;
+
+ _percent = _resolvedJobs /(float) _currJobs;
+ }
+
+ private void Draw()
+ {
+ var barCount = 10;
+ var fullCount = (int)(barCount * _percent);
+ var emptyCount = barCount - fullCount;
+
+ Console.Write("\r");
+
+ for (var i = 0; i < fullCount; i++)
+ {
+ Console.Write("#");
+ }
+
+ for (var i = 0; i < emptyCount; i++)
+ {
+ Console.Write(" ");
+ }
+
+ Console.Write($"\t {_resolvedJobs}/{_currJobs}");
+
}
}
\ No newline at end of file
diff --git a/Nebula.Shared/FileApis/FileApi.cs b/Nebula.Shared/FileApis/FileApi.cs
index 08187a0..d876851 100644
--- a/Nebula.Shared/FileApis/FileApi.cs
+++ b/Nebula.Shared/FileApis/FileApi.cs
@@ -2,7 +2,7 @@
namespace Nebula.Shared.FileApis;
-public class FileApi : IReadWriteFileApi
+public sealed class FileApi : IReadWriteFileApi
{
public string RootPath;
@@ -13,10 +13,19 @@ public class FileApi : IReadWriteFileApi
public bool TryOpen(string path, out Stream? stream)
{
- if (File.Exists(Path.Join(RootPath, path)))
+ var fullPath = Path.Join(RootPath, path);
+ if (File.Exists(fullPath))
{
- stream = File.OpenRead(Path.Join(RootPath, path));
- return true;
+ try
+ {
+ stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ return true;
+ }
+ catch
+ {
+ stream = null;
+ return false;
+ }
}
stream = null;
@@ -27,27 +36,43 @@ public class FileApi : IReadWriteFileApi
{
var currPath = Path.Join(RootPath, path);
- var dirInfo = new DirectoryInfo(Path.GetDirectoryName(currPath));
- if (!dirInfo.Exists) dirInfo.Create();
+ try
+ {
+ var dirInfo = new DirectoryInfo(Path.GetDirectoryName(currPath) ?? throw new InvalidOperationException());
+ if (!dirInfo.Exists) dirInfo.Create();
- using var stream = File.OpenWrite(currPath);
- input.CopyTo(stream);
- stream.Flush();
- stream.Close();
- return true;
+ using var stream = new FileStream(currPath, FileMode.Create, FileAccess.Write, FileShare.None);
+ input.CopyTo(stream);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
}
public bool Remove(string path)
{
- if (!Has(path)) return false;
- File.Delete(Path.Join(RootPath, path));
- return true;
+ var fullPath = Path.Join(RootPath, path);
+ try
+ {
+ if (File.Exists(fullPath))
+ {
+ File.Delete(fullPath);
+ return true;
+ }
+ }
+ catch
+ {
+ // Log exception if necessary
+ }
+ return false;
}
public bool Has(string path)
{
- var currPath = Path.Join(RootPath, path);
- return File.Exists(currPath);
+ var fullPath = Path.Join(RootPath, path);
+ return File.Exists(fullPath);
}
public IEnumerable AllFiles => Directory.EnumerateFiles(RootPath, "*.*", SearchOption.AllDirectories);
diff --git a/Nebula.Shared/Models/ILoadingHandler.cs b/Nebula.Shared/Models/ILoadingHandler.cs
new file mode 100644
index 0000000..b5ccae2
--- /dev/null
+++ b/Nebula.Shared/Models/ILoadingHandler.cs
@@ -0,0 +1,47 @@
+namespace Nebula.Shared.Models;
+
+public interface ILoadingHandler
+{
+ public void SetJobsCount(int count);
+ public int GetJobsCount();
+
+ public void SetResolvedJobsCount(int count);
+ public int GetResolvedJobsCount();
+
+ public void AppendJob(int count = 1)
+ {
+ SetJobsCount(GetJobsCount() + count);
+ }
+
+ public void AppendResolvedJob(int count = 1)
+ {
+ SetResolvedJobsCount(GetResolvedJobsCount() + count);
+ }
+
+ public void Clear()
+ {
+ SetResolvedJobsCount(0);
+ SetJobsCount(0);
+ }
+
+ public QueryJob GetQueryJob()
+ {
+ return new QueryJob(this);
+ }
+}
+
+public sealed class QueryJob: IDisposable
+{
+ private readonly ILoadingHandler _handler;
+
+ public QueryJob(ILoadingHandler handler)
+ {
+ _handler = handler;
+ handler.AppendJob();
+ }
+
+ public void Dispose()
+ {
+ _handler.AppendResolvedJob();
+ }
+}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/ContentService.Download.cs b/Nebula.Shared/Services/ContentService.Download.cs
index 5700dfb..54462e8 100644
--- a/Nebula.Shared/Services/ContentService.Download.cs
+++ b/Nebula.Shared/Services/ContentService.Download.cs
@@ -15,7 +15,7 @@ public partial class ContentService
return fileService.ContentFileApi.Has(item.Hash);
}
- public async Task> EnsureItems(ManifestReader manifestReader, Uri downloadUri,
+ public async Task> EnsureItems(ManifestReader manifestReader, Uri downloadUri, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
List allItems = [];
@@ -36,14 +36,14 @@ public partial class ContentService
debugService.Log("Download Count:" + items.Count);
- await Download(downloadUri, items, cancellationToken);
+ await Download(downloadUri, items, loadingHandler, cancellationToken);
fileService.ManifestItems = allItems;
return allItems;
}
- public async Task> EnsureItems(RobustManifestInfo info,
+ public async Task> EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler,
CancellationToken cancellationToken)
{
debugService.Log("Getting manifest: " + info.Hash);
@@ -51,7 +51,7 @@ public partial class ContentService
if (fileService.ManifestFileApi.TryOpen(info.Hash, out var stream))
{
debugService.Log("Loading manifest from: " + info.Hash);
- return await EnsureItems(new ManifestReader(stream), info.DownloadUri, cancellationToken);
+ return await EnsureItems(new ManifestReader(stream), info.DownloadUri, loadingHandler, cancellationToken);
}
debugService.Log("Fetching manifest from: " + info.ManifestUri);
@@ -63,14 +63,16 @@ public partial class ContentService
fileService.ManifestFileApi.Save(info.Hash, streamContent);
streamContent.Seek(0, SeekOrigin.Begin);
using var manifestReader = new ManifestReader(streamContent);
- return await EnsureItems(manifestReader, info.DownloadUri, cancellationToken);
+ return await EnsureItems(manifestReader, info.DownloadUri, loadingHandler, cancellationToken);
}
- public async Task Unpack(RobustManifestInfo info, IWriteFileApi otherApi, CancellationToken cancellationToken)
+ public async Task Unpack(RobustManifestInfo info, IWriteFileApi otherApi, ILoadingHandler loadingHandler, CancellationToken cancellationToken)
{
debugService.Log("Unpack manifest files");
- var items = await EnsureItems(info, cancellationToken);
+ var items = await EnsureItems(info, loadingHandler, cancellationToken);
+ loadingHandler.AppendJob(items.Count);
foreach (var item in items)
+ {
if (fileService.ContentFileApi.TryOpen(item.Hash, out var stream))
{
debugService.Log($"Unpack {item.Hash} to: {item.Path}");
@@ -81,9 +83,11 @@ public partial class ContentService
{
debugService.Error("OH FUCK!! " + item.Path);
}
+ loadingHandler.AppendResolvedJob();
+ }
}
- public async Task Download(Uri contentCdn, List toDownload, CancellationToken cancellationToken)
+ public async Task Download(Uri contentCdn, List toDownload, ILoadingHandler loadingHandler, CancellationToken cancellationToken)
{
if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
{
@@ -91,6 +95,8 @@ public partial class ContentService
return;
}
+ var downloadJobWatch = loadingHandler.GetQueryJob();
+
debugService.Log("Downloading from: " + contentCdn);
var requestBody = new byte[toDownload.Count * 4];
@@ -117,6 +123,8 @@ public partial class ContentService
debugService.Log("Downloading is cancelled!");
return;
}
+
+ downloadJobWatch.Dispose();
response.EnsureSuccessStatusCode();
@@ -155,6 +163,9 @@ public partial class ContentService
var readBuffer = new byte[1024];
var i = 0;
+
+ loadingHandler.AppendJob(toDownload.Count);
+
foreach (var item in toDownload)
{
if (cancellationToken.IsCancellationRequested)
@@ -230,6 +241,7 @@ public partial class ContentService
fileService.ContentFileApi.Save(item.Hash, fileStream);
debugService.Log("file saved:" + item.Path);
+ loadingHandler.AppendResolvedJob();
i += 1;
}
}
diff --git a/Nebula.Shared/Services/EngineService.cs b/Nebula.Shared/Services/EngineService.cs
index c4a3f58..5c3a9b3 100644
--- a/Nebula.Shared/Services/EngineService.cs
+++ b/Nebula.Shared/Services/EngineService.cs
@@ -81,13 +81,16 @@ public sealed class EngineService
try
{
- return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi));
+ var api = _fileService.OpenZip(version, _fileService.EngineFileApi);
+ if (api != null) return _assemblyService.Mount(api);
}
catch (Exception e)
{
_fileService.EngineFileApi.Remove(version);
throw;
}
+
+ return null;
}
public async Task DownloadEngine(string version)
diff --git a/Nebula.Shared/Services/FileService.cs b/Nebula.Shared/Services/FileService.cs
index 922f026..08eb196 100644
--- a/Nebula.Shared/Services/FileService.cs
+++ b/Nebula.Shared/Services/FileService.cs
@@ -51,15 +51,24 @@ public class FileService
return new FileApi(Path.Join(RootPath, path));
}
- public ZipFileApi OpenZip(string path, IFileApi fileApi)
+ public ZipFileApi? OpenZip(string path, IFileApi fileApi)
{
- if (!fileApi.TryOpen(path, out var zipStream))
- return null;
+ Stream? zipStream = null;
+ try
+ {
+ if (!fileApi.TryOpen(path, out zipStream))
+ return null;
- var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read);
+ var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read);
- var prefix = "";
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) prefix = "Space Station 14.app/Contents/Resources/";
- return new ZipFileApi(zipArchive, prefix);
+ var prefix = "";
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) prefix = "Space Station 14.app/Contents/Resources/";
+ return new ZipFileApi(zipArchive, prefix);
+ }
+ catch (Exception e)
+ {
+ zipStream?.Dispose();
+ throw;
+ }
}
}
\ No newline at end of file
diff --git a/Nebula.Shared/Services/PopupMessageService.cs b/Nebula.Shared/Services/PopupMessageService.cs
index ab4c8d1..0fcd9c0 100644
--- a/Nebula.Shared/Services/PopupMessageService.cs
+++ b/Nebula.Shared/Services/PopupMessageService.cs
@@ -3,13 +3,14 @@ namespace Nebula.Shared.Services;
[ServiceRegister]
public class PopupMessageService
{
- public Action