Giter VIP home page Giter VIP logo

hearthstone-log-watcher's Introduction

Hearthstone Log Watcher

This module is simple. It takes care of the low-level monitoring of the Hearthstone log file and emits events based on what happens in the log file. Use this module if you want to build your own Hearthstone deck tracker and don't want to do the work of parsing through the nasty log file yourself.

Usage

$ npm install hearthstone-log-watcher

var LogWatcher = require('hearthstone-log-watcher');
var logWatcher = new LogWatcher();

logWatcher.on('zone-change', function (data) {
  console.log(data.cardName + ' has moved from ' + data.fromTeam + ' ' + data.fromZone + ' to ' + data.toTeam + ' ' + data.toZone);
});

logWatcher.start();

Here's an example of the output from the above script:

Knife Juggler has moved from FRIENDLY DECK to FRIENDLY HAND

Here's a little demo video as well:

Parse an existing log file

logWatcher.start() begins reading any updates to the Hearthstone log file that occur after it has begun watching. If you need to parse a log from the beginning, you can use parseBuffer.

var fs = require('fs');

fs.readFile('./my-old-player-log.log', function(err, buffer) {
  if (err) return console.error(err);
  logWatcher.parseBuffer(buffer);
});

Methods

start()

Starts watching the log file and parses any changes to it.

stop()

Stops the watcher.

parseBuffer(buffer [, parserState])

Parses a buffer (log file).

Useful if you have log files that you want to parse without watching them. See the usage example above for how to get the buffer from existing files.

The second argument, parserState, is optional, and will be created for you if you omit it.

If you want to use your own parserState instance it must have these properties:

  • players: Array (keeps track of the players)
  • playerCount: Number (keeps track of when players enter the game to know when to emit the start event)
  • gameOverCount: Number (keeps track of when players won/lost/tied to know when the game is over)
  • reset: Function (resets the parserState instance at the end of a game)

Events

The available events you can listen for are as follows:

game-start

The game-start event fires at the beginning of a match when the watcher has gathered enough data from the log to determine which of the two players is the local player. It was a lot more complicated to figure that out than one might think, so this event is pretty valuable because it eliminates the guess work. Not even the hearthstats deck tracker can determine what player is the local player ;)

Callback Arguments:

  • players - an array of the players in the game and what team they are on (friendly or opposing);

Example player object:

{
  name: 'Hologrid',
  id: 1,
  team: 'FRIENDLY'
}

game-over

The game-over event fires at the end of a match and includes additional data showing who won and who lost.

Callback Arguments:

  • players - the same array of players from the game-start event except the players have an additional status property.

Example player object:

{
  name: 'Hologrid',
  id: 1,
  team: 'FRIENDLY',
  status: 'WON'
}

zone-change

The zone-change event fires whenever a game entity moves from one zone to another. Most entities are cards, but heroes and hero powers are also considered game entities and will show up in these events as well. I'm working on a way to filter those out, but they don't cause any problems currently other than just being useless data most of the time.

Hearthstone has 8 zones (that I'm aware of):

  • DECK
  • HAND
  • PLAY
  • PLAY (Hero)
  • PLAY (Hero Power)
  • PLAY (Weapon)
  • SECRET
  • GRAVEYARD

The "PLAY (Hero)" and "PLAY (Hero Power)" zones are pretty useless to us because the heroes and hero powers go into their respective play zones at the beginning of the game and don't usually go to the GRAVEYARD zone until the game is over. There is one exception that I'm aware of and that is Jaraxxus. Jaraxxus sends the Gul'dan hero and the Life Tap hero power to the GRAVEYARD zone when he is played, and then the Jaraxxus entity himself and his INFERNO! hero power enter the respective play zones.

The other zones are pretty straightforward. Cards in your deck are in the DECK zone. Cards in your hand are in the HAND zone. Minions on the board are in the PLAY zone. Secrets and weapons are in the SECRET and PLAY (Weapon) zones respectively. When writing a deck tracker UI it usually makes the most sense to consider PLAY, SECRET, and PLAY (Weapon) as a single zone; that way you can show visually whether a card is in your deck, your hand, in play, or destroyed.

The zone-change event receives an object as an argument with data that describes the event. It contains the card name, the card ID, the entity ID for that match, the team and zone the card came from, and the team and zone the card is moving to.

Example zone change data object:

{
  cardName: 'Knife Juggler',
  cardId: 'NEW1_019',
  entityId: 37,
  fromTeam: 'OPPOSING',
  fromZone: 'PLAY',
  toTeam: 'OPPOSING',
  toZone: 'GRAVEYARD'
}

Don't be confused by the entityId field. The ID is not consistent across games. Rather, the entity ID is an identifier that is assigned to that specific card for the duration of just that match. It is what you need in order to track a card's status as the game progresses. For example, if you have two Knife Jugglers in your deck, you need to be able to tell which one is which. The entity ID is the only way to track changes to a specific card during that game. The cardId field never changes however, and you may use it to look up card data in a card database such as the one found at HearthstoneJSON.com.

Planned

Right now the log watcher only emits three events. The Hearthstone log contains A LOT of data and I believe there are a lot more events that this module could emit. For example, I believe there is enough data in the log to even track the damage/buff states of the minions in play. I'm going to experiment with the log more and see if I can pull out more useful data and provide useful events.

Frequently Asked Questions

Q. How do I see all the cards in my deck?

A. This module doesn't provide any functionality like that. This is just a log watcher that emits events that describe what it sees happening in the log. If you're building a deck tracker, you'll want to provide some kind of deck builder where users can fill out their deck beforehand. One helpful tool for this is HearthstoneJSON.com where you can get a JSON blob of all the Hearthstone card data. You could use that JSON data to do a card name autocomplete input, making it super easy for users to build their deck in your tool.

Q. Did you build a deck tracker that uses your own log watcher module?

A. Why yes I did! You can find my Hearthpal Tracker here.

Q. Why do some events seem to happen out of order?

A. This is not the fault of the log watcher. Hearthstone performs many things asynchronously even though the game appears to be very synchronous where things happen in a certain order. Unfortunately, Hearthstone does not always write to its own log file in the order in which things actually happened. For example, you may receive a game over event seconds before a card transition event, even if the transition occurred before the game ended. It's usually not a big deal but it's something to be aware of.

hearthstone-log-watcher's People

Contributors

keis avatar kennethsundqvist avatar kevinfjbecker avatar tidwell avatar

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hearthstone-log-watcher's Issues

Game Over condition in Adventure mode

When you play against bosses in adventure mode, oponent "Entity" value in newPlayerRegex is different than in gameOverRegex

The relevant parts of the log:

newPlayerRegex:

[Power] GameState.DebugPrintPower() - TAG_CHANGE Entity=The Innkeeper tag=TEAM_ID value=2
...
[Power] GameState.DebugPrintPower() - TAG_CHANGE Entity=DooB tag=TEAM_ID value=1

mulligan:

[Power] GameState.DebugPrintChoice() - id=1 ChoiceType=MULLIGAN Cancelable=False CountMin=0 CountMax=3

gameOverRegex:

[Power] GameState.DebugPrintPower() - TAG_CHANGE Entity=DooB tag=PLAYSTATE value=LOST
[Power] GameState.DebugPrintPower() - TAG_CHANGE Entity=Instructor Razuvious tag=PLAYSTATE value=WON

I tested it only on Naxxramas.

Broken on OS X?

This module used to work. Now it seems it no longer does. Did Blizzard change the log format or something?

DEBUG=* node watch_log.js 
HLW OS X platform detected. +0ms
HLW config file path: /Users/Jesse/Library/Preferences/Blizzard/Hearthstone/log.config +3ms
HLW log file path: /Users/Jesse/Library/Logs/Unity/Player.log +1ms
HLW Copied log.config file to force Hearthstone to write to its log file. +3ms
watching...
HLW Log watcher started. +150ms

No further output is produced as I play multiple games.

partial log lines are discarded

any log lines that are not complete when the file is being read will be discarded because the next read will start in the middle of that line. This happens a lot for me on wine (In fact I never seem to see a complete line) but could in theory happen on any platform.

adding a console.log after each buffer read I get output like this, where each CHUNK is a separate read

CHUNK  ZoneChangeList.Pr
CHUNK ocessChanges() - 
CHUNK START waiting for
CHUNK  [name=Knife Jug
CHUNK gler id=22 zon
CHUNK e=HAND zoneP
CHUNK os=0 cardId=NEW1_
CHUNK 019 player=1] 
CHUNK to load (z
CHUNK oneChange
CHUNK d=True co
CHUNK ntrollerC
CHUNK hanged=Fa
CHUNK lse power
CHUNK Task=(not
CHUNK  null))

The module will have to keep a internal buffer where it keeps any lines not completed yet. You could perhaps also take a look at using https://www.npmjs.com/package/tail which promises to solve the gritty details of watching a file for new lines. I never used that module however so I can't really vouch for the quality of it.

Question: Getting player names?

I'm not talking about battletag, just player names... Is it possible to get them from the logs? So I can post into my back-end something like "Josh123 just won a duel against Yess456"

actions on module require

The module does a bunch of stuff when loaded most notably writing the config file. This makes it hard to test and to pass any options to the code. It would be really handy to specify a configpath/logpath for when you're not running in the default installation path or like me running hearthstone through wine on linux.

A better way to use might look something like this

var LogWatcher = require('hearthstone-log-watcher');
var logWatcher = new LogWatcher({
  configPath: "/whatever/here"
});
logWatcher.on(...);
logWatcher.start();

WDYT? I'll try to get a PR ready if you think this sounds good.

Readme Typo

Super minor, but this line in the readme:

var LogWatcher = require('heartstone-log-watcher');

is missing the second 'h' in hearthstone, it should read

var LogWatcher = require('hearthstone-log-watcher');

Accounting for action queueing

As I was working on implementing turns I noticed that zone changes would sometimes happen out of order with turn changes. It is not a problem if you always let the game execute all actions before you instruct it to perform the next action, but it doesn't work when you queue up multiple actions.

Here is an example you can try with the current implementation using the game-over event:

  1. Have a minion on the board
  2. Buff the minion with several spells and then make it attack the opposing hero so the hero dies from the attack, do it very quickly so the game has only applied the first spell when you're done queueing everything
  3. The log parser will now detect and emit the game-over event before the zone-change events

I use two regexps for detecting turn changes, and they work very well:

  • Detect next turn number: /GameEntity tag=TURN value=(\d+)$/
  • Detect next turn start: /Entity=(.+) tag=TURN_START/

The main issue is that the parser does not keep track of queuing like the game does. This is very noticeable when parsing games against the computer, which always queues all actions very fast.

If this parser should grow and implement more events, like turn changes, it must handle queueing, and it should do so right away so it's easier to extend it without having to refactor the detection all the time.

Unfortunately I don't have the time to work on this right now, but I thought I'd share these findings so others can benefit from them and continue the detective work.

New features ?

Hi,

Your package seems really great, I want to build my own deck tracker and your package seems really great ;).

I woulk like to know if some new features are in progess ?

Friendly/Opposing player detection fails

var mulliganCountRegex = /id=(\d) ChoiceType=MULLIGAN Cancelable=False CountMin=0 CountMax=\d$/;

It seems the above line is no longer part of the log. I do however see the following 3 lines:

[Power] GameState.DebugPrintChoices() - id=1 PlayerId=1 ChoiceType=MULLIGAN CountMin=0 CountMax=3
[Power] GameState.DebugPrintChoices() - id=2 PlayerId=2 ChoiceType=MULLIGAN CountMin=0 CountMax=5
[Power] GameState.SendChoices() - id=1 ChoiceType=MULLIGAN

Does anybody know where to go from here?

Turn switching events.

I would like to add an event when the turn has switched. This would allow tournament broadcasters to build apps that automatically switch camera perspectives based on whose turn it is.

This may be difficult because there is no log entry directly representing a turn switch and card draws in friendly/opposing zones are not 1:1 with who's turn it is. For example, I could hit my enemy's Acolyte of Pain and draw my enemy a card while it's still my turn.

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.