npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nimashoghi/demofile

v0.0.8

Published

[![Join the chat at https://gitter.im/saul/demofile](https://badges.gitter.im/saul/demofile.svg)](https://gitter.im/saul/demofile?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Downloads

2

Readme

demofile

Join the chat at https://gitter.im/saul/demofile

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.

Supported Demo Features

  • Entity updates, server classes, data tables (including instance baselines)
  • Both perspectives (GOTV and client-side recorded)
  • User messages
  • String tables
  • Game events
  • Console variables

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 query 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, player_footstep) 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.getByHandledemoFile.entities.entities[entIndex]|Base class of all in-game entities, even non-renderable entities (e.g. CCSTeam). BaseEntity||Base class of most in-game entities (e.g. players, weapons, all other renderable entities). Player|demoFile.entities.playersdemoFile.entities.getByUserId|Represents an in-game player. Weapon|demoFile.entities.weaponsplayer.weaponplayer.weapons|Represents an in-game weapon (guns, grenades, knifes).

Installation

Browser

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

The DemoFile module will be available as window.demofile.

Node

npm install --save demofile

Screenshot

Example output

API

This library provides full access to the 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.

Take a look at the examples/dumpfile.js file for an indication as to how the library can be used to introspect demo files. This example is by no means exhaustive -- see the 'docs' folder for documentation on all public classes and methods.

Note: It is important to note that events are fired at the end of a tick, after all entity props and string tables have been updated.

Useful links

Examples

Print player information when it changes

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

fs.readFile("test.dem", (err, buffer) => {
  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.parse(buffer);
});

/* Outputs:

Player info updated:
12 { unknown_lo: 4294967295,
  unknown_hi: 4294963202,
  xuid_lo: 17825793,
  xuid_hi: 3417033,
  name: 'HS',
  userId: 20,
  guid: 'STEAM_1:1:1708516',
  friendsId: 3417033,
  friendsName: '',
  fakePlayer: false,
  isHltv: false,
  customFiles: [ 0, 0, 0, 0 ],
  xuid: Long { low: 3417033, high: 17825793, unsigned: false } }

[repeated for other players]
*/

Print kills

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

fs.readFile("test.dem", (err, buffer) => {
  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.parse(buffer);
});

/* Outputs:

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

*/

Print round changes

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

fs.readFile("test.dem", (err, buffer) => {
  const demoFile = new demofile.DemoFile();

  demoFile.gameEvents.on("round_end", e => {
    console.log(
      "*** Round ended '%s' (reason: %s, time: %d seconds)",
      demoFile.gameRules.phase,
      e.reason,
      demoFile.currentTime
    );

    // We can't print the team scores here as they haven't been updated yet.
    // See round_officially_ended below.
  });

  demoFile.gameEvents.on("round_officially_ended", e => {
    const teams = demoFile.teams;

    const terrorists = teams[2];
    const cts = teams[3];

    console.log(
      "\tTerrorists: %s score %d\n\tCTs: %s score %d",
      terrorists.clanName,
      terrorists.score,
      cts.clanName,
      cts.score
    );
  });

  demoFile.parse(buffer);
});

/* Outputs:

*** Round ended 'first' (reason: 7, time: 74.3828125 seconds)
        Terrorists: fnatic score 0
        CTs: OpTic Gaming score 1
*** Round ended 'first' (reason: 8, time: 134.203125 seconds)
        Terrorists: fnatic score 0
        CTs: OpTic Gaming score 2
*** Round ended 'first' (reason: 9, time: 217.609375 seconds)
        Terrorists: fnatic score 1
        CTs: OpTic Gaming score 2

*/

Print player joining/leaving

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

fs.readFile("test.dem", (err, buffer) => {
  const demoFile = new demofile.DemoFile();

  demoFile.entities.on("create", e => {
    // We're only interested in player entities being created.
    if (!(e.entity instanceof demofile.Player)) {
      return;
    }

    console.log(
      "[Time: %d] %s (%s) joined the game",
      demoFile.currentTime,
      e.entity.name,
      e.entity.steamId
    );
  });

  demoFile.entities.on("beforeremove", e => {
    if (!(e.entity instanceof demofile.Player)) {
      return;
    }

    console.log(
      "[Time: %d] %s left the game",
      demoFile.currentTime,
      e.entity.name
    );
  });

  demoFile.parse(buffer);
});

/* Outputs:

[Time: 51.3125] btx`akatro (STEAM_1:0:2143797827) joined the game
[Time: 60.78125] Nabeegh (STEAM_1:0:427524390) joined the game
[Time: 65.71875] TAURUS left the game
[Time: 76.65625] drragster (STEAM_1:0:2144425259) joined the game

*/

Print demo information (e.g. tick rate)

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

fs.readFile("test.dem", (err, buffer) => {
  const demoFile = new demofile.DemoFile();

  demoFile.on("start", () => {
    console.log("Demo header:", demoFile.header);
    console.log("Tick rate:", demoFile.tickRate);

    // Stop parsing - we're finished
    demoFile.cancel();
  });

  demoFile.parse(buffer);
});

/* Outputs:

Demo header: { magic: 'HL2DEMO',
  protocol: 4,
  networkProtocol: 13637,
  serverName: '--== CSGO.PGLARENA.RO # Competitive ==--',
  clientName: 'GOTV Demo',
  mapName: 'de_inferno',
  gameDirectory: 'csgo',
  playbackTime: 3001.1875,
  playbackTicks: 192076,
  playbackFrames: 95995,
  signonLength: 325854 }
Tick rate: 64

*/

Release History

1.0.6 (2019-03-23)

  • :zap: #112: Removed reference to ts-assert-exists

1.0.5 (2019-03-20)

  • :zap: #112: Moved some package dependencies to be devDependencies (thanks @razor-x)

1.0.4 (2019-02-19)

  • :bug: #106: Fixed Cannot read property 'm_iPrimaryAmmoType' of undefined

1.0.3 (2019-02-19)

  • :zap: Add source maps to dist .js files

1.0.2 (2019-01-13)

  • :bug: #101: Fixed player resource indexing one-off error

1.0.1 (2019-01-07)

  • :bug: #98: Fixed Steam 64 IDs being invalid

1.0.0 (2019-01-06)

  • :zap: Removed process.emitWarning on parsing error. Use IDemoEndEvent#error instead.
  • :bug: #95: Fixed array decoding bug which made demos made after the Danger Zone update unparseable (thanks @JeffreyLimbacher)

1.0.0-beta.1 (2018-10-21)

  • :sparkles: #56: Ported to TypeScript!
  • :sparkles: Added Networkable type, which is the new base class of all entities
  • :sparkles: BaseEntity is now only the base class of renderable entities
  • :zap: Removed BaseEntity#velocity
  • :zap: User messages are now listened without the 'CS_UM_' prefix
  • :zap: String tables are now no longer initialised to their maximum size, instead they grow as needed
  • :bug: #86: Fixed Cannot read property 'messageToObject' of undefined on some game events
  • :bug: Fixed error when trying to parse svc_EntityMsg messages

0.4.20 (2018-08-24)

  • :bug: Fixed #80, #78: Resource properties returning undefined

0.4.19 (2018-08-24)

  • :sparkles: Added DemoFile#tickRate
  • :sparkles: Added support for MP5SD (thanks @thorebear)

0.4.18 (2018-05-14)

  • :sparkles: Added BaseEntity#handle
  • :sparkles: Added BaseEntity#modelName - Name of the model that should be rendered for this entity. (e.g. 'models/Weapons/w_eq_smokegrenade_thrown.mdl')

0.4.17 (2018-04-29)

  • :sparkles: Added BaseEntity#velocity, BaseEntity#speed.
  • :sparkles: Improved dumpfile.js example to be more through.
  • :zap: #69: Player#allSpotters and Player#allSpotted now only returns alive players.

0.4.16 (2018-03-22)

  • :zap: #62: Fix entity position calculation. Use cell coordinates to determine entity positions.

0.4.15 (2018-03-10)

  • :zap: #58: Entities are now removed at tickend, after events are fired. This means the grenade entity can be accessed in flashbang_detonate and decoy_detonate.

0.4.14 (2018-02-26)

  • :sparkles: Added Entities#weapons (thanks @thorebear)

0.4.13 (2018-02-26)

  • :sparkles: Added Player#matchStats to represent player performance on a per-round basis

0.4.12 (2018-02-02)

  • :sparkles: #11: Fixed malformed demos resulting in an exception. Added error property to the DemoFile#end event
  • :zap: #52: GameRules#roundNumber has been renamed to GameRules#roundsPlayed - the old name is available for backwards compatibility

0.4.11 (2017-12-20)

  • :bug: #43: Fixed parsing of string tables updates for tables with fixed userdata sizes

0.4.10 (2017-12-08)

  • :sparkles: Added Player#cashSpendTotal and Player#cashSpendThisRound

0.4.9 (2017-10-05)

  • :bug: #31: Fixed DemoFile#currentTime returning the wrong game time

0.4.8 (2017-05-08)

  • :bug: #11: Fixed parse error when a 'stop' command is missing from the demo file

0.4.7 (2017-05-08)

  • :sparkles: Added Weapon entity class to represent in-game items (thanks @pedrofornaza)

0.4.6 (2017-04-22)

  • :sparkles: Added Player#score, Player#mvps and Player#clanTag (thanks @derpalmer)

0.4.5 (2017-04-15)

  • :bug: Fix exception when calling DemoFile#cancel (thanks @derpalmer)

0.4.4 (2017-03-21)

  • :bug: Revert accidental bit-buffer upgrade

0.4.2 (2017-03-20)

  • :sparkles: Added Player#steam64Id for easy access to Steam64 ID (thanks @derpalmer)

0.4.1 (2017-02-23)

  • :bug: Fixed exception in Player#isAlive

0.4.0 (2017-02-05)

Major update

  • Added new Player, Team and GameRules entities
  • Added DemoFile#conVars for accessing console variables and listening for changes
  • Updated dumpfile.js example to be more representative of real-world usage

0.3.1 (2017-01-30)

  • :bug: Fixed Entity#getProp exception when a prop is updated that is not part of the entity baseline

0.3 (2016-11-16)

Major update

  • The library now support Browserify and the compiled bundle can be included in web browsers
  • Added DemoFile#progress event that indicates what percentage of the demo file has been parsed
  • Removed dependency on the pace library

0.2.11 (2016-10-16)

  • :sparkles: Added entities isHandleSet to determine if a networked entity handle is set (the game uses (1 << 21) - 1 to mean empty)

0.2.10 (2016-10-11)

  • :sparkles: Added entities getByHandle to find an entity from a given networked entity handle (e.g., from m_hMyWeapons)

0.2.9 (2016-09-12)

  • :sparkles: Added entities baselineupdate event which is fired whenever the instancebaseline string table is updated with new properties.

0.2.8 (2016-08-30)

  • :sparkles: The dem_StringTables command, which holds additional string table data on client-side recorded demos, is now parsed
  • :zap: String table postcreate is now fired after svc_CreateStringTable and dem_StringTables are parsed

0.2.7 (2016-08-23)

  • :sparkles: Added entities datatablesready event which is fired when data tables have been parsed.

0.2.6 (2016-08-23)

  • :sparkles: Added entity postcreate event which is fired after an entity has been created and all of its properties parsed.

0.2.5 (2016-07-08)

  • :sparkles: Added string table postcreate event which is fired after a new stringtable's entries have been populated

0.2.4 (2016-07-08)

  • :bug: Fixed retrieving properties on entities whose server class has no instance baseline

0.2.3 (2016-07-03)

  • :sparkles: Instance baselines are now parsed and used by Entity#getProp

0.2.2 (2016-07-03)

  • :bug: Fixed stringtable updates affecting all entries in the table

0.2.1 (2016-03-25)

  • :card_index: Removed node-protobuf un-used dependency

0.2.0 (2016-01-30)

  • :zap: Each tick is now parsed on a separate process tick.
  • :zap: Game event callbacks are now fired at the end of the tick.
  • :bug: Fixed Vector props decoding to undefined.

0.1.0 (2016-01-18)

  • :tada: Initial release.

Acknowledgements