Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Understanding the Hearthstone Protocol
Update 2017-11-14: A more thorough and up-to-date article is available here.
The Hearthstone protocol is protobuf-based. For the decompiled protos, see the hs-proto repository.
As we established in the Game State article, the Hearthstone state is a bucket of entities. It starts off completely empty, creates a Game object, Player entities, all other relevant entities and then updates those entities through various means.
All tag values in the protocol are integers. Boolean-like values can be 1 or 0.
There are three "main" types of entities: Game, Player and Card. Each entity comes with an entity ID (also available in ENTITY_ID tag). Each player also comes with a PlayerID which is not the same as their entity ID - it is available in the PLAYER_ID tag. Cards contain an additional bit of data: The Card ID. They are not necessarily available immediately, in which case the Card is considered unrevealed. Unrevealed entities do not broadcast tag changes to players that are not allowed to see in thr entity's zone (such as the enemy's hand, or a deck).
Game State Packets
Here is the list of state update packets:
- CREATE_GAME (7)
- FULL_ENTITY (1)
- TAG_CHANGE (4)
- SHOW_ENTITY (2)
- HIDE_ENTITY (3)
- ACTION_START (5)
- ACTION_END (6)
- META_DATA (8)
This packet is sent to create the initial state of the Game entity and the Player entities. It contains lists of tag/value pairs to initialize each of those entities with.
This packet is sent to create any non-player/game entity. Similarly to CREATE_GAME, it contains a list of tag/value pairs to initialize the entity with. It may contain a Card ID.
This changes the value of a single tag on a single entity.
This packet is sent when a previously-unrevealed entity is being revealed. It contains a list of tag/value pairs that for tags that were previously hidden to the player.
This unreveals an entity. Further changes to its state will not be broadcasted to unauthorized players. However, its card ID does not change, so it can still be tracked.
This begins an action block. There are multiple types of action blocks (in order of their enum):
- ATTACK (A character's attack)
- JOUST (A joust)
- POWER (The effects of a main power)
- TRIGGER (The effects of a trigger power, AKA an event listener)
- DEATHS (The "death sweep", when entities are moved to the graveyard)
- PLAY (A card being played)
- FATIGUE (A tick of fatigue)
Action blocks may contain TAG_CHANGEs, further action blocks, META_DATA and choices (eg. Discover).
This closes the immediately-open action block.
This is used by the game to understand additional data about various events, mostly for animation purposes. The following META_DATA types are supported (from
This does not affect the game state.
The client does not know anything about the game rules when it comes to which cards can be played and how. Instead, every input update, the server sends a list of "options" to the current player. Each option has an entity, an optional target and an optional suboption. Each suboption has an entity and an optional target.
Targets and targeting requirements are done entirely serverside. The clientside card definitions XML file contains a copy of the serverside PlayRequirements for card powers, allowing it to display the correct errors when an invalid action is attempted without having to contact the server.
Suboptions are Choose One effects, with their entities stored in SETASIDE. When the client prepares to play an option with a suboption, its suboptions are presented to the player.
Playing a card is done by sending an option ID with an index (position, eg on the board), target (default 0) and suboption (default -1).
Not to be confused with "Choose One" (suboptions), choices are effects such as Discover and Mulligan