Giter Club home page Giter Club logo

demofile's Introduction

demofile npm CI

A node.js library for parsing Counter-Strike Global Offensive (CSGO) demo files. The library is also Browserify-able, and a standalone bundle that you can <script src="..."> is available in browser/bundle.js.

This library also supports streaming from GOTV broadcasts over HTTP, with DemoFile#parseBroadcast.

⚠️ This library requires Node v14 or later.

❓ Need help

Supported demo features

  • GOTV and POV perspective fully supported
  • Game events (e.g. player_death)
  • User messages (e.g. chat messages, HUD text)
  • Console variables (cvar/convars)
  • Entity updates, server classes, data tables
  • String tables
  • Reading encrypted messages (e.g. chat in public matchmaking demos)
  • Streaming from GOTV broadcasts over HTTP

Running the examples

Getting started is simple:

.../demofile$ npm ci
.../demofile$ cd examples
.../demofile/examples$ npm ci

You can now run the example scripts. Take a look in the examples folder for some scripts to try out. Detailed descriptions of these scripts can be found below in the Examples section below.

If you don't have any demo files to hand, use the demos/download.sh Bash script to download the ones used for testing.

.../demofile/examples$ npx ts-node dumpfile.ts ../demos/pc419-vs-chiefs-mirage.dem
npx: installed 14 in 1.883s
Demo header: {
  magic: 'HL2DEMO',
  protocol: 4,
  networkProtocol: 13753,
  serverName: 'Counter-Strike: Global Offensive',
  clientName: 'GOTV Demo',
  mapName: 'de_mirage',
  gameDirectory: 'csgo',
  playbackTime: 2569.375,
  playbackTicks: 328880,
  playbackFrames: 164271,
  signonLength: 433479
}
...

Installation

Node

npm install --save demofile

Browser

<script src="browser/bundle.js"></script>

The DemoFile module will be available as window.demofile.

Screenshot

Using the dumpfile example:

Example output

Documentation

Auto-generated API documentation is available at saul.github.io/demofile.

Class Description
DemoFile Represents a demo file for parsing.

The DemoFile object has properties which point to instances of several other classes that can be used to inspect and listen to changes in the game world:

Class Property Description
ConVars demoFile.conVars Manages console variables. (Only FCVAR_NOTIFY and FCVAR_REPLICATED are available.)
Entities demoFile.entities Represents entities and networked properties within a demo.
GameEvents demoFile.gameEvents Manages game events for a demo file. (e.g. player_death, bomb_defused)
StringTables demoFile.stringTables Handles string tables for a demo file. (e.g. userinfo)
UserMessages demoFile.userMessages Handles user messages for a demo file. (e.g. SayText for in-game chat messages)

There are several classes which allow access to different types of entities (e.g. players, items, props). These are summarised below:

Entity Usage Description
Networkable demoFile.entities.getByHandle
demoFile.entities.entities.get(entIndex)
Base class of all in-game entities, even non-renderable entities (e.g. CCSTeam).
BaseEntity Base class of the vast majority of in-game entities (e.g. players, weapons, all other renderable entities).
Player demoFile.entities.players
demoFile.entities.getByUserId
Represents an in-game player.
Team demoFile.entities.teams
player.team
Represents a team (terrorists, counter-terrorists, spectators).
Weapon demoFile.entities.weapons
player.weapon
player.weapons
Represents an in-game weapon (guns, grenades, knifes).
Projectile Represents a thrown grenade projectile (e.g. a flying smoke grenade).
GameRules demoFile.gameRules Represents the game rules and parts of the match state (e.g. round number, is warmup)

API

This library provides full access to all data available in CSGO demo files. Unlike some other libraries, demofile is feature complete and supports the latest demos. As well as providing high-level APIs to access the state of the game, low-level access is available and is not discouraged.

Note that events are fired at the end of a tick, after all entity props and string tables have been updated.

Examples

Various examples are available in the examples folder:

Example Description
join-leave.ts Print all players that join and leave the game during the course of the demo.
molotov.ts Prints the location of molotov/incendiary grenade explosions.
paintkits.ts Collects paint kits of each weapon that is used in a kill.
plant-site.ts Prints which player planted the bomb and at which site.
purchases.ts Prints which items are purchased by each player.
rank.ts At the end of the game, prints all player ranks.
scores.ts Prints team scores after each round.
tickrate.ts Prints demo tick rate and duration in seconds.
trajectory.ts Prints grenade trajectories and who threw them.
⚠ Advanced: dumpfile.ts Advanced example of recreating coloured chat messages, round scores and the kill feed.

Print kills

const fs = require("fs");
const demofile = require("demofile");

const demoFile = new demofile.DemoFile();

demoFile.gameEvents.on("player_death", e => {
  const victim = demoFile.entities.getByUserId(e.userid);
  const victimName = victim ? victim.name : "unnamed";

  // Attacker may have disconnected so be aware.
  // e.g. attacker could have thrown a grenade, disconnected, then that grenade
  // killed another player.
  const attacker = demoFile.entities.getByUserId(e.attacker);
  const attackerName = attacker ? attacker.name : "unnamed";

  const headshotText = e.headshot ? " HS" : "";

  console.log(`${attackerName} [${e.weapon}${headshotText}] ${victimName}`);
});

demoFile.parseStream(fs.createReadStream("test.dem"));

/* Outputs:

HS [cz75a HS] flusha
Lekr0 [ak47 HS] friberg
KRIMZ [ak47] HS
JW [mac10 HS] Mixwell
JW [hegrenade] HS
JW [mac10 HS] Magisk

*/

Print player information when it changes

const fs = require("fs");
const demofile = require("demofile");

const demoFile = new demofile.DemoFile();

demoFile.stringTables.on("update", e => {
  if (e.table.name === "userinfo" && e.userData != null) {
    console.log("\nPlayer info updated:");
    console.log(e.entryIndex, e.userData);
  }
});

demoFile.parseStream(fs.createReadStream("test.dem"));

/* Outputs:

Player info updated:
0 {
  xuid: Long { low: 0, high: 0, unsigned: false },
  name: 'ESEA SourceTV',
  userId: 2,
  guid: 'BOT',
  friendsId: 0,
  friendsName: '',
  fakePlayer: true,
  isHltv: false
}

Player info updated:
1 {
  xuid: Long { low: 32578248, high: 17825793, unsigned: false },
  name: 'PC419 m0nt-S-',
  userId: 3,
  guid: 'STEAM_1:0:16289124',
  friendsId: 32578248,
  friendsName: '',
  fakePlayer: false,
  isHltv: false
}

[repeated for other players]
*/

Useful links

Contributing

Please read the Contributing Guidelines to learn how you can help out on the project.

demofile's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

demofile's Issues

Demos not being parsed after last update

Ever since the update (i think it was this one: http://blog.counter-strike.net/index.php/2017/11/19757/), that demo parser are crashing very early in the game, on this:

TypeError: Cannot read property 'slice' of undefined
    at _.reduce (/home/demos2/csgodemoparser/stringtables.js:193:38)
    at arrayReduce (/home/demos2/csgodemoparser/node_modules/lodash/index.js:1450:23)
    at Function.<anonymous> (/home/demos2/csgodemoparser/node_modules/lodash/index.js:3445:13)
    at StringTables._parseStringTableUpdate (/home/demos2/csgodemoparser/stringtables.js:175:7)
    at StringTables._handleUpdateStringTable (/home/demos2/csgodemoparser/stringtables.js:287:10)
    at emitOne (events.js:96:13)
    at DemoFile.emit (events.js:188:7)
    at DemoFile._handleDemoPacket (/home/demos2/csgodemoparser/demo.js:218:12)
    at DemoFile._parseRecurse (/home/demos2/csgodemoparser/demo.js:312:14)
    at runCallback (timers.js:672:20)

Solutions?

Incorrect bomb position

Hello. I want to track position of bomb. This is my code:

demo.gameEvents.on('bomb_dropped', function(e) {
    console.log(demo.entities.entities[e.entindex].position);
});

However I am getting incorrect values. Output looks like this:

...
{ x: 27.5625,  y: 0.40625,  z: 29.4375 }
{ x: 26.125,   y: 9.4375,   z: 29.96875 }
{ x: 29.65625, y: 6.84375,  z: 16.1875 }
{ x: 21.71875, y: 29.5,     z: 21.1875 }
{ x: 13.1875,  y: 12.59375, z: 12.09375 }
{ x: 15.09375, y: 2.75,     z: 20.15625 }
{ x: 13.875,   y: 23.90625, z: 20.96875 }
{ x: 6.40625,  y: 17.59375, z: 19.15625 }
{ x: 2.96875,  y: 19.3125,  z: 15.75 }
{ x: 5,        y: 29.75,    z: 15.65625 }
...

Notice all values are less than 30. Range of coordinates should be much bigger. Now it looks like bomb is always dropped very close to the center of map, but I'm sure this is not the case.

m_iMatchStats_Damage

Hey mate,

Happy new years!

I'm having a bit of trouble trying to get the damage dealt per user per round with m_iMatchStats_Damage

I've tried using:

let pr = this._demo.entities.getSingleton('DT_CSPlayer');
let values = pr.props['m_iMatchStats_Damage'];
return values[Object.keys(values)[this.index]];	

But it just results with a zero 0 value.

Ideally I want to calculate damage dealt per round and average damage per player.

Parsing Error

I got an error with this demo file

C:\temp\git\demoanalyzer\node_modules\demofile\entities.js:54
    return this.props[tableName][varName];
                                ^

TypeError: Cannot read property 'm_iPrimaryAmmoType' of undefined
    at Entity.getProp (C:\temp\git\demoanalyzer\node_modules\demofile\entities.js:54:33)
    at Entities._readNewEntity (C:\temp\git\demoanalyzer\node_modules\demofile\entities.js:414:29)
    at Entities._handlePacketEntities (C:\temp\git\demoanalyzer\node_modules\demofile\entities.js:451:14)
    at emitOne (events.js:77:13)
    at DemoFile.emit (events.js:169:7)
    at DemoFile._handleDemoPacket (C:\temp\git\demoanalyzer\node_modules\demofile\demo.js:177:12)
    at DemoFile._parseRecurse (C:\temp\git\demoanalyzer\node_modules\demofile\demo.js:271:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

Is the demo file broken or is there something with the library? Thanks!

ESEA Demos with missing playbackTime, ticks, frames & signonLength

Hey mate,

I've got a lot of ESEA demos that appear to be missing the end of the file.

    at ByteBuffer.module.exports.ByteBufferPrototype.readBytes (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:499:23)
    at ByteBuffer.readIBytes (C:\test\node_modules\demofile\ext\bytebuffer.js:21:15)
    at DemoFile._handleDemoPacket (C:\test\node_modules\demofile\demo.js:207:31)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:321:16)
    at runCallback (timers.js:794:20)
    at tryOnImmediate (timers.js:752:5)
    at processImmediate [as _immediateCallback] (timers.js:729:5)

demoFile.header returns:

  protocol: 4,
  networkProtocol: 13626,
  serverName: 'Counter-Strike: Global Offensive',
  clientName: 'GOTV Demo',
  mapName: 'de_mirage',
  gameDirectory: 'csgo',
  playbackTime: 0,
  playbackTicks: 0,
  playbackFrames: 0,
  signonLength: 0 }

sv_minupdaterate & sv_maxupdaterate are set to 128 - what is the best way to try to heal the header information?

`npm run generate-docs` fails

Christophers-MacBook-Pro:~ chris$ npm run generate-docs
npm ERR! path /Users/chris/package.json
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall open
npm ERR! enoent ENOENT: no such file or directory, open '/Users/chris/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/chris/.npm/_logs/2017-08-15T05_54_54_452Z-debug.log

Wrong players position for POV Demo ?

I recorded a demo from my POV. When I try to get other players position when an event is fired, I got mine, is that a correct behavior ?


 var player = demo.entities.getByUserId(e.userid);
            console.log(player.name);

            if (isWeaponMonitored(e.weapon) !== -1) {
                var position = player.position;
                console.log(position);

Displays :

Agouha 
{ x: 1257.800048828125, y: -229.25465393066406, z: -163.96875 }

소리
{ x: 1159.30859375,  y: 45.68016052246094,  z: -167.10072326660156 }

While one player is at T spawn, and the other at CT spawn, it just displays my position at T spawn for both players.

TypeScript typings

The module would be easier to manage with TypeScript if there were a typings file. @types/demofile, or demo.d.ts typings definition file for the module.

Demo currentTime scale

As the docblock says @returns {float} Number of seconds elapsed. But i did some tests and im almost sure that its miliseconds. Which one is right?

How can I translate the position

How can I convert the position in the file to the position in the map ?
The postion I get is like this (3305171700,1159083300, 1094367000) .
I don't know where is the point in the map of the match !

Improved handling for ESEA demos

Hey mate,

This is a bit of an odd request but ESEA and bot is resetting the match at halftime and overtime so it breaks round counts and overtime triggers in the game, including m_gamePhase.

I've written my own phase and overtime detection logic, do you think it's worth adding this into the demofile parser? It's very edge case but a lot of games are played on ESEA these days. Would also be nice to have an auto overtime lo3/lo5 detection.

Cheers,

Mav

Improved Examples

Hey, awesome module first up!

I'm still getting my head around the documentation and what data is valuable and how it's all linked together. I see a lot of questions popping up around how can I do 'x' or how do I get 'y' data for a player etc.

I think it would be valuable to come up with a list of popular requests and some additional ones then build some code examples.

For me I want to be able to determine if a player is "tracing" or essentially "aimbotting" using the data available in the demofile. I don't have much experience with demofiles so there is a bit of a learning curve. If you wanted to leave it open for people to learn on their own then perhaps an FAQ, with examples like:

"If you want to determine where a player is looking then using the entities.on("change") method and look for DT_CTPlayerBase->m_blah value".

What do you think?

Any way to find a players rank?

I have been looking through the docs and I can't seem to find anything leading to players ranks. Is there any way of finding a players rank and if not is it possible to implement in the future?

Some detonate events cannot be resolved to an entity

I am trying to get the entity on a detonate event using the entityid. For the hegrenade_detonate and smokegrenade_detonate events, this resolves to an entity. However, for flashbang_detonate and decoy_detonate this cannot be resolved to an entity. (This is also the case for molotov_detonate, but this does not seem to have an entityid property, according to https://wiki.alliedmods.net/Counter-Strike:_Global_Offensive_Events#molotov_detonate)

I am running the following code, which results in grenade being undefined for the events mentioned above.

demoFile.gameEvents.on('***_detonate', e => {
	let thrower = demoFile.entities.getByUserId(e.userid);
	let grenade = demoFile.entities.entities[e.entityid];
	if (thrower && grenade) {
		let throwerPosition = thrower.position;
		let grenadePostiion = grenade.position;
		console.log('%s threw a grenade from position [%s,%s,%s] to position [%s,%s,%s]', thrower.name, throwerPosition.x, throwerPosition.y, throwerPosition.z,  grenadePostiion.x, grenadePostiion.y, grenadePostiion.z);
			}
    });

start datetime

Can't seem to find the property to get the datetime for when the map started?

Some events missing from alliedmods

I found the following events in my demo that aren't in AlliedMods. Can't figure out how to make an account there though. 🤔

begin_new_match
cs_round_final_beep
cs_round_start_beep
hltv_chase
hltv_status
item_remove
player_disconnect
player_spawn
player_team
round_announce_last_round_half
round_announce_match_point
round_announce_match_start
round_officially_ended
round_time_warning

P.S. Thanks for the awesome library.

Get Game Patch

Hey,

I was wondering if there was a way to get the CSGO game patch version of a given HLTV demofile.

I looked for that in the demoHeader and parseHeader of your parser, the L33T/CSGO-Reflection repository for the csgo entity classes as well as CSGO and CS Source events but did not find anything.

I basically want to know on which patch (http://liquipedia.net/counterstrike/Patches) the game of the demofile was played on.

thanks and cheers,

Raphael Hippe

RangeError when parsing ESEA demos

Hey, I got an error when parsing huge demo files (120mb).

I tried same code with a 34mb (old) demo and with a new demo from ESL with 140mb, and in the second case it throws a error on bytebuffer-node:

bytebuffer-node.js:499
throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.length);
^

RangeError: Illegal offset: 0 <= 137980764 (+152) <= 137980880
at RangeError (native)
at module.exports.ByteBufferPrototype.readBytes (C:\Users\marce\Desktop\csgo_demoaparser__NEW\node_modules\bytebuffer\dist\bytebuffer-node.js:499:23)
at DemoFile._handleDemoPacket (C:\Users\marce\Desktop\csgo_demoaparser__NEW\demo.js:156:44)
at DemoFile._parseRecurse (C:\Users\marce\Desktop\csgo_demoaparser__NEW\demo.js:271:14)
at tryOnImmediate (timers.js:543:15)
at processImmediate [as _immediateCallback] (timers.js:523:5)

Any ideia on how to solve it?

How to get user entity?

So how can I get DT_BasePlayer based on userinfo table data? Is there corresponding field I should look for? Since I cant find steam id in DT_BasePlayer.

round_end event not being triggered correctly

image

i'm only seeing a total of 20 round_end events for match 2 that should have 26.

Match Data: https://www.hltv.org/matches/2318632/faze-vs-cloud9-eleague-major-2018

All 3 demos from the GOTV are missing round_end events.

match 1: https://mega.nz/#!OwFC2Q6J!mye2CCLGE8xojqDd6h6gy2aXPrH5kkfRSEIcUtlXtgI
match 2: https://mega.nz/#!ihURHbxK!IumMuyWguCWg4DKmj8jKFPyDTq0wyrRolN_JjmRcnpA
match 3: https://mega.nz/#!e9sWEJSD!9WYbyWozXZOZ7Ftxpv01G_S5NBx12paJz_ysHQr71wc

But if I process a match from IEM Oakland https://www.hltv.org/matches/2317279/sk-vs-astralis-iem-oakland-2017

The round_end events are correct.

Using latest version of demofile

Streaming interface

Right now in order to parse the demo, we have to read the entire file first. I think it'd be really cool if we can also support Node streams, so that we can have this stream: download .dem.bz file from Valve server -> unzip -> parse. It'd be much faster than download the zip file, unzip it completely, then read the unzipped file and parse it.

I thought about trying demofile as is, to see if it'd just magically work, using the following code:

function streamingAnalyze(stream, done) {
  const demoFile = new demofile.DemoFile();
  stream.pipe(through2(function handle(chunk, enc, callback) {
    demoFile.parse(chunk);
    callback();
  }))
  .on('finish', () => done(result));
}

But it failed, throwing the following stack trace:

C:\Dev\demo\node_modules\bytebuffer\dist\bytebuffer-node.js:499
                throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.length);
                ^

RangeError: Illegal offset: 0 <= 170 (+107899) <= 64464
    at ByteBuffer.Object.<anonymous>.module.exports.ByteBufferPrototype.readBytes (C:\Dev\demo\node_modules\bytebuffer\dist\bytebuffer-node.js:499:23)
    at ByteBuffer.Object.<anonymous>.ByteBuffer.readIBytes (C:\Dev\demo\node_modules\demofile\ext\bytebuffer.js:21:15)
    at DemoFile._handleDemoPacket (C:\Dev\demo\node_modules\demofile\demo.js:203:31)
    at DemoFile._parseRecurse (C:\Dev\demo\node_modules\demofile\demo.js:312:14)
    at ontimeout (timers.js:380:14)
    at tryOnTimeout (timers.js:244:5)
    at Timer.listOnTimeout (timers.js:214:5)

demo date

is it possible to get the date of the demo?

Finding the "thrower" of a molo/incin for inferno_startburn events

Hey Saul; thanks so much for the awesome lib and all the effort you put in!

I've been working on something to parse demos and get all the he/smoke/flash/molo/incin data. For all the others it's been relatively straight forward to match up the start & end locations (of a throw) by getting the event userid on weapon_fire and then matching it up when seeing the corresponding event like flashbang_detonate or hegrenade_detonate.

From what I can tell though there is no userid in the inferno_startburn event? Have I missed something or is there some special trick to finding out who threw a moly/incin in the startburn event?

Thanks in advance!
Nic.

Know when a grenade is launched ?

How would you proceed to get when a grenade (whatever the type is) is lauched (that means that the click has been released) ? Does the "fire" event works for grenade ?

Inconsistency between Player.kills and Player.matchStats.kills

I'm trying to calculate headshot-percentage of kills.

Executing the following code gives percentages between 0 and 100%, but totalKills[i] == p.kills is false most of the time.

let totalKills = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let totalHeadshotKills = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let headShotPercentage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

demoFile.gameEvents.on('round_end', () => {
	let roundNumber = demoFile.gameRules.roundsPlayed;
	let i = 0;
	for (p of teamT.members) {
		totalKills[i] += p.matchStats[roundNumber-1].kills;
		totalHeadshotKills[i] += p.matchStats[roundNumber-1].headShotKills;
		headShotPercentage[i] = totalHeadshotKills[i] / totalKills[i] * 100;
		// totalKills[i] == p.kills
		i++;
	}
	for (p of teamCT.members) {
		totalKills[i] += p.matchStats[roundNumber-1].kills;
		totalHeadshotKills[i] += p.matchStats[roundNumber-1].headShotKills;
		headShotPercentage[i] = totalHeadshotKills[i] / totalKills[i] * 100;
		// totalKills[i] == p.kills
		i++;
	}
});

The following code gives percentage over 100%. But here I am sure that the amount of kills is correct (looking at vod of the game).

let totalHeadshotKills = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let headShotPercentage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

demoFile.gameEvents.on('round_end', () => {
	let roundNumber = demoFile.gameRules.roundsPlayed;
	let i = 0;
	for (p of teamT.members) {
		totalHeadshotKills[i] += p.matchStats[roundNumber-1].headShotKills;
		headShotPercentage[i] = totalHeadshotKills[i] / p.kills * 100;
		i++;
	}
	for (p of teamCT.members) {
		totalHeadshotKills[i] += p.matchStats[roundNumber-1].headShotKills;
		headShotPercentage[i] = totalHeadshotKills[i] / p.kills * 100;
		i++;
	}
});

get correct roundtime

Hello, me again.

I am trying to get the roundtime of when e.g. a kill happened in during a round.

I am currently trying this:

    let stat = {
          attackerName: attacker.name,
          attackerHealth: attacker.health,
          weapon: e.weapon,
          // ...
          roundNumber: demoFile.gameRules.roundNumber,
          roundTime: demoFile.gameRules.getProp('DT_CSGameRules', 'm_iRoundTime')
        };

but that just returns 115 which I believe is the total round time.

Thanks,
Raphael Hippe

roundNumber is amibguous

Thanks for this awesome library, and all your great work on it. I hope I can contribute to it in the future.

roundNumber sounds like it'll be the round number for the round currently in progress, but it changes when the score changes, before the round actually ends. The field's name of m_totalRoundsPlayed sounds more accurate, and if people wanna keep track of the round currently in progress then roundNumber would probably be m_totalRoundsPlayed + 1, except between the round ending and the round actually ending.

round_end: when the bomb explodes / last player dies and the score changes, but folks can still move around for some amount of time to save weapons or whatever.
round_officially_ended: when players finally "respawn" / move back to their spawns.

Trying to read past the end of the stream

I'm trying to use demofile to parse a recently played demo file, and it's failing right after the match starts (This happens to multiple demo files). Here's the output from running the examples/dumpfile.js:

Demo header: { magic: 'HL2DEMO',
  protocol: 4,
  networkProtocol: 13569,
  serverName: 'Valve CS:GO US SouthEast Server (srcds007.125.44)',
  clientName: 'GOTV Demo',
  mapName: 'de_cache',
  gameDirectory: 'csgo',
  playbackTime: 3098.859375,
  playbackTicks: 198327,
  playbackFrames: 99125,
  signonLength: 614211 }
cash_team_rescued_hostage: undefined -> 600
bot_autodifficulty_threshold_high: undefined -> 0
cash_team_win_by_defusing_bomb: undefined -> 3500
game_mode: undefined -> 1
mp_ggtr_bomb_pts_for_upgrade: undefined -> 2
ff_damage_reduction_bullets: undefined -> 0.33
bot_quota_mode: undefined -> fill
cash_player_interact_with_hostage: undefined -> 300
mp_maxrounds: undefined -> 30
ammo_grenade_limit_total: undefined -> 4
mp_respawn_immunitytime: undefined -> 0
mp_roundtime_defuse: undefined -> 1.92
mp_ggprogressive_round_restart_delay: undefined -> 15
mp_timelimit: undefined -> 0
mp_warmuptime: undefined -> 60
sv_deadtalk: undefined -> 1
mp_randomspawn_los: undefined -> 0
mp_weapons_allow_map_placed: undefined -> 1
sv_hosting_lobby: undefined -> 1
nextlevel: undefined -> de_cache
mp_ggtr_bomb_defuse_bonus: undefined -> 1
mp_buytime: undefined -> 20
bot_difficulty: undefined -> 2
mp_roundtime_hostage: undefined -> 1.92
mp_freezetime: undefined -> 15
bot_defer_to_human_goals: undefined -> 1
sv_skyname: undefined -> sky_cs15_daylight01_hdr
sv_reliableavatardata: undefined -> 1
mp_friendlyfire: undefined -> 1
cash_team_hostage_interaction: undefined -> 600
spec_freeze_panel_extended_time: undefined -> 0
ff_damage_reduction_other: undefined -> 0.4
mp_molotovusedelay: undefined -> 0
cash_team_elimination_hostage_map_t: undefined -> 3000
mp_ggtr_bomb_detonation_bonus: undefined -> 1
ammo_grenade_limit_flashbang: undefined -> 2
mp_roundtime: undefined -> 1.92
mp_halftime: undefined -> 1
mp_ggtr_bomb_respawn_delay: undefined -> 0
think_limit: undefined -> 0
ff_damage_reduction_grenade: undefined -> 0.85
cash_team_elimination_hostage_map_ct: undefined -> 3000
cash_team_win_by_hostage_rescue: undefined -> 2900
tv_transmitall: undefined -> 1
GOTV (BOT) joined the game
syncopate (STEAM_1:1:18164814) joined the game
Larry (BOT) joined the game
Bert (BOT) joined the game
*** Round ended 'first' (reason: 16)
	Terrorists:  score 0
	CTs:  score 0
Fergus (BOT) joined the game
Bob (BOT) joined the game
Ryan (BOT) joined the game
Yanni (BOT) joined the game
Graham (BOT) joined the game
Allen (BOT) joined the game
Opie (BOT) joined the game
TAK1N L1VES (STEAM_1:0:42267311) joined the game
/home/dangmai/Dev/demo-analyzer/node_modules/demofile/ext/bitbuffer.js:201
  CW_LowPrecision,
           ^

Error: Trying to read past the end of the stream
    at BitStream.<anonymous> (/home/dangmai/Dev/demo-analyzer/node_modules/bit-buffer/bit-buffer.js:191:10)
    at _.map (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/ext/bitbuffer.js:46:54)
    at arrayMap (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/node_modules/lodash/index.js:1406:25)
    at Function.map (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/node_modules/lodash/index.js:6710:14)
    at BitStream.bitBuffer.BitStream.readBytes (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/ext/bitbuffer.js:46:23)
    at _.reduce (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/stringtables.js:217:34)
    at arrayReduce (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/node_modules/lodash/index.js:1450:23)
    at Function.<anonymous> (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/node_modules/lodash/index.js:3445:13)
    at StringTables._parseStringTableUpdate (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/stringtables.js:175:7)
    at StringTables._handleUpdateStringTable (/home/dangmai/Dev/demo-analyzer/node_modules/demofile/stringtables.js:287:10)

.on('error') handler for corrupt/incomplete demos

Hey mate,

I've been testing against a couple thousand demos and have noticed a good few of them are corrupt and it throws and exception.

I'm running this as a lambda function so an exception is very expensive. It would be great to have an .on('error') handler.

Currently I've solved this with:

process.on("uncaughtException", function (err) 
{
  console.error((new Date).toUTCString() + " uncaughtException:", err.message);
  console.error(err.stack);
});

Some of the errors I'm seeing:

at, 27 Jan 2018 02:24:38 GMT uncaughtException: Illegal offset: 0 <= 45087626 (+152) <= 45087696
RangeError: Illegal offset: 0 <= 45087626 (+152) <= 45087696
    at ByteBuffer.module.exports.ByteBufferPrototype.readBytes (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:499:23)
    at DemoFile._handleDemoPacket (C:\test\node_modules\demofile\demo.js:197:44)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:319:14)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: undefined
undefined
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: Illegal offset: 0 <= 45087693 (+4) <= 45087696
RangeError: Illegal offset: 0 <= 45087693 (+4) <= 45087696
    at ByteBuffer.module.exports.ByteBufferPrototype.readInt32 (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:906:23)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:306:32)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: Illegal offset: 0 <= 45087694 (+4) <= 45087696
RangeError: Illegal offset: 0 <= 45087694 (+4) <= 45087696
    at ByteBuffer.module.exports.ByteBufferPrototype.readInt32 (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:906:23)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:306:32)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: Illegal offset: 0 <= 45087695 (+4) <= 45087696
RangeError: Illegal offset: 0 <= 45087695 (+4) <= 45087696
    at ByteBuffer.module.exports.ByteBufferPrototype.readInt32 (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:906:23)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:306:32)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)
Sat, 27 Jan 2018 02:24:38 GMT uncaughtException: Illegal offset: 0 <= 45087696 (+4) <= 45087696
RangeError: Illegal offset: 0 <= 45087696 (+4) <= 45087696
    at ByteBuffer.module.exports.ByteBufferPrototype.readInt32 (C:\test\node_modules\bytebuffer\dist\bytebuffer-node.js:906:23)
    at DemoFile._parseRecurse (C:\test\node_modules\demofile\demo.js:306:32)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)

The demo's are indeed corrupt or the match was killed via a command or the server was killed/restarted. The .on('end') appears to be called for a few of the demos.

Make a browser version

mikeemoo/jsgo has one, would be really nice as it would open a lot of possible uses

Is there something hindering this, or is it just a matter of correctly configuring webpack/browserify (which I was unable to)?

The team Id

Is there a number for each team such as fnatic ?
Can I get the number of the team from the dem file with this project?

add to player m_iCashSpentThisRound and m_iTotalCashSpent

G'day mate,

Reference: https://github.com/L33T/CSGO-Reflection/blob/master/DT_CSPlayerResource.txt

Is it possible to add m_iCashSpentThisRound and m_iTotalCashSpent to the Player entity?

I see the is account() that returns m_iAccount

Would adding these be similar to the the mvps() getter?

get mvps() {
    let pr = this._demo.entities.getSingleton('DT_CSPlayerResource');
    let mvps = pr.props['m_iMVPs'];
    return mvps[Object.keys(mvps)[this.index]];
  }

Cheers

Getting weapon name and info

Since player.weapon returns a BaseEntity, how to get that weapon name (i didnt found it on the entity props)? Could we consider making a class to Weapon (like Player, Team, etc.) ?

Add basic API methods to README

I think it can be helpful to new comers and even for who already uses the project. Sometimes, getting to the docs folder can be expensive and having a quickview at the README is nice, more when you are on your phone or just in a hurry.

It dont need to be extensive or even provide ALL methods, but the main ones. Maybe something like this:

// Player entity
player.health // int
player.name // string
player.kills // int
player.isDefusing // bool

Demo start time

I need to find in which time demo started e.g. freeze time, round time etc, so that i can start counting my own time. I'm able to do that after receiving round end/round start, but not before.

Is there any property i missed, where is stored time of server, when demo was started?

get players position at x roundtime

Hey, me again ;-),

first of all: I saw this issue: #3 and tried to use it.
but I don't want to get the position of each player every tick or every event but at a set round timer mark so e.g. after 30 seconds have past in each round.

    demoFile.on('tickend', e => {
      let currentRoundTime = demoFile.currentTime - currentRoundStarttime;
      if (currentRoundTime > 30.0 && currentRoundTime < 30.025) {
        console.log(e, demoFile.currentTime, currentRoundTime);
        console.log('NEW ROUND 30 SEC MARK');
        for (var i = 0; i < myUserIds.length; i++) {
          let ent = demoFile.entities.entities[myUserIds[i]];
          console.log(myUserIds[i], ent.position);
          let xy = ent.getProp('DT_CSNonLocalPlayerExclusive', 'm_vecOrigin');
          console.log('xy', xy);
          let pos = {
            x: xy.x,
            y: xy.y,
            z: ent.getProp('DT_CSNonLocalPlayerExclusive', 'm_vecOrigin[2]')
          }
          console.log(myUserIds[i], pos);
        }
      }
    });


demoFile.gameEvents.on('round_start', e => {
      currentRoundStarttime = demoFile.currentTime;
    });

The issues I have with this are:

  • multiple results for the same second because ticks happen faster than seconds
  • different amount of results per round (sometimes 2, sometimes 3 and so on)
  • xy seems to be undefined sometimes:
x: xy.x,
TypeError: Cannot read property 'x' of undefined

after that I tried the ent.position but that returns the same position everytime (every round, and every tick) for each player, which I find very odd.

Thank,

Raphael Hippe

how can i get item_purchase event?

When use demofile i wan't to get item info , i code like this
demoFile.gameEvents.on('item_purchase',e=>{ console.log(e); });
I can get player_death event but can't get any item about event, is there anything wrong?
Home for you replay!

userId of player changes

Hello,

first of all I'd like to thank you for this awesome library.

I am currently working with this GOTV demofile: https://www.hltv.org/matches/2317947/mousesports-vs-faze-ecs-season-4-finals

and noticed that the userId for rain seems to change right at the end of the game:

for every kill / assist / death for rain it is: 187

when the 'end' event is fired I am checking for all userIds:

demoFile.on('end', () => {
      let testPlayerIds = demoFile.players.map(p => p.userId);
      console.log('testPlayerIds', testPlayerIds);

and get the following output:

testPlayerIds [ 176, 177, 178, 179, 192, 181, 182, 183, 189, 185, 190, 188, 191 ]

and the 187 is missing.

I left in the example code for notification when a player joins:

 demoFile.entities.on('create', e => {
      if (e.entity.serverClass.name !== 'CCSPlayer') {
        return;
      }
      if (e.entity.userInfo) {
        console.log('%s (%s) joined the game', e.entity.name, e.entity.steamId);
      }
    });

and don't get any console prints mid game for rain rejoining the game. Therefore it seems like the userId is changing mid game for no obvious reason.

Do you have any idea why that would be the case?

Thanks,
Raphael Hippe

How to get team information from player

Hello,

I want to take the team information that corresponds to a player. I've read the documentation but I wasn't able to get it done.

Thanks in advance.

DemoFile.cancel() not working

As I try to build a workaround for #11 I get an error when trying to cancel the parsing.

D:\git\demoanalyzer\node_modules\demofile\demo.js:287
      timers.cancelImmediate(this._immediateTimerToken);
             ^

TypeError: timers.cancelImmediate is not a function
    at DemoFile.cancel (D:\git\demoanalyzer\node_modules\demofile\demo.js:287:14)
    at DemoFile.demo.on.v (D:\git\demoanalyzer\demoworker.js:70:14)
    at emitOne (events.js:96:13)
    at DemoFile.emit (events.js:188:7)
    at DemoFile._parseRecurse (D:\git\demoanalyzer\node_modules\demofile\demo.js:297:10)
    at runCallback (timers.js:666:20)
    at tryOnImmediate (timers.js:639:5)
    at processImmediate [as _immediateCallback] (timers.js:611:5)

As described in the timers documentation the function name is "clearImmediate" but even when I change it the parsing goes on. Is there anything else wrong?
https://nodejs.org/api/timers.html#timers_cancelling_timers

Node v6.10.2

Smoke detonate position

Hey Saul,

First of all, thank you so much for all your work on this lib.

I'm writing a replay viewer using demofile but I'm not able to get the correct position of the smoke when smoke_detonate is triggered.

I've checked your last commit and updated demofile to master, but still, I'm not able to get the exactly smoke's position when it happens.

I'm listening for 'smokegrenade_detonate' and I get x, y and z.
Is it expected to get the exactly position of this entity when it explodes or not?

I'm already checking it as follows (the same I do to get the player position on map):

let overviewData = {
    "mirage": {
      "pos_x": -3230,
      "pos_y": 1713,
      "scale": 5
    }
};
var positionX = (gameEvent.x - overviewData[map].pos_x) / overviewData[map].scale;
var positionY = (overviewData[map].pos_y - gameEvent.y) / overviewData[map].scale;

What am I doing wrong?

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.