Skip to content

Commit

Permalink
Switched to absolute manifest buffer indexes in Acq and Aux entries
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuclearistt committed Jan 25, 2024
1 parent bed4106 commit d58a0bb
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 102 deletions.
2 changes: 1 addition & 1 deletion TEKSteamClient.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<BaseVersion>1.2.0</BaseVersion>
<BaseVersion>1.3.0</BaseVersion>
<Version>$(BaseVersion)</Version>
<Version Condition="'$(GITHUB_RUN_NUMBER)' != ''">$(BaseVersion)-alpha.$(GITHUB_RUN_NUMBER)</Version>
<Authors>Nuclearist</Authors>
Expand Down
133 changes: 73 additions & 60 deletions src/AppManager.cs

Large diffs are not rendered by default.

48 changes: 23 additions & 25 deletions src/CDNClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ internal void DownloadContent(ItemState state, DepotManifest manifest, DepotDelt
string? chunkBufferFilePath = null;
LimitedUseFileHandle? chunkBufferFileHandle = null;
string baseRequestUrl = $"depot/{state.Id.DepotId}/chunk/";
void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acquisitionDir, string path, int recursionLevel)
void downloadDir(in DirectoryEntry.AcquisitionEntry dir, string path, int recursionLevel)
{
int index;
if (state.ProgressIndexStack.Count > recursionLevel)
Expand All @@ -162,10 +162,10 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
state.ProgressIndexStack.Add(0);
index = 0;
}
for (; index < acquisitionDir.Files.Count; index++)
for (; index < dir.Files.Count; index++)
{
var acquisitonFile = acquisitionDir.Files[index];
var file = dir.Files[acquisitonFile.Index];
var acquisitonFile = dir.Files[index];
var file = manifest.FileBuffer[acquisitonFile.Index];
if (file.Size is 0)
continue;
if (linkedCts.IsCancellationRequested)
Expand All @@ -182,10 +182,10 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
state.ProgressIndexStack.Add(0);
chunkIndex = 0;
}
var chunks = file.Chunks;
if (acquisitonFile.Chunks.Count is 0)
{
string filePath = Path.Combine(path, file.Name);
var chunks = file.Chunks;
LimitedUseFileHandle? handle;
if (numResumedContexts > 0)
{
Expand Down Expand Up @@ -275,7 +275,7 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
return;
}
var acquisitionChunk = acquisitionChunks[chunkIndex];
var chunk = chunks[acquisitionChunk.Index];
var chunk = manifest.ChunkBuffer[acquisitionChunk.Index];
int contextIndex = -1;
for (int i = 0; i < contexts.Length; i++)
{
Expand Down Expand Up @@ -332,15 +332,14 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
}
state.ProgressIndexStack.RemoveAt(chunkRecLevel);
}
index -= acquisitionDir.Files.Count;
for (; index < acquisitionDir.Subdirectories.Count; index++)
index -= dir.Files.Count;
for (; index < dir.Subdirectories.Count; index++)
{
var acquisitionSubdir = acquisitionDir.Subdirectories[index];
var subdir = dir.Subdirectories[acquisitionSubdir.Index];
downloadDir(in subdir, in acquisitionSubdir, Path.Combine(path, subdir.Name), recursionLevel + 1);
var subdir = dir.Subdirectories[index];
downloadDir(in subdir, Path.Combine(path, manifest.DirectoryBuffer[subdir.Index].Name), recursionLevel + 1);
if (linkedCts.IsCancellationRequested)
{
state.ProgressIndexStack[recursionLevel] = acquisitionDir.Files.Count + index;
state.ProgressIndexStack[recursionLevel] = dir.Files.Count + index;
return;
}
}
Expand Down Expand Up @@ -411,7 +410,7 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
for (int i = 0; i < numResumedContexts; i++)
tasks[i] = Task.Factory.StartNew(AcquireChunk, contexts[i], TaskCreationOptions.DenyChildAttach);
}
downloadDir(in manifest.Root, in delta.AcquisitionTree, Path.Combine(DownloadsDirectory!, state.Id.ToString()), 0);
downloadDir(in delta.AcquisitionTree, Path.Combine(DownloadsDirectory!, state.Id.ToString()), 0);
foreach (var task in tasks)
{
if (task is null)
Expand Down Expand Up @@ -458,7 +457,7 @@ void downloadDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
/// <param name="cancellationToken">Token to monitor for cancellation requests.</param>
internal void Preallocate(ItemState state, DepotManifest manifest, DepotDelta delta, CancellationToken cancellationToken)
{
void preallocDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acquisitionDir, string path, int recursionLevel)
void preallocDir(in DirectoryEntry.AcquisitionEntry dir, string path, int recursionLevel)
{
int index;
if (state.ProgressIndexStack.Count > recursionLevel)
Expand All @@ -467,34 +466,33 @@ void preallocDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
{
state.ProgressIndexStack.Add(0);
index = 0;
if (acquisitionDir.IsNew || acquisitionDir.Files.Any(a => a.Chunks.Count is 0))
if (dir.IsNew || dir.Files.Any(a => a.Chunks.Count is 0))
Directory.CreateDirectory(path);
}
for (; index < acquisitionDir.Files.Count; index++)
for (; index < dir.Files.Count; index++)
{
var acquisitonFile = acquisitionDir.Files[index];
var acquisitonFile = dir.Files[index];
if (acquisitonFile.Chunks.Count is not 0)
continue;
if (cancellationToken.IsCancellationRequested)
{
state.ProgressIndexStack[recursionLevel] = index;
return;
}
var file = dir.Files[acquisitonFile.Index];
var file = manifest.FileBuffer[acquisitonFile.Index];
var handle = File.OpenHandle(Path.Combine(path, file.Name), FileMode.Create, FileAccess.Write, preallocationSize: file.Size);
RandomAccess.SetLength(handle, file.Size);
handle.Dispose();
ProgressUpdated?.Invoke(++state.DisplayProgress);
}
index -= acquisitionDir.Files.Count;
for (; index < acquisitionDir.Subdirectories.Count; index++)
index -= dir.Files.Count;
for (; index < dir.Subdirectories.Count; index++)
{
var acquisitionSubdir = acquisitionDir.Subdirectories[index];
var subdir = dir.Subdirectories[acquisitionSubdir.Index];
preallocDir(in subdir, in acquisitionSubdir, Path.Combine(path, subdir.Name), recursionLevel + 1);
var subdir = dir.Subdirectories[index];
preallocDir(in subdir, Path.Combine(path, manifest.DirectoryBuffer[subdir.Index].Name), recursionLevel + 1);
if (cancellationToken.IsCancellationRequested)
{
state.ProgressIndexStack[recursionLevel] = acquisitionDir.Files.Count + index;
state.ProgressIndexStack[recursionLevel] = dir.Files.Count + index;
return;
}
}
Expand All @@ -510,7 +508,7 @@ void preallocDir(in DirectoryEntry dir, in DirectoryEntry.AcquisitionEntry acqui
if (new DriveInfo(DownloadsDirectory!).AvailableFreeSpace < delta.DownloadCacheSize)
throw new SteamNotEnoughDiskSpaceException(delta.DownloadCacheSize);
ProgressInitiated?.Invoke(ProgressType.Numeric, delta.NumDownloadFiles, state.DisplayProgress);
preallocDir(in manifest.Root, in delta.AcquisitionTree, Path.Combine(DownloadsDirectory!, state.Id.ToString()), 0);
preallocDir(in delta.AcquisitionTree, Path.Combine(DownloadsDirectory!, state.Id.ToString()), 0);
if (cancellationToken.IsCancellationRequested)
{
state.SaveToFile();
Expand Down
5 changes: 4 additions & 1 deletion src/CM/WebSocketConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ public void Connect()
_socket.Dispose();
continue;
}
_thread = new Thread(ConnectionLoop);
_thread = new Thread(ConnectionLoop)
{
Name = "CM WebSocket Connection Thread"
};
_thread.Start();
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Manifest/DepotDelta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void countAcq(in DirectoryEntry dir, DirectoryEntry.AcquisitionStaging acquisiti
foreach (var acquisitionFile in acquisitionDir.Files)
{
numChunks += acquisitionFile.Chunks.Count;
var file = dir.Files[acquisitionFile.Index];
var file = manifest.FileBuffer[acquisitionFile.Index];
if (acquisitionFile.Chunks.Count is 0)
{
numDownloadFiles++;
Expand All @@ -40,7 +40,7 @@ void countAcq(in DirectoryEntry dir, DirectoryEntry.AcquisitionStaging acquisiti
for (int i = 0; i < acquisitionFile.Chunks.Count; i++)
{
int index = acquisitionFile.Chunks[i].Index;
var chunk = file.Chunks[index];
var chunk = manifest.ChunkBuffer[index];
downloadSize += chunk.CompressedSize;
acquisitionFile.Chunks[i] = new(index) { Offset = chunkBufferFileSize };
chunkBufferFileSize += chunk.UncompressedSize;
Expand Down
10 changes: 5 additions & 5 deletions src/Manifest/DirectoryEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal readonly struct AcquisitionEntry
{
/// <summary>Indicates whether directory has been added or modifed.</summary>
public required bool IsNew { get; init; }
/// <summary>Index of the directory entry in its parent directory.</summary>
/// <summary>Index of the directory entry in <see cref="DepotManifest.DirectoryBuffer"/>.</summary>
public required int Index { get; init; }
/// <summary>Directory's file entries.</summary>
public required ArraySegment<FileEntry.AcquisitionEntry> Files { get; init; }
Expand All @@ -24,7 +24,7 @@ internal readonly struct AcquisitionEntry
/// <summary>Structure used in <see cref="DepotDelta"/> to store auxiliary data like patched chunks and relocations for files or removed items.</summary>
internal readonly struct AuxiliaryEntry
{
/// <summary>Index of the directory entry in its parent directory, or in source manifest directory buffer if <see cref="FilesToRemove"/> is empty.</summary>
/// <summary>Index of the directory entry in <see cref="DepotManifest.DirectoryBuffer"/>. If <see cref="FilesToRemove"/> is empty, the index is for source manifest.</summary>
public required int Index { get; init; }
/// <summary>Indexes of the files that must be removed. If empty, the directory with all its contents is removed instead.</summary>
public ArraySegment<int>? FilesToRemove { get; init; }
Expand All @@ -37,7 +37,7 @@ internal readonly struct AuxiliaryEntry
internal class AcquisitionStaging
{
/// <summary>Creates an empty staging directory entry with specified index.</summary>
/// <param name="index">Index of the directory entry in its parent directory.</param>
/// <param name="index">Index of the directory entry in <see cref="DepotManifest.DirectoryBuffer"/>.</param>
/// <param name="isNew">Value indicating whether directory has been added or modifed.</param>
public AcquisitionStaging(int index, bool isNew)
{
Expand All @@ -64,7 +64,7 @@ public AcquisitionStaging(ReadOnlySpan<int> buffer, ref int offset)
}
/// <summary>Indicates whether directory has been added or modifed.</summary>
public bool IsNew { get; internal set; }
/// <summary>Index of the directory entry in its parent directory.</summary>
/// <summary>Index of the directory entry in <see cref="DepotManifest.DirectoryBuffer"/>.</summary>
public int Index { get; }
/// <summary>Directory's file entries.</summary>
public List<FileEntry.AcquisitionStaging> Files { get; }
Expand All @@ -90,7 +90,7 @@ public void WriteToBuffer(Span<int> buffer, ref int offset)
/// <param name="index">Index of the directory entry in its parent directory.</param>
internal class AuxiliaryStaging(int index)
{
/// <summary>Index of the directory entry in its parent directory, or in source manifest directory buffer if <see cref="FilesToRemove"/> is empty.</summary>
/// <summary>Index of the directory entry in <see cref="DepotManifest.DirectoryBuffer"/>. If <see cref="FilesToRemove"/> is empty, the index is for source manifest.</summary>
public int Index { get; } = index;
/// <summary>Indexes of the files that must be removed. If empty, the directory with all its contents is removed instead.</summary>
public List<int>? FilesToRemove { get; set; }
Expand Down
18 changes: 10 additions & 8 deletions src/Manifest/FileEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ public enum Flag
/// <summary>Structure used in <see cref="DepotDelta"/> to store indexes of files and chunks that must be acquired.</summary>
internal readonly struct AcquisitionEntry
{
/// <summary>Index of the file entry in its parent directory.</summary>
/// <summary>Index of the file entry in <see cref="DepotManifest.FileBuffer"/>.</summary>
public required int Index { get; init; }
/// <summary>File's chunks that must be acquired. If empty, the whole file is acquired.</summary>
public required ArraySegment<ChunkEntry> Chunks { get; init; }
/// <summary>Structure storing chunk's index along with its optional offset in chunk buffer file.</summary>
/// <param name="index">Index of the chunk in its file.</param>
public readonly struct ChunkEntry(int index)
{
/// <summary>Index of the chunk entry in its file.</summary>
/// <summary>Index of the chunk entry in <see cref="DepotManifest.ChunkBuffer"/>.</summary>
public int Index { get; } = index;
/// <summary>Offset of the chunk data from the beginning of chunk buffer file.</summary>
public long Offset { get; init; }
Expand All @@ -46,7 +46,7 @@ public readonly struct ChunkEntry(int index)
/// <summary>Structure used in <see cref="DepotDelta"/> to store auxiliary data like patched chunks and relocations.</summary>
internal readonly struct AuxiliaryEntry
{
/// <summary>Index of the file entry in its parent directory.</summary>
/// <summary>Index of the file entry in <see cref="DepotManifest.FileBuffer"/>.</summary>
public required int Index { get; init; }
/// <summary>File's chunk patch and relocation entries.</summary>
public ArraySegment<ITransferOperation> TransferOperations { get; init; }
Expand All @@ -55,7 +55,7 @@ public interface ITransferOperation { }
/// <summary>Contains data that is needed to patch a chunk.</summary>
public class ChunkPatchEntry : ITransferOperation
{
/// <summary>Index of the target chunk in the file.</summary>
/// <summary>Index of target chunk entry in <see cref="DepotManifest.ChunkBuffer"/>.</summary>
public required int ChunkIndex { get; init; }
/// <summary>Index of the corresponding patch chunk.</summary>
public required int PatchChunkIndex { get; init; }
Expand Down Expand Up @@ -96,7 +96,7 @@ public AcquisitionStaging(ReadOnlySpan<int> buffer, ref int offset)
for (int i = 0; i < numChunks; i++)
Chunks.Add(new(buffer[offset++]));
}
/// <summary>Index of the file entry in its parent directory.</summary>
/// <summary>Index of the file entry in <see cref="DepotManifest.FileBuffer"/>.</summary>
public int Index { get; }
/// <summary>File's chunks that must be acquired. If empty, the whole file is acquired.</summary>
public List<AcquisitionEntry.ChunkEntry> Chunks { get; }
Expand All @@ -115,7 +115,7 @@ public void WriteToBuffer(Span<int> buffer, ref int offset)
/// <param name="index">Index of the file entry in its parent directory.</param>
internal class AuxiliaryStaging(int index)
{
/// <summary>Index of the file entry in its parent directory.</summary>
/// <summary>Index of the file entry in <see cref="DepotManifest.FileBuffer"/>.</summary>
public int Index { get; } = index;
/// <summary>File's chunk patch entries.</summary>
public List<ChunkPatchEntry> ChunkPatches { get; } = [];
Expand All @@ -130,15 +130,15 @@ public class ChunkPatchEntry : ITransferOperation
{
/// <summary>Indicates whether intermediate file needs to be used as a buffer to avoid overlapping further chunks.</summary>
public bool UseIntermediateFile { get; set; }
/// <summary>Index of the target chunk in the file.</summary>
/// <summary>Index of target chunk entry in <see cref="DepotManifest.ChunkBuffer"/>.</summary>
public required int ChunkIndex { get; init; }
/// <summary>Index of the corresponding patch chunk.</summary>
public required int PatchChunkIndex { get; init; }
/// <summary>Size of either source chunk or patched chunk data, whichever is smaller.</summary>
public required long Size { get; init; }
}
/// <summary>Describes data that needs to be moved within the file.</summary>
public class RelocationEntry : ITransferOperation
public class RelocationEntry : ITransferOperation, IComparable<RelocationEntry>
{
/// <summary>Indicates whether intermediate file needs to be used as a buffer to avoid overlapping further relocations and chunk patches.</summary>
public bool UseIntermediateFile { get; set; }
Expand All @@ -148,6 +148,8 @@ public class RelocationEntry : ITransferOperation
public required long TargetOffset { get; init; }
/// <summary>Size of data bulk.</summary>
public required long Size { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(RelocationEntry? other) => other is null ? 1 : SourceOffset.CompareTo(other.SourceOffset);
}
/// <summary>Represents an operation that takes data from certain region of a file and writes data to another region of a file.</summary>
public class TransferOperation : IComparable<TransferOperation>
Expand Down

0 comments on commit d58a0bb

Please sign in to comment.