From 2cca5420dff328dc0d2c5a47dcd90ae568cb80e6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2017 11:08:20 -0700 Subject: [PATCH 1/5] Added ServerInfo and TickInterval --- DemoInfo/DP/FastNetmessages/ServerInfo.cs | 137 ++++++++++++++++++++++ DemoInfo/DemoParser.cs | 5 + 2 files changed, 142 insertions(+) create mode 100644 DemoInfo/DP/FastNetmessages/ServerInfo.cs diff --git a/DemoInfo/DP/FastNetmessages/ServerInfo.cs b/DemoInfo/DP/FastNetmessages/ServerInfo.cs new file mode 100644 index 0000000..e388741 --- /dev/null +++ b/DemoInfo/DP/FastNetmessages/ServerInfo.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; + +namespace DemoInfo +{ + public struct ServerInfo + { + public Int32 Protocol; + public Int32 ServerCount; + public bool IsDedicated; + public bool IsOfficialValveServer; + public bool IsHltv; + public bool IsReplay; + public bool IsRedirectingToProxyRelay; + public Int32 COs; + public UInt32 MapCrc; + public UInt32 ClientCrc; + public UInt32 StringTableCrc; + public Int32 MaxClients; + public Int32 MaxClasses; + public Int32 PlayerSlot; + public float TickInterval; + public string GameDir; + public string MapName; + public string MapGroupName; + public string SkyName; + public string HostName; + public UInt32 PublicIp; + public UInt64 UgcMapId; + + + public void Parse(IBitStream bitstream, DemoParser parser) + { + while (!bitstream.ChunkFinished) + { + var desc = bitstream.ReadProtobufVarInt(); + var wireType = desc & 7; + var fieldnum = desc >> 3; + + if (wireType == 5) + { + if (fieldnum == 14) + { + parser.TickInterval = bitstream.ReadFloat(); + } + else + { + var val = bitstream.ReadInt(32); + switch (fieldnum) + { + case 8: + MapCrc = val; + break; + case 9: + ClientCrc = val; + break; + case 10: + StringTableCrc = val; + break; + } + } + } + else if (wireType == 2) + { + var val = bitstream.ReadProtobufString(); + + switch (fieldnum) + { + case 15: + GameDir = val; + break; + case 16: + MapName = val; + break; + case 17: + MapGroupName = val; + break; + case 18: + SkyName = val; + break; + case 19: + HostName = val; + break; + } + } + else if (wireType == 0) + { + var val = bitstream.ReadProtobufVarInt(); + var boolval = (val == 0) ? false : true; + + switch (fieldnum) + { + case 1: + Protocol = val; + break; + case 2: + ServerCount = val; + break; + case 3: + IsDedicated = boolval; + break; + case 4: + IsOfficialValveServer = boolval; + break; + case 5: + IsHltv = boolval; + break; + case 6: + IsReplay = boolval; + break; + case 7: + COs = val; + break; + case 11: + MaxClients = val; + break; + case 12: + MaxClasses = val; + break; + case 13: + PlayerSlot = val; + break; + case 20: + PublicIp = (uint)val; + break; + case 21: + IsRedirectingToProxyRelay = boolval; + break; + case 22: + UgcMapId = (uint)val; + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/DemoInfo/DemoParser.cs b/DemoInfo/DemoParser.cs index f24c425..fcdfef7 100644 --- a/DemoInfo/DemoParser.cs +++ b/DemoInfo/DemoParser.cs @@ -474,6 +474,11 @@ public float ParsingProgess { /// The current tick. public int CurrentTick { get; private set; } + /// + /// The tickrate *of the server* + /// + public float TickInterval { get; internal set; } + /// /// The current ingame-tick as reported by the demo-file. /// From c12451b8f47dfb8aa81a6017781ea875608caf10 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2017 12:08:46 -0700 Subject: [PATCH 2/5] HeaderCorrupted event added --- DemoInfo/DemoParser.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/DemoInfo/DemoParser.cs b/DemoInfo/DemoParser.cs index fcdfef7..fcaf852 100644 --- a/DemoInfo/DemoParser.cs +++ b/DemoInfo/DemoParser.cs @@ -34,6 +34,11 @@ public class DemoParser : IDisposable /// public event EventHandler HeaderParsed; + /// + /// Raised when header data is corrupted and timings are zero and null. + /// + public event EventHandler HeaderCorrupted; + /// /// Occurs when the match started, so when the "begin_new_match"-GameEvent is dropped. /// This usually right before the freezetime of the 1st round. Be careful, since the players @@ -531,6 +536,15 @@ public void ParseHeader() Header = header; + if (header.PlaybackTime == 0) + { + Console.WriteLine("WARNING: The header for this demo file is corrupted. TickRate, TickTime, ParsingProgress, CurrentTime will be 0 for the first 50 ticks. PlaybackFrames, PlaybackTicks, PlaybackTime will always be 0. HeaderCorrupted event triggered."); + + if (HeaderCorrupted != null) + { + HeaderCorrupted(this, new HeaderParsedEventArgs(Header)); + } + } if (HeaderParsed != null) HeaderParsed(this, new HeaderParsedEventArgs(Header)); @@ -1467,6 +1481,7 @@ public void Dispose () this.FireNadeWithOwnerStarted = null; this.FlashNadeExploded = null; this.HeaderParsed = null; + this.HeaderCorrupted = null; this.MatchStarted = null; this.NadeReachedTarget = null; this.PlayerKilled = null; From 0e178dc748facfae83cfe3a60ae54ecaf3acb473 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2017 13:37:23 -0700 Subject: [PATCH 3/5] forgot to actually parse ServerInfo... --- DemoInfo/DP/DemoPacketParser.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DemoInfo/DP/DemoPacketParser.cs b/DemoInfo/DP/DemoPacketParser.cs index 83138aa..7414572 100644 --- a/DemoInfo/DP/DemoPacketParser.cs +++ b/DemoInfo/DP/DemoPacketParser.cs @@ -45,9 +45,11 @@ public static void ParsePacket(IBitStream bitstream, DemoParser demo) } else if (cmd == (int)SVC_Messages.svc_UpdateStringTable) { new UpdateStringTable().Parse(bitstream, demo); } else if (cmd == (int)NET_Messages.net_Tick) { //and all this other stuff - new NETTick().Parse(bitstream, demo); + new NETTick().Parse(bitstream, demo); } else if (cmd == (int)SVC_Messages.svc_UserMessage) { new UserMessage().Parse(bitstream, demo); + } else if (cmd == (int)SVC_Messages.svc_ServerInfo) { + new ServerInfo().Parse(bitstream, demo); } else { //You can use this flag to see what information the other packets contain, //if you want. Then you can look into the objects. Has some advnatages, and some disdavantages (mostly speed), From 730b7978af76d2a678a15ab6bdfc1d127893c9bd Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2017 13:54:52 -0700 Subject: [PATCH 4/5] TickTime and TickRate fixed for corrupted headers. HeaderCorrupted event and IsCorruptedHeader bool added. --- DemoInfo/DemoParser.cs | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/DemoInfo/DemoParser.cs b/DemoInfo/DemoParser.cs index fcaf852..0b471f6 100644 --- a/DemoInfo/DemoParser.cs +++ b/DemoInfo/DemoParser.cs @@ -264,6 +264,16 @@ public string Map { /// The header. public DemoHeader Header { get; private set; } + /// + /// True when header is corrupted + /// + public bool IsHeaderCorrupted { get; private set; } + + /// + /// Used to sample gap in ticks when header is corrupted + /// + internal List TickGaps = new List(); + /// /// Gets the participants of this game /// @@ -453,15 +463,16 @@ public string TFlag /// /// The tick rate. public float TickRate { - get { return this.Header.PlaybackFrames / this.Header.PlaybackTime; } + get { return IsHeaderCorrupted ? 1/_ticktime : this.Header.PlaybackFrames / this.Header.PlaybackTime; } } /// /// How long a tick of the demo is in s^-1 /// /// The tick time. + private float _ticktime; public float TickTime { - get { return this.Header.PlaybackTime / this.Header.PlaybackFrames; } + get { return IsHeaderCorrupted ? _ticktime : this.Header.PlaybackTime / this.Header.PlaybackFrames; } } /// @@ -535,10 +546,11 @@ public void ParseHeader() throw new InvalidDataException("Invalid Demo-Protocol"); Header = header; + IsHeaderCorrupted = (header.PlaybackTime == 0); - if (header.PlaybackTime == 0) + if (IsHeaderCorrupted) { - Console.WriteLine("WARNING: The header for this demo file is corrupted. TickRate, TickTime, ParsingProgress, CurrentTime will be 0 for the first 50 ticks. PlaybackFrames, PlaybackTicks, PlaybackTime will always be 0. HeaderCorrupted event triggered."); + Console.WriteLine("WARNING: The header for this demo file is corrupted. TickRate, TickTime, CurrentTime will be 0 for ticks at the start of the demo. ParsingProgress, PlaybackFrames, PlaybackTicks, PlaybackTime will always be 0. HeaderCorrupted event triggered."); if (HeaderCorrupted != null) { @@ -571,6 +583,25 @@ public void ParseToEnd(CancellationToken token) } } + private void FixTickTime() + { + // at the beginning of demos the tickgap can be erratic, so make sure we have 10 consecutive that are the same + int gap = TickGaps[1] - TickGaps[0]; + bool isConsecutive = true; + for (int i = 1; i < TickGaps.Count - 1; i++) { + if (TickGaps[i + 1] - TickGaps[i] != gap) + { + TickGaps.Clear(); + isConsecutive = false; + break; + } + } + + if (isConsecutive) { + _ticktime = gap * TickInterval; + } + } + /// /// Parses the next tick of the demo. /// @@ -580,6 +611,15 @@ public bool ParseNextTick() if (Header == null) throw new InvalidOperationException ("You need to call ParseHeader first before you call ParseToEnd or ParseNextTick!"); + int consecutiveGaps = 10; + if (IsHeaderCorrupted && _ticktime == 0 && IngameTick > 20) { + if (TickGaps.Count < consecutiveGaps) + TickGaps.Add(IngameTick); + else if (TickGaps.Count == consecutiveGaps) { + FixTickTime(); + } + } + bool b = ParseTick(); for (int i = 0; i < RawPlayers.Length; i++) { From 0d0fd865c47b2b3eb81554c8976ab67d0c1cabef Mon Sep 17 00:00:00 2001 From: econoraptor Date: Thu, 18 Jan 2018 21:25:05 +0000 Subject: [PATCH 5/5] Add TimeFixed event --- DemoInfo/DemoParser.cs | 38 ++++++++++++++++++++++---------------- DemoInfo/Events.cs | 4 ++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/DemoInfo/DemoParser.cs b/DemoInfo/DemoParser.cs index 0b471f6..58ea907 100644 --- a/DemoInfo/DemoParser.cs +++ b/DemoInfo/DemoParser.cs @@ -39,6 +39,11 @@ public class DemoParser : IDisposable /// public event EventHandler HeaderCorrupted; + /// + /// Raised when the time variables have been fixed for a demo with a corrupted header + /// + public event EventHandler TimeFixed; + /// /// Occurs when the match started, so when the "begin_new_match"-GameEvent is dropped. /// This usually right before the freezetime of the 1st round. Be careful, since the players @@ -269,11 +274,6 @@ public string Map { /// public bool IsHeaderCorrupted { get; private set; } - /// - /// Used to sample gap in ticks when header is corrupted - /// - internal List TickGaps = new List(); - /// /// Gets the participants of this game /// @@ -470,10 +470,11 @@ public float TickRate { /// How long a tick of the demo is in s^-1 /// /// The tick time. - private float _ticktime; public float TickTime { get { return IsHeaderCorrupted ? _ticktime : this.Header.PlaybackTime / this.Header.PlaybackFrames; } } + private List tickGaps = new List(); + private float _ticktime; /// /// Gets the parsing progess. 0 = beginning, ~1 = finished (it can actually be > 1, so be careful!) @@ -550,12 +551,13 @@ public void ParseHeader() if (IsHeaderCorrupted) { - Console.WriteLine("WARNING: The header for this demo file is corrupted. TickRate, TickTime, CurrentTime will be 0 for ticks at the start of the demo. ParsingProgress, PlaybackFrames, PlaybackTicks, PlaybackTime will always be 0. HeaderCorrupted event triggered."); + Console.WriteLine("WARNING: The header for this demo file is corrupted. TickRate, TickTime, CurrentTime will be 0 for ticks at the start of the demo. ParsingProgress, PlaybackFrames, PlaybackTicks, PlaybackTime will always be 0."); + Console.WriteLine("HeaderCorrupted event raised, TimeFixed event will be raised when time variables are repaired."); if (HeaderCorrupted != null) { HeaderCorrupted(this, new HeaderParsedEventArgs(Header)); - } + } } if (HeaderParsed != null) @@ -586,12 +588,12 @@ public void ParseToEnd(CancellationToken token) private void FixTickTime() { // at the beginning of demos the tickgap can be erratic, so make sure we have 10 consecutive that are the same - int gap = TickGaps[1] - TickGaps[0]; + int gap = tickGaps[1] - tickGaps[0]; bool isConsecutive = true; - for (int i = 1; i < TickGaps.Count - 1; i++) { - if (TickGaps[i + 1] - TickGaps[i] != gap) + for (int i = 1; i < tickGaps.Count - 1; i++) { + if (tickGaps[i + 1] - tickGaps[i] != gap) { - TickGaps.Clear(); + tickGaps.Clear(); isConsecutive = false; break; } @@ -599,6 +601,9 @@ private void FixTickTime() if (isConsecutive) { _ticktime = gap * TickInterval; + + if (TimeFixed != null) + TimeFixed(this, new TimeFixedEventArgs()); } } @@ -611,11 +616,11 @@ public bool ParseNextTick() if (Header == null) throw new InvalidOperationException ("You need to call ParseHeader first before you call ParseToEnd or ParseNextTick!"); - int consecutiveGaps = 10; if (IsHeaderCorrupted && _ticktime == 0 && IngameTick > 20) { - if (TickGaps.Count < consecutiveGaps) - TickGaps.Add(IngameTick); - else if (TickGaps.Count == consecutiveGaps) { + int consecutiveGaps = 10; + if (tickGaps.Count < consecutiveGaps) + tickGaps.Add(IngameTick); + else if (tickGaps.Count == consecutiveGaps) { FixTickTime(); } } @@ -1522,6 +1527,7 @@ public void Dispose () this.FlashNadeExploded = null; this.HeaderParsed = null; this.HeaderCorrupted = null; + this.TimeFixed = null; this.MatchStarted = null; this.NadeReachedTarget = null; this.PlayerKilled = null; diff --git a/DemoInfo/Events.cs b/DemoInfo/Events.cs index 7d70521..cb5604e 100644 --- a/DemoInfo/Events.cs +++ b/DemoInfo/Events.cs @@ -21,6 +21,10 @@ public class TickDoneEventArgs : EventArgs { } + public class TimeFixedEventArgs : EventArgs + { + } + public class MatchStartedEventArgs : EventArgs { }