Code Monkey home page Code Monkey logo

demofile-net's People

Contributors

saul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

demofile-net's Issues

Seeking is still failing

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Seeking is still failing, even after the recent fix.

The example below gives following error : System.Exception: Delta on non-existent entity 185

Code to reproduce

Console.WriteLine($"starting seek test, tick count {demo.TickCount.Value}");

        for (int i = 50; i < demo.TickCount.Value; i += 75 * 64)
        {
            Console.WriteLine($"seeking to tick {i}");
            await demo.SeekToTickAsync(new DemoTick(i), default);

            int tickBackward = Math.Max(1, i - 75 * 64);
            Console.WriteLine($"seeking backward to tick {tickBackward}");
            await demo.SeekToTickAsync(new DemoTick(tickBackward), default);

            int tickOnOppositeSide = demo.TickCount.Value - i;
            Console.WriteLine($"seeking to opposite side at tick {tickOnOppositeSide}");
            await demo.SeekToTickAsync(new DemoTick(tickOnOppositeSide), default);
        }

        Console.WriteLine($"seek test done");

Affected demos

https://www.hltv.org/matches/2372034/mouz-nxt-vs-space-cct-season-2-europe-series-3

map : Vertigo

Also happens on some recent Matchmaking demos.

`RoundStartTicks` not available

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

CDemoFileInfo.GameInfo?.Cs?.RoundStartTicks property is not available in any demo.

CDemoFileInfo.GameInfo is always null.

Code to reproduce

var stream = new MemoryStream(File.ReadAllBytes(path));

var demo = new DemoParser();

demo.DemoEvents.DemoFileInfo += e =>
{
    Console.WriteLine($"DemoFileInfo:\n");

    var jsonSerializerOptions = new JsonSerializerOptions() { WriteIndented = true };
    string jsonStr = JsonSerializer.Serialize(e, jsonSerializerOptions);

    Console.WriteLine($"RoundStartTicks: {e.GameInfo?.Cs?.RoundStartTicks?.Count}\n");
    Console.WriteLine(jsonStr);

    Console.WriteLine($"\n");
};

await demo.ReadAllAsync(stream);

Affected demos

No response

Expose Server Classes

Basically this:

public ServerClass?[] ServerClasses => _serverClasses;

inside of DemoParser.Entities.cs.

Right now, you would need to manually process CDemoClassInfo message to get Server Classes. So this would be more convenient.

on PlayerHurt USP returns as P2000

e.Weapon on PlayerHurt returns hpk2000 instead of usp_silencer. Also the same problem appears in player's Weapons, usp_silencer returned as hpk2000. It is strange since on PlayerDeath everything works right.

Pawn position still jittering

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Pawn position is still jittering. Tested in one of latest demo files as of today (21 feb), and in older demos also.

To visualize:

Code to reproduce

No response

Affected demos

No response

Unity engine support

Currently I am doing a lot of struggle to marshal data into Unity from external .NET process or external WebAssembly module.

It would be much better if this library would support Unity engine by default.

As you may know, Unity supports only up to .NET 4.8 or .NET Standard 2.1, see docs.

I managed to compile library to .NET Standard 2.1, which runs fine in .NET 7 executable, but gives some weird errors in Unity, like InvalidProtocolBufferException. Then there is Unity's custom AOT solution (called IL2CPP) which I haven't even tried to run with.

What do you think about it ?

Add support for messages from `cstrike15_usermessages.proto`

This file drags in many dependencies, maybe it's better to create smaller version that only has messages which are used in demos.

Messages used in demos:

CS_UM_WeaponSound 369
CS_UM_RadioText 322
CS_UM_PlayerStatsUpdate 336
CS_UM_VotePass 347
CS_UM_VoteFailed 348
CS_UM_EndOfMatchAllPlayersData 375
CS_UM_XpUpdate 365
CS_UM_ServerRankUpdate 352
CS_UM_MatchEndConditions 334
CS_UM_SendPlayerItemFound 363

Some Demos broken after new cs2 update "Missing CSoundEventPathCornerEntity"

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Hi, it seems we have another error with parsing after the new cs2 update, its an error that dosnt happen often occoured in 4 out of 40 demos.

error is:
CSoundEventPathCornerEntity

from another project i have a stack trace too:
Logs: 27/05/2024 - 21.08.34 - Error at HandleAbove25Demos System.Exception: Attempted to create unknown entity: CSoundEventPathCornerEntity
at DemoFile.DemoParser.<>c__DisplayClass115_0.b__1(EntityContext context)
at DemoFile.DemoParser.OnPacketEntities(CSVCMsg_PacketEntities msg)
at DemoFile.PacketEvents.ParseNetMessage(Int32 msgType, ReadOnlySpan1 buf) at DemoFile.DemoParser.OnDemoPacket(CDemoPacket msg) at DemoFile.DemoEvents.ReadDemoCommandCore[T](Action1 callback, MessageParser1 parser, ReadOnlySpan1 buffer, Boolean isCompressed)
at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands msgType, ReadOnlySpan1 buffer, Boolean isCompressed) at DemoFile.DemoParser.MoveNextCoreAsync(EDemoCommands msgType, Boolean isCompressed, Int32 size, CancellationToken cancellationToken) at DemoFile.DemoParser.ReadAllAsync(Stream stream, CancellationToken cancellationToken) at PowerLigaStats.DemoParsing.DemoHandler.ProcessCS2DemoRewrite(String demoFilePath, Int32 season, String division, DateTime dateTime, Boolean playoff) in C:\Users\Patri\Documents\Kode\Full-stack\PowerLigaStats\PowerLigaStats\DemoParsing\DemoHandler.cs:line 915 at PowerLigaStats.DemoParsing.DemoHandler.ProcessCS2DemoRewrite(String demoFilePath, Int32 season, String division, DateTime dateTime, Boolean playoff) in C:\Users\Patri\Documents\Kode\Full-stack\PowerLigaStats\PowerLigaStats\DemoParsing\DemoHandler.cs:line 1046 at PowerLigaStats.DemoParsing.DemoHandler.ProcessDemoFileAbove24(String demoFilePath, Int32 season, String division, DateTime dateTime, Boolean playoff) in C:\Users\Patri\Documents\Kode\Full-stack\PowerLigaStats\PowerLigaStats\DemoParsing\DemoHandler.cs:line 57 at PowerLigaStats.DemoParsing.DemoManager.HandleDemosByDivision(Int32 season, String division, List1 allResults) in C:\Users\Patri\Documents\Kode\Full-stack\PowerLigaStats\PowerLigaStats\DemoParsing\DemoManager.cs:line 271
at PowerLigaStats.DemoParsing.DemoManager.Start(Int32 season) in C:\Users\Patri\Documents\Kode\Full-stack\PowerLigaStats\PowerLigaStats\DemoParsing\DemoManager.cs:line 720

Code to reproduce

public static class Program
{
    //public static string flashNoWork = "C:\\Users\\xguys\\Desktop\\Flash issues\\crystal_vulture.dem";
    public static string flashWork = "C:\\Users\\xguys\\Desktop\\Demos To test\\661577c94c197423489bd882_0_1716836561580.dem";




    public static async Task Main(string[] args)
    {
        try
        {
            Stopwatch stopwatch3 = new Stopwatch();
            stopwatch3.Start();
            MemoryStream _fileStream;
            byte[] _demoBytes;
            //_demoBytes = File.ReadAllBytes(demoFilePath);
            _demoBytes = await File.ReadAllBytesAsync(flashWork);
            _fileStream = new MemoryStream(_demoBytes);

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var demo = new DemoParser();

            demo.Source1GameEvents.BombExploded += e =>
            {
                Console.WriteLine("Bomb exploded");
            };

            demo.Source1GameEvents.PlayerBlind += e =>
            {
                Console.WriteLine("Player blind");

            };

            demo.Source1GameEvents.TrPlayerFlashbanged += e =>
            {
                Console.WriteLine("Player flashbanged");
            };

            //demo.Source1GameEvents.FlashbangDetonate += e =>
            //{
            //    Console.WriteLine("Flashbang detonate");
            //};

            demo.Source1GameEvents.RoundStart += e =>
            {

                if (!demo.GameRules.HasMatchStarted)
                {
                    //PowerLigaManager.Log("ROund end called but we have an issue, it seems match was not started yet");
                    return;
                }
                Console.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
                Debug.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
            };

            demo.Source1GameEvents.CsWinPanelMatch += e =>
            {
                Console.WriteLine("WIn match has ended");
            };
            await demo.ReadAllAsync(_fileStream);
            stopwatch3.Stop();

            stopwatch.Stop();
            Console.WriteLine("Time elapsed after file data: {0}", stopwatch.Elapsed);
            Console.WriteLine("Time elapsed: {0}", stopwatch3.Elapsed);
            Console.WriteLine("\nFinished!");
        } catch (Exception e)
        { 
            Console.WriteLine("Exception: " + e.Message.ToString());
        }

        Console.ReadKey();
    }

Affected demos

https://drive.google.com/file/d/1ks0sX_5EVK-Cy7BT66bpXorhmJUrVAqp/view?usp=sharing

Player's pawn position is jittering

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Position of player's pawn is jittering (quickly snapping from one place to another). In only 1 tick, player is teleported to distant location, and then returned back. And this is not a round restart or death. This happens throughout entire match.

What is weird is that 2 of his position coordinates become a multiple of 1024.

The example shows position of pawn in 3 consecutive ticks. The result prints:

Position (tick 39984): Vector { X = -1102.1124, Y = -73.48895, Z = -167.96875 }
Position (tick 39985): Vector { X = -2048, Y = -1024, Z = -167.96875 }
Position (tick 39986): Vector { X = -1102.3584, Y = -71.92908, Z = -167.96875 }

2nd tick is the problematic one in this case, X and Y coordinates are multiple of 1024.

Example code uses navi-javelins-vs-9-pandas-fearless-m1-mirage.dem file.

Code to reproduce

var demo = new DemoParser();

demo.CreateTimer(new DemoTick(39984), () =>
{
    var player = demo.Players.Single(_ => _.PlayerName == "Angelka");
    Console.WriteLine($"Position (tick 39984): {player?.Pawn?.Origin}");
});
demo.CreateTimer(new DemoTick(39985), () =>
{
    var player = demo.Players.Single(_ => _.PlayerName == "Angelka");
    Console.WriteLine($"Position (tick 39985): {player?.Pawn?.Origin}");
});
demo.CreateTimer(new DemoTick(39986), () =>
{
    var player = demo.Players.Single(_ => _.PlayerName == "Angelka");
    Console.WriteLine($"Position (tick 39986): {player?.Pawn?.Origin}");
});

await demo.Start(File.OpenRead(path));

Affected demos

Every demo, but example works only with navi-javelins-vs-9-pandas-fearless-m1-mirage.dem

Silenced weapons are not showing up

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

CCSPlayerPawn.ActiveWeapon?.ServerClass?.Name doesn't return silenced weapons (m4a1-s, usp-s), but returns their couterparts (m4a1, hkp2000).

Code to reproduce

No response

Affected demos

No response

PlayerBlind inconsistent and not working

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

It seems that there must be some issue with the "PlayerBlind" event not always being parsed correctly from a demo, as it gets called
in some demos and on some it dosnt, it seems that the "FlashbangDetonate" event gets called every time so just seems like a parsing issue on your side, but i could be wrong. below ive attached 1 that works and 1 that dosnt that i used in my test.

Code to reproduce

public static class Program
{
    public static string flashNoWork = "C:\\Users\\xguys\\Desktop\\Flash issues\\crystal_vulture.dem";
    public static string flashWork = "C:\\Users\\xguys\\Desktop\\Flash issues\\hedensted_ingearme.dem";



    public static async Task Main(string[] args)
    {
        Stopwatch stopwatch3 = new Stopwatch();
        stopwatch3.Start();
        MemoryStream _fileStream;
        byte[] _demoBytes;
        //_demoBytes = File.ReadAllBytes(demoFilePath);
        _demoBytes = await File.ReadAllBytesAsync(flashWork);
        _fileStream = new MemoryStream(_demoBytes);

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        var demo = new DemoParser();

        demo.Source1GameEvents.BombExploded += e =>
        {
            Console.WriteLine("Bomb exploded");
        };

        demo.Source1GameEvents.PlayerBlind += e =>
        {
            Console.WriteLine("Player blind");
            
        };

        demo.Source1GameEvents.FlashbangDetonate += e =>
        {
            Console.WriteLine("Flashbang detonate");
        };

        demo.Source1GameEvents.RoundStart += e =>
        {

            if (!demo.GameRules.HasMatchStarted)
            {
                //PowerLigaManager.Log("ROund end called but we have an issue, it seems match was not started yet");
                return;
            }
            Console.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
            Debug.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
        };

        demo.Source1GameEvents.CsWinPanelMatch += e =>
    {
        Console.WriteLine("WIn match has ended");
    };
        await demo.ReadAllAsync(_fileStream);
        stopwatch3.Stop();

        stopwatch.Stop();
        Console.WriteLine("Time elapsed after file data: {0}", stopwatch.Elapsed);
        Console.WriteLine("Time elapsed: {0}", stopwatch3.Elapsed);
        Console.WriteLine("\nFinished!");
        Console.ReadKey();
    }
}

Affected demos

https://drive.google.com/drive/folders/1VdU7tr2s_4SV_EHUOViCJm4AUWxNzvDy?usp=sharing

Pawn position is outside of buy-zone during freeze time

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Sometimes, during freeze time pawn is placed outside of buy-zone.

To reproduce:

Go to: https://csunity-5172f.web.app/
Click Open -> Ok -> Select navi-javelins-vs-9-pandas-fearless-m1-mirage.dem -> Click Pause

Observe player "victoria" on the middle of screen. If you look around, you'll see his teammates are too far away from him.

Once the round starts, his position will be correct.

His position coordinates are again 1024. You can see that in Inspector (but converted to meters, it will be 32).

Code to reproduce

No response

Affected demos

navi-javelins-vs-9-pandas-fearless-m1-mirage.dem

Incorrect round results

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Some demos are reporting wrong round results.

Both RoundEnd event and GameRules.CSRoundResults give the same result, but that result doesn't match the one from HLTV.

Example : (map Vertigo) https://www.hltv.org/matches/2369946/ence-vs-astralis-pgl-cs2-major-copenhagen-2024-europe-rmr-b
official : 13 - 6
parser : 10 - 9

Code to reproduce

var path = "test.dem";

var demo = new DemoParser();

var winnerCounts = new Dictionary<int, int>();

demo.Source1GameEvents.RoundEnd += e =>
{
    Console.WriteLine($"Round end: winner {(CSTeamNumber)e.Winner}");

    if (winnerCounts.TryGetValue(e.Winner, out int count))
        winnerCounts[e.Winner] = count + 1;
    else
        winnerCounts[e.Winner] = 1;
};

await demo.StartReadingAsync(File.OpenRead(path), default);

while (await demo.MoveNextAsync(default))
{
}

Console.WriteLine();

foreach (var pair in winnerCounts)
{
    Console.WriteLine($"winner {pair.Key}: {pair.Value}");
}

Console.WriteLine();
Console.WriteLine("Game rules round results:");
Console.WriteLine(string.Join("\n", demo.GameRules.CSRoundResults));

Console.WriteLine("\nFinished!");

Affected demos

No response

CDemoFileInfo command is not invoked

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

I was trying to get total duration (total tick count) of demo file, and found that CDemoFileInfo demo command has this info.

However, this command is never invoked. I am not sure if this is a bug in the library, or the command is simply not in demo file.

Anyway, is there any way to find out total duration of the match (without parsing entire file) ?

Code to reproduce

var demo = new DemoParser();
demo.DemoEvents.DemoFileInfo += (info) => Console.WriteLine("got DemoFileInfo");
await demo.Start(stream, default);

Affected demos

No response

Round Start and Round End events not called in new update on new demos.

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Title really says all, but here is a demo if you need it:

Code example, player death event will run as normal but no RoundStart, RoundEnd or GameStart will be called, events that is rather needed to display round changes etc.

Code to reproduce

public static async Task Main(string[] args)
    {
        var demo = new DemoParser();
        demo.Source1GameEvents.PlayerDeath += e =>
        {
            Console.WriteLine($"{e.Attacker?.PlayerName} [{e.Weapon}] {e.Player?.PlayerName}");
        };

        demo.Source1GameEvents.RoundStart += e =>
        {
            Console.WriteLine($"Round start");
        };
        demo.Source1GameEvents.GameStart += e =>
        {
            Console.WriteLine($"Game start");
        };
        demo.Source1GameEvents.RoundEnd += e =>
        {
            Console.WriteLine($"Round end");
        };

        await demo.ReadAllAsync(File.OpenRead("D:\\Testing\\Sæson 25\\3divC\\goatgang_vs_UnsignedPuggers_map1_de_ancient_2024-02-07_20-01-15_6599f0ecca2465f94caa2467_1707336075213\\6599f0ecca2465f94caa2467_0_1707336075213.dem"));

        Console.WriteLine("\nFinished!");
    }

Affected demos

https://we.tl/t-3WP54hnNAc
Apologizes for 2 different link types, i realized google made alot more sense after uploading the first one 😓
https://drive.google.com/file/d/1QCmjuZsS2QkglWWbccO77I2Ag6R5bJJ8/view?usp=sharing

Unrecognised serializer field: m_firePositions

When attempting to use the example from readme.md with a Dathost match API demo we receive the following error. The demo in question can be found here

Unhandled exception. System.NotSupportedException: Unrecognised serializer field: m_firePositions
   at DemoFile.Sdk.CInferno.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
   at DemoFile.Sdk.CFireCrackerBlast.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
   at DemoFile.Sdk.DecoderSet.<>c__DisplayClass4_0`1.<CreateDecoder>b__1(SerializableField field)
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray()
   at DemoFile.Sdk.DecoderSet.CreateDecoder[T](SerializerKey serializerKey)
   at DemoFile.Sdk.DecoderSet.GetDecoder[T](SerializerKey serializerKey)
   at DemoFile.Sdk.DecoderSet.GetDecoder(String className)
   at DemoFile.DemoParser.OnDemoClassInfo(CDemoClassInfo msg)
   at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands msgType, ReadOnlySpan`1 buffer)
   at DemoFile.DemoParser.Start(Stream stream, CancellationToken cancellationToken)
   at Program.Main(String[] args) in D:\Users\syoung\Downloads\demoscrape3\demoscrape3\Program.cs:line 18
   at Program.<Main>(String[] args)

InvalidProtocolBufferException

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

For reproducing this issue you need parse special demo. I`m gonna send you demo file tomorrow. May be you can find some decisions about this. I am using latest version of DemoFile

Google.Protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
   at Google.Protobuf.ParsingPrimitives.ParseTag(ReadOnlySpan`1& buffer, ParserInternalState& state)
   at CNETMsg_Tick.pb::Google.Protobuf.IBufferMessage.InternalMergeFrom(ParseContext& input)
   at DemoFile.PacketEvents.ParseNetMessage(Int32 msgType, ReadOnlySpan`1 buf)
   at DemoFile.DemoParser.OnDemoPacket(CDemoPacket msg)
   at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands msgType, ReadOnlySpan`1 buffer)
   at DemoFile.DemoParser.Start(Stream stream, CancellationToken cancellationToken)
   at Questera.CsGo.Parser.Parser.ParseAsync(String fileName, Int64 userSteamId) in /src/CsGoProvider/Questera.CsGo.Parser/Parser.cs:line 44
   at Questera.CsGo.Flows.DownloadMatchStatistics.Steps.ParseDemoStep.RunAsync() in /src/CsGoProvider/Questera.CsGo.Flows/DownloadMatchStatistics/Steps/ParseDemoStep.cs:line 29
   at CyberCube.Common.TinyFlow.Services.FlowExecutor.ExecuteStepAsync[TData](FlowStep step, TData data)
   at CyberCube.Common.TinyFlow.Services.FlowExecutor.ExecuteStepsAsync[TData,TResult](IList`1 steps, TData data)
   at CyberCube.Common.TinyFlow.Services.FlowExecutor.ExecuteAsync[TFlow,TData,TResult](TData data)
   at Questera.CsGo.Handlers.Statistics.DownloadMatchStatisticsHandler.Handle(Request request, CancellationToken cancellationToken)

Code to reproduce

No response

Affected demos

No response

CSoundEventPathCornerEntity failed to parse

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

System.Exception: Attempted to create unknown entity: CSoundEventPathCornerEntity
at DemoFile.DemoParser.<>c__DisplayClass115_0.b__1(EntityContext context)
at DemoFile.DemoParser.OnPacketEntities(CSVCMsg_PacketEntities msg)
at DemoFile.PacketEvents.ParseNetMessage(Int32 msgType, ReadOnlySpan1 buf) at DemoFile.DemoParser.OnDemoPacket(CDemoPacket msg) at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands msgType, ReadOnlySpan1 buffer, Boolean isCompressed)
at DemoFile.DemoParser.MoveNextCoreAsync(EDemoCommands msgType, Boolean isCompressed, Int32 size, CancellationToken cancellationToken)
at DemoFile.DemoParser.ReadAllAsync(Stream stream, CancellationToken cancellationToken)****

Code to reproduce

No response

Affected demos

http://replay186.valve.net/730/003688111663585689781_1784675935.dem.bz2

Performance issues

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Performance issues, in the Git you state that parsing a whole 1 hour demo can be done in 1.5 seconds.

i have a pc with 64 gigs of ddr5
AMD Ryzen 7 7800X3D
RTX 3070ti
running on a nvme drive.

but i am getting 8-10 seconds to just run this demo that ended 13-4. like 20 minutes of gameplay. Am i doing something wrong here? it seems rather slow.

Test was done on a console application on .net 8 with the below code.

Code to reproduce

public static async Task Main(string[] args)
    {
        await using var fileStream = File.OpenRead(demoFilePath);
        Stopwatch stopwatch3 = new Stopwatch();
        stopwatch3.Start();
        bool bombExploded = false;
        var demo = new DemoParser();

        demo.Source1GameEvents.BombExploded += e =>
        {
            Console.WriteLine("Bomb exploded");
            bombExploded = true;
        };

        demo.Source1GameEvents.RoundStart += e =>
        {

            if (!demo.GameRules.HasMatchStarted)
            {
                //PowerLigaManager.Log("ROund end called but we have an issue, it seems match was not started yet");
                return;
            }
            Console.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
        };

            demo.Source1GameEvents.CsWinPanelMatch += e =>
        {
            Console.WriteLine("WIn match has ended");
        };
        await demo.ReadAllAsync(fileStream); 
        stopwatch3.Stop();
        Console.WriteLine("Time elapsed: {0}", stopwatch3.Elapsed);
        Console.WriteLine("\nFinished!");
    }

Affected demos

https://drive.google.com/file/d/1Oi80oKMHf2DvIYSI42UL4kxvICLMGj2G/view?usp=sharing

NativeAOT support

It would be great if NativeAOT compilation would be supported by the library.

This would give the benefits of faster execution, and possibility to build native library (DLL) which can then be linked by any other program (regardless of programming language).

Right now, I get this error for most of demos:

Unhandled Exception: Google.Protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
   at Google.Protobuf.ParsingPrimitives.ParseTag(ReadOnlySpan`1&, ParserInternalState&) + 0x133
   at CNETMsg_Tick.pb::Google.Protobuf.IBufferMessage.InternalMergeFrom(ParseContext&) + 0x1f
   at Google.Protobuf.MessageExtensions.MergeFrom(IMessage, ReadOnlySpan`1, Boolean, ExtensionRegistry) + 0xdb
   at Google.Protobuf.MessageParser`1.ParseFrom(ReadOnlySpan`1) + 0x2f
   at DemoFile.PacketEvents.ParseNetMessage(Int32, ReadOnlySpan`1) + 0xd3
   at DemoFile.DemoParser.OnDemoPacket(CDemoPacket msg) + 0x328
   at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands, ReadOnlySpan`1) + 0x1cf
   at DemoFile.DemoParser.<Start>d__46.MoveNext() + 0x824
--- End of stack trace from previous location ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x20
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xb6
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x42
   at Program.<Main>d__0.MoveNext() + 0x2b6
--- End of stack trace from previous location ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x20
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0xb6
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task) + 0x42
   at Program.<Main>(String[]) + 0x20
   at DemoFile.Example.Basic!<BaseAddress>+0x44a35b

It could be a bug in Google.Protobuf library, but I did not investigate further, for now.

Unexpected EOF aka InvalidOperationException on 2 demos

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

I have 2 demos that are resulting in EOF. Demos works fine on 2D demo sites and in cs2 itself. It seems to be a contained error to this parser.

Code to reproduce

public static async Task Main(string[] args)
    {
        bool bombExploded = false;
        var demo = new DemoParser();

        demo.Source1GameEvents.BombExploded += e =>
        {
            Console.WriteLine("Bomb exploded");
            bombExploded = true;
        };
        demo.Source1GameEvents.PlayerDeath += e =>
        {
            Console.WriteLine($"{e.Attacker?.PlayerName} from {e.Attacker?.Team} [{e.Weapon}] {e.Player?.PlayerName} from {e.Player?.Team}");
        };


        demo.EntityEvents.CCSGameRulesProxy.AddChangeCallback(proxy => proxy.GameRules?.RoundStartCount, (proxy, _, _) =>
        {
            var roundStartCountStuff = proxy.GameRules!.RoundStartCount;
            var roundStartCountStuff2 = proxy.GameRules!.RoundStartRoundNumber;
            bombExploded = false;

            Console.WriteLine($"count 1{roundStartCountStuff} count 2 {roundStartCountStuff2} ct score: {demo.TeamCounterTerrorist.Score} t score: {demo.TeamTerrorist.Score}");
        });

        demo.Source1GameEvents.CsWinPanelMatch += e =>
        {
            Console.WriteLine("WIn match has ended");
        };

        await demo.ReadAllAsync(File.OpenRead(baseDirectory + "\\FatalUtility_vs_EgedalEsport_map1_de_nuke_2024-01-16_18-00-45_6599f0f1ca2465f94caa2607_1705428045421.dem"));
        //await demo.
        Console.WriteLine("\nFinished!");
    }

Affected demos

https://drive.google.com/drive/folders/1dgPmwyjy5UlEsDW-eF8rdyRNFxAmEzCa?usp=sharing

Userid event vars treated as entity indexes

This error pops up when trying to load a CS2 match from a .dem file:

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

Example code used:

using DemoFile;

internal class Program
{
    public static async Task Main(string[] args)
    {
        var path = args.SingleOrDefault() ?? throw new Exception("Expected a single argument: <path to .dem>");

        var demo = new DemoParser();
        demo.Source1GameEvents.PlayerDeath += e =>
        {
            Console.WriteLine($"{e.Attacker?.PlayerName} [{e.Weapon}] {e.Player?.PlayerName}");
        };

        await demo.Start(File.OpenRead(path));

        Console.WriteLine("\nFinished!");
    }
}

The error seems to appear when trying to access the e.Player or e.Attacker properties.

I'm in WinForms using version NET version .NET 7.0.

I'm leaving a link to the file used here.

Parsing demofile throws 'Unrecognised serializer field: m_ePlayerFireEvent'

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

I'm trying to run the KillFeed sample with a demo from 2023-12-04 but it throws the error:

'Unrecognised serializer field: m_ePlayerFireEvent'

I've tried to run the SdkGen, but it also throws the same error.

Code to reproduce

No response

Affected demos

http://replay201.valve.net/730/003654429170693308593_1898149149.dem.bz2

`CBaseCSGrenade.GrenadeCount` is always 1

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

CBaseCSGrenade.GrenadeCount is always 1, meaning you can't detect if player has 2 flashbangs.

I tried reading Clip1, Clip2, other entries in ReserveAmmo array, and checking if there are multiple CFlashbang entities.

It happens in all demos that I tried (~ 15 of them).

Code to reproduce

var path = ...;
        var demo = new DemoParser();
        var stream = new MemoryStream(File.ReadAllBytes(path));

        await demo.StartReadingAsync(stream, default);

        while (await demo.MoveNextAsync(default))
        {
            var pawns = demo.Players
                .Where(p => p.Pawn != null && p.Pawn.IsAlive)
                .Select(p => (CCSPlayerPawn)p.Pawn!);
               
            var weapons = pawns
                .SelectMany(p => p.Weapons)
                .Where(w => w.ReserveAmmo.Length > 1)
                .Where(w => w.ReserveAmmo[1] > 1)
                .OfType<CBaseCSGrenade>()
                .ToArray();

            if (weapons.Length == 0)
                continue;

            Console.WriteLine($"[{demo.CurrentDemoTick.Value}]\n{string.Join("\n", weapons.Select(w => $"{w.OwnerEntityHandle.Value}, {w.AttributeManager?.Item?.Name}, clip {w.Clip1} {w.Clip2}, ammo {w.ReserveAmmo.ElementAtOrDefault(0)} {w.ReserveAmmo.ElementAtOrDefault(1)} {w.ReserveAmmo.ElementAtOrDefault(2)}"))}\n\n");
            Console.Out.Flush();
        }

        Console.WriteLine("\nFinished!");

Affected demos

No response

Seeking support

There should be seeking support in the library.

It seems that demos already have enough data for this to be possible, eg. snapshots on every 60 sec, round-start ticks, etc...

Backward seeking through time should also be possible.

For best usability, I think library should have an API like this:

DemoParser.SaveToSnapshot(Snapshot s);
DemoParser.RestoreFromSnapshot(Snapshot s);

class Snapshot
{
    List<Entity> entities;
    List<StringTable> stringTables;
    int tick;
    long streamPosition;
    CDemoFileHeader header;

    ...
}

Support new field m_PredictedDamageTags

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

Looks like there is new field which is not supported by decoders. Please, look at the attached demo

Code to reproduce

No response

Affected demos

broken_demo.zip

Parser is not working after latest CS2 update

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

CS2 updated recently, and none of the demos from HLTV (after 7th February) are working.

Currently they give the following error:

System.NotSupportedException: Unrecognised serializer field: m_hSequence
   at DemoFile.Sdk.ISkeletonAnimationController.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0xb4
   at DemoFile.Sdk.CBaseAnimGraphController.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x64b
   at DemoFile.Sdk.CBodyComponentBaseAnimGraph.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x175
   at System.Linq.Enumerable.SelectArrayIterator`2.Fill(ReadOnlySpan`1, Span`1, Func`2) + 0x45
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray() + 0x8d
   at DemoFile.Sdk.DecoderSet.CreateDecoder[T](SerializerKey) + 0xe2
   at DemoFile.Sdk.DecoderSet.GetDecoder[T](SerializerKey) + 0x77
   at DemoFile.Sdk.CBodyComponent.CreateDowncastDecoder(SerializerKey, DecoderSet, Func`1&) + 0x2fe
   at DemoFile.Sdk.CBaseEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0xd8
   at DemoFile.Sdk.CBaseModelEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x12a6
   at DemoFile.Sdk.CBaseAnimGraph.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x47c
   at DemoFile.Sdk.CBaseFlex.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x214
   at DemoFile.Sdk.CEconEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x4e6
   at DemoFile.Sdk.CBasePlayerWeapon.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x40a
   at DemoFile.Sdk.CCSWeaponBase.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0xf18
   at DemoFile.Sdk.CCSWeaponBaseGun.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet) + 0x1d4
   at System.Linq.Enumerable.SelectArrayIterator`2.Fill(ReadOnlySpan`1, Span`1, Func`2) + 0x45
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray() + 0x8d
   at DemoFile.Sdk.DecoderSet.CreateDecoder[T](SerializerKey) + 0xe2
   at DemoFile.Sdk.DecoderSet.GetDecoder[T](SerializerKey) + 0x77
   at DemoFile.Sdk.DecoderSet.TryGetDecoder(String, Type&, SendNodeDecoder`1&) + 0x3d20
   at DemoFile.Sdk.DecoderSet.GetDecoder(String) + 0x27
   at DemoFile.DemoParser.OnDemoClassInfo(CDemoClassInfo msg) + 0x2c9
   at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands, ReadOnlySpan`1) + 0x198
   at DemoFile.DemoParser.<MoveNextAsync>d__61.MoveNext() + 0x496

Code to reproduce

No response

Affected demos

No response

Usage from real-time application

How would I use the library from real-time application which has it's own Message Loop (eg. GUI application) ?

Right now, the only way to parse demo file would be to call async method which would run in a different thread. It would be much better if the control of parsing is done by user of the library. That way, he can pause the parser, move to next tick, seek through history, etc.

Basically, a non-async version which allows the user to advance through demo file tick by tick.

I already implemented this myself (with additional API, no breaking changes), and would like to open PR, but I wanted to ask you what you think about it ?

Is it possible to retrieve UTC time of match start?

Hi, decided to ask this via an issue.

I've been looking through the API but have not found a way to retrieve a UTC for the match start, I don't know if this is a part of the demo file though. I assumed since csstats and leetify for example do know when matches start (but this could also be because they receive the demos from Valve and in that delivery, an UTC start timestamp might get included? Don't know).
Does anyone know if there is a way to get the UTC timestamp of a match via the demo?

Thanks in advance, this parser has already helped me a lot! ❤️

Seeking fails for some demos

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

In the specified demo, seeking fails when trying to seek past the tick 192001, with error:

Unhandled exception. DemoFile.InvalidDemoException: Could not find DemFullPacket at tick 192001
   at DemoFile.DemoParser.SkipToFullPacketTickAsync(DemoTick targetTick, CancellationToken cancellationToken) in demofile-net\src\DemoFile\DemoParser.FullPacket.cs:line 145
   at DemoFile.DemoParser.SeekToTickAsync(DemoTick targetTick, CancellationToken cancellationToken) in demofile-net\src\DemoFile\DemoParser.FullPacket.cs:line 97
   at Program.Main(String[] args) in demofile-net\examples\DemoFile.Example.Basic\Program.cs:line 155
   at Program.<Main>(String[] args)

Code to reproduce

var stream = new MemoryStream(File.ReadAllBytes(path));

await demo.StartReadingAsync(stream, default);

await demo.SeekToTickAsync(new DemoTick(192000), default); // works for anything below 192001

Console.WriteLine($"seeked to tick {demo.CurrentDemoTick.Value}");

await demo.SeekToTickAsync(new DemoTick(193000), default); // fails

Console.WriteLine($"seeked to tick {demo.CurrentDemoTick.Value}");

Affected demos

https://www.hltv.org/matches/2372034/mouz-nxt-vs-space-cct-season-2-europe-series-3

map : Vertigo

Also happens on some recent Matchmaking demos.

Failed parsing with bloodType

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

System.NotSupportedException: Unrecognised serializer field: m_nBloodType
at DemoFile.Sdk.CEntityInstance.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
at DemoFile.Sdk.CBaseEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
at DemoFile.Sdk.CBaseModelEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
at DemoFile.Sdk.CBaseAnimGraph.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
at DemoFile.Sdk.CEconEntity.CreateFieldDecoder(SerializableField field, DecoderSet decoderSet)
at System.Linq.Enumerable.SelectArrayIterator2.Fill(ReadOnlySpan1 source, Span1 destination, Func2 func)
at System.Linq.Enumerable.SelectArrayIterator2.ToArray() at DemoFile.Sdk.DecoderSet.CreateDecoder[T](SerializerKey serializerKey) at DemoFile.Sdk.DecoderSet.GetDecoder[T](SerializerKey serializerKey) at DemoFile.Sdk.DecoderSet.TryGetDecoder(String className, Type& classType, SendNodeDecoder1& decoder)
at DemoFile.DemoParser.OnDemoClassInfo(CDemoClassInfo msg)
at DemoFile.DemoEvents.ReadDemoCommandCore[T](Action1 callback, MessageParser1 parser, ReadOnlySpan1 buffer, Boolean isCompressed) at DemoFile.DemoEvents.ReadDemoCommand(EDemoCommands msgType, ReadOnlySpan1 buffer, Boolean isCompressed)
at DemoFile.DemoParser.MoveNextCoreAsync(EDemoCommands msgType, Boolean isCompressed, Int32 size, CancellationToken cancellationToken)
at DemoFile.DemoParser.StartReadingAsync(Stream stream, CancellationToken cancellationToken)
at DemoFile.DemoParser.ReadAllAsync(Stream stream, CancellationToken cancellationToken)

Code to reproduce

No response

Affected demos

http://replay189.valve.net/730/003686249481730261055_0831476125.dem.bz2

Events not firing for player who was initially disconnected

I'm attempting to load a demo file depicting a scenario where a player is initially disconnected. Prior to the start of the first round, the previously missing player eventually connects.

However, when attempting to display the names of the spawning players, the player who was initially absent does not seem to be included in the recorded events of the game demo.

Example code used:

demo.Source1GameEvents.PlayerSpawn += e =>
{
    if (e.Player != null)
    {
        Console.WriteLine($"Player spawn: {e.Player.SteamID} | {e.Player.PlayerName}");
    }
};

demo.Source1GameEvents.RoundEnd += e =>
{
    Console.Write("\n");
};

The expected output from the previous code should be of a list containing 9 players for each round played. This list should reflect the absence of the player who was initially disconnected.

I'm in WinForms using version NET version .NET 7.0.

I'm leaving a link to the file used here.

EDIT:

Player death events don't seem to be working for the initially disconnected player either:

demo.Source1GameEvents.PlayerDeath += e =>
{
    if (e.Player != null)
    {
        Console.WriteLine($"Player death: {e.Player.SteamID} | {e.Player.PlayerName}");
    }
};

Maybe all events are not working for this player?

Demos broken with the latest update.

Research

  • I've already searched and I could not find an existing issue or discussion about this issue.

Description

The new update broke demos again.
Here are some of the parsing errors:

  1. Exception: Exception of type 'System.ArgumentOutOfRangeException' was thrown. (Parameter 'msgType')
    Actual value was DemMax.

  2. DemoFile.InvalidDemoException: Unexpected demo command: 81

Code to reproduce

public static class Program
{
    //public static string flashNoWork = "C:\\Users\\xguys\\Desktop\\Flash issues\\crystal_vulture.dem";
    public static string flashWork = "C:\\Users\\xguys\\Desktop\\demoinfo\\661577d64c197423489bdd0f_0_1714410167560.dem";




    public static async Task Main(string[] args)
    {
        try
        {
            Stopwatch stopwatch3 = new Stopwatch();
            stopwatch3.Start();
            MemoryStream _fileStream;
            byte[] _demoBytes;
            //_demoBytes = File.ReadAllBytes(demoFilePath);
            _demoBytes = await File.ReadAllBytesAsync(flashWork);
            _fileStream = new MemoryStream(_demoBytes);

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var demo = new DemoParser();

            demo.Source1GameEvents.BombExploded += e =>
            {
                Console.WriteLine("Bomb exploded");
            };

            demo.Source1GameEvents.PlayerBlind += e =>
            {
                Console.WriteLine("Player blind");

            };

            demo.Source1GameEvents.TrPlayerFlashbanged += e =>
            {
                Console.WriteLine("Player flashbanged");
            };

            //demo.Source1GameEvents.FlashbangDetonate += e =>
            //{
            //    Console.WriteLine("Flashbang detonate");
            //};

            demo.Source1GameEvents.RoundStart += e =>
            {

                if (!demo.GameRules.HasMatchStarted)
                {
                    //PowerLigaManager.Log("ROund end called but we have an issue, it seems match was not started yet");
                    return;
                }
                Console.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
                Debug.WriteLine("Round start called at round ct:" + demo.TeamCounterTerrorist.Score + " and t:" + demo.TeamTerrorist.Score);
            };

            demo.Source1GameEvents.CsWinPanelMatch += e =>
            {
                Console.WriteLine("WIn match has ended");
            };
            await demo.ReadAllAsync(_fileStream);
            stopwatch3.Stop();

            stopwatch.Stop();
            Console.WriteLine("Time elapsed after file data: {0}", stopwatch.Elapsed);
            Console.WriteLine("Time elapsed: {0}", stopwatch3.Elapsed);
            Console.WriteLine("\nFinished!");
        } catch (Exception e)
        { 
            Console.WriteLine("Exception: " + e.Message.ToString());
        }

        Console.ReadKey();
    }

Affected demos

https://drive.google.com/file/d/1xPpl-ZU_IljuPnEW3X-jKrm4-XRhiGk9/view?usp=sharing

Multi-threaded parsing

Library should be able to parse 1 demo using multiple threads at the same time.

This would bring huge performance improvement, which scales linearly with number of CPUs.

The way I imagined it, is to split demo into multiple parts (eg. based on round-start ticks), then send each part to a different thread to process it.

The prerequisite for this is to have seeking support (#47), where each thread would get a snapshot of demo at the tick where it's starting to process.

I think it would reduce the time to process 1 demo file to as low as 0.3 seconds.

Can I get player profile picture?

I have a need in getting player pictures. Can i get those via DemoParser?
I can use Steam API and SteamID from parser but would be easier if they are already parsed.

I realy like the library. Great work

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.