- tweak: Change content hold think
This commit is contained in:
@@ -197,12 +197,12 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
|
|||||||
|
|
||||||
var rurl = serverUrl.ToRobustUrl();
|
var rurl = serverUrl.ToRobustUrl();
|
||||||
var info = await ContentService.GetBuildInfo(rurl, CancellationService.Token);
|
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);
|
CancellationService.Token);
|
||||||
|
|
||||||
var rootEntry = new ContentEntry(this, "", "", serverUrl);
|
var rootEntry = new ContentEntry(this, "", "", serverUrl);
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in hashApi.Manifest.Values)
|
||||||
{
|
{
|
||||||
var path = new ContentPath(item.Path);
|
var path = new ContentPath(item.Path);
|
||||||
rootEntry.CreateItem(path, item);
|
rootEntry.CreateItem(path, item);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Nebula.Launcher.ViewModels.Popup;
|
|||||||
public sealed partial class ExceptionListViewModel : PopupViewModelBase
|
public sealed partial class ExceptionListViewModel : PopupViewModelBase
|
||||||
{
|
{
|
||||||
[GenerateProperty] public override PopupMessageService PopupMessageService { get; }
|
[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 override bool IsClosable => true;
|
||||||
|
|
||||||
public ObservableCollection<Exception> Errors { get; } = new();
|
public ObservableCollection<Exception> Errors { get; } = new();
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ public partial class ServerEntryModelView : ViewModelBase
|
|||||||
if (File.Exists(dotnetPath)) return dotnetPath;
|
if (File.Exists(dotnetPath)) return dotnetPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Dotnet not found!");
|
return "dotnet";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@
|
|||||||
https://cinka.ru/nebula-launcher/
|
https://cinka.ru/nebula-launcher/
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</Button>
|
</Button>
|
||||||
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">v0.02-a</TextBlock>
|
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center">v0.05-a</TextBlock>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Label>
|
</Label>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Robust.LoaderApi;
|
|||||||
namespace Nebula.Runner;
|
namespace Nebula.Runner;
|
||||||
|
|
||||||
[ServiceRegister]
|
[ServiceRegister]
|
||||||
public sealed class App(DebugService debugService, RunnerService runnerService, ContentService contentService)
|
public sealed class App(RunnerService runnerService, ContentService contentService)
|
||||||
: IRedialApi
|
: IRedialApi
|
||||||
{
|
{
|
||||||
public void Redial(Uri uri, string text = "")
|
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)
|
public async Task Run(string[] args1)
|
||||||
{
|
{
|
||||||
debugService.Log("HELLO!!! ");
|
|
||||||
|
|
||||||
var login = Environment.GetEnvironmentVariable("AUTH_LOGIN") ?? "Alexandra";
|
var login = Environment.GetEnvironmentVariable("AUTH_LOGIN") ?? "Alexandra";
|
||||||
var urlraw = Environment.GetEnvironmentVariable("GAME_URL") ?? "ss14://localhost";
|
var urlraw = Environment.GetEnvironmentVariable("GAME_URL") ?? "ss14://localhost";
|
||||||
|
|
||||||
|
|||||||
@@ -75,5 +75,5 @@ public sealed class FileApi : IReadWriteFileApi
|
|||||||
return File.Exists(fullPath);
|
return File.Exists(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> AllFiles => Directory.EnumerateFiles(RootPath, "*.*", SearchOption.AllDirectories);
|
public IEnumerable<string> AllFiles => Directory.EnumerateFiles(RootPath, "*.*", SearchOption.AllDirectories).Select(p=>p.Replace(RootPath,"").Substring(1));
|
||||||
}
|
}
|
||||||
@@ -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 Nebula.Shared.Models;
|
||||||
using Robust.LoaderApi;
|
using Robust.LoaderApi;
|
||||||
|
|
||||||
@@ -6,26 +9,59 @@ namespace Nebula.Shared.FileApis;
|
|||||||
|
|
||||||
public class HashApi : IFileApi
|
public class HashApi : IFileApi
|
||||||
{
|
{
|
||||||
private readonly IFileApi _fileApi;
|
private readonly IReadWriteFileApi _fileApi;
|
||||||
public Dictionary<string, RobustManifestItem> Manifest;
|
private readonly Dictionary<string, RobustManifestItem> _manifest;
|
||||||
|
public IReadOnlyDictionary<string, RobustManifestItem> Manifest => _manifest;
|
||||||
|
|
||||||
public HashApi(List<RobustManifestItem> manifest, IFileApi fileApi)
|
public HashApi(List<RobustManifestItem> manifest, IReadWriteFileApi fileApi)
|
||||||
{
|
{
|
||||||
_fileApi = fileApi;
|
_fileApi = fileApi;
|
||||||
Manifest = new Dictionary<string, RobustManifestItem>();
|
_manifest = new Dictionary<string, RobustManifestItem>();
|
||||||
foreach (var item in manifest) Manifest.TryAdd(item.Path, item);
|
foreach (var item in manifest) _manifest.TryAdd(item.Path, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryOpen(string path,[NotNullWhen(true)] out Stream? stream)
|
public bool TryOpen(string path,[NotNullWhen(true)] out Stream? stream)
|
||||||
{
|
{
|
||||||
if (path[0] == '/') path = path.Substring(1);
|
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;
|
return true;
|
||||||
|
|
||||||
stream = null;
|
stream = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> 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<string> AllFiles => _manifest.Keys;
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ public class LoginInfo
|
|||||||
{
|
{
|
||||||
public Guid UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public string Username { get; set; } = default!;
|
public string Username { get; set; } = default!;
|
||||||
public LoginToken Token { get; set; }
|
public LoginToken Token { get; set; } = default!;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Nebula.Shared.Models;
|
|||||||
|
|
||||||
public class RobustBuildInfo
|
public class RobustBuildInfo
|
||||||
{
|
{
|
||||||
public ServerInfo BuildInfo;
|
public ServerInfo BuildInfo = default!;
|
||||||
public RobustManifestInfo RobustManifestInfo;
|
public RobustManifestInfo RobustManifestInfo;
|
||||||
public RobustUrl Url;
|
public RobustUrl Url = default!;
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Nebula.Shared.FileApis;
|
||||||
using Nebula.Shared.FileApis.Interfaces;
|
using Nebula.Shared.FileApis.Interfaces;
|
||||||
using Nebula.Shared.Models;
|
using Nebula.Shared.Models;
|
||||||
using Nebula.Shared.Utils;
|
using Nebula.Shared.Utils;
|
||||||
@@ -15,7 +16,7 @@ public partial class ContentService
|
|||||||
return fileService.ContentFileApi.Has(item.Hash);
|
return fileService.ContentFileApi.Has(item.Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<RobustManifestItem>> EnsureItems(ManifestReader manifestReader, Uri downloadUri,
|
public async Task<HashApi> EnsureItems(ManifestReader manifestReader, Uri downloadUri,
|
||||||
ILoadingHandler loadingHandler,
|
ILoadingHandler loadingHandler,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -25,26 +26,22 @@ public partial class ContentService
|
|||||||
while (manifestReader.TryReadItem(out var item))
|
while (manifestReader.TryReadItem(out var item))
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
throw new TaskCanceledException();
|
||||||
debugService.Log("ensuring is cancelled!");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckManifestExist(item.Value))
|
|
||||||
items.Add(item.Value);
|
|
||||||
allItems.Add(item.Value);
|
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);
|
debugService.Log("Download Count:" + items.Count);
|
||||||
|
await Download(downloadUri, items, hashApi, loadingHandler, cancellationToken);
|
||||||
|
|
||||||
await Download(downloadUri, items, loadingHandler, cancellationToken);
|
return hashApi;
|
||||||
|
|
||||||
fileService.ManifestItems = allItems;
|
|
||||||
|
|
||||||
return allItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<RobustManifestItem>> EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler,
|
public async Task<HashApi> EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
debugService.Log("Getting manifest: " + info.Hash);
|
debugService.Log("Getting manifest: " + info.Hash);
|
||||||
@@ -71,11 +68,12 @@ public partial class ContentService
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
debugService.Log("Unpack manifest files");
|
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);
|
loadingHandler.AppendJob(items.Count);
|
||||||
foreach (var item in items)
|
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}");
|
debugService.Log($"Unpack {item.Hash} to: {item.Path}");
|
||||||
otherApi.Save(item.Path, stream);
|
otherApi.Save(item.Path, stream);
|
||||||
@@ -90,7 +88,7 @@ public partial class ContentService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Download(Uri contentCdn, List<RobustManifestItem> toDownload, ILoadingHandler loadingHandler,
|
public async Task Download(Uri contentCdn, List<RobustManifestItem> toDownload, HashApi hashApi, ILoadingHandler loadingHandler,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
|
if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested)
|
||||||
@@ -189,10 +187,6 @@ public partial class ContentService
|
|||||||
EnsureBuffer(ref readBuffer, length);
|
EnsureBuffer(ref readBuffer, length);
|
||||||
var data = readBuffer.AsMemory(0, length);
|
var data = readBuffer.AsMemory(0, length);
|
||||||
|
|
||||||
// Data to write to database.
|
|
||||||
var compression = ContentCompressionScheme.None;
|
|
||||||
var writeData = data;
|
|
||||||
|
|
||||||
if (preCompressed)
|
if (preCompressed)
|
||||||
{
|
{
|
||||||
// Compressed length from extended header.
|
// Compressed length from extended header.
|
||||||
@@ -210,10 +204,6 @@ public partial class ContentService
|
|||||||
|
|
||||||
if (decompressedLength != data.Length)
|
if (decompressedLength != data.Length)
|
||||||
throw new Exception($"Compressed blob {i} had incorrect decompressed size!");
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -225,25 +215,8 @@ public partial class ContentService
|
|||||||
await stream.ReadExactAsync(data, null);
|
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());
|
using var fileStream = new MemoryStream(data.ToArray());
|
||||||
fileService.ContentFileApi.Save(item.Hash, fileStream);
|
hashApi.Save(item, fileStream);
|
||||||
|
|
||||||
debugService.Log("file saved:" + item.Path);
|
debugService.Log("file saved:" + item.Path);
|
||||||
loadingHandler.AppendResolvedJob();
|
loadingHandler.AppendResolvedJob();
|
||||||
|
|||||||
@@ -14,14 +14,11 @@ public class FileService
|
|||||||
Environment.SpecialFolder.ApplicationData), "Datum");
|
Environment.SpecialFolder.ApplicationData), "Datum");
|
||||||
|
|
||||||
private readonly DebugService _debugService;
|
private readonly DebugService _debugService;
|
||||||
public readonly IReadWriteFileApi ConfigurationApi;
|
|
||||||
|
|
||||||
|
public readonly IReadWriteFileApi ConfigurationApi;
|
||||||
public readonly IReadWriteFileApi ContentFileApi;
|
public readonly IReadWriteFileApi ContentFileApi;
|
||||||
public readonly IReadWriteFileApi EngineFileApi;
|
public readonly IReadWriteFileApi EngineFileApi;
|
||||||
public readonly IReadWriteFileApi ManifestFileApi;
|
public readonly IReadWriteFileApi ManifestFileApi;
|
||||||
|
|
||||||
private HashApi? _hashApi;
|
|
||||||
|
|
||||||
public FileService(DebugService debugService)
|
public FileService(DebugService debugService)
|
||||||
{
|
{
|
||||||
_debugService = debugService;
|
_debugService = debugService;
|
||||||
@@ -29,21 +26,15 @@ public class FileService
|
|||||||
EngineFileApi = CreateFileApi("engine");
|
EngineFileApi = CreateFileApi("engine");
|
||||||
ManifestFileApi = CreateFileApi("manifest");
|
ManifestFileApi = CreateFileApi("manifest");
|
||||||
ConfigurationApi = CreateFileApi("config");
|
ConfigurationApi = CreateFileApi("config");
|
||||||
}
|
|
||||||
|
|
||||||
public List<RobustManifestItem> ManifestItems
|
// Some migrating think
|
||||||
{
|
foreach(var file in ContentFileApi.AllFiles){
|
||||||
set => _hashApi = new HashApi(value, ContentFileApi);
|
if(file.Contains("\\") || !ContentFileApi.TryOpen(file, out var stream)) continue;
|
||||||
}
|
|
||||||
|
ContentFileApi.Save(HashApi.GetManifestPath(file), stream);
|
||||||
public HashApi HashApi
|
stream.Dispose();
|
||||||
{
|
ContentFileApi.Remove(file);
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_hashApi is null) throw new Exception("Hash API is not initialized!");
|
|
||||||
return _hashApi;
|
|
||||||
}
|
}
|
||||||
set => _hashApi = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadWriteFileApi CreateFileApi(string path)
|
public IReadWriteFileApi CreateFileApi(string path)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public sealed class RunnerService(
|
|||||||
ContentService contentService,
|
ContentService contentService,
|
||||||
DebugService debugService,
|
DebugService debugService,
|
||||||
ConfigurationService varService,
|
ConfigurationService varService,
|
||||||
FileService fileService,
|
|
||||||
EngineService engineService,
|
EngineService engineService,
|
||||||
AssemblyService assemblyService)
|
AssemblyService assemblyService)
|
||||||
{
|
{
|
||||||
@@ -20,7 +19,7 @@ public sealed class RunnerService(
|
|||||||
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
|
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
|
||||||
|
|
||||||
if (engine is null)
|
if (engine is null)
|
||||||
throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion);
|
throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion);
|
||||||
|
|
||||||
await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken);
|
await contentService.EnsureItems(buildInfo.RobustManifestInfo, loadingHandler, cancellationToken);
|
||||||
await engineService.EnsureEngineModules("Robust.Client.WebView", buildInfo.BuildInfo.Build.EngineVersion);
|
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);
|
var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion);
|
||||||
|
|
||||||
if (engine is null)
|
if (engine is null)
|
||||||
throw new Exception("Engine version is not usable: " + buildInfo.BuildInfo.Build.EngineVersion);
|
throw new Exception("Engine version 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<ApiMount>
|
var extraMounts = new List<ApiMount>
|
||||||
{
|
{
|
||||||
new(fileService.HashApi, "/")
|
new(hashApi, "/")
|
||||||
};
|
};
|
||||||
|
|
||||||
var module =
|
var module =
|
||||||
|
|||||||
Reference in New Issue
Block a user