diff --git a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs index c4a2439..c5eb82e 100644 --- a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs +++ b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs @@ -197,12 +197,12 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel var rurl = serverUrl.ToRobustUrl(); var info = await ContentService.GetBuildInfo(rurl, CancellationService.Token); - var items = await ContentService.EnsureItems(info.RobustManifestInfo, loading, + var hashApi = await ContentService.EnsureItems(info.RobustManifestInfo, loading, CancellationService.Token); var rootEntry = new ContentEntry(this, "", "", serverUrl); - foreach (var item in items) + foreach (var item in hashApi.Manifest.Values) { var path = new ContentPath(item.Path); rootEntry.CreateItem(path, item); diff --git a/Nebula.Launcher/ViewModels/Popup/ExceptionListViewModel.cs b/Nebula.Launcher/ViewModels/Popup/ExceptionListViewModel.cs index 6fdf032..207b84d 100644 --- a/Nebula.Launcher/ViewModels/Popup/ExceptionListViewModel.cs +++ b/Nebula.Launcher/ViewModels/Popup/ExceptionListViewModel.cs @@ -10,7 +10,7 @@ namespace Nebula.Launcher.ViewModels.Popup; public sealed partial class ExceptionListViewModel : PopupViewModelBase { [GenerateProperty] public override PopupMessageService PopupMessageService { get; } - public override string Title => "Oopsie! Some shit is happened now!"; + public override string Title => "Exception was thrown"; public override bool IsClosable => true; public ObservableCollection Errors { get; } = new(); diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs index 208e566..d5c319a 100644 --- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs +++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs @@ -305,7 +305,7 @@ public partial class ServerEntryModelView : ViewModelBase if (File.Exists(dotnetPath)) return dotnetPath; } - throw new Exception("Dotnet not found!"); + return "dotnet"; } } diff --git a/Nebula.Launcher/Views/MainView.axaml b/Nebula.Launcher/Views/MainView.axaml index b6205c2..a1e9813 100644 --- a/Nebula.Launcher/Views/MainView.axaml +++ b/Nebula.Launcher/Views/MainView.axaml @@ -120,7 +120,7 @@ https://cinka.ru/nebula-launcher/ - v0.02-a + v0.05-a diff --git a/Nebula.Runner/App.cs b/Nebula.Runner/App.cs index d3e7590..45bfaba 100644 --- a/Nebula.Runner/App.cs +++ b/Nebula.Runner/App.cs @@ -7,7 +7,7 @@ using Robust.LoaderApi; namespace Nebula.Runner; [ServiceRegister] -public sealed class App(DebugService debugService, RunnerService runnerService, ContentService contentService) +public sealed class App(RunnerService runnerService, ContentService contentService) : IRedialApi { public void Redial(Uri uri, string text = "") @@ -16,8 +16,6 @@ public sealed class App(DebugService debugService, RunnerService runnerService, public async Task Run(string[] args1) { - debugService.Log("HELLO!!! "); - var login = Environment.GetEnvironmentVariable("AUTH_LOGIN") ?? "Alexandra"; var urlraw = Environment.GetEnvironmentVariable("GAME_URL") ?? "ss14://localhost"; diff --git a/Nebula.Shared/FileApis/FileApi.cs b/Nebula.Shared/FileApis/FileApi.cs index ee11c84..513cb69 100644 --- a/Nebula.Shared/FileApis/FileApi.cs +++ b/Nebula.Shared/FileApis/FileApi.cs @@ -75,5 +75,5 @@ public sealed class FileApi : IReadWriteFileApi return File.Exists(fullPath); } - public IEnumerable AllFiles => Directory.EnumerateFiles(RootPath, "*.*", SearchOption.AllDirectories); + public IEnumerable AllFiles => Directory.EnumerateFiles(RootPath, "*.*", SearchOption.AllDirectories).Select(p=>p.Replace(RootPath,"").Substring(1)); } \ No newline at end of file diff --git a/Nebula.Shared/FileApis/HashApi.cs b/Nebula.Shared/FileApis/HashApi.cs index 929e313..60f1c21 100644 --- a/Nebula.Shared/FileApis/HashApi.cs +++ b/Nebula.Shared/FileApis/HashApi.cs @@ -1,4 +1,7 @@ -using System.Diagnostics.CodeAnalysis; +using System.Collections.Frozen; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using Nebula.Shared.FileApis.Interfaces; using Nebula.Shared.Models; using Robust.LoaderApi; @@ -6,26 +9,59 @@ namespace Nebula.Shared.FileApis; public class HashApi : IFileApi { - private readonly IFileApi _fileApi; - public Dictionary Manifest; + private readonly IReadWriteFileApi _fileApi; + private readonly Dictionary _manifest; + public IReadOnlyDictionary Manifest => _manifest; - public HashApi(List manifest, IFileApi fileApi) + public HashApi(List manifest, IReadWriteFileApi fileApi) { _fileApi = fileApi; - Manifest = new Dictionary(); - foreach (var item in manifest) Manifest.TryAdd(item.Path, item); + _manifest = new Dictionary(); + foreach (var item in manifest) _manifest.TryAdd(item.Path, item); } public bool TryOpen(string path,[NotNullWhen(true)] out Stream? stream) { if (path[0] == '/') path = path.Substring(1); - if (Manifest.TryGetValue(path, out var a) && _fileApi.TryOpen(a.Hash, out stream)) + if (_manifest.TryGetValue(path, out var a) && _fileApi.TryOpen(GetManifestPath(a), out stream)) return true; stream = null; return false; } - public IEnumerable AllFiles => Manifest.Keys; + public bool TryOpen(RobustManifestItem item ,[NotNullWhen(true)] out Stream? stream){ + if(_fileApi.TryOpen(GetManifestPath(item), out stream)) + return true; + + stream = null; + return false; + } + + public bool TryOpenByHash(string hash ,[NotNullWhen(true)] out Stream? stream){ + if(_fileApi.TryOpen(GetManifestPath(hash), out stream)) + return true; + + stream = null; + return false; + } + + public bool Save(RobustManifestItem item, Stream stream){ + return _fileApi.Save(GetManifestPath(item), stream); + } + + public bool Has(RobustManifestItem item){ + return _fileApi.Has(GetManifestPath(item)); + } + + private string GetManifestPath(RobustManifestItem item){ + return GetManifestPath(item.Hash); + } + + public static string GetManifestPath(string hash){ + return hash[0].ToString() + hash[1].ToString() + '/' + hash; + } + + public IEnumerable AllFiles => _manifest.Keys; } \ No newline at end of file diff --git a/Nebula.Shared/Models/Auth/LoginInfo.cs b/Nebula.Shared/Models/Auth/LoginInfo.cs index 800f013..2d83fb0 100644 --- a/Nebula.Shared/Models/Auth/LoginInfo.cs +++ b/Nebula.Shared/Models/Auth/LoginInfo.cs @@ -4,7 +4,7 @@ public class LoginInfo { public Guid UserId { get; set; } public string Username { get; set; } = default!; - public LoginToken Token { get; set; } + public LoginToken Token { get; set; } = default!; public override string ToString() { diff --git a/Nebula.Shared/Models/RobustBuildInfo.cs b/Nebula.Shared/Models/RobustBuildInfo.cs index 6e66a4f..90549ab 100644 --- a/Nebula.Shared/Models/RobustBuildInfo.cs +++ b/Nebula.Shared/Models/RobustBuildInfo.cs @@ -2,7 +2,7 @@ namespace Nebula.Shared.Models; public class RobustBuildInfo { - public ServerInfo BuildInfo; + public ServerInfo BuildInfo = default!; public RobustManifestInfo RobustManifestInfo; - public RobustUrl Url; + public RobustUrl Url = default!; } \ No newline at end of file diff --git a/Nebula.Shared/Services/ContentService.Download.cs b/Nebula.Shared/Services/ContentService.Download.cs index f4009a4..1fae235 100644 --- a/Nebula.Shared/Services/ContentService.Download.cs +++ b/Nebula.Shared/Services/ContentService.Download.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Net.Http.Headers; using System.Numerics; +using Nebula.Shared.FileApis; using Nebula.Shared.FileApis.Interfaces; using Nebula.Shared.Models; using Nebula.Shared.Utils; @@ -15,7 +16,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) { @@ -25,26 +26,22 @@ public partial class ContentService while (manifestReader.TryReadItem(out var item)) { if (cancellationToken.IsCancellationRequested) - { - debugService.Log("ensuring is cancelled!"); - return []; - } - - if (!CheckManifestExist(item.Value)) - items.Add(item.Value); + throw new TaskCanceledException(); + allItems.Add(item.Value); } + var hashApi = new HashApi(allItems, fileService.ContentFileApi); + + items = allItems.Where(a=> !hashApi.Has(a)).ToList(); + debugService.Log("Download Count:" + items.Count); + await Download(downloadUri, items, hashApi, loadingHandler, cancellationToken); - await Download(downloadUri, items, loadingHandler, cancellationToken); - - fileService.ManifestItems = allItems; - - return allItems; + return hashApi; } - public async Task> EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler, + public async Task EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler, CancellationToken cancellationToken) { debugService.Log("Getting manifest: " + info.Hash); @@ -71,11 +68,12 @@ public partial class ContentService CancellationToken cancellationToken) { debugService.Log("Unpack manifest files"); - var items = await EnsureItems(info, loadingHandler, cancellationToken); + var hashApi = await EnsureItems(info, loadingHandler, cancellationToken); + var items = hashApi.Manifest.Values.ToList(); loadingHandler.AppendJob(items.Count); foreach (var item in items) { - if (fileService.ContentFileApi.TryOpen(item.Hash, out var stream)) + if (hashApi.TryOpen(item, out var stream)) { debugService.Log($"Unpack {item.Hash} to: {item.Path}"); otherApi.Save(item.Path, stream); @@ -90,7 +88,7 @@ public partial class ContentService } } - public async Task Download(Uri contentCdn, List toDownload, ILoadingHandler loadingHandler, + public async Task Download(Uri contentCdn, List toDownload, HashApi hashApi, ILoadingHandler loadingHandler, CancellationToken cancellationToken) { if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested) @@ -189,10 +187,6 @@ public partial class ContentService EnsureBuffer(ref readBuffer, length); var data = readBuffer.AsMemory(0, length); - // Data to write to database. - var compression = ContentCompressionScheme.None; - var writeData = data; - if (preCompressed) { // Compressed length from extended header. @@ -210,10 +204,6 @@ public partial class ContentService if (decompressedLength != data.Length) throw new Exception($"Compressed blob {i} had incorrect decompressed size!"); - - // Set variables so that the database write down below uses them. - compression = ContentCompressionScheme.ZStd; - writeData = compressedData; } else { @@ -225,25 +215,8 @@ public partial class ContentService await stream.ReadExactAsync(data, null); } - if (!preCompressed) - { - // File wasn't pre-compressed. We should try to manually compress it to save space in DB. - - - EnsureBuffer(ref compressBuffer, ZStd.CompressBound(data.Length)); - var compressLength = compressContext!.Compress(compressBuffer, data.Span); - - // Don't bother saving compressed data if it didn't save enough space. - if (compressLength + 10 < length) - { - // Set variables so that the database write down below uses them. - compression = ContentCompressionScheme.ZStd; - writeData = compressBuffer.AsMemory(0, compressLength); - } - } - using var fileStream = new MemoryStream(data.ToArray()); - fileService.ContentFileApi.Save(item.Hash, fileStream); + hashApi.Save(item, fileStream); debugService.Log("file saved:" + item.Path); loadingHandler.AppendResolvedJob(); diff --git a/Nebula.Shared/Services/FileService.cs b/Nebula.Shared/Services/FileService.cs index 3a5a34d..0c9a843 100644 --- a/Nebula.Shared/Services/FileService.cs +++ b/Nebula.Shared/Services/FileService.cs @@ -14,14 +14,11 @@ public class FileService Environment.SpecialFolder.ApplicationData), "Datum"); private readonly DebugService _debugService; - public readonly IReadWriteFileApi ConfigurationApi; + public readonly IReadWriteFileApi ConfigurationApi; public readonly IReadWriteFileApi ContentFileApi; public readonly IReadWriteFileApi EngineFileApi; public readonly IReadWriteFileApi ManifestFileApi; - - private HashApi? _hashApi; - public FileService(DebugService debugService) { _debugService = debugService; @@ -29,21 +26,15 @@ public class FileService EngineFileApi = CreateFileApi("engine"); ManifestFileApi = CreateFileApi("manifest"); ConfigurationApi = CreateFileApi("config"); - } - public List ManifestItems - { - set => _hashApi = new HashApi(value, ContentFileApi); - } - - public HashApi HashApi - { - get - { - if (_hashApi is null) throw new Exception("Hash API is not initialized!"); - return _hashApi; + // Some migrating think + foreach(var file in ContentFileApi.AllFiles){ + if(file.Contains("\\") || !ContentFileApi.TryOpen(file, out var stream)) continue; + + ContentFileApi.Save(HashApi.GetManifestPath(file), stream); + stream.Dispose(); + ContentFileApi.Remove(file); } - set => _hashApi = value; } public IReadWriteFileApi CreateFileApi(string path) diff --git a/Nebula.Shared/Services/RunnerService.cs b/Nebula.Shared/Services/RunnerService.cs index 53713bc..805fee4 100644 --- a/Nebula.Shared/Services/RunnerService.cs +++ b/Nebula.Shared/Services/RunnerService.cs @@ -8,7 +8,6 @@ public sealed class RunnerService( ContentService contentService, DebugService debugService, ConfigurationService varService, - FileService fileService, EngineService engineService, AssemblyService assemblyService) { @@ -20,7 +19,7 @@ public sealed class RunnerService( var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); if (engine is null) - throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion); + throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion); await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken); await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion); @@ -35,13 +34,13 @@ public sealed class RunnerService( var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); if (engine is null) - throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion); + throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion); - await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken); + var hashApi = await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken); var extraMounts = new List { - new(fileService.HashApi, "/") + new(hashApi, "/") }; var module =