Data Formats
Binary-to-Text
- Base64: https://tools.ietf.org/html/rfc4648#section-4
- Crockford Base32: https://crockford.com/base32.html
Varint
Defined the same way as it is defined in Protocol Buffers, that is:
- Byte values between 0 and 127 represent their own value and stop.
- Other byte values represent their 7 least significant bits and continue.
- Byte order is little-endian.
Match Code
(Crockford Base32 encoded)
15 random bytes
The first 20 bits are overridden based on the client type:
- Vanilla: 11001101 10111100 1100
- Custom: 01100110 01110101 0100
Some custom game modes have their own prefixes.
The prefix followed by all zeroes is used for the quick join match code.
Deck Code
(Crockford Base32 encoded)
Short Form
Byte 0:
- 1 bit (most significant): 0
- 5 bits: boss card index (see table below)
- 2 bits: top 2 bits of first mini-boss card index
Byte 1:
- 3 bits: bottom 3 bits of first mini-boss card index
- 5 bits: second mini-boss card index
Bytes 2, 5, 8, ...:
- 6 bits: enemy card index
- 2 bits: top 2 bits of enemy card index
Bytes 3, 6, 9, ...:
- 4 bits: bottom 4 bits of enemy card index
- 4 bits: top 4 bits of enemy card index
Bytes 4, 7, 10, ...:
- 2 bits: bottom 2 bits of enemy card index
- 6 bits: enemy card index
Long Form (v1)
Byte 0:
- 1 bit (most significant): 1
- 6 bits: 0 (reserved)
- 1 bit (least significant): 0
Remaining bytes:
- repeated uint8: global card ID
Long Form (v2)
Byte 0:
- 1 bit (most significant): 1
- 6 bits: 0 (reserved)
- 1 bit (least significant): 1
Remaining bytes:
- repeated varint: global card ID
Match Recording
- varint FormatVersion (1)
- varint SpyCardsVersionMajor
- varint SpyCardsVersionMinor
- varint SpyCardsVersionPatch
- uint8 ModeLength
- byte[ModeLength] ModeName (UTF-8)
- uint8 Perspective (0 = none, 1 = player 1, 2 = player 2)
- CosmeticData Player1CosmeticData
- CosmeticData Player2CosmeticData
- varint RematchCount (since version 1)
- byte[32] SharedSeed
- byte[4] PrivateSeedPlayer1
- byte[4] PrivateSeedPlayer2
- varint NumberOfCustomCards
- CustomCardDef[NumberOfCustomCards] CustomCards
- EncodedDeck Player1InitialDeck
- EncodedDeck Player2InitialDeck
- varint NumberOfTurns
- TurnData[NumberOfTurns] Turns
CosmeticData
- uint8 CharacterNameLength
- byte[CharacterNameLength] CharacterName (UTF-8)
CustomCardDef
- varint EncodedLength
- byte[EncodedLength] CardData (see Custom Card; not base64-encoded)
EncodedDeck
- varint EncodedLength
- byte[EncodedLength] DeckData (see Deck Code; not base32-encoded)
TurnData
- byte[8] TurnSeed
- byte[8] TurnSeed2 (since version 1)
- varint Player1Ready (bitfield of cards played from hand)
- varint Player2Ready (bitfield of cards played from hand)
Game Mode Data
Since game modes and custom cards are shared using the same mechanism, game mode format versions are disjoint with custom card versions.
(base64-encoded)
- varint FormatVersion (3)
- varint FieldCount
- GameModeField[FieldCount] Fields
GameModeField
- varint Type
- varint DataLength
- byte[DataLength] Data
0: Metadata
- varint TitleLength
- byte[TitleLength] Title (UTF-8)
- varint AuthorLength
- byte[AuthorLength] Author (UTF-8)
- varint DescriptionLength
- byte[DescriptionLength] Description (UTF-8)
- varint LatestChangesLength
- byte[LatestChangesLength] LatestChanges (UTF-8)
1: BannedCards
If BannedCards is present and BannedCardCount is 0, all vanilla cards are banned.
- varint BannedCardCount
- varint[BannedCardCount] BannedCardIDs
2: GameRules
Repeated:
- varint Rule
- varint Value
If Rule is None, there is no value and this field ends.
Rules that have their default value are not encoded.
Rules and their default values are as follows:
- 0: None (no value)
- 1: MaxHP (5)
- 2: HandMinSize (3)
- 3: HandMaxSize (5) (max value 50)
- 4: DrawPerTurn (2)
- 5: CardsPerDeck (15)
- 6: MinTP (2)
- 7: MaxTP (10)
- 8: TPPerTurn (1)
- 9: BossCards (1)
- 10: MiniBossCards (2)
3: SummonCard
- varint Flags
- bit 0: both players (if not set, summon for player 1 only)
- varint CardID
Custom Card (v2+)
(base64-encoded)
- varint FormatVersion (4)
- varint GlobalCardID
- see Appendix A for a list of possible values below 128
- for values 128 and above:
- bits 5-6 are rank:
- value 0 is attacker
- value 1 is effect
- value 2 is mini-boss
- value 3 is boss
- bits 5-6 are rank:
- uint8 Tribes
- most significant 4 bits are tribe 1
- least significant 4 bits are tribe 2
- tribe 1 cannot be (none) and tribe 2 cannot be the same as tribe 1
- see Appendix A for a list of possible values
- SpecialField[...] SpecialFields
- uint8 TP
- most significant 4 bits must be set to 0.
- least significant 4 bits are the card's TP cost
- values 0..10 are used as-is
- values 11..14 are reserved
- value 15 means infinity
- uint8 Portrait
- values 0..234 are an index into the portrait spritesheet
- value 254 means an embedded PNG follows the card
- value 255 means an external portrait ID follows the card
- varint NameLength (0 means use original name)
- byte[NameLength] DisplayName (UTF-8)
- varint EffectCount
- EffectDef[EffectCount] Effects
- Remaining fields are conditional:
- CustomTribeData tribe1 (if tribe 1 is 14)
- CustomTribeData tribe2 (if tribe 2 is 14)
- varint CustomPortraitLength (if portrait is 254 or 255)
- byte[CustomPortraitLength] CustomPortrait (if portrait is 254 or 255)
SpecialField
SpecialField is defined as a 4 bit prefix followed by a payload of a defined length.
The prefix 0000 is invalid as it represents the TP field, which is after the last SpecialField.
Currently defined SpecialField types:
0001: ExtraTribe
Allows a card to have more than 2 tribes.
- uint4 tribe (see Appendix A for a list)
- CustomTribeData custom (if tribe is 14)
All tribes on a card must be unique, and if ExtraTribe is present, no tribe can be none (15).
EffectDef
- uint8 EffectID
- uint8 EffectFlags (each flag is allowed only on effects that define a meaning for it)
- bit 7: (reserved)
- bit 6: (reserved)
- bit 5: defense
- bit 4: generic
- bit 3: late
- bit 2: each
- bit 1: opponent
- bit 0: negate
- Data (defined below)
Effect 0: Flavor Text
Defined flags:
- negate
Data:
- varint TextLength
- byte[TextLength] Text
Effect 1: Stat
Defined flags:
- negate
- opponent
- defense
Data:
- uint8 Amount (255 = infinity)
Effect 2: Empower
Defined flags:
- opponent
- negate
- generic
- defense
Data:
- uint8 Amount (255 = infinity)
- PackedRankTribe Filter (if flag generic)
- varint GlobalCardID (if not flag generic)
Effect 3: Summon
Defined flags:
- negate
- opponent
- generic
- defense
Data:
- uint8 CountMinusOne
- PackedRankTribe Filter (if flag generic)
- varint GlobalCardID (if not flag generic)
Effect 4: Heal
Defined flags:
- negate
- opponent
- each
- generic
- defense (no effect with generic)
Data:
- uint8 Amount (255 = infinity)
Effect 5: TP
Defined flags:
- negate
Data:
- uint8 Amount (255 = infinity)
Effect 6: Numb
Defined flags:
- generic
- opponent
Data:
- uint8 Amount (255 = infinity)
- PackedRankTribe Filter (if flag generic, since version 4)
- varint GlobalCardID (if not flag generic, since version 4)
When decoding version 2, act as if the effect has the generic and opponent flags set and is targeting Rank: Attacker, Tribe: None.
Effect 128: Card (Condition)
Defined flags:
- negate
- opponent
- each
- generic
Data:
- uint8 CountMinusOne (if not flag each)
- PackedRankTribe Filter (if flag generic)
- varint GlobalCardID (if not flag generic)
- EffectDef Result
Effect 129: Limit (Condition)
Defined flags:
- negate
Data:
- uint8 CountMinusOne
- EffectDef Result
Effect 130: Winner (Condition)
Defined flags:
- negate
- opponent
Data:
- EffectDef Result
Effect 131: Apply (Condition)
Defined flags:
- negate
- opponent
- late
Data:
- EffectDef Result
Effect 132: Coin (Condition)
Defined flags:
- negate
- generic
- defense
Data:
- uint8 CountMinusOne
- EffectDef HeadsResult
- EffectDef TailsResult (if flag generic)
Effect 133: HP (Condition)
Defined flags:
- negate
- opponent
- late
Data:
- uint8 CountMinusOne
- EffectDef Result
Effect 134: Stat (Condition)
Defined flags:
- negate
- opponent
- late
- defense
Data:
- uint8 CountMinusOne
- EffectDef Result
PackedRankTribe
- uint8 RankTribe
- most significant bit: reserved (0)
- bits 4-6: rank
- value 0 is attacker
- value 1 is effect
- value 2 is mini-boss
- value 3 is boss
- value 4 is enemy (attacker and effect)
- value 5 is reserved (invalid)
- value 6 is reserved (invalid)
- value 7 is (none)
- bits 0-3: tribe (see Appendix A for a list)
- if tribe is 14:
- varint NameLength
- byte[NameLength] TribeName
CustomTribeData
- uint8 Red
- uint8 Green
- uint8 Blue
- varint NameLength
- byte[NameLength] TribeName
Custom Card (v1, deprecated)
(base64-encoded)
- Byte 0: Format version number (1)
- Byte 1: Global card ID to replace
- Byte 2 (most significant 4 bits): Tribe 1
- Byte 2 (least significant 4 bits): Tribe 2
- Byte 3 (most significant bit): (reserved)
- Byte 3 (bit 6): if set, and byte 4 is 0xff, custom image ID follows instead of raw PNG
- Byte 3 (bits 4-5): 0 = Attacker, 1 = Effect, 2 = Mini-Boss, 3 = Boss (this must match the original card)
- Byte 3 (least significant 4 bits): TP cost (range 0..10, 15 = infinite)
- Byte 4 (most significant 4 bits): Portrait Y coordinate
- Byte 4 (least significant 4 bits): Portrait X coordinate
- (If byte 4 is 0xff, a PNG follows the custom card.)
- Byte 5: Name length (0 for "no replacement")
- Bytes 6 to 6+n: Name (UTF-8)
- Byte 7+n: Number of effects
- Bytes 8+n to end: (repeated) effect definition
- If Tribe 1 is 14:
- 3 bytes: RGB color
- 1 byte: Tribe 1 name length
- n bytes: Tribe 1 name
- If Tribe 2 is 14:
- 3 bytes: RGB color
- 1 byte: Tribe 2 name length
- n bytes: Tribe 2 name
Effect Definition
- Byte 0: Effect ID
For all effects:
- Amount = 0 means infinity
- If Tribe = 14, it is followed by:
- Byte (name length)
- UTF-8 bytes (name)
0: ATK (static)
- Byte 1: Amount
1: DEF (static)
- Byte 1: Amount
2: ATK (static, once per turn)
- Byte 1: Amount
3: ATK+n
- Byte 1: Amount
4: DEF+n
- Byte 1: Amount
5: Empower (card)
- Byte 1: Global Card ID
- Byte 2: Count
6: Empower (Tribe)
- Byte 1 (most significant 4 bits): (reserved)
- Byte 1 (least significant 4 bits): Tribe ID
- Byte 2: Count
7: Heal
- Byte 1: Amount
8: Lifesteal
- Byte 1: Amount
9: Numb
- Byte 1: Amount
10: Pierce
- Byte 1: Amount
11: Summon
- Byte 1: Global Card ID
12: Summon Rank
- Byte 1 (since version 1, most significant 6 bits): reserved (0)
- Byte 1 (since version 1, least significant 2 bits): 0 = attacker, 1 = effect, 2 = mini-boss, 3 = boss
13: Summon Tribe
- Byte 1 (most significant 4 bits): 0 = enemy, 1 = attacker, 2 = effect, 3 = mini-boss, 4 = boss
- Byte 1 (least significant 4 bits): Tribe ID
- Byte 2: Count
14: Unity
(if Tribe is (none), replace this effect with ATK when decoding)
- Byte 1 (most significant 4 bits): (reserved)
- Byte 1 (least significant 4 bits): Tribe ID
- Byte 2: Amount
15: TP
- Byte 1: Amount
16: Summon as Opponent
- Byte 1: Global Card ID
17: Multiply Healing
- Byte 1: Amount minus 1 (1 -> multiply by 2, -1 -> multiply by 0, etc.)
18: Flavor Text
- Byte 1 (most significant 7 bits): (reserved)
- Byte 1 (least significant bit): hide remaining effects
- Byte 2: text length
- Bytes 3-n: text (UTF-8)
128: Coin
- Byte 1 (since version 1): condition flags
- Byte 2 (most significant bit): 0=heads only, 1=heads or tails
- Byte 2 (least significant 7 bits): Count minus 1
- Bytes 3 to end: (possibly repeated) Effect Definition
129: If Card
- Byte 1 (since version 1): condition flags
- Byte 2: Global Card ID
- Byte 3 (since version 1): Count
- Bytes 4 to end: Effect Definition
130: Per Card
- Byte 1 (since version 1): condition flags
- Byte 2: Global Card ID
- Bytes 3 to end: Effect Definition
131: If Tribe
- Byte 1 (since version 1): condition flags
- Byte 2 (most significant 4 bits): (reserved)
- Byte 2 (least significant 4 bits): Tribe ID
- Byte 3: Count
- Bytes 4 to end: Effect Definition
132: VS Tribe
- Byte 1 (since version 1): condition flags
- Byte 2 (most significant 4 bits): (reserved)
- Byte 2 (least significant 4 bits): Tribe ID
- Byte 3 (since version 1): Count
- Bytes 4 to end: Effect Definition
133: If Stat
- Byte 1 (since version 1): condition flags
- Byte 2 (most significant bit): 0 = ATK, 1 = DEF
- Byte 2 (least significant 7 bits): Count
- Bytes 3 to end: Effect Definition
134: Setup
- Byte 1 (since version 1): condition flags
- Bytes 2 to end: Effect Definition
135: Limit
- Byte 1 (since version 1): condition flags
- Byte 2: Count
- Bytes 3 to end: Effect Definition
136: VS Card
- Byte 1: condition flags
- Byte 2: Global Card ID
- Byte 3: Count
- Bytes 4 to end: Effect Definition
137: If Rank
- Byte 1: condition flags
- Byte 2 (most significant 6 bits): reserved (0)
- Byte 2 (least significant 2 bits): 0 = attacker, 1 = effect, 2 = mini-boss, 3 = boss
- Byte 3: Count
- Bytes 4 to end: Effect Definition
138: VS Rank
- Byte 1: condition flags
- Byte 2 (most significant 6 bits): reserved (0)
- Byte 2 (least significant 2 bits): 0 = attacker, 1 = effect, 2 = mini-boss, 3 = boss
- Byte 3: Count
- Bytes 4 to end: Effect Definition
139: If Win
- Byte 1: condition flags
- Bytes 2 to end: Effect Definition
140: If Tie
- Byte 1: condition flags
- Bytes 2 to end: Effect Definition
141: If Stat (after modifiers)
- Byte 1: condition flags
- Byte 2 (most significant bit): 0 = ATK, 1 = DEF
- Byte 2 (least significant 7 bits): Count
- Bytes 3 to end: Effect Definition
142: VS Stat
- Byte 1: condition flags
- Byte 2 (most significant bit): 0 = ATK, 1 = DEF
- Byte 2 (least significant 7 bits): Count
- Bytes 3 to end: Effect Definition
143: VS Stat (after modifiers)
- Byte 1: condition flags
- Byte 2 (most significant bit): 0 = ATK, 1 = DEF
- Byte 2 (least significant 7 bits): Count
- Bytes 3 to end: Effect Definition
144: If HP
- Byte 1: condition flags
- Byte 2: Count
- Bytes 3 to end: Effect Definition
145: Per Tribe
- Byte 1: condition flags
- Byte 2 (most significant 4 bits): (reserved)
- Byte 2 (least significant 4 bits): Tribe ID
- Bytes 3 to end: Effect Definition
146: Per Rank
- Byte 1: condition flags
- Byte 2 (most significant 6 bits): reserved (0)
- Byte 2 (least significant 2 bits): 0 = attacker, 1 = effect, 2 = mini-boss, 3 = boss
- Bytes 3 to end: Effect Definition
147: VS HP
- Byte 1: condition flags
- Byte 2: Count
- Bytes 3 to end: Effect Definition
Condition Flags
- Bits 1-7 (most significant 7 bits): reserved
- Bit 0 (least significant): invert
Termacade Recording
- varint FormatVersion = 1
- varint[3] SpyCardsOnlineVersion
- varint GameType
- 1: Mite Knight
- 2: Flower Journey
- varint GameRuleOverridesLength
- byte[GameRuleOverridesLength] GameRuleOverrides (described below)
- varint SeedLength
- byte[SeedLength] Seed (UTF-8)
- varint StartPlayingTimestamp (milliseconds since unix epoch)
- varint GameDataLength
- byte[GameDataLength] GameData (see below)
- varint RandomIndex (final index of the deterministic random number generator)
- varint PlayerNameLength
- byte[PlayerNameLength] PlayerName (UTF-8)
Game Rule Overrides
Repeated:
- varint GameRuleID (game-specific)
- one of: (depending on the game rule)
- uint8 BooleanValue (0 or 1)
- float64 NumberValue
- float64[2] CoordinateValue
TODO: Document rules for each game
Game Data: Shared (v1)
Repeated:
- varint ButtonsHeld (bitfield)
- bit 0: Up
- bit 1: Down
- bit 2: Left
- bit 3: Right
- bit 4: Confirm
- bit 5: Cancel
- bit 6: Switch
- bit 7: Toggle
- bit 8: Pause
- bit 9: Help
- varint AdditionalTicks (number of ticks this set of buttons is held, minus 1)
The recording starts on tick 0. Game logic starts on tick 1. This means that the first tick of input is dropped. The reference implementation records tick 0's input as all buttons released.
Game Data: Mite Knight (v0)
Repeated:
- uint8 Buttons (bitfield)
- Up (least significant bit)
- Down
- Left
- Right
- Shield
- most significant 3 bits: reserved (0)
- varint FramesMinusOne (number of one-sixtieth second intervals until the next state, minus 1 - buttons are always held for at least one frame)
Game Data: Flower Journey (v0)
Repeated:
- varint TicksSincePreviousFlap (number of one-sixtieth second intervals since the last time the bee flapped its wings, or since the countdown finished if this is the first flap. the last of these items is the timestamp of the bee colliding with something.)
Appendix A: Card Indices
This section contains spoilers.
Global
- 0: Zombiant
- 1: Jellyshroom
- 2: Spider
- 3: Zasp
- 4: Cactiling
- 5: Psicorp
- 6: Thief
- 7: Bandit
- 8: Inichas
- 9: Seedling
- 14: Numbnail
- 15: Mothiva
- 16: Acornling
- 17: Weevil
- 19: Venus' Bud
- 20: Chomper
- 21: Acolyte Aria
- 23: Kabbu
- 24: Venus' Guardian
- 25: Wasp Trooper
- 26: Wasp Bomber
- 27: Wasp Driller
- 28: Wasp Scout
- 29: Midge
- 30: Underling
- 31: Monsieur Scarlet
- 32: Golden Seedling
- 33: Arrow Worm
- 34: Carmina
- 35: Seedling King
- 36: Broodmother
- 37: Plumpling
- 38: Flowerling
- 39: Burglar
- 40: Astotheles
- 41: Mother Chomper
- 42: Ahoneynation
- 43: Bee-Boop
- 44: Security Turret
- 45: Denmuki
- 46: Heavy Drone B-33
- 47: Mender
- 48: Abomihoney
- 49: Dune Scorpion
- 50: Tidal Wyrm
- 51: Kali
- 52: Zombee
- 53: Zombeetle
- 54: The Watcher
- 55: Peacock Spider
- 56: Bloatshroom
- 57: Krawler
- 58: Haunted Cloth
- 61: Warden
- 63: Jumping Spider
- 64: Mimic Spider
- 65: Leafbug Ninja
- 66: Leafbug Archer
- 67: Leafbug Clubber
- 68: Madesphy
- 69: The Beast
- 70: Chomper Brute
- 71: Mantidfly
- 72: General Ultimax
- 73: Wild Chomper
- 74: Cross
- 75: Poi
- 76: Primal Weevil
- 77: False Monarch
- 78: Mothfly
- 79: Mothfly Cluster
- 80: Ironnail
- 81: Belostoss
- 82: Ruffian
- 83: Water Strider
- 84: Diving Spider
- 85: Cenn
- 86: Pisci
- 87: Dead Lander α
- 88: Dead Lander β
- 89: Dead Lander γ
- 90: Wasp King
- 91: The Everlasting King
- 92: Maki
- 93: Kina
- 94: Yin
- 95: ULTIMAX Tank
- 96: Zommoth
- 97: Riz
- 98: Devourer
- 128-159: custom attacker
- 160-191: custom effect
- 192-223: custom mini-boss
- 224-255: custom boss
Boss
- 0: Spider
- 1: Venus' Guardian
- 2: Heavy Drone B-33
- 3: The Watcher
- 4: The Beast
- 5: ULTIMAX Tank
- 6: Mother Chomper
- 7: Broodmother
- 8: Zommoth
- 9: Seedling King
- 10: Tidal Wyrm
- 11: Peacock Spider
- 12: Devourer
- 13: False Monarch
- 14: Maki
- 15: Wasp King
- 16: The Everlasting King
Mini-Boss
- 0: Ahoneynation
- 1: Acolyte Aria
- 2: Mothiva
- 3: Zasp
- 4: Astotheles
- 5: Dune Scorpion
- 6: Primal Weevil
- 7: Cross
- 8: Poi
- 9: General Ultimax
- 10: Cenn
- 11: Pisci
- 12: Monsieur Scarlet
- 13: Kabbu
- 14: Kali
- 15: Carmina
- 16: Riz
- 17: Kina
- 18: Yin
- 19: Dead Lander α
- 20: Dead Lander β
- 21: Dead Lander γ
Enemy
- 0: Seedling
- 1: Underling
- 2: Golden Seedling
- 3: Zombiant
- 4: Zombee
- 5: Zombeetle
- 6: Jellyshroom
- 7: Bloatshroom
- 8: Chomper
- 9: Chomper Brute
- 10: Psicorp
- 11: Arrow Worm
- 12: Thief
- 13: Bandit
- 14: Burglar
- 15: Ruffian
- 16: Security Turret
- 17: Abomihoney
- 18: Krawler
- 19: Warden
- 20: Haunted Cloth
- 21: Mantidfly
- 22: Jumping Spider
- 23: Mimic Spider
- 24: Diving Spider
- 25: Water Strider
- 26: Belostoss
- 27: Mothfly
- 28: Mothfly Cluster
- 29: Wasp Scout
- 30: Wasp Trooper
- 31: Acornling
- 32: Cactiling
- 33: Flowerling
- 34: Plumpling
- 35: Inichas
- 36: Denmuki
- 37: Madesphy
- 38: Numbnail
- 39: Ironnail
- 40: Midge
- 41: Wild Chomper
- 42: Weevil
- 43: Bee-Boop
- 44: Mender
- 45: Leafbug Ninja
- 46: Leafbug Archer
- 47: Leafbug Clubber
- 48: Wasp Bomber
- 49: Wasp Driller
- 50: Venus' Bud
Tribe
- 0: Seedling
- 1: Wasp
- 2: Fungi
- 3: Zombie
- 4: Plant
- 5: Bug
- 6: Bot
- 7: Thug
- 8: ???
- 9: Chomper
- 10: Leafbug
- 11: Dead Lander
- 12: Mothfly
- 13: Spider
- 14: (reserved)
- 15: (none)