saul / demofile-net Goto Github PK
View Code? Open in Web Editor NEWBlazing fast cross-platform demo parser library for Counter-Strike 2, written in C#.
License: MIT License
Blazing fast cross-platform demo parser library for Counter-Strike 2, written in C#.
License: MIT License
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.
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.
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.
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.
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));
Every demo, but example works only with navi-javelins-vs-9-pandas-fearless-m1-mirage.dem
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! ❤️
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?
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) ?
var demo = new DemoParser();
demo.DemoEvents.DemoFileInfo += (info) => Console.WriteLine("got DemoFileInfo");
await demo.Start(stream, default);
No response
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.
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)
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.
No response
http://replay201.valve.net/730/003654429170693308593_1898149149.dem.bz2
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)
No response
No response
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 ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.