private void CL_DeltaEntity(Q3HuffmanReader decoder, CLSnapshot frame, int newnum, EntityState old, bool unchanged) { EntityState state; // save the parsed entity state into the big circular buffer so // it can be used as the source for a later delta state = Ext2 <int, EntityState> .GetOrCreate(client.parseEntities, client.parseEntitiesNum& (Q3Const.MAX_PARSE_ENTITIES - 1)); if (unchanged) { state.copy(old); } else { decoder.readDeltaEntity(state, newnum); } if (state.number == (Q3Const.MAX_GENTITIES - 1)) { return; // entity was delta removed } client.parseEntitiesNum++; frame.numEntities++; }
private void updateClientEvents(CLSnapshot snapshot) { if (client.dfvers <= 0 || client.mapname.Length <= 0) { return; } var time = getTime(snapshot.ps, (int)snapshot.serverTime, client.dfvers, client.mapNameChecksum); var events = client.clientEvents; ClientEvent clientEvent = new ClientEvent(time, snapshot); var prevStat = 0; var newStat = snapshot.ps.stats[12]; if (events.Count == 0) { clientEvent.eventStartFile = true; if (snapshot.ps.pm_type == (int)ClientEvent.PlayerMode.PM_NORMAL) { if ((prevStat & 4) != (newStat & 4) && (prevStat & 2) == 0) { clientEvent.eventStartTime = true; } } } else { var prevEvent = events[events.Count - 1]; if (prevEvent.playerNum != snapshot.ps.clientNum) { clientEvent.eventChangeUser = true; } if (prevEvent.playerMode != snapshot.ps.pm_type) { clientEvent.eventChangePmType = true; } prevStat = prevEvent.userStat; if (prevStat != newStat) { if ((prevStat & 4) != (newStat & 4)) { if (snapshot.ps.pm_type == (int)ClientEvent.PlayerMode.PM_NORMAL) { if ((prevStat & 2) == 0) { clientEvent.eventStartTime = true; } else { clientEvent.eventTimeReset = true; } } } else if ((prevStat & 8) == 0 && (newStat & 8) != 0) { if (!clientEvent.eventChangeUser) { clientEvent.eventFinish = true; } } else if ((prevStat & 16) != (newStat & 16)) { if (snapshot.ps.pm_type == (int)ClientEvent.PlayerMode.PM_NORMAL) { clientEvent.eventCheckPoint = true; } } else if (prevEvent.eventFinish && (prevStat & 2) != 0 && (newStat & 2) == 0) { //fix double finish if (!clientEvent.eventChangeUser) { prevEvent.eventFinish = false; if (!prevEvent.hasAnyEvent) { events.RemoveAt(events.Count - 1); } clientEvent.eventFinish = true; } } else if (prevEvent.eventStartTime && (prevStat & 2) == 0 && (newStat & 2) != 0) { //fix double start timer if (snapshot.ps.pm_type == (int)ClientEvent.PlayerMode.PM_NORMAL) { prevEvent.eventStartTime = false; if (!prevEvent.hasAnyEvent) { events.RemoveAt(events.Count - 1); } clientEvent.eventStartTime = true; } } else if (prevEvent.eventTimeReset && (prevStat & 4) == 0 && (newStat & 2) != 0) { //fix double tr if (snapshot.ps.pm_type == (int)ClientEvent.PlayerMode.PM_NORMAL) { prevEvent.eventTimeReset = false; if (!prevEvent.hasAnyEvent) { events.RemoveAt(events.Count - 1); } clientEvent.eventTimeReset = true; } } else { clientEvent.eventSomeTrigger = true; } } } if (clientEvent.hasAnyEvent) { events.Add(clientEvent); } }
private void parsePacketEntities(Q3HuffmanReader decoder, CLSnapshot oldframe, CLSnapshot newframe) { newframe.parseEntitiesNum = client.parseEntitiesNum; newframe.numEntities = 0; int newnum = 0; int oldindex = 0; int oldnum = 0; EntityState oldstate = null; if (oldframe == null) { oldnum = 99999; } else { if (oldindex >= oldframe.numEntities) { oldnum = 99999; } else { oldstate = Ext2 <int, EntityState> .GetOrCreate(client.parseEntities, (oldframe.parseEntitiesNum + oldindex)& (Q3Const.MAX_PARSE_ENTITIES - 1)); oldnum = oldstate.number; } } while (true) { newnum = (int)decoder.readNumBits(Q3Const.GENTITYNUM_BITS); if (newnum == (Q3Const.MAX_GENTITIES - 1)) { break; } if (decoder.isEOD()) { Q3Utils.PrintDebug(clc.errors, "ERR_DROP, CL_ParsePacketEntities: end of message"); return; } while (oldnum < newnum) { // one or more entities from the old packet are unchanged CL_DeltaEntity(decoder, newframe, oldnum, oldstate, true); oldindex++; if (oldindex >= oldframe.numEntities) { oldnum = 99999; } else { oldstate = Ext2 <int, EntityState> .GetOrCreate(client.parseEntities, (oldframe.parseEntitiesNum + oldindex)& (Q3Const.MAX_PARSE_ENTITIES - 1)); oldnum = oldstate.number; } } if (oldnum == newnum) { // delta from previous state CL_DeltaEntity(decoder, newframe, newnum, oldstate, false); oldindex++; if (oldindex >= oldframe.numEntities) { oldnum = 99999; } else { oldstate = Ext2 <int, EntityState> .GetOrCreate(client.parseEntities, (oldframe.parseEntitiesNum + oldindex)& (Q3Const.MAX_PARSE_ENTITIES - 1)); oldnum = oldstate.number; } continue; } if (oldnum > newnum) { // delta from baseline EntityState es = Ext2 <int, EntityState> .GetOrCreate(client.entityBaselines, newnum); CL_DeltaEntity(decoder, newframe, newnum, es, false); continue; } } // any remaining entities in the old frame are copied over while (oldnum != 99999) { // one or more entities from the old packet are unchanged CL_DeltaEntity(decoder, newframe, oldnum, oldstate, true); oldindex++; if (oldindex >= oldframe.numEntities) { oldnum = 99999; } else { oldstate = Ext2 <int, EntityState> .GetOrCreate(client.parseEntities, (oldframe.parseEntitiesNum + oldindex)& (Q3Const.MAX_PARSE_ENTITIES - 1)); oldnum = oldstate.number; } } }
private void parseSnapshot(Q3HuffmanReader decoder) { if (client.clientConfig == null) { client.clientConfig = new Dictionary <string, string>(); if (clc.configs.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_GAME)) { var gameConfig = Q3Utils.split_config(clc.configs[Q3Const.Q3_DEMO_CFG_FIELD_GAME]); client.isCheatsOn = Ext.GetOrZero(gameConfig, "sv_cheats") > 0; } if (clc.configs.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_CLIENT)) { client.clientConfig = Q3Utils.split_config(clc.configs[Q3Const.Q3_DEMO_CFG_FIELD_CLIENT]); client.dfvers = Ext.GetOrZero(client.clientConfig, "defrag_vers"); client.mapname = Ext.GetOrNull(client.clientConfig, "mapname"); client.mapNameChecksum = getMapNameChecksum(client.mapname); client.isOnline = Ext.GetOrZero(client.clientConfig, "defrag_gametype") > 4; } } CLSnapshot newSnap = new CLSnapshot(); CLSnapshot old = null; newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = decoder.readLong(); newSnap.messageNum = clc.serverMessageSequence; int deltaNum = decoder.readByte(); if (deltaNum == 0) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = decoder.readByte(); // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message if (newSnap.deltaNum <= 0) { newSnap.valid = true; // uncompressed frame old = null; clc.demowaiting = false; // we can start recording now } else { old = Ext2 <int, CLSnapshot> .GetOrCreate(client.snapshots, newSnap.deltaNum& Q3Const.PACKET_MASK); if (old == null || !old.valid) { // should never happen Q3Utils.PrintDebug(clc.errors, "Delta from invalid frame (not supposed to happen!)"); } else if (old.messageNum != newSnap.deltaNum) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Q3Utils.PrintDebug(clc.errors, "Delta frame too old."); } else if ((client.parseEntitiesNum - old.parseEntitiesNum) > (Q3Const.MAX_PARSE_ENTITIES - 128)) { Q3Utils.PrintDebug(clc.errors, "Delta parseEntitiesNum too old"); } else { newSnap.valid = true; // valid delta parse } } int len = decoder.readByte(); if (len > newSnap.areamask.Length) { Q3Utils.PrintDebug(clc.errors, "CL_ParseSnapshot: Invalid size {0} for areamask", len); return; } decoder.readData(newSnap.areamask, len); if (old != null) { newSnap.ps.copy(old.ps); } decoder.readDeltaPlayerState(newSnap.ps); parsePacketEntities(decoder, old, newSnap); // if not valid, dump the entire thing now that it has // been properly read if (!newSnap.valid) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer int oldMessageNum = client.snap.messageNum + 1; if (newSnap.messageNum - oldMessageNum >= Q3Const.PACKET_BACKUP) { oldMessageNum = newSnap.messageNum - (Q3Const.PACKET_BACKUP - 1); } for (; oldMessageNum < newSnap.messageNum; oldMessageNum++) { CLSnapshot s; if (client.snapshots.TryGetValue(oldMessageNum & Q3Const.PACKET_MASK, out s)) { if (s != null) { s.valid = false; } } } // copy to the current good spot client.snap = newSnap; // skip ping calculations client.snap.ping = 0; // save the frame off in the backup array for later delta comparisons client.snapshots[client.snap.messageNum & Q3Const.PACKET_MASK] = client.snap; client.newSnapshots = true; updateClientEvents(newSnap); }