Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more examples, add Weapon#quality #198

Merged
merged 5 commits into from
Jan 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 38 additions & 166 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,62 +65,25 @@ npm install --save demofile

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.
❓ **Need help?** [Raise an issue](https://github.com/saul/demofile/issues/new/choose) or ask in the [Gitter community](https://gitter.im/saul/demofile).

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

- [CS:GO Game Events - AlliedModders Wiki](https://wiki.alliedmods.net/Counter-Strike:_Global_Offensive_Events)
- [Gitter community](https://gitter.im/saul/demofile)
### Examples

## Contributing

Please read the [Contributing Guidelines](./CONTRIBUTING.md) to learn how you can help out on the project.
Various examples are available in the `examples` folder:

## Examples
| Example | Description |
| ------------------------------------------- | -------------------------------------------------------------------------------------- |
| [`join-leave.ts`](./examples/join-leave.ts) | Print all players that join and leave the game during the course of the demo. |
| [`plant-site.ts`](./examples/plant-site.ts) | Prints which player planted the bomb and at which site. |
| [`purchases.ts`](./examples/purchases.ts) | Prints which items are purchased by each player. |
| [`rank.ts`](./examples/rank.ts) | At the end of the game, prints all player ranks. |
| [`scores.ts`](./examples/scores.ts) | Prints team scores after each round. |
| [`tickrate.ts`](./examples/tickrate.ts) | Prints demo tick rate and duration in seconds. |
| [`dumpfile.ts`](./examples/dumpfile.ts) | Advanced example of recreating coloured chat messages, round scores and the kill feed. |

### Print player information when it changes

```ts
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
#### Print kills

```ts
import fs = require("fs");
Expand Down Expand Up @@ -159,61 +122,7 @@ JW [mac10 HS] Magisk
*/
```

### Print round changes

```ts
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
#### Print player information when it changes

```ts
import fs = require("fs");
Expand All @@ -222,79 +131,42 @@ 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;
demoFile.stringTables.on("update", e => {
if (e.table.name === "userinfo" && e.userData != null) {
console.log("\nPlayer info updated:");
console.log(e.entryIndex, e.userData);
}

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
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 demo information (e.g. tick rate)

```js
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);
});
### Useful links

/* Outputs:
- [CS:GO Game Events - AlliedModders Wiki](https://wiki.alliedmods.net/Counter-Strike:_Global_Offensive_Events)
- [Gitter community](https://gitter.im/saul/demofile)

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
## Contributing

*/
```
Please read the [Contributing Guidelines](./CONTRIBUTING.md) to learn how you can help out on the project.
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
- Unreleased release notes here
-->

### 2.0.0-beta.4 (2021-01-10)

- :sparkles: Add `Weapon#quality` to represent an item's quality
- :sparkles: Add `cancel` method to `DemoFile#start` event to cancel any further parsing.

### 2.0.0-beta.3 (2021-01-10)

- :sparkles: #164: Add CUserCmd parsing on in-eye/perspective demos
Expand Down
79 changes: 79 additions & 0 deletions examples/join-leave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Example output:

[Time: 0] ESEA SourceTV (BOT) joined the game
[Time: 0] PC419 m0nt-S- (STEAM_1:0:16289124) joined the game
[Time: 0] PC419 Chrismagnum (STEAM_1:1:16522881) joined the game
[Time: 0] PC419 FluentSwede (STEAM_1:0:101477774) joined the game
[Time: 0] PC419 valentino (STEAM_1:0:234221856) joined the game
[Time: 0] CHF| zephh (STEAM_1:0:88720676) joined the game
[Time: 0] CHF| yam- (STEAM_1:1:15329) joined the game
[Time: 0] CHF| apocdud (STEAM_1:0:29162650) joined the game
...
[Time: 23.2265625] zeph (STEAM_1:0:88720676) joined the game
[Time: 2451.65625] PC419 Chrismagnum left the game
[Time: 2451.6875] CHF| yam- left the game
[Time: 2451.90625] CHF| HUGHMUNGUS left the game
[Time: 2453.609375] PC419 FluentSwede left the game
[Time: 2453.875] CHF| apocdud left the game
[Time: 2454] CHF| zephh left the game
[Time: 2454.1875] PC419 DANZ left the game
[Time: 2454.71875] CHF| soju_j left the game
[Time: 2454.796875] PC419 m0nt-S- left the game
[Time: 2457.765625] PC419 valentino left the game
Finished.
*/

import * as assert from "assert";
import { DemoFile, Player } from "demofile";
import * as fs from "fs";

function parseDemoFile(path: string) {
fs.readFile(path, (err, buffer) => {
assert.ifError(err);

const demoFile = new DemoFile();

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

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

demoFile.gameEvents.on("player_disconnect", e => {
const player = demoFile.entities.getByUserId(e.userid);
if (!player) {
console.log(`! player_disconnect: unknown player ${e.userid}`);
return;
}

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

demoFile.on("end", e => {
if (e.error) {
console.error("Error during parsing:", e.error);
process.exitCode = 1;
}

console.log("Finished.");
});

// Start parsing the buffer now that we've added our event listeners
demoFile.parse(buffer);
});
}

parseDemoFile(process.argv[2]);
48 changes: 48 additions & 0 deletions examples/plant-site.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
Example output:

'PC419 Chrismagnum' planted the bomb at 'BombsiteA'
'PC419 FluentSwede' planted the bomb at 'BombsiteA'
'PC419 DANZ' planted the bomb at 'BombsiteA'
'PC419 Chrismagnum' planted the bomb at 'BombsiteA'
'PC419 DANZ' planted the bomb at 'BombsiteA'
'PC419 valentino' planted the bomb at 'BombsiteA'
'CHF| HUGHMUNGUS' planted the bomb at 'BombsiteA'
'CHF| soju_j' planted the bomb at 'BombsiteA'
'CHF| apocdud' planted the bomb at 'BombsiteA'
'CHF| soju_j' planted the bomb at 'BombsiteB'
'CHF| yam-' planted the bomb at 'BombsiteB'
'CHF| yam-' planted the bomb at 'BombsiteA'
Finished.
*/

import * as assert from "assert";
import { DemoFile } from "demofile";
import * as fs from "fs";

function parseDemoFile(path: string) {
fs.readFile(path, (err, buffer) => {
assert.ifError(err);

const demoFile = new DemoFile();

demoFile.gameEvents.on("bomb_planted", e => {
const player = demoFile.entities.getByUserId(e.userid)!;
console.log(`'${player.name}' planted the bomb at '${player.placeName}'`);
});

demoFile.on("end", e => {
if (e.error) {
console.error("Error during parsing:", e.error);
process.exitCode = 1;
}

console.log("Finished.");
});

// Start parsing the buffer now that we've added our event listeners
demoFile.parse(buffer);
});
}

parseDemoFile(process.argv[2]);
Loading