Skip to content

Commit

Permalink
Fix p1 4104 and individual demos not timing with tick 0
Browse files Browse the repository at this point in the history
  • Loading branch information
UncraftedName committed Oct 10, 2024
1 parent 11ab034 commit e0c8a5c
Show file tree
Hide file tree
Showing 13 changed files with 40 additions and 55 deletions.
7 changes: 1 addition & 6 deletions ConsoleApp/src/DemoArgProcessing/DemoParserSubCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,7 @@ private void EnableTimeOptionDefault(DemoParsingSetupInfo setupInfo) {
// Due to the portal community's love for hundreds of saveloads, the time option will time w/ first tick by default
// for p1 and l4d (because hindsight or something). This is a hack for now and will hopefully by improved with a
// more advanced timing system in the utopian future.
((OptTime)option).SetForceFirstTickTiming(new[] {
SourceGame.L4D1_1005, SourceGame.L4D1_1040, SourceGame.L4D2_2000, SourceGame.L4D2_2011,
SourceGame.L4D2_2012, SourceGame.L4D2_2027, SourceGame.L4D2_2042, SourceGame.L4D2_2091, SourceGame.L4D2_2147,
SourceGame.L4D2_2203, SourceGame.PORTAL_1_3420, SourceGame.PORTAL_1_5135, SourceGame.PORTAL_1_1910503,
SourceGame.PORTAL_REVOLUTION
});
((OptTime)option).SetForceFirstTickTiming((SourceGame game) => game.IsPortal1() || game.IsLeft4Dead() || game == SourceGame.PORTAL_REVOLUTION);
} else {
throw new ArgProcessProgrammerException("listdemo option not passed to demo sub-command.");
}
Expand Down
26 changes: 10 additions & 16 deletions ConsoleApp/src/DemoArgProcessing/Options/OptTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public class OptTime : DemoOption<OptTime.TimeFlags> {

private const int FmtIdt = -25;

// a hack to allow only p1 & l4d to time w/ first tick
private ISet<SourceGame> _forceFirstTickTiming = null!;


[Flags]
public enum TimeFlags {
Expand Down Expand Up @@ -51,7 +48,6 @@ public OptTime() : base(
public override void Reset() {
base.Reset();
_sdt = null!;
_forceFirstTickTiming = new HashSet<SourceGame>();
}


Expand All @@ -71,7 +67,7 @@ protected override void Process(DemoParsingInfo infoObj, TimeFlags arg, bool isD
WriteAdjustedTime(
infoObj.CurrentDemo,
Console.Out,
(arg & TimeFlags.TimeFirstTick) != 0 || _forceFirstTickTiming.Contains(infoObj.CurrentDemo.DemoInfo.Game)
(arg & TimeFlags.TimeFirstTick) != 0 || _sdt.TimeFirstTick
);
Console.WriteLine();
}
Expand Down Expand Up @@ -207,15 +203,15 @@ public static void WriteAdjustedTime(SourceDemo demo, TextWriter tw, bool timeFi
tw.WriteLine($"{"Measured ticks ",FmtIdt}: {demo.TickCount(tfs)}");

// hack before we implement proper timing strategies
if (demo.DemoInfo.IsLeft4Dead1() && demo.SequenceType == TimingAdjustment.SequenceType.SingleDemo && demo.StartAdjustmentTick.HasValue) {
if (demo.DemoInfo.Game.IsLeft4Dead1() && demo.SequenceType == TimingAdjustment.SequenceType.SingleDemo && demo.StartAdjustmentTick.HasValue) {
// all maps except finales will have a map_transition event at the end if the map was completed
(var gameEvent, int? endTick) = demo.FilterForMessage<SvcGameEvent>().FirstOrDefault(ev => ev.message.EventDescription!.Name == "map_transition");

// assume it's a finale if we don't find a map_transition event and copy the end adjustment tick
if (gameEvent == null)
endTick = demo.EndAdjustmentTick;
if (endTick.HasValue) {
int mapTime = endTick.Value - demo.StartAdjustmentTick.Value + 1;
int mapTime = endTick!.Value - demo.StartAdjustmentTick.Value + 1;
tw.WriteLine($"{"Individual Map time ",FmtIdt}: {Utils.FormatTime(mapTime * tickInterval)}");
tw.Write($"{"Individual Map ticks ",FmtIdt}: {mapTime}");
tw.WriteLine($" ({demo.StartAdjustmentTick}-{endTick})");
Expand All @@ -229,7 +225,7 @@ public static void WriteAdjustedTime(SourceDemo demo, TextWriter tw, bool timeFi
}

// another hack until we time things better :)
if (demo.DemoInfo.IsPortal2()) {
if (demo.DemoInfo.Game.IsPortal2()) {
ScoreboardTempUpdate? lastScoreBoard =
demo.FilterForUserMessage<ScoreboardTempUpdate>()
.Select(tuple => tuple.userMessage)
Expand All @@ -249,10 +245,8 @@ public static void WriteAdjustedTime(SourceDemo demo, TextWriter tw, bool timeFi


// a hack to allow only p1 & l4d to time w/ first tick
internal void SetForceFirstTickTiming(IEnumerable<SourceGame> games) {
var gameSet = games.ToImmutableHashSet();
_forceFirstTickTiming = gameSet;
_sdt.SetForceFirstTickTiming(gameSet);
internal void SetForceFirstTickTiming(Predicate<SourceGame> pred) {
_sdt.SetForceFirstTickTiming(pred);
}
}

Expand Down Expand Up @@ -286,14 +280,14 @@ public SimpleDemoInfo(SourceDemo demo) {

// a hack to allow only p1 & l4d to time w/ first tick
private SourceDemo _curDemo = null!;
private ISet<SourceGame> _forceFirstTickTiming;
public bool TimeFirstTick => _timeFirstTick || _forceFirstTickTiming.Contains(_curDemo.DemoInfo.Game);
private Predicate<SourceGame> _forceFirstTickTiming;
public bool TimeFirstTick => _timeFirstTick || _forceFirstTickTiming(_curDemo.DemoInfo.Game);


public SimpleDemoTimer(bool timeFirstTick) {
_timeFirstTick = timeFirstTick;
ValidFlags = Flags.TotalTimeValid | Flags.AdjustedTimeValid;
_forceFirstTickTiming = new HashSet<SourceGame>();
_forceFirstTickTiming = (SourceGame) => false;
_simpleDemoDifferences = new HashSet<string>();
}

Expand All @@ -304,7 +298,7 @@ public SimpleDemoTimer(IEnumerable<SourceDemo> demos, bool timeFirstTick) : this
}


internal void SetForceFirstTickTiming(IEnumerable<SourceGame> games) => _forceFirstTickTiming = games.ToImmutableHashSet();
internal void SetForceFirstTickTiming(Predicate<SourceGame> pred) => _forceFirstTickTiming = pred;


/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override void Parse(ref BitStreamReader bsr) {
TableName = bsr.ReadNullTerminatedString();
MaxEntries = (short)bsr.ReadUShort();
NumEntries = (int)bsr.ReadUInt(ParserUtils.HighestBitIndex((uint)MaxEntries) + 1);
uint dataLen = bsr.ReadUInt(DemoInfo.IsLeft4Dead2() ? 21 : 20);
uint dataLen = bsr.ReadUInt(DemoInfo.Game.IsLeft4Dead2() ? 21 : 20);
UserDataFixedSize = bsr.ReadBool();
UserDataSize = (int)(UserDataFixedSize ? bsr.ReadUInt(12) : 0);
UserDataSizeBits = (int)(UserDataFixedSize ? bsr.ReadUInt(4) : 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ protected override void Parse(ref BitStreamReader bsr) {
Updates.Add(update);
};
if (IsDelta) {
if (DemoInfo.IsLeft4Dead2() && DemoInfo.Game >= SourceGame.L4D2_2091) {
if (DemoInfo.Game.IsLeft4Dead2() && DemoInfo.Game >= SourceGame.L4D2_2091) {
idx = -1;
uint deletionCount = entBsr.ReadUBitInt();
for (int i = 0; i < deletionCount; i++) {
Expand Down
2 changes: 1 addition & 1 deletion DemoParser/src/Parser/Components/Messages/SvcPrefetch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public SvcPrefetch(SourceDemo? demoRef, byte value) : base(demoRef, value) {}


protected override void Parse(ref BitStreamReader bsr) {
var soundIndexBits = DemoInfo.IsLeft4Dead2() ? DemoInfo.Game >= SourceGame.L4D2_2091 ? 15 : 14 : 13;
var soundIndexBits = DemoInfo.Game.IsLeft4Dead2() ? DemoInfo.Game >= SourceGame.L4D2_2091 ? 15 : 14 : 13;
SoundIndex = (int)bsr.ReadUInt(soundIndexBits);

var mgr = GameState.StringTablesManager;
Expand Down
4 changes: 2 additions & 2 deletions DemoParser/src/Parser/Components/Messages/SvcServerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected override void Parse(ref BitStreamReader bsr) {
ServerCount = bsr.ReadUInt();
IsHltv = bsr.ReadBool();
IsDedicated = bsr.ReadBool();
if (DemoInfo.IsLeft4Dead() && DemoInfo.Game >= SourceGame.L4D2_2147)
if (DemoInfo.Game.IsLeft4Dead() && DemoInfo.Game >= SourceGame.L4D2_2147)
RestrictWorkshopAddons = bsr.ReadBool();
ClientCrc = bsr.ReadSInt();
if (DemoInfo.NewDemoProtocol)
Expand All @@ -61,7 +61,7 @@ protected override void Parse(ref BitStreamReader bsr) {
MapName = bsr.ReadNullTerminatedString();
SkyName = bsr.ReadNullTerminatedString();
HostName = bsr.ReadNullTerminatedString();
if (DemoInfo.IsLeft4Dead() && DemoInfo.Game >= SourceGame.L4D2_2147)
if (DemoInfo.Game.IsLeft4Dead() && DemoInfo.Game >= SourceGame.L4D2_2147)
{
MissionName = bsr.ReadNullTerminatedString();
MutationName = bsr.ReadNullTerminatedString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected override void Parse(ref BitStreamReader bsr) {
EntryCount = bsr.ReadByte();
uint bitLen = DemoInfo.Game is SourceGame.PORTAL_1_1910503 or SourceGame.TF2
? bsr.ReadVarUInt32()
: bsr.ReadUInt(DemoInfo.IsLeft4Dead2() ? 18 : 17);
: bsr.ReadUInt(DemoInfo.Game.IsLeft4Dead2() ? 18 : 17);
_data = bsr.ForkAndSkip((int)bitLen);

// todo
Expand Down
2 changes: 1 addition & 1 deletion DemoParser/src/Parser/Components/Messages/SvcVoiceData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protected override void Parse(ref BitStreamReader bsr) {
FromClient = bsr.ReadByte();
Proximity = bsr.ReadByte() != 0;
BitLen = bsr.ReadUShort();
if (DemoRef.DemoInfo.IsLeft4Dead()) {
if (DemoRef.DemoInfo.Game.IsLeft4Dead()) {
Unknown = new bool[4];
for (int i = 0; i < 4; i++)
Unknown[i] = bsr.ReadBool();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public PlayerInfo(SourceDemo? demoRef, int? decompressedIndex = null) : base(dem

// we're reading a player_info_t, so take alignment into account
protected override void Parse(ref BitStreamReader bsr) {
if (DemoInfo.NewDemoProtocol && !DemoInfo.IsLeft4Dead1())
if (DemoInfo.NewDemoProtocol && !DemoInfo.Game.IsLeft4Dead1())
SteamId = (CSteamId)bsr.ReadULong();
Name = bsr.ReadStringOfLength(MaxPlayerNameLength);
UserId = bsr.ReadSInt();
Expand Down
40 changes: 18 additions & 22 deletions DemoParser/src/Parser/DemoInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public DemoInfo(SourceDemo demo) {
}

// provide default values in case of unknown game
if (IsPortal1() || IsHL2() || (Game == UNKNOWN && h.DemoProtocol == 3)) {
if (Game.IsPortal1() || Game.IsHL2() || (Game == UNKNOWN && h.DemoProtocol == 3)) {
TickInterval = 0.015f;
MaxSplitscreenPlayers = 1;
if (h.NetworkProtocol <= 15 && Game != HL2_OE)
Expand All @@ -114,7 +114,7 @@ public DemoInfo(SourceDemo demo) {
demo.DemoParseResult |= DemoParseResult.EntParsingEnabled;
PacketTypes = DemoPacket.DemoProtocol4Table;
UserMessageTypes = UserMessage.Portal2Table;
} else if (IsLeft4Dead()) {
} else if (Game.IsLeft4Dead()) {
TickInterval = 1f / 30;
MaxSplitscreenPlayers = 4;
demo.DemoParseResult |= DemoParseResult.EntParsingEnabled;
Expand All @@ -137,7 +137,7 @@ public DemoInfo(SourceDemo demo) {
SendPropTypes = SendPropEnums.OldNetPropTypes;
} else {
NetMsgTypeBits = Game == L4D1_1005 ? 5 : 6;
SendPropNumBitsToGetNumBits = IsLeft4Dead() ? 6 : 7;
SendPropNumBitsToGetNumBits = Game.IsLeft4Dead() ? 6 : 7;
SendPropTypes = SendPropEnums.NewNetPropTypes;
}
SendPropTypesReverseLookup = SendPropTypes.CreateReverseLookupDict();
Expand All @@ -148,9 +148,9 @@ public DemoInfo(SourceDemo demo) {
SendPropFlagBits = 11;
goto default; // I don't know anything else about protocol 2 :/
case 3:
case 4 when IsLeft4Dead1():
case 4 when Game.IsLeft4Dead1():
// l4d1 behaves mostly like version 3 except for places where "NewDemoProtocol" is used
NewDemoProtocol = IsLeft4Dead1();
NewDemoProtocol = Game.IsLeft4Dead1();
SendPropFlagBits = Game == HL2_OE ? 13 : 16;
PropFlagChecker = new SendPropEnums.DemoProtocol3FlagChecker();
PlayerMfFlagChecker = new PropEnums.PlayerMfFlagsOldDemoProtocol();
Expand All @@ -171,12 +171,12 @@ public DemoInfo(SourceDemo demo) {
case 4:
NewDemoProtocol = true;
SendPropFlagBits = 19;
UserMessageLengthBits = IsLeft4Dead() ? 11 : 12;
UserMessageLengthBits = Game.IsLeft4Dead() ? 11 : 12;
PropFlagChecker = new SendPropEnums.DemoProtocol4FlagChecker();
if (Game == PORTAL_2) {
PlayerMfFlagChecker = new PropEnums.PlayerMfFlagsPortal2();
CollisionsGroupList = PropEnums.CollisionGroupListPortal2;
} else if (IsLeft4Dead()) {
} else if (Game.IsLeft4Dead()) {
PlayerMfFlagChecker = new PropEnums.PlayerMfFlagsOldDemoProtocol();
CollisionsGroupList = PropEnums.CollisionGroupListNewDemoProtocol; // unchecked
} else {
Expand All @@ -196,9 +196,9 @@ public DemoInfo(SourceDemo demo) {
}
MessageTypesReverseLookup = MessageTypes.CreateReverseLookupDict(MessageType.Invalid);

if (IsPortal1() && Game >= PORTAL_1_1910503)
if (Game.IsPortal1() && Game >= PORTAL_1_1910503)
SpModelIndexBits = 13;
else if (IsLeft4Dead2() && Game >= L4D2_2203)
else if (Game.IsLeft4Dead2() && Game >= L4D2_2203)
SpModelIndexBits = 12;
else
SpModelIndexBits = 11;
Expand All @@ -213,19 +213,6 @@ public DemoInfo(SourceDemo demo) {

TimeAdjustmentTypes = TimingAdjustment.AdjustmentTypeFromMap(h.MapName, Game, demo.SequenceType);
}


public bool IsLeft4Dead() => Game >= L4D1_1005 && Game <= L4D2_2203;

public bool IsLeft4Dead1() => Game >= L4D1_1005 && Game <= L4D1_1040;

public bool IsLeft4Dead2() => Game >= L4D2_2000 && Game <= L4D2_2203;

public bool IsPortal1() => Game >= PORTAL_1_3420 && Game <= PORTAL_1_1910503;

public bool IsPortal2() => Game == PORTAL_2;

public bool IsHL2() => Game == HL2_OE;
}


Expand Down Expand Up @@ -260,4 +247,13 @@ public enum SourceGame {

UNKNOWN
}

public static class SourceGameExtensions {
public static bool IsHL2(this SourceGame game) { return game == HL2_OE; }
public static bool IsPortal1(this SourceGame game) { return game >= SourceGame.PORTAL_1_3420 && game <= SourceGame.PORTAL_1_1910503; }
public static bool IsPortal2(this SourceGame game) { return game == SourceGame.PORTAL_2; }
public static bool IsLeft4Dead1(this SourceGame game) { return game >= L4D1_1005 && game <= L4D1_1040; }
public static bool IsLeft4Dead2(this SourceGame game) { return game >= L4D2_2000 && game <= L4D2_2203; }
public static bool IsLeft4Dead(this SourceGame game) { return game.IsLeft4Dead1() || game.IsLeft4Dead2(); }
}
}
2 changes: 1 addition & 1 deletion DemoParser/src/Parser/EntityStuff/SendTableProp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected override void Parse(ref BitStreamReader bsr) {
SendPropType = DemoInfo.SendPropTypes[type];
Name = bsr.ReadNullTerminatedString();
Flags = (int)bsr.ReadUInt(DemoInfo.SendPropFlagBits);
if (DemoInfo.NewDemoProtocol && !DemoInfo.IsLeft4Dead1())
if (DemoInfo.NewDemoProtocol && !DemoInfo.Game.IsLeft4Dead1())
Priority = bsr.ReadByte();
if (SendPropType == SendPropType.DataTable || DemoInfo.PropFlagChecker.HasFlag(Flags, PropFlag.Exclude)) {
ExcludeDtName = bsr.ReadNullTerminatedString();
Expand Down
2 changes: 1 addition & 1 deletion DemoParser/src/Parser/GameState/DataTablesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private void IterateProps(
// priority or a "ChangesOften" flag in newer versions which bumps them up in the table. This is done with a
// custom non-stable sort.
private void SortProps(IList<FlattenedProp> fProps) {
if (_demoRef.DemoInfo.NewDemoProtocol && !_demoRef.DemoInfo.IsLeft4Dead1()) {
if (_demoRef.DemoInfo.NewDemoProtocol && !_demoRef.DemoInfo.Game.IsLeft4Dead1()) {
int start = 0;
var priorities = fProps.Select(entry => entry.PropInfo.Priority!.Value).Concat(new[] { 64 }).Distinct();
foreach (int priority in priorities.OrderBy(i => i)) {
Expand Down
2 changes: 1 addition & 1 deletion DemoParser/src/Utils/BitStreams/PropDecodeReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ public void ReadVectorCoord(out Vector3 vec3) {

int i = -1;
if (demoRef.DemoInfo.NewDemoProtocol) {
bool newWay = !demoRef.DemoInfo.IsLeft4Dead1() && ReadBool();
bool newWay = !demoRef.DemoInfo.Game.IsLeft4Dead1() && ReadBool();
while ((i = ReadFieldIndex(i, newWay)) != -1 && !HasOverflowed) {
if (i < 0 || i >= fProps.Count) {
HasOverflowed = true;
Expand Down

0 comments on commit e0c8a5c

Please sign in to comment.