From 7f5fbd828b442c528ebbdd80cad8898f3b8932d5 Mon Sep 17 00:00:00 2001 From: Saul Rennison Date: Sat, 9 Jan 2021 17:32:27 +0000 Subject: [PATCH 1/3] Add more examples --- examples/package-lock.json | 40 ++++++++++++++++++-------- examples/plant-site.ts | 48 +++++++++++++++++++++++++++++++ examples/purchases.ts | 55 ++++++++++++++++++++++++++++++++++++ examples/rank.ts | 58 ++++++++++++++++++++++++++++++++++++++ examples/scores.ts | 28 ++++++++++++++---- src/entities/weapon.ts | 28 ++++++++++++++++++ 6 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 examples/plant-site.ts create mode 100644 examples/purchases.ts create mode 100644 examples/rank.ts diff --git a/examples/package-lock.json b/examples/package-lock.json index cf866b4ca..30249c13d 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -4,15 +4,23 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "demofile": { "version": "file:..", "requires": { "@types/bytebuffer": "^5.0.37", - "@types/lodash": "^4.14.116", "big-integer": "^1.6.48", "bit-buffer": "0.0.3", "bytebuffer": "^5.0.0", - "lodash": "^4.17.11", + "immutable": "^4.0.0-rc.12", + "iter-tools": "^7.0.2", "long": "^3.0.3", "protobufjs": "^6.8.8" }, @@ -3493,11 +3501,6 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, - "immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" - }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -8574,10 +8577,7 @@ "tslint-sonarts": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/tslint-sonarts/-/tslint-sonarts-1.8.0.tgz", - "integrity": "sha512-tpijO5VR18e+Ny99uMNNov3Hw7diiYQ8KoJkezpHGw9hSFFrO5g2PhwdQQo7O9puhJKMIutLl9g+ICMgg+bh0w==", - "requires": { - "immutable": "^3.8.2" - } + "integrity": "sha512-tpijO5VR18e+Ny99uMNNov3Hw7diiYQ8KoJkezpHGw9hSFFrO5g2PhwdQQo7O9puhJKMIutLl9g+ICMgg+bh0w==" }, "tsutils": { "version": "2.29.0", @@ -9007,6 +9007,24 @@ } } }, + "immutable": { + "version": "4.0.0-rc.12", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", + "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==" + }, + "iter-tools": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/iter-tools/-/iter-tools-7.0.2.tgz", + "integrity": "sha512-eQOeYO/EocvvxYJO0rcuMNr3sqsOe+LRxDLmAPmKmqIlLoJ90GFxRv/oSJ3yr0FcxOOcrH0ivmKoal/DOmSO+w==", + "requires": { + "@babel/runtime": "^7.12.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, "typescript": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", diff --git a/examples/plant-site.ts b/examples/plant-site.ts new file mode 100644 index 000000000..c2cbca59b --- /dev/null +++ b/examples/plant-site.ts @@ -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]); diff --git a/examples/purchases.ts b/examples/purchases.ts new file mode 100644 index 000000000..67577676f --- /dev/null +++ b/examples/purchases.ts @@ -0,0 +1,55 @@ +/* +Example output: + +... +'PC419 m0nt-S-' just bought a 'M9 Bayonet' (unusual) +'PC419 m0nt-S-' just bought a 'USP-S' (strange) +'PC419 valentino' just bought a 'Karambit' (unusual) +'PC419 valentino' just bought a 'USP-S' (unique) +'PC419 DANZ' just bought a 'Karambit' (unusual) +'PC419 DANZ' just bought a 'USP-S' (unique) +'CHF| soju_j' just bought a 'Knife' (normal) +'CHF| soju_j' just bought a 'Glock-18' (unique) +'CHF| zephh' just bought a 'Butterfly Knife' (unusual) +'CHF| zephh' just bought a 'Glock-18' (unique) +'CHF| soju_j' just bought a 'C4 Explosive' (normal) +Finished. +*/ + +import * as assert from "assert"; +import { DemoFile, Weapon } 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("postcreate", e => { + const weapon = e.entity; + if (!(weapon instanceof Weapon)) return; + + const owner = weapon.owner; + if (!owner) return; + + console.log( + `'${owner.name}' just bought a '${weapon.itemName}' (${weapon.quality})` + ); + }); + + 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]); diff --git a/examples/rank.ts b/examples/rank.ts new file mode 100644 index 000000000..4f85c2018 --- /dev/null +++ b/examples/rank.ts @@ -0,0 +1,58 @@ +/* +Example output: + +Player rank updates: +> GundelGaukeley2: 11 -> 11 +> Frezzz chlenix.exe: 10 -> 10 +> ็: 10 -> 10 +> Cerberus: 9 -> 9 +> d0h: 8 -> 8 +> Comrade Gamer: 10 -> 10 +> Tor: 10 -> 10 +> twitch.tv/jacus_minecraft_cs: 10 -> 10 +> Łukasz Borzym: 9 -> 10 +> (unknown player 396707352) +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.userMessages.on("ServerRankUpdate", um => { + console.log("Player rank updates:"); + for (const update of um.rankUpdate) { + const player = demoFile.players.find( + player => + player.userInfo && + player.userInfo.xuid.getLowBits() === update.accountId + ); + if (!player) console.log(`> (unknown player ${update.accountId})`); + else + console.log( + `> ${player.name}: ${update.rankOld} -> ${update.rankNew}` + ); + } + }); + + 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]); diff --git a/examples/scores.ts b/examples/scores.ts index 6f6f589fa..e75d73141 100644 --- a/examples/scores.ts +++ b/examples/scores.ts @@ -1,4 +1,26 @@ -// tslint:disable:no-console +/* +Example output: + +*** Round ended 'first' (reason: 12) + TERRORIST: Heroic score 0 + CT: BIG score 1 +*** Round ended 'first' (reason: 7) + TERRORIST: Heroic score 0 + CT: BIG score 1 +... +*** Round ended 'second' (reason: 1) + TERRORIST: BIG score 14 + CT: Heroic score 14 +*** Round ended 'second' (reason: 1) + TERRORIST: BIG score 15 + CT: Heroic score 14 +*** Round ended 'postgame' (reason: 9) + TERRORIST: BIG score 16 + CT: Heroic score 14 + TERRORIST: Heroic score 0 + CT: BIG score 0 +Finished. +*/ import * as assert from "assert"; import { DemoFile, TeamNumber } from "demofile"; @@ -27,10 +49,6 @@ function parseDemoFile(path: string) { ); } - demoFile.on("start", () => { - console.log("Demo header:", demoFile.header); - }); - demoFile.gameEvents.on("round_end", e => { console.log( "*** Round ended '%s' (reason: %s)", diff --git a/src/entities/weapon.ts b/src/entities/weapon.ts index 6a91c28ba..dca0c595e 100644 --- a/src/entities/weapon.ts +++ b/src/entities/weapon.ts @@ -3,6 +3,26 @@ import { BaseEntity } from "./baseentity"; import { itemDefinitionIndexMap } from "./itemdefs"; import { Player } from "./player"; +// tslint:disable-next-line:no-useless-cast +const qualities = [ + "normal", + "genuine", + "vintage", + "unusual", + "unique", + "community", + "developer", + "selfmade", + "customized", + "strange", + "completed", + "haunted", + "tournament", + "favored" +] as const; + +export type ItemQuality = typeof qualities[number]; + /** * Represents an in-game weapon (guns, grenades, knifes). */ @@ -70,4 +90,12 @@ export class Weapon extends BaseEntity { if (ammoType === -1) return null; return this.owner.getIndexedProps("m_iAmmo")[ammoType]; } + + /** + * @returns Quality of the item. + */ + get quality(): ItemQuality { + const quality = this.getProp("DT_ScriptCreatedItem", "m_iEntityQuality"); + return qualities[quality]; + } } From 4526a1337537bd7d488ef9f7bee424d9357aa094 Mon Sep 17 00:00:00 2001 From: Saul Rennison Date: Sun, 10 Jan 2021 15:27:26 +0000 Subject: [PATCH 2/3] Add more examples --- README.md | 204 ++++++++--------------------------------- examples/join-leave.ts | 79 ++++++++++++++++ examples/tickrate.ts | 38 ++++++++ src/demo.ts | 20 +++- 4 files changed, 171 insertions(+), 170 deletions(-) create mode 100644 examples/join-leave.ts create mode 100644 examples/tickrate.ts diff --git a/README.md b/README.md index ab90e0f70..fc0efccf2 100644 --- a/README.md +++ b/README.md @@ -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"); @@ -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"); @@ -222,30 +131,11 @@ 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); @@ -253,48 +143,30 @@ fs.readFile("test.dem", (err, 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. diff --git a/examples/join-leave.ts b/examples/join-leave.ts new file mode 100644 index 000000000..1ede0be1e --- /dev/null +++ b/examples/join-leave.ts @@ -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]); diff --git a/examples/tickrate.ts b/examples/tickrate.ts new file mode 100644 index 000000000..8ac95f80d --- /dev/null +++ b/examples/tickrate.ts @@ -0,0 +1,38 @@ +/* +Example output: + +Tick rate: 128 +Duration (seconds): 2569.375 +*/ + +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.on("start", ({ cancel }) => { + console.log("Tick rate:", demoFile.tickRate); + console.log("Duration (seconds):", demoFile.header.playbackTime); + + // Stop parsing - we're finished + cancel(); + }); + + demoFile.on("end", e => { + if (e.error) { + console.error("Error during parsing:", e.error); + process.exitCode = 1; + } + }); + + // Start parsing the buffer now that we've added our event listeners + demoFile.parse(buffer); + }); +} + +parseDemoFile(process.argv[2]); diff --git a/src/demo.ts b/src/demo.ts index 6e719e6bd..f30362409 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -170,6 +170,13 @@ function readIBytes(bytebuf: ByteBuffer) { return bytebuf.readBytes(length); } +export interface IDemoStartEvent { + /** + * Cancel parsing the demo. + */ + cancel: () => void; +} + export interface IDemoEndEvent { /** * Error that caused the premature end of parsing. @@ -186,8 +193,8 @@ export declare interface DemoFile { /** * Fired when parsing begins. */ - on(event: "start", listener: () => void): this; - emit(name: "start"): boolean; + on(event: "start", listener: (event: IDemoStartEvent) => void): this; + emit(name: "start", event: IDemoStartEvent): boolean; /** * Fired when parsing failed. @@ -436,9 +443,14 @@ export class DemoFile extends EventEmitter { this._bytebuf = ByteBuffer.wrap(buffer.slice(1072), true); - this.emit("start"); + let cancelled = false; + this.emit("start", { + cancel: () => { + cancelled = true; + } + }); - timers.setTimeout(this._parseRecurse.bind(this), 0); + if (!cancelled) timers.setTimeout(this._parseRecurse.bind(this), 0); } /** From 5abb946d8347812401ced094d4491ee35d7a79b1 Mon Sep 17 00:00:00 2001 From: Saul Rennison Date: Sun, 10 Jan 2021 15:30:23 +0000 Subject: [PATCH 3/3] Update release notes --- RELEASE_NOTES.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 550d8b007..07ad4a585 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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 diff --git a/package.json b/package.json index d8838bef5..0b1da4d0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "demofile", - "version": "2.0.0-beta.3", + "version": "2.0.0-beta.4", "author": "Saul Rennison ", "typings": "dist/index.d.ts", "main": "dist/index.js",