demoinfo
demoinfo copied to clipboard
ServerInfo
Added ServerInfo, implemented TickInterval, added molotov_projectile to MapEquipment
Thanks for the pull request!
Things I noticed:
-
While indentation throughout this project is already severely screwed up, please at least try to keep things consistent within your contribution. There's no reason to mix tabs and spaces in a file you're adding (as opposed to changing).
-
The molotov change is unrelated and could be a separate pull request. This is not a big deal, but we could merge this simple and straightforward fix immediately while the bigger changes require some discussion.
-
I assume the
UgcMapIdfield is working at the moment because the upper 32 bits are always zero. Because if that, I'll fix the varint code to support 64 bits when merging this (this is mostly a note for myself but as I'm short on free time these days, this will probably be faster if you decide to take a stab at this). -
Please try to avoid
staticvariables. Using them correctly is incredibly hard and in fact, your code introduces a rather critical bug: Consider a multithreaded application that uses demoinfo. The static fields would always contain the data of the "current" demo but this breaks when multiple demos are parsed concurrently!Instead, please try to propagate the data back to the
DemoParserobject:// In DemoParser.cs: public float TickTime { get; internal set; } // obviously derive TickRate from that // In ServerInfo.cs: public void Parse(IBitStream bitstream, DemoParser parser) { // [parsing code] parser.TickTime = TickInterval; }
Ok later tonight I'll make the changes and submit a new request and a separate one for the molotov change. The tabbing is different in the upload than it is in Visual Studio for some reason.
Espeically the static needs to be fixed, because else it is an outright bug at the moment.
I made the changes but I'm holding off on the pull request for now because after trying some more demos I've found that it's not always the case that TickInterval = Header.PlaybackTime / Header.PlaybackFrames
For instance in the demo for this match http://www.hltv.org/?pageid=188&matchid=28372:
TickInterval = 1/128, but PlaybackTime/PlaybackFrames = 1/64
The tabbing is different in the upload than it is in Visual Studio for some reason.
That's because most IDEs (including Visual Studio) display tabs as 4 spaces - even though they're defined as 8 spaces which is also the way GitHub renders them.
submit a new request
You can just force-push onto this one - GitHub handles that just fine.
For some reason I didn't realize this before, but tick_interval is a server metric while PlaybackTime and PlaybackFrames are metrics for the demo itself. I've been trying to find something that I could possibly compare tick_interval to in order to get the demo tickrate, but I haven't come up with anything. Does anyone have any suggestions?
P.S. I noticed that after loading the demo there was some console output that read as follows: "Attempting to heal incomplete demo file: assuming 507904 ticks, 507707 frames, 3968.00 seconds @128Hz"
This leads me to believe that valve is imputing the values rather than reading them directly from the stream.
I feel like stating the obvious but server tick rate and demo snapshot rate are stored in the demo header. You just have to compute them from the (frames,ticks,time) triplet.
Some headers get corrupted, so we're trying to find a workaround. See #91
In that case the CSVCMsg_ServerInfo::tick_interval gives the server tick rate. The snapshot rate can easily be deduce by CNETMsg_Tick::tick message step/increment.
In other words the number of ticks and frames should be simply the values of the tick and seq-in/seq-out field of the last packet of the demo. It should be consistant CNETMsg_Tick messages which should give you a way to verify everything is in order.
I'm not sure what you mean by the seq-in/seq-out field, but it sounds like what you're suggesting is to compare tick_interval to the difference between sequential ticks. So, for instance, a 64 tick demo recorded on a 128 tick server should have a gap of two between the ticks.
Yes, there is a gap in the ticks. That's not very important if you only intend to fix the demo. All you need is to count the ticks (last tick number minus first ``real tick'' number) and the number of frames (should be more or less the number of CNETMsg_Tick messages). Then compute the playtime with the server tick rate in the CSVCMsg_ServerInfo message. Update the demo header then (optionally but it's cleaner) close the demo with a stop demo message (type 7) which should be missing in those broken demos.
What I call seq-in and seq-out are the 2 32bit integers part of the sequence info stored in every demo packet messages (demo message type 2). Basically you don't have to decode the demo packet data (protobuf) to get those, you just need to parse the demo messages. However AFAIK the only way to get the server tick rate (beside the header info) is to decode the CSVCMsg_ServerInfo message. Because the CNETMsg_Tick::tick does not start with 0 it's easier to locate the first ``real'' packet using the demo messages tick and sequence info (tick == 0 and seq-in/out == 1).
Ok that makes sense. I'll get my feet wet with the bitstream internals and see if I can figure out how to access the demo messages.
Note that messing with bitstream internals shouldn't be necessary for this particular part - the sequence numbers are right here.
Based on some comments on gitter it looks like this is still a problem so I took a stab at it tonight. I now have what appears to be a working version that results in the same imputed values as what valve has when the demo is viewed in game. I still need to check a few things and clean up the code.
The bad news is that, at least with my test case of the one corrupted demo I'm working with, the parser still tries to read past the end of the file which results in a System.IO.EndOfStreamException. When viewed in game the demo ends abruptly probably somewhere within a minute or so early (what I assume is the last round, 5v3).
How do you want the exception handled? Should I leave it as is and let end users decide what to do? Or do you want it processed in some way?
Correctly parsing corrupt demos is pretty much impossible and not a goal of this project. Throwing an EndOfStreamException for a demo that ends prematurely is actually perfect.
Correctly parsing corrupt demos is pretty much impossible and not a goal of this project
The reason for this boils down to two points:
- Work vs. Reward: Most demos aren't broken, and if they are broken it's in many different ways
- Fixing bugs of corrupt demos might even introduce bugs for 'normal' demos, what I would hate to do.
Also, as @main-- said: EndOfStreamException is perfect here.
If anyone has additional corrupted demos they can link that would be helpful.
I figured it would be best to leave the exception as it is, but I just thought I would check.
The branch is ready to be tested. The main thing I'm worried about is making sure that no variables are being modified or events raised on the first pass through the demo and that nothing is being duplicated when the second bitstream is instantiated and set to the end of the header. As far as I can tell that's the case, but I'm not certain.
It looks like there have been some more comments on this problem. Sorry it has taken me so long to get back to it. I have some time to work on this again, and I have a much better solution.
Here's how it would work:
We want: Tickrate = Demo Frames/Second
From ServerInfo we have: TickInterval = Seconds/Server Tick
We can look at the difference in IngameTicks between consecutive TickDone events to get: TickGap = Server Ticks/Demo Frame
So the result is: TickInterval * TickGap = 1/Tickrate
There is one small problem with this. TickGap isn't consistent for the first few ticks. I've looked at about 15 demos and the highest I've seen is an IngameTick of 12 before it became consistent. How would you like this handled? I figure the best way would be to leave the timings as they currently are for the first 50 ticks or so and throw a warning so that users know their timing data for those ticks will be wrong.
I forced over the old junk, so everything in these recent commits should stem from your master.
I'm not really sure what the standard is for giving this kind of warning. #warning didn't seem entirely appropriate, so I just used Console.Writeline.