Tf2logparser
A log parser for the game Team Fortress 2, written in Javascript for use with node.js. It retrieves stats and game events, and then outputs the data to JSON format.
Install / Use
/learn @barncow/Tf2logparserREADME
tf2logparser v0.2.0
A log parser for the game Team Fortress 2, written in Javascript for use with node.js. It retrieves stats and game events, and then outputs the data to JSON format.
With Node and NPM installed, you can install using:
npm install tf2logparser
And in the code, use:
var TF2LogParser = require('tf2logparser').TF2LogParser;
var parser = new TF2LogParser(); //need to create a new instance, since this stores state between lines.
//the 'done' event is thrown when processing is complete.
parser.on('done', function(log) {
//log is the final log object, that contains all stats.
console.log("Blue Score: %d\nRed Score: %d", log.blueScore, log.redScore);
});
//the 'line' event is thrown when processing for a line is complete.
parser.on('line', function(line) {
console.log(line);
});
//the 'error' event is thrown when an error is encountered.
parser.on('error', function(err) {
throw err;
});
//start processing
parser.parseLogFile('blah.log');
Real Time Mode
Real time mode is a way for you to manually feed through lines to the parser. There are some differences between normal log file processing and real time:
- No line events are emitted. Since you have to manually call
parseLine, it doesn't make sense to emit this event. Thedoneevent is still emitted. parseLinewill return what events and positions were added from the given line, called "delta".- The parseLogFile will consider the log file as a complete game, with multiple halves, etc. In real time, there is no good way to determine what should be considered a game, so the
doneevent is fired when a game over occurs orLog file closedis sent to the parser.
How to use:
var TF2LogParser = require('tf2logparser').TF2LogParser;
var parser = new TF2LogParser({isRealTime: true}); //need to create a new instance, since this stores state between lines.
//the 'done' event is thrown when processing is complete.
parser.on('done', function(log) {
//log is the final log object, that contains all stats.
console.log("Blue Score: %d\nRed Score: %d", log.blueScore, log.redScore);
});
//the 'error' event is thrown when an error is encountered.
parser.on('error', function(err) {
throw err;
});
//send a line to the parser - do this for each line
var deltas = parser.parseLine('L 07/11/2011 - 18:45:36: "Target<7><STEAM_0:0:6845279><Red>" spawned as "scout"');
//deltas.events will have an array of event objects for events that occurred in that line.
//deltas.positions will have an array of position objects for positions that occurred in that line.
The tf2logparser Command
This log parser ships with a tf2logparser binary that can be used to generate JSON output from the command line.
tf2logparser mylog.log will output the resulting log object from mylog.log to the console. You can save it to a file by doing: tf2logparser mylog.log > mylog.json
The resulting JSON, by default, does not contain whitespace, to keep the file small. However, you can make it indented and pretty by doing:
tf2logparser mylog.log -p and save it to a file by doing: tf2logparser mylog.log -p > mylog.json
A Note About This File
The following documentation is a work in progress, and will change as time goes on. However, it should be enough to get you going. Also, the code is fairly well documented.
The log Object
The done event from TF2LogParser.parseLogFile returns a log object, which is an object that holds all data about the game that was played. The overall structure is listed below, along with comments. Some properties of the object have more explanation further below.
{
blueScore: 0, //blue team's score
redScore: 0, //red team's score
gameStartTimestamp: null, //the timestamp of when the game started, not necessarily where the log file starts.
gameEndTimestamp: null, //the timestamp of when the game ends, not necessarily where the log file ends.
elapsedSeconds: 0, //the number of total seconds, from start to finish
playableSeconds: 0, //the number of seconds that were played: elapsedSeconds - humiliation rounds - pauses
mapName: "", //name of the map, if found in the log
mapType: "cp", //map type. This will only likely be "cp" or "ctf", and will likely be changed in a future release.
events: [], //events that occurred in the game, such as kills. Expanded further below.
players: [], //players that were in the game, along with their stats. Expanded further below.
weapons: [], //a list of all weapons that were in the game. These are their raw names from the log. You will need to use WeaponList.findWeapon() to translate to the actual weapon name and the class (we use the term role to prevent language conflicts), if applicable, that uses it.
positions: [] //an array of the positions of the players that were in the game, if enabled in the log.
}
The events Property
The events property of the log object is an array of objects that represent kills, point captures, flag events, etc. The format of these objects can differ based on what data they need to carry, however all event objects will all have the type property, that shows what type of event the object represents. They will also have a timestamp, which is the timestamp from the log of when the action occurred, and a elapsedSeconds property, which is the number of seconds from when the game started (useful for doing playback).
The kill Type Event
{
timestamp: new Date(2010, 8, 29, 19, 14, 43, 0), //when the event occurred in the log
elapsedSeconds: 347, //number of seconds that this event occurred from the start of the game
type: 'kill', //type of event
//"player" and "victim" are the player that attacked, and the player that died, respectively.
player: {
name: 'Ctrl+f Muffin!', //name of the player
userid: 50, //server userid of the player
steamid: 'STEAM_0:1:9852193', //steamid of the player
team: 'Red', //current team of the player
position: {x: -2771, y: 1546, z: -295}, //the in-game coordinates of the player when the action occurred.
role: { key: 'sniper', name: 'Sniper' } //the class of the player when performing the action. "key" is how the class is referred to in the log, and "name" is a more human-readable format.
},
victim: {name: 'Target', userid: 46, steamid: 'STEAM_0:0:6845279', team: 'Blue', position: {x: -3308, y: 1790, z: -220}, role: { key: 'scout', name: 'Scout' }},
assister: false, //if false, there was no player that had an assist for this kill. Otherwise, there would be a full player object, just like the "player" and "victim" objects above.
weapon: 'sniperrifle_hs', //the raw weapon name from the log, except that for headshots, "_hs" is appended, and for backstabs, "_bs" is appended. Use WeaponList.findWeapon() to translate to the actual weapon name and the class.
customKill: 'headshot' //if false, no extra information. Otherwise this will be "headshot", "backstab", or "feign_death" (spy using Dead Ringer).
}
(more events to be added)
The players Property
The players property of the log object is an array of objects that represent the players that were in the game, along with their stats. Below is an example object.
{
name: 'FSTNG! Barncow', //current name of the player
userid: 53, //the player's current server userid
steamid: 'STEAM_0:1:16481274', //the player's steam id
team: 'Blue', //the player's current team
friendid: '76561197993228277', //the player's friend id (for example, http://steamcommunity.com/profiles/76561197993228277)
joinedGame: Date(2010, 8, 29, 19, 8, 56, 0), //when the player joined the game, either when the game starts, or afterwards.
role: {
key: 'medic', //the player's current class, as the log refers to it
name: 'Medic' //the player's current class, in a more human-readable form
},
damage: 0, //the amount of damage that a player has done to other players
online: true, //whether or not the player is still in the game
kills: 0,
deaths: 2,
assists: 0,
longestKillStreak: 0, //highest number of kills without a death
longestDeathStreak: 2, //highest number of deaths without a kill
headshots: 0,
backstabs: 0,
pointCaptures: 0,
pointCaptureBlocks: 0,
flagDefends: 0,
flagCaptures: 0,
dominations: 0,
timesDominated: 0,
revenges: 0,
extinguishes: 0, //number of times that a player put out another player on fire
ubers: 1,
droppedUbers: 1,
healing: 2310,
medPicksTotal: 0, //total number of medics killed
medPicksDroppedUber: 0, //number of medics killed, where the medic dropped their uber
position: { x: -3308, y: 1790, z: -220 }, //current position of the player
roleSpread: { //the classes that the player played, along with the number of seconds that they played it
'medic': {
key: 'medic',
name: 'Medic',
secondsPlayed: 1666
}
},
itemSpread: { //the items that a player picked up
medkit_small: 2,
medkit_medium: 1
},
healSpread: { //the amount of healing done to each player
'STEAM_0:0:8581157': {
name: 'Cres',
steamid: 'STEAM_0:0:8581157',
healing: 72
},
'STEAM_0:0:6845279': {
name: 'Target',
steamid: 'STEAM_0:0:6845279',
healing: 27
}
},
weaponSpread: { //weapons that this player killed with and/or died from
'scattergun': {
key: 'scattergun',
kills: 0,
deaths: 1
},
'sniperrifle': {
key: 'sniperrifle',
kills: 0,
deaths: 1
}
},
playerSpread: { //players that this player killed and/or died from
'STEAM_0:0:6845279': {
name: 'Target',
steamid: 'STEAM_0:0:6845279',
kills: 0,
deaths: 1
},
'STEAM_0:1:9852193': {
name: 'Ctrl+f Muffin!',
steamid: 'STEAM_0:1:9852193',
kills: 0,
deaths: 1
}
}
}
The positions Property
This holds the positions of players, if the
