private void parseGameState(Q3HuffmanReader reader) { reader.readLong(); while (true) { byte cmd = reader.readByte(); if (cmd == Q3_SVC.EOF) { break; } switch (cmd) { case Q3_SVC.CONFIGSTRING: short key = reader.readShort(); if (key < 0 || key > Q3Const.MAX_CONFIGSTRINGS) { return; } clc.configs[key] = reader.readBigString(); break; case Q3_SVC.BASELINE: long newnum = reader.readNumBits(Q3Const.GENTITYNUM_BITS); if (newnum < 0 || newnum >= Q3Const.MAX_GENTITIES) { Q3Utils.PrintDebug(clc.errors, "Baseline number out of range: {0}", newnum); return; } EntityState es = Ext2 <long, EntityState> .GetOrCreate(clc.entityBaselines, newnum); if (!reader.readDeltaEntity(es, (int)newnum)) { Q3Utils.PrintDebug(clc.errors, "unable to parse delta-entity state"); return; } break; default: Q3Utils.PrintDebug(clc.errors, "bad command in parseGameState"); return; } } //clc.clientNum clc.clientNum = reader.readLong(); //clc.checksumFeed clc.checksumFeed = reader.readLong(); }
public bool readDeltaPlayerState(PlayerState state) { int lc = readByte(); if (lc < 0 || lc > MapperFactory.PlayerStateFieldNum) { Q3Utils.PrintDebug("invalid entityState field count: {" + lc + "}"); return(false); } for (int i = 0; i < lc; i++) { if (readNumBits(1) == 0) { // no change; continue; } MapperFactory.updatePlayerState(state, i, this, false); } // read arrays if (readNumBits(1) != 0) { //parse stats if (readNumBits(1) != 0) { pstArrayRead(state.stats, Q3Const.MAX_STATS); } // parse persistant stats if (readNumBits(1) != 0) { pstArrayRead(state.persistant, Q3Const.MAX_PERSISTANT); } // parse ammo if (readNumBits(1) != 0) { pstArrayRead(state.ammo, Q3Const.MAX_WEAPONS); } // parse powerups if (readNumBits(1) != 0) { pstLongArrayRead(state.powerups, Q3Const.MAX_POWERUPS); } } return(true); }
private Q3DemoConfigParser doParse(Q3DemoConfigParser msgParser) { Q3MessageStream messageStream = new Q3MessageStream(this.file_name); try { Q3DemoMessage msg = null; while ((msg = messageStream.nextMessage()) != null) { if (!msgParser.parse(msg)) { break; } } } catch (Exception r) { Q3Utils.PrintDebug(msgParser.clc.errors, r.Message); } messageStream.close(); return(msgParser); }
public bool readDeltaEntity(EntityState state, int number) { if (readNumBits(1) == 1) { state.number = Q3Const.MAX_GENTITIES - 1; // clear state and return return(true); } // check for no delta if (readNumBits(1) == 0) { state.number = number; return(true); } int lc = readByte(); if (lc < 0 || lc > MapperFactory.EntityStateFieldNum) { Q3Utils.PrintDebug("invalid entityState field count: {" + lc + "}"); return(false); } state.number = number; for (int i = 0; i < lc; i++) { if (readNumBits(1) == 0) { //no change continue; } bool reset = readNumBits(1) == 0; MapperFactory.updateEntityState(state, i, this, reset); } return(true); }
public Dictionary <string, Dictionary <string, string> > getFriendlyInfo() { if (friendlyInfo != null) { return(friendlyInfo); } //All players var keyP = Q3Const.Q3_DEMO_CFG_FIELD_PLAYER; for (short i = 0; i < 32; i++) { var k1 = (short)(keyP + i); if (rawConfig.ContainsKey(k1)) { allPlayersConfigs.Add(k1, split_config_player(rawConfig[k1])); } } //Current player if (fin.HasValue) { kPlayer = getPlayerInfoByPlayerNum(fin.Value.Value.playerNum); } if (kPlayer == null && clientEvents.Count > 0) { //if spectator view player and there was no finish var lastEvent = clientEvents.LastOrDefault(); if (lastEvent != null) { kPlayer = getPlayerInfoByPlayerNum(lastEvent.playerNum); } } if (kPlayer == null) { kPlayer = getPlayerInfoByPlayerNum(clc.clientNum); } var keys = allPlayersConfigs.Keys.ToList(); if (kPlayer == null && keys.Count == 1) { kPlayer = allPlayersConfigs[keys[0]]; } friendlyInfo = new Dictionary <string, Dictionary <string, string> >(); //console Times Dictionary <string, string> times = new Dictionary <string, string>(); times.Add(keyDemoName, new FileInfo(demoPath).Name); if (timeStrings.Count > 0) { var strInfo = GetGoodTimeStringInfo(); if (strInfo != null) { if (!string.IsNullOrEmpty(strInfo.recordDateString)) { times.Add(keyRecordDate, strInfo.recordDateString); } times.Add(keyRecordTime, strInfo.timeString); } else { for (int i = 0; i < timeStrings.Count; i++) { var timeInfo = timeStrings[i]; if (!string.IsNullOrEmpty(timeInfo.recordDateString)) { string keyDate = timeStrings.Count > 1 ? keyRecordDate + " " + (i + 1) : keyRecordDate; times.Add(keyDate, timeInfo.recordDateString); } string keyTime = timeStrings.Count > 1 ? keyRecordTime + " " + (i + 1) : keyRecordTime; times.Add(keyTime, timeInfo.timeString); } } } if (fin != null) { string bestTime = getTimeByMillis(fin.Value.Value.time); bool hasTr = fin.Value.Key > 1; string trAdd = hasTr ? " (Time reset)" : ""; times.Add(keyBestTime, bestTime + trAdd); } friendlyInfo.Add(keyRecord, times); //demo triggers if (clientEvents != null && clientEvents.Count > 0) { Dictionary <string, string> triggers = new Dictionary <string, string>(); try { int stCount = 0; int trCount = 0; int cpCount = 0; int ftCount = 0; int pmCount = 0; int cuCount = 0; int tCount = 0; for (int i = 0; i < clientEvents.Count; i++) { ClientEvent ce = clientEvents[i]; string diff = ""; if (i > 0) { var prev = clientEvents[i - 1]; long t = ce.serverTime - prev.serverTime; if (t > 0 && prev.serverTime > 0) { diff = string.Format(" (+{0})", getDiffByMillis(t)); } } if (ce.eventStartFile) { var user = getPlayerInfoByPlayerNum(clc.clientNum); if (user == null) { user = getPlayerInfoByPlayerNum(ce.playerNum); } string username = user == null ? null : Ext.GetOrNull(user, "name"); string userString = string.IsNullOrEmpty(username) ? "" : "Client: " + username; triggers.Add("StartFile", userString); } if (ce.eventStartTime) { var user = getPlayerInfoByPlayerNum(ce.playerNum); string username = user == null ? null : Ext.GetOrNull(user, "name"); string userString = string.IsNullOrEmpty(username) ? "" : "Player: " + username; triggers.Add("StartTimer" + getNumKey(++stCount), userString + diff); } if (ce.eventTimeReset) { var user = getPlayerInfoByPlayerNum(ce.playerNum); string username = user == null ? null : Ext.GetOrNull(user, "name"); string userString = string.IsNullOrEmpty(username) ? "" : "Player: " + username; triggers.Add("TimeReset" + getNumKey(++trCount), userString + diff); } if (ce.eventFinish) { triggers.Add("FinishTimer" + getNumKey(++ftCount), getTimeByMillis(ce.time) + diff); } if (ce.eventCheckPoint) { triggers.Add("CheckPoint" + getNumKey(++cpCount), getTimeByMillis(ce.time) + diff); } if (ce.eventChangePmType) { string pmString = ce.playerMode < ClientEvent.pmTypesStrings.Length ? ClientEvent.pmTypesStrings[ce.playerMode] : ce.playerMode.ToString(); triggers.Add("ChangePlayerMode" + getNumKey(++pmCount), pmString + diff); } if (ce.eventSomeTrigger && ce.playerMode == (int)ClientEvent.PlayerMode.PM_NORMAL) { triggers.Add("EventTrigger" + getNumKey(++tCount), getTimeByMillis(ce.time) + diff); } if (ce.eventChangeUser) { var user = getPlayerInfoByPlayerNum(ce.playerNum); string username = user == null ? null : Ext.GetOrNull(user, "name"); string userString = string.IsNullOrEmpty(username) ? "" : "Player: " + username; triggers.Add("ChangeUser" + getNumKey(++cuCount), userString + diff); } } } catch (Exception ex) { Q3Utils.PrintDebug(clc.errors, ex.Message); } friendlyInfo.Add(keyTriggers, triggers); } if (rawConfig == null) { return(friendlyInfo); } //Player if (kPlayer != null) { friendlyInfo.Add(keyPlayer, kPlayer); } else { for (int i = 0; i < keys.Count; i++) { friendlyInfo.Add(keyPlayer + " " + (i + 1).ToString(), allPlayersConfigs[keys[i]]); } } Dictionary <string, string> clInfo = null; Dictionary <string, string> gInfo = null; if (rawConfig.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_CLIENT)) { clInfo = Q3Utils.split_config(rawConfig[Q3Const.Q3_DEMO_CFG_FIELD_CLIENT]); } if (rawConfig.ContainsKey(Q3Const.Q3_DEMO_CFG_FIELD_GAME)) { gInfo = split_config_game(rawConfig[Q3Const.Q3_DEMO_CFG_FIELD_GAME]); } //Gametype var parameters = Ext.Join(clInfo, gInfo); gameInfo = new GameInfo(parameters); var gameInfoDict = new Dictionary <string, string>(); if (parameters.Count > 0) { bool diff = gameInfo.gameName.ToLowerInvariant() != gameInfo.gameNameShort.ToLowerInvariant(); if (diff) { gameInfoDict.Add("gameName", string.Format("{0} ({1})", gameInfo.gameName, gameInfo.gameNameShort)); } else { gameInfoDict.Add("gameName", gameInfo.gameName); } gameInfoDict.Add("gameType", string.Format("{0} ({1})", gameInfo.gameType, gameInfo.gameTypeShort)); if (!string.IsNullOrEmpty(gameInfo.gameplayTypeShort)) { gameInfoDict.Add("gameplay", string.Format("{0} ({1})", gameInfo.gameplayType, gameInfo.gameplayTypeShort)); } if (!string.IsNullOrEmpty(gameInfo.modType)) { gameInfoDict.Add("modType", string.Format("{0} ({1})", gameInfo.modTypeName, gameInfo.modType)); } } //Game var game = Ext.Join(gameInfoDict, gInfo); if (game.Count > 0) { friendlyInfo.Add(keyGame, game); } //Client if (clInfo != null) { friendlyInfo.Add(keyClient, clInfo); } //Raw configs Dictionary <string, string> raw = new Dictionary <string, string>(); foreach (var r in rawConfig) { raw.Add(r.Key.ToString(), r.Value); } friendlyInfo.Add(keyRaw, raw); //Console commands if (clc.console.Count > 0) { Dictionary <string, string> conTexts = new Dictionary <string, string>(); foreach (var kv in clc.console) { conTexts.Add(kv.Key.ToString(), removeColors(kv.Value.ToString())); } friendlyInfo.Add(keyConsole, conTexts); } if (clc.errors.Count > 0) { Dictionary <string, string> errTexts = new Dictionary <string, string>(); int i = 0; foreach (var kv in clc.errors) { errTexts.Add((++i).ToString(), kv.Key.ToString()); } friendlyInfo.Add(keyErrors, errTexts); } return(friendlyInfo); }
private long getTime(PlayerState ps, int snap_serverTime, int df_ver, int mapNameChecksum) { int time = shl32(ps.stats[7], 0x10) | (ps.stats[8] & 0xffff); if (time == 0) { return(0); } //encryption in cheated demos changed somewhere between >=19110 and <=19112 if (client.isOnline || (df_ver >= 19112 && client.isCheatsOn)) { return(time); } time ^= Math.Abs((int)(Math.Floor(ps.origin[0]))) & 0xffff; time ^= shl32(Math.Abs((int)Math.Floor(ps.velocity[0])), 0x10); time ^= ps.stats[0] > 0 ? ps.stats[0] & 0xff : 150; time ^= shl32(ps.movementDir & 0xf, 0x1c); //if time was byte array(least significant at time[0]): //time[3] ^= time[2] //time[2] ^= time[1] //time[1] ^= time[0] //time[0] unchanged for (int i = 0x18; i > 0; i -= 8) { var temp = (shr32(time, i) ^ shr32(time, i - 8)) & 0xff; time = (time & ~shl32(0xff, i)) | shl32(temp, i); } var local1c = shl32(snap_serverTime, 2); //df_ver = 19124; //map_type = 24; // global_11cdc8, not sure why i called this map_type local1c += shl32(df_ver + mapNameChecksum, 8); local1c ^= shl32(snap_serverTime, 0x18); time ^= local1c; local1c = shr32(time, 0x1c); // time[28:32] local1c |= shl32(~local1c, 4) & 0xff; local1c |= shl32(local1c, 8); local1c |= shl32(local1c, 0x10); time ^= local1c; local1c = shr32(time, 0x16) & 0x3f; // time[22:28] time &= 0x3fffff; // local20 = time[0:6] + time[6:12] + time[12:18] ... var local20 = 0; for (int l = 0; l < 3; l++) { local20 += shr32(time, 6 * l) & 0x3f; } // ... + time[18:22] local20 += shr32(time, 0x12) & 0xf; if (local1c != (local20 & 0x3f)) { Q3Utils.PrintDebug(clc.errors, "bad checksum at decoding demo time"); } return(time); }
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); }