2023-08-23: Added Relics after the release of the 'Secrets of the Obscure' expansion.
The following considerations were taken into account in descending priority:
- Printable characters only: The codes are meant to be used in chats and urls, so the codes must only use standard printable ASCII/UTF8 characters. They should avoid 'special characters' as to not break urls (or commonly used url highlighters/parsers!) that contain them. This basically means the character pool is reduced to the alphanumerical set (character code 0x30 - 0x39, 0x41 - 0x5A, 0x6A - 0x7A).
- Completeness: The codes must cover all possible scenarios. This includes all gamemodes, professions, (elite-)specializations, trait choices, weapon sets (weapon types), slot skills, stat attributes, sigils, runes, relics, infusions and all special class features. [RFC] Rune mixing was deemed irrelevant. [/RFC]
- Compactness: The codes must be as short as possible. Allow omission of fields where sensible.
- Robustness: The codes should be as robust to further game (content-) updates as possible. It is, however, quite hard to anticipate game-mechanical updates and the changes introduced by those. This mostly boils down to use or enforce stable ordering wherever possible and using sufficiently large fields to store ids that won't overflow in the forseeable future.
Let character_set be ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-
This is also the character set used for base64 encoding. It is identical to the default set except the last character, which is usually a /
(slash). This character however has special meaning in urls and is therefore problematic, so it is replaced by a -
(minus). Something similar could be said about the plus sign, however, this one works fine in query parameters. The only issue with this is that server frameworks might automatically decode request uris which will result in plusses being converted to spaces. This however doesn't really pose a problem and is just something to be aware of. We require exactly 64 characters in the set for encoding trait choices, and two more characters (currently using _
(underscore) and ~
(tilde)) to indicate certain empty conditions. This completely exhausts the url character set, except .
(period). The issue with this character is that url highlighters tend to only treat periods as part of urls if they aren't at the end of the url, so https://my-site.com/path/?query=adasd. does not contain the last period, as it assumes its supposed to end a sentence. This is a problem in our case since the code might end in either an id or emptiness indicator, so we cant use the period in either of those without breaking links.
Small indices are directly encoded with the aforementioned character_set[index]
. Indices greater than that are encoded by encode defined below, which will also be mentioned in places where its used.
encode(index, len):
result := "";
while(index > 0)
result = result + character_set[index & 0b00111111];
index = index >>> 6;
if(length(result) < len)
result = result + '~';
return result;
With +
being concatenation and >>>
being the sign preserving shift right.
This is basically base64 with a set result length and early exit code. The encoding for indices below 64 could also be expressed as encode(index, 1)
.
All references to an API without hostname (e.g: /v2/pets
) reference the official gw2 api.
The first line contains a v1 buildcode with only two weapons and uniform stats for width comparison. It is aligned to cover the width of fields that would be present, not rearranged to match the fields that exist in v1.
a c e ccabbcbgc bbcSUTRge -cfc-cfm_GTxg_i ft_ ikm+ + + +
V T P S.TS.TS.T WS..wS..WS..wS.. S..S..S..S..S.. R.. R.. A..n,,,, I..n,,,, F..U.. A,,,,,
[V]
Version [1 character. currently E
] used for backwards compatibility.
- Not contained in codes below version
D
. - Lower case letters indicate binary format base64 encoded.
[T]
Type [1 character] p
: pvp, w
: wvw, o
: other(pve)
[P]
Profession [1 character] resolved by /v2/professions
A-I
: Guardian, Warrior, Engineer, Ranger, Thief, Elementalist, Mesmer, Necromancer, Revenant
[S.TS.TS.T]
Specializations [3 * 3 characters] pairs of (2 char specialization + 1 char selected traits):
_
(underscore): empty trait line2 characters
: specialization id, resolved by/v2/specializations
,encode(id, 2)
- Trait choices [1 character] encoded as follows:
-
Let
pos(c) : {1, 2, 3} => {0, 1, 2, 3}
be the position of the selected trait in the current trait line in columnc
as it appears in game:pos(c) := { 0 if empty, 1 if top, 2 if mid, 3 if bottom }
. -
Calculate an index by
index = 0; for(c: 1..3) index |= pos(c) << (6 - c * 2)
.This effectively constructs
0b00aabbcc
withaa
= pos of first choice,bb
= pos of second choice,cc
= pos of third choice. With a max value of 63 this can be used to indexcharacter_set
and obtain the final encoding. Omit if trait line is empty.
-
[WS..wS..WS..wS..]
Weapons [2 * 3-8 characters] pairs of (1 char weapon type index, 1-3 char sigil id, 0-1 char weapon type index, 1-3 char sigil id):
- Repeat twice, once for each weapon set (or for each underwater weapon):
-
_
(underscore): empty weapon slotA-T
: weapon type index. Axe, Dagger, Mace, Pistol, Sword, Scepter, Focus, Shield, Torch, Warhorn, Shortbow, Greatsword, Hammer, Longbow, Rifle, Staff, HarpoonGun, Spear, Trident
-
_
(underscore): empty sigil slot1-3 characters
: sigil id resolved by/v2/items
,encode(id, 3)
- repeat of (1.)
- repeat of (2.)
- If the weapon is two handed, the id is stored in the first slot of the set, and the second weapon type index in the set is omitted.
- If the weapon is not set and the first weapon is not two handed the sigil corresponding to that weapon is omitted.
- The second weapon set may be omitted from the code by replacing the whole second set (
[WS..wS..]
) with a~
(tilde). - This section can be omitted completely by replacing the whole section with a single
~
(tilde).
[S..S..S..S..S..]
Slot skills [5 * 1-3 characters] each:
_
(underscore): empty skill slot1-3 characters
: skill id resolved by/v2/skills
,encode(id, 3)
[R..]
Rune [1-3 characters]. Always the same for all slots.
_
(underscore): if empty1-3 characters
: item id resolved by/v2/items
,encode(id, 3)
[R..]
Relic [1-3 characters].
_
(underscore): if empty1-3 characters
: item id resolved by/v2/items
,encode(id, 3)
[A..n,,,,]
Attribute ids [1-n characters] itemstat id resolved by /v2/itemstats
- For pvp codes encode the stat id of the amulet with
encode(id, 2)
- For other gamemodes:
- Order the equipment in the following way: armor (helmet to boots), backpiece, accessory1, accessory2, ring1, ring2, weapons (main hand then offhand for all sets), amulet.
- Only include weapons in this list that actually exist in the code.
- Going trough that list
encode(itemstat_id, 2)
of the current item, - then append
B-O
(one char) for the amount of times this stat type repeats in the list.- Omit the repetition count if there is only one item left as it would always be 1 as we know how many pieces of equipment are in the code.
- Repeat step 2. and 3. until the whole list has been processed.
- Order the equipment in the following way: armor (helmet to boots), backpiece, accessory1, accessory2, ring1, ring2, weapons (main hand then offhand for all sets), amulet.
[I..n,,,,]
Infusions [1-n characters]
- For pvp codes encodes omit this section completely.
- To omit infusions replace this section with a single
~
(tilde). - Encoded in the same way as attribute ids, except with
encode(item_id, 3)
. - Use
_
(underscore) to encode an empty slot, this also requires the usual repetition indicator.
[F..U..]
Food + Utility [2-6 characters] pair of 1-3 characters, each:
- For pvp codes encodes omit this section completely.
_
(underscore): empty food or utility1-3 characters
: item id resolved by/v2/items
,encode(id, 3)
[A,,,,,]
Arbitrary data [0-n characters], currently used for:
- Ranger pets: [1-2 characters]
- assume the following pet order: pet1, pet2
~
(tilde): omit pets_
(underscore): empty pet slot2 characters
:encode(pet_id, 2)
from/v2/pets
- Revenant Legends + utility: [3-11 characters]
- 2 times
_
(underscore): empty legend slot, only the second legend can be empty.A-F
: legend index from/v2/legends
to indexcharacter_set
. Be careful: the online list is defective as of writing this. Use the list in here: Shiro, Glint, Mallyx, Jalis, Ventari, Kalla, Vindicator
- 3 times
_
(underscore): empty alternate legend utility skill slot1-3 characters
: alternate legend utility skill id resolved by/v2/skills
,encode(id, 3)
- omit whole block if second legend is empty
- 2 times
There are no special fields defined for handling underwater data. To define underwater data just make the following adjustments:
- Weapon 1 of set1 becomes the first UW weapon, weapon 1 of set2 becomes the second UW weapon.
- The helmet is replaced by the aquabreather.
- Ranger Pets now define the underwater pets.
- Revenant legends and alternate skills now define the UW legends / skills.
There is no signal build into a code to determine if a code is an underwater code, as there aren't really any functional differences. The end user may however inspect the weapons, if present, to reach a conclusion.
This can be base64 encoded. Doing so will expand the code by about 1/3 of its length.
Field widths are measured in bits. See textual specification for details on how to obtain numeric values and when to omit fields. Fields use big endianess.
8 : Version char
2 : Type: p: 0, w: 1, o: 2
4 : Profession index
repeat 3
7 : specializations: 0 if trait line is empty, 1 + spec index otherwise
either
omitted
or
repeat 3
2 : selected trait position. 0 if nothing selected, position otherwise
either
5 : 0 if code does not contain weapons
or
5 : set1 main hand weapon. 1 if slot is empty, 2 + weapon type index otherwise
24 : slot 1 sigil. 0 if no sigil in slot1, sigil item id otherwise
5 : set1 offhand weapon. 1 if slot is empty, 2 + weapon type index otherwise. omit this if set1 main hand is two handed
24 : slot 2 sigil. 0 if no sigil in slot2, sigil item id otherwise
either
5 : 0 if code des not contain second weapon set
or
5 : set2 main hand weapon. 1 if slot is empty, 2 + weapon type index otherwise
24 : slot 1 sigil. 0 if no sigil in slot1, sigil item id otherwise
5 : set2 offhand weapon. 1 if slot is empty, 2 + weapon type index otherwise. omitted if set2 main hand is two handed
24 : slot 2 sigil. 0 if no sigil in slot2, sigil item id otherwise
repeat 5: skill ids
24 : 0 if empty, Skill id otherwise
24 : 0 if empty, Rune item id otherwise
24 : 0 if empty, Relic item id otherwise
either
16 : stat id (when type = pvp)
or
dynamic repeat
16 : stat id
4 : repeat count - 1
either
24 : 0 if infusions omitted
or
dynamic repeat
24 : 1 if empty slot, 1 + infusion item id otherwise
5 : repeat count - 1
24 : food item. 0 if none, item_id otherwise, omit for pvp codes
24 : utility item. 0 if none, item_id otherwise, omit for pvp codes
either
no additional data
or
7 : pet 1. 0 to omit block, 1 for empty slot, 1 + pet_id otherwise
7 : pet 2. 1 for empty slot, 1 + pet_id otherwise, omit if pet 1 is 0
or
4 : legend 1. 1 + legend index
4 : legend 2. 0 for empty slot, 1 + legend index otherwise
repeat 3. alternate legend skills. omit if legend 2 is empty
24 : 0 if empty, Skill id otherwise
(406 <-> 482) bits / 8 * 4/3 = (68 <-> 81) chars. Interestingly only about 12 chars (~ 20%) less than the textual representation, the encoding does expand it a lot.