Using memory pooling and spans for tick parsing

This commit is contained in:
Tretiner 2024-07-27 18:41:19 +05:00
parent 31ada6ba55
commit 41a29662ec
7 changed files with 152 additions and 137 deletions

View File

@ -24,9 +24,9 @@ public class Benchy
[Params( [Params(
false false
, ,
true true
)] )]
public bool SkipImages; public bool SkipImages;
[GlobalSetup] [GlobalSetup]

View File

@ -12,26 +12,9 @@
* *
* | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated | * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated |
* |---------- |----------- |----------:|---------:|---------:|--------:|----------:| * |---------- |----------- |----------:|---------:|---------:|--------:|----------:|
* | ReadFully | False | 187.55 us | 3.079 us | 2.880 us | 45.4102 | 93.52 KB |
* | ReadFully | True | 26.44 us | 0.335 us | 0.297 us | 6.4392 | 13.17 KB |
*
* | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated |
* |---------- |----------- |----------:|---------:|---------:|--------:|----------:|
* | ReadFully | False | 217.73 us | 4.190 us | 4.483 us | 45.4102 | 93.9 KB | * | ReadFully | False | 217.73 us | 4.190 us | 4.483 us | 45.4102 | 93.9 KB |
* | ReadFully | True | 23.28 us | 0.391 us | 0.366 us | 6.1340 | 12.54 KB | * | ReadFully | True | 23.28 us | 0.391 us | 0.366 us | 6.1340 | 12.54 KB |
* *
* | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated |
* |---------- |----------- |----------:|---------:|---------:|--------:|----------:|
* | ReadFully | False | 211.93 us | 2.418 us | 2.144 us | 43.4570 | 89.84 KB |
* | ReadFully | True | 20.60 us | 0.296 us | 0.262 us | 4.3640 | 8.94 KB |
*
* | Method | SkipImages | UseFileStream | Mean | Error | StdDev | Median | Gen0 | Gen1 | Allocated |
* |---------- |----------- |-------------- |----------:|----------:|----------:|----------:|--------:|-------:|----------:|
* | ReadFully | False | False | 265.56 us | 13.334 us | 39.316 us | 258.33 us | 43.4570 | 0.4883 | 90.08 KB |
* | ReadFully | False | True | 599.75 us | 14.393 us | 39.644 us | 587.87 us | 42.9688 | 0.9766 | 90.08 KB |
* | ReadFully | True | False | 21.15 us | 0.385 us | 0.674 us | 21.01 us | 4.4861 | - | 9.17 KB |
* | ReadFully | True | True | 70.86 us | 1.392 us | 1.302 us | 70.92 us | 4.3945 | - | 9.17 KB |
*
* | Method | SkipImages | UseFileStream | Mean | Error | StdDev | Gen0 | Allocated | * | Method | SkipImages | UseFileStream | Mean | Error | StdDev | Gen0 | Allocated |
* |---------- |----------- |-------------- |----------:|----------:|----------:|--------:|----------:| * |---------- |----------- |-------------- |----------:|----------:|----------:|--------:|----------:|
* | ReadFully | False | False | 237.75 us | 4.725 us | 13.092 us | 43.4570 | 88.95 KB | * | ReadFully | False | False | 237.75 us | 4.725 us | 13.092 us | 43.4570 | 88.95 KB |
@ -78,21 +61,6 @@ Init Replay.Actions list with capacity of Replay.TotalTicks / 2
| ReadFull | 4v4.rec | False | 1,417.59 us | 28.016 us | 23.395 us | 1,407.82 us | 97.6563 | 68.3594 | 27.3438 | 616.45 KB | | ReadFull | 4v4.rec | False | 1,417.59 us | 28.016 us | 23.395 us | 1,407.82 us | 97.6563 | 68.3594 | 27.3438 | 616.45 KB |
| ReadFull | 4v4.rec | True | 1,098.68 us | 21.912 us | 38.949 us | 1,087.36 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | | ReadFull | 4v4.rec | True | 1,098.68 us | 21.912 us | 38.949 us | 1,087.36 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB |
| Method | Replay | SkipImages | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated |
|----------- |---------------- |----------- |--------------:|------------:|------------:|--------------:|----------:|---------:|---------:|-----------:|
| ReadHeader | 1v1.rec | False | 1.572 us | 0.0294 us | 0.0275 us | 1.572 us | 0.6695 | - | - | 1.37 KB |
| ReadInfo | 1v1.rec | False | 141.536 us | 2.6164 us | 2.3194 us | 141.797 us | 42.4805 | - | - | 87.67 KB |
| ReadFull | 1v1.rec | False | 141.130 us | 1.8004 us | 1.4056 us | 141.015 us | 43.2129 | - | - | 89.06 KB |
| ReadHeader | 1v1.rec | True | 1.680 us | 0.0376 us | 0.1074 us | 1.646 us | 0.6695 | - | - | 1.37 KB |
| ReadInfo | 1v1.rec | True | 11.819 us | 0.2225 us | 0.3955 us | 11.720 us | 3.2501 | - | - | 6.64 KB |
| ReadFull | 1v1.rec | True | 14.857 us | 0.2916 us | 0.4088 us | 14.860 us | 3.9215 | - | - | 8.03 KB |
| ReadHeader | 4p_withbots.rec | False | 1.849 us | 0.0362 us | 0.0355 us | 1.844 us | 0.7801 | - | - | 1.59 KB |
| ReadInfo | 4p_withbots.rec | False | 75.350 us | 1.4535 us | 1.4927 us | 74.821 us | 22.2168 | - | - | 45.76 KB |
| ReadFull | 4p_withbots.rec | False | 15,368.249 us | 303.3914 us | 755.5498 us | 15,409.927 us | 1078.1250 | 890.6250 | 421.8750 | 6492.96 KB |
| ReadHeader | 4p_withbots.rec | True | 1.912 us | 0.0382 us | 0.1000 us | 1.884 us | 0.7782 | - | - | 1.59 KB |
| ReadInfo | 4p_withbots.rec | True | 11.963 us | 0.1580 us | 0.1401 us | 11.948 us | 2.5177 | - | - | 5.16 KB |
| ReadFull | 4p_withbots.rec | True | 14,558.302 us | 288.3673 us | 803.8528 us | 14,454.221 us | 1046.8750 | 906.2500 | 390.6250 | 6452.2 KB |
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | | Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| |--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | False | 151.58 us | 2.997 us | 5.703 us | 43.2129 | 0.2441 | - | 89.02 KB | | ReadFull | 1v1.rec | False | 151.58 us | 2.997 us | 5.703 us | 43.2129 | 0.2441 | - | 89.02 KB |
@ -115,33 +83,6 @@ Init Replay.Actions list with capacity of Replay.TotalTicks / 2
| ReadFull | 4v4.rec | False | 1,444.44 us | 28.807 us | 48.130 us | 97.6563 | 68.3594 | 27.3438 | 616.81 KB | | ReadFull | 4v4.rec | False | 1,444.44 us | 28.807 us | 48.130 us | 97.6563 | 68.3594 | 27.3438 | 616.81 KB |
| ReadFull | 4v4.rec | True | 1,082.37 us | 18.606 us | 19.107 us | 70.3125 | 54.6875 | 27.3438 | 414.53 KB | | ReadFull | 4v4.rec | True | 1,082.37 us | 18.606 us | 19.107 us | 70.3125 | 54.6875 | 27.3438 | 414.53 KB |
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-------------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | False | 102.87 us | 1.552 us | 2.176 us | 43.4570 | - | - | 89 KB |
| ReadFull | 1v1.rec | True | 15.57 us | 0.310 us | 0.839 us | 3.9063 | - | - | 8.03 KB |
| ReadFull | 2v2v2.rec | False | 733.72 us | 14.360 us | 18.672 us | 124.0234 | 41.9922 | - | 477.07 KB |
| ReadFull | 2v2v2.rec | True | 461.28 us | 8.453 us | 15.877 us | 75.1953 | 23.4375 | - | 234.35 KB |
| ReadFull | 4p_withbots.rec | False | 17,231.75 us | 589.520 us | 1,738.212 us | 1031.2500 | 843.7500 | 375.0000 | 6493.06 KB |
| ReadFull | 4p_withbots.rec | True | 16,119.01 us | 320.763 us | 710.788 us | 1000.0000 | 875.0000 | 343.7500 | 6452.12 KB |
| ReadFull | 4v4.rec | False | 1,561.33 us | 29.811 us | 31.898 us | 97.6563 | 68.3594 | 27.3438 | 616.81 KB |
| ReadFull | 4v4.rec | True | 1,188.38 us | 23.616 us | 27.196 us | 70.3125 | 54.6875 | 27.3438 | 414.53 KB |
// ascii string equals with stackalloc bytes sequenceEqual no ignore case
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | True | 13.17 us | 0.258 us | 0.265 us | 3.7079 | - | - | 7.6 KB |
| ReadFull | 2v2v2.rec | True | 400.75 us | 7.956 us | 12.848 us | 76.6602 | 20.9961 | - | 233.86 KB |
| ReadFull | 4p_withbots.rec | True | 14,380.66 us | 285.889 us | 768.022 us | 1062.5000 | 921.8750 | 406.2500 | 6451.56 KB |
| ReadFull | 4v4.rec | True | 1,069.55 us | 20.197 us | 20.741 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB |
// ascii string equals with stackalloc bytes custom sequenceEqual with ignore case
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | True | 13.96 us | 0.273 us | 0.507 us | 3.7079 | - | - | 7.6 KB |
| ReadFull | 2v2v2.rec | True | 409.48 us | 7.863 us | 6.970 us | 77.1484 | 20.9961 | - | 233.86 KB |
| ReadFull | 4p_withbots.rec | True | 13,161.68 us | 238.163 us | 185.942 us | 1000.0000 | 875.0000 | 343.7500 | 6451.48 KB |
| ReadFull | 4v4.rec | True | 1,049.49 us | 20.183 us | 17.892 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB |
// Extract of logic and cleaning ExBinaryReader // Extract of logic and cleaning ExBinaryReader
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | | Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| |--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:|
@ -155,24 +96,26 @@ Init Replay.Actions list with capacity of Replay.TotalTicks / 2
| ReadFull | 4v4.rec | True | 1,106.32 us | 21.747 us | 35.731 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | | ReadFull | 4v4.rec | True | 1,106.32 us | 21.747 us | 35.731 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB |
26.07.2024 // 27.07.2024 Images parsing is better
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | | Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |------------:|----------:|----------:|----------:|---------:|---------:|-----------:| |--------- |---------------- |----------- |-------------:|-----------:|-------------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | False | 109.6 us | 1.65 us | 1.46 us | 42.4805 | 7.0801 | - | 88.45 KB | | ReadFull | 1v1.rec | False | 42.87 us | 0.473 us | 0.395 us | 42.5415 | 7.0801 | - | 88.27 KB |
| ReadFull | 2v2v2.rec | False | 735.7 us | 14.28 us | 12.66 us | 109.3750 | 58.5938 | - | 476.2 KB | | ReadFull | 1v1.rec | True | 12.87 us | 0.095 us | 0.080 us | 3.6926 | - | - | 7.55 KB |
| ReadFull | 4p_withbots.rec | False | 14,794.2 us | 295.22 us | 635.50 us | 1078.1250 | 906.2500 | 421.8750 | 6492.21 KB | | ReadFull | 2v2v2.rec | False | 551.09 us | 10.990 us | 23.656 us | 107.4219 | 53.7109 | - | 475.77 KB |
| ReadFull | 4v4.rec | False | 1,352.9 us | 16.24 us | 14.40 us | 93.7500 | 64.4531 | 27.3438 | 615.98 KB | | ReadFull | 2v2v2.rec | True | 427.14 us | 3.915 us | 3.269 us | 74.7070 | 20.9961 | - | 233.8 KB |
| ReadFull | 4p_withbots.rec | False | 14,880.91 us | 294.969 us | 561.210 us | 1078.1250 | 906.2500 | 421.8750 | 6492.25 KB |
| ReadFull | 4p_withbots.rec | True | 15,426.41 us | 403.218 us | 1,103.803 us | 1046.8750 | 890.6250 | 390.6250 | 6451.52 KB |
27.07.2024 Images parsing is better | ReadFull | 4v4.rec | False | 1,227.00 us | 24.521 us | 28.238 us | 93.7500 | 64.4531 | 27.3438 | 615.61 KB |
| Method | Replay | SkipImages | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | | ReadFull | 4v4.rec | True | 1,152.13 us | 26.309 us | 73.340 us | 70.3125 | 54.6875 | 27.3438 | 413.96 KB |
|--------- |---------------- |----------- |-------------:|-----------:|-------------:|-------------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | False | 42.87 us | 0.473 us | 0.395 us | 42.80 us | 42.5415 | 7.0801 | - | 88.27 KB |
| ReadFull | 1v1.rec | True | 12.87 us | 0.095 us | 0.080 us | 12.87 us | 3.6926 | - | - | 7.55 KB |
| ReadFull | 2v2v2.rec | False | 551.09 us | 10.990 us | 23.656 us | 541.74 us | 107.4219 | 53.7109 | - | 475.77 KB |
| ReadFull | 2v2v2.rec | True | 427.14 us | 3.915 us | 3.269 us | 427.37 us | 74.7070 | 20.9961 | - | 233.8 KB |
| ReadFull | 4p_withbots.rec | False | 14,880.91 us | 294.969 us | 561.210 us | 15,118.21 us | 1078.1250 | 906.2500 | 421.8750 | 6492.25 KB |
| ReadFull | 4p_withbots.rec | True | 15,426.41 us | 403.218 us | 1,103.803 us | 15,222.37 us | 1046.8750 | 890.6250 | 390.6250 | 6451.52 KB |
| ReadFull | 4v4.rec | False | 1,227.00 us | 24.521 us | 28.238 us | 1,222.49 us | 93.7500 | 64.4531 | 27.3438 | 615.61 KB |
| ReadFull | 4v4.rec | True | 1,152.13 us | 26.309 us | 73.340 us | 1,117.18 us | 70.3125 | 54.6875 | 27.3438 | 413.96 KB |
// 27.07.2024 Using Memory pooling and spans for tick parsing
| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:|
| ReadFull | 1v1.rec | False | 47.21 us | 0.916 us | 1.091 us | 42.5415 | 7.0801 | - | 88.27 KB |
| ReadFull | 1v1.rec | True | 12.81 us | 0.090 us | 0.075 us | 3.6926 | - | - | 7.55 KB |
| ReadFull | 2v2v2.rec | False | 456.30 us | 5.616 us | 5.253 us | 105.4688 | 54.6875 | - | 475.77 KB |
| ReadFull | 2v2v2.rec | True | 359.79 us | 6.755 us | 5.988 us | 74.7070 | 21.4844 | - | 233.8 KB |
| ReadFull | 4p_withbots.rec | False | 14,020.18 us | 278.529 us | 581.394 us | 1093.7500 | 984.3750 | 437.5000 | 6491.99 KB |
| ReadFull | 4p_withbots.rec | True | 13,371.46 us | 267.402 us | 383.500 us | 1093.7500 | 984.3750 | 453.1250 | 6451.67 KB |
| ReadFull | 4v4.rec | False | 1,095.98 us | 14.512 us | 12.865 us | 93.7500 | 64.4531 | 27.3438 | 615.62 KB |
| ReadFull | 4v4.rec | True | 954.46 us | 9.933 us | 9.291 us | 70.3125 | 54.6875 | 27.3438 | 413.96 KB |

View File

@ -1,8 +1,8 @@
namespace SoulstormReplayReader.Core.Domain.Action; namespace SoulstormReplayReader.Core.Domain.Action;
public sealed class GameActionModel(int tick) : IGameAction public sealed class GameActionModel : IGameAction
{ {
public int Tick { get; } = tick; public int Tick { get; set; }
public int PlayerId { get; set; } public int PlayerId { get; set; }
public int PlayerActionCount { get; set; } public int PlayerActionCount { get; set; }

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>Debug;Release;DebugLogging</Configurations> <Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms> <Platforms>AnyCPU</Platforms>
<Version>1.0.0</Version>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
using System.Buffers.Binary; using System.Buffers;
using System.Runtime.CompilerServices; using System.Buffers.Binary;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SoulstormReplayReader.Core.Domain; using SoulstormReplayReader.Core.Domain;
@ -431,9 +431,33 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
if (tickType == TickType.Normal) if (tickType == TickType.Normal)
{ {
if (tickSize == 17) if (tickSize == 17)
SkipEmptyTick(); {
_binaryReader.Skip(tickSize); // Всегда 17 байт
}
else else
ReadOrdinaryTick(); {
byte[] rentBytes = null;
var bytes = tickSize switch
{
> 0 and < 0xFF => stackalloc byte[tickSize],
_ => rentBytes = ArrayPool<byte>.Shared.Rent(tickSize)
};
try
{
bytes = _binaryReader.ReadBytes(bytes[..tickSize]);
ParseOrdinaryTick(bytes);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
if (rentBytes is not null)
ArrayPool<byte>.Shared.Return(rentBytes);
}
}
CurrentTick++; CurrentTick++;
} }
@ -443,36 +467,32 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
ReadWierdTick((int)tickType, tickSize); ReadWierdTick((int)tickType, tickSize);
} }
private void ParseOrdinaryTick(Span<byte> bytes)
// Всегда 17 байт
private void SkipEmptyTick()
{ {
_binaryReader.Skip(17); var spanReader = new SpanReader(bytes);
}
// -- TICK HEADER -- size: 17
private void ReadOrdinaryTick() spanReader.Skip(1); // 1 byte = 0x50
{ var tickCount = spanReader.ReadInt32();
// -- TICK HEADER -- spanReader.Skip(8); // 8 bytes = int32 "Номер действия игрока" ??? + int32 Random
_binaryReader.Skip(1); // 0x50 var playerChunksCount = spanReader.ReadInt32();
var tickCount = _binaryReader.ReadInt32();
_binaryReader.Skip(8); // int32 "Номер действия игрока" ??? + int32 Random
var playerChunksCount = _binaryReader.ReadInt32();
// -- TICK BODY -- // -- TICK BODY --
for (var i = 0; i < playerChunksCount; i++) // для каждого игрока предусмотрен свой чанк со своим размером for (var i = 0; i < playerChunksCount; i++) // для каждого игрока предусмотрен свой чанк со своим размером
{ {
// -- PLAYER CHUNK HEADER -- // -- PLAYER CHUNK HEADER -- size: 13 + пока размер чанка != 0
_binaryReader.Skip(8); // ??? spanReader.Skip(8); // 8 bytes = ???
var playerChunkSize = _binaryReader.ReadInt32(); var playerChunkSize = spanReader.ReadInt32();
_binaryReader.Skip(1); // то же самое spanReader.Skip(1); // 1 byte = playerChunkSize, но байт
// -- PLAYER CHUNK BODY -- // -- PLAYER CHUNK BODY --
while (playerChunkSize != 0) while (playerChunkSize != 0)
{ {
var actionSize = _binaryReader.ReadInt16(); // размер захватывает 2 байта след чанка var actionSize = spanReader.ReadInt16(); // размер захватывает 2 байта (размера) следующего экшена
var action = ReadPlayerAction(tickCount, actionSize); spanReader.SliceToOffset();
var action = ParsePlayerAction(ref spanReader, actionSize - 2);
action.Tick = tickCount;
Replay.Actions.Add(action); Replay.Actions.Add(action);
playerChunkSize -= actionSize; playerChunkSize -= actionSize;
@ -480,34 +500,21 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
} }
} }
private GameActionModel ParsePlayerAction(ref SpanReader spanReader, int actionSize)
private GameActionModel ReadPlayerAction(int tickCount, int actionSize)
{ {
var actionEndPos = _binaryReader.Position + actionSize - 2;
// -- ACTION HEADER -- // -- ACTION HEADER --
_binaryReader.SkipInt32(); // какое то время (свое для каждого игрока) spanReader.Skip(4); // 4 bytes = какое то время (свое для каждого игрока)
// (начинается с рандомного значения и увеличивается по ходу игры) // (начинается с рандомного значения и увеличивается по ходу игры)
// Скорее всего является global timer // Скорее всего является global timer
// var s_cmd = _binaryReader.ReadInt32(); var cmd = spanReader.ReadInt32();
// var s_param = _binaryReader.ReadInt32(); var arg = spanReader.ReadByte(); // int32 in game
// var s_shiftPressed = _binaryReader.ReadByte() == 1; var subCmd = spanReader.ReadInt16();
// var s_playerId = _binaryReader.Skip(2).ReadInt16() % 10; var someNum = spanReader.ReadByte();
// var shiftPressed = spanReader.ReadByte() == 1;
// // 2 bytes = player id
// var leftOverBytes2 = actionEndPos - _binaryReader.Position; var playerId = spanReader.Skip(2).ReadInt16() % 10;
// Console.WriteLine(_binaryReader.ReadBytes((int)leftOverBytes2).ToContentString()); var playerActionCount = spanReader.ReadInt16();
// return new GameActionModel(1);
var cmd = _binaryReader.ReadInt32();
var arg = _binaryReader.ReadByte(); // int32 in game
var subCmd = _binaryReader.ReadInt16();
var someNum = _binaryReader.ReadByte();
var shiftPressed = _binaryReader.ReadByte() == 1;
var playerId = _binaryReader.Skip(2).ReadInt16() % 10;
var playerActionCount = _binaryReader.ReadInt16();
#if DEBUGLOGGING #if DEBUGLOGGING
var selectedCount = _binaryReader.ReadByte(); var selectedCount = _binaryReader.ReadByte();
@ -523,10 +530,10 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
var primaryId = _binaryReader.ReadInt32(); var primaryId = _binaryReader.ReadInt32();
var actionBodyDataType = _binaryReader.ReadByte(); var actionBodyDataType = _binaryReader.ReadByte();
#else #else
_binaryReader.Skip(7); // _binaryReader.Skip(7);
#endif #endif
var curAction = new GameActionModel(tickCount) var curAction = new GameActionModel
{ {
Cmd = cmd, Cmd = cmd,
Arg = arg, Arg = arg,
@ -542,6 +549,8 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
if (CheckForBugs) if (CheckForBugs)
Replay.Players[playerId].BugChecker?.Check(curAction); Replay.Players[playerId].BugChecker?.Check(curAction);
spanReader.Skip((uint)(actionSize - spanReader.offset));
#if DEBUGLOGGING #if DEBUGLOGGING
// -- ACTION BODY -- // -- ACTION BODY --
@ -565,8 +574,8 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
// } // }
#else #else
// -- ACTION BODY -- // -- ACTION BODY --
var leftOverBytes = actionEndPos - _binaryReader.Position; // var leftOverBytes = actionEndPos - _binaryReader.Position;
_binaryReader.Skip((int)leftOverBytes); // _binaryReader.Skip((int)leftOverBytes);
#endif #endif
return curAction; return curAction;
@ -577,7 +586,7 @@ public sealed class SsReplayReader(Stream stream) : IDisposable
{ {
var tickType = _binaryReader.ReadInt32(); var tickType = _binaryReader.ReadInt32();
_binaryReader.Skip(4); // Размер тика _binaryReader.Skip(4); // Размер тика
_binaryReader.Skip(1); // То же самое _binaryReader.Skip(1); // Размер тика, но байт
if (tickType == 0) if (tickType == 0)
ReadPlayerQuit(); ReadPlayerQuit();

View File

@ -0,0 +1,59 @@
namespace SoulstormReplayReader.Core.Utils;
public ref struct SpanReader
{
public Span<byte> bytes;
public int offset = 0;
public SpanReader(Span<byte> bytes)
{
this.bytes = bytes;
}
public SpanReader SliceToOffset()
{
bytes = bytes[offset..];
offset = 0;
return this;
}
public SpanReader Skip(uint count)
{
offset += (int)count;
return this;
}
public byte ReadByte()
{
var value = bytes[offset];
offset++;
return value;
}
public short ReadInt16()
{
var value = BitConverter.ToInt16(bytes[offset..]);
offset += sizeof(short);
return value;
}
public int ReadInt32()
{
var value = BitConverter.ToInt32(bytes[offset..]);
offset += sizeof(int);
return value;
}
public long ReadInt64()
{
var value = BitConverter.ToInt64(bytes[offset..]);
offset += sizeof(long);
return value;
}
public readonly void RequireInRange()
{
if (offset >= bytes.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
}
}

View File

@ -8,7 +8,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SoulstormReplayReader.Core\SoulstormReplayReader.Core.csproj" /> <ProjectReference Include="..\SoulstormReplayReader.Core\SoulstormReplayReader.Core.csproj"/>
</ItemGroup>
<ItemGroup>
<None Include="TestReplays/*" CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup> </ItemGroup>
</Project> </Project>