- tweak: loading popup thinks

* - tweak: change loading handle logic

* - tweak: beautify loading thinks

* - fix: speed thinks while downloading
This commit is contained in:
Cinkafox
2025-12-06 23:25:25 +03:00
committed by GitHub
parent d7f775e80c
commit 0c6bbaadac
39 changed files with 710 additions and 491 deletions

View File

@@ -1,138 +0,0 @@
using System.Diagnostics;
namespace Nebula.Shared.Utils;
public sealed class BandwidthStream : Stream
{
private const int NumSeconds = 8;
private const int BucketDivisor = 2;
private const int BucketsPerSecond = 2 << BucketDivisor;
// TotalBuckets MUST be power of two!
private const int TotalBuckets = NumSeconds * BucketsPerSecond;
private readonly Stream _baseStream;
private readonly long[] _buckets;
private readonly Stopwatch _stopwatch;
private long _bucketIndex;
public BandwidthStream(Stream baseStream)
{
_stopwatch = Stopwatch.StartNew();
_baseStream = baseStream;
_buckets = new long[TotalBuckets];
}
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length;
public override long Position
{
get => _baseStream.Position;
set => _baseStream.Position = value;
}
private void TrackBandwidth(long value)
{
const int bucketMask = TotalBuckets - 1;
var bucketIdx = CurBucketIdx();
// Increment to bucket idx, clearing along the way.
if (bucketIdx != _bucketIndex)
{
var diff = bucketIdx - _bucketIndex;
if (diff > TotalBuckets)
for (var i = _bucketIndex; i < bucketIdx; i++)
_buckets[i & bucketMask] = 0;
else
// We managed to skip so much time the whole buffer is empty.
Array.Clear(_buckets);
_bucketIndex = bucketIdx;
}
// Write value.
_buckets[bucketIdx & bucketMask] += value;
}
private long CurBucketIdx()
{
var elapsed = _stopwatch.Elapsed.TotalSeconds;
return (long)(elapsed / BucketsPerSecond);
}
public long CalcCurrentAvg()
{
var sum = 0L;
for (var i = 0; i < TotalBuckets; i++) sum += _buckets[i];
return sum >> BucketDivisor;
}
public override void Flush()
{
_baseStream.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return _baseStream.FlushAsync(cancellationToken);
}
protected override void Dispose(bool disposing)
{
if (disposing)
_baseStream.Dispose();
}
public override ValueTask DisposeAsync()
{
return _baseStream.DisposeAsync();
}
public override int Read(byte[] buffer, int offset, int count)
{
var read = _baseStream.Read(buffer, offset, count);
TrackBandwidth(read);
return read;
}
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
var read = await base.ReadAsync(buffer, cancellationToken);
TrackBandwidth(read);
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
return _baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_baseStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_baseStream.Write(buffer, offset, count);
TrackBandwidth(count);
}
public override async ValueTask WriteAsync(
ReadOnlyMemory<byte> buffer,
CancellationToken cancellationToken = default)
{
await _baseStream.WriteAsync(buffer, cancellationToken);
TrackBandwidth(buffer.Length);
}
}

View File

@@ -0,0 +1,43 @@
namespace Nebula.Shared.Utils;
public static class ContentManifestParser
{
public static List<string> ExtractModules(Stream manifestStream)
{
using var reader = new StreamReader(manifestStream);
return ExtractModules(reader.ReadToEnd());
}
public static List<string> ExtractModules(string manifestContent)
{
var modules = new List<string>();
var lines = manifestContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
bool inModulesSection = false;
foreach (var rawLine in lines)
{
var line = rawLine.Trim();
if (line.StartsWith("modules:"))
{
inModulesSection = true;
continue;
}
if (inModulesSection)
{
if (line.StartsWith("- "))
{
modules.Add(line.Substring(2).Trim());
}
else if (!line.StartsWith(" "))
{
break;
}
}
}
return modules;
}
}

View File

@@ -1,21 +1,37 @@
using System.Buffers;
using Nebula.Shared.Models;
namespace Nebula.Shared.Utils;
public static class StreamHelper
{
public static async ValueTask<byte[]> ReadExactAsync(this Stream stream, int amount, CancellationToken? cancel)
public static void CopyTo(this Stream input, Stream output, ILoadingHandler loadingHandler)
{
const int bufferSize = 81920;
var buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
loadingHandler.AppendResolvedJob(bytesRead);
}
}
public static async ValueTask<byte[]> ReadExactAsync(this Stream stream, int amount, CancellationToken cancel = default)
{
var data = new byte[amount];
await ReadExactAsync(stream, data, cancel);
return data;
}
public static async ValueTask ReadExactAsync(this Stream stream, Memory<byte> into, CancellationToken? cancel)
public static async ValueTask ReadExactAsync(this Stream stream, Memory<byte> into, CancellationToken cancel = default, ILoadingHandler? loadingHandler = null)
{
while (into.Length > 0)
{
var read = await stream.ReadAsync(into);
var read = await stream.ReadAsync(into, cancel);
loadingHandler?.AppendResolvedJob(read);
// Check EOF.
if (read == 0)
@@ -24,31 +40,4 @@ public static class StreamHelper
into = into[read..];
}
}
public static async Task CopyAmountToAsync(
this Stream stream,
Stream to,
int amount,
int bufferSize,
CancellationToken cancel)
{
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
while (amount > 0)
{
Memory<byte> readInto = buffer;
if (amount < readInto.Length)
readInto = readInto[..amount];
var read = await stream.ReadAsync(readInto, cancel);
if (read == 0)
throw new EndOfStreamException();
amount -= read;
readInto = readInto[..read];
await to.WriteAsync(readInto, cancel);
}
}
}