Generated ids¶
For autocomplete, the ids of one items version are also generated as enums:
from empire_core.gamedata import Currency, General, GeneralSkill, Unit
General.TORIL # 101
Currency.SKIP_5_MINUTES # "MS2", the key the server uses
Unit.MEAD_RANGER_L6 # 211: type plus level
GeneralSkill.TORIL_ASPECTOFTHE_DRAGON_L1
Units, tools, effects, effect types, currencies (Currency by key,
CurrencyId by id), generals, general abilities and skills, legend skills,
raid bosses, global effects, buildings, researches, construction items,
events, loot boxes and their types, equipment groups, event difficulty types,
quests (QuestId), daily quests (DailyQuestId), main quests (MainQuest),
gems, sceat skills, achievements, horses, titles and alliance crest layouts and
colours each have one. Like every enum, each also imports from
empire_core.enums, and from each area whose models or methods use it
(from empire_core.army import Unit); every path gives the same class, loaded on
first use. The 27,000 rewards have none: the game shows no text
for a reward, and the notes some rows carry name where it is given, not the
reward. Reward ids stay ints; the game reads each as the collectables it holds.
Names¶
Member names come from the row, in UPPER_SNAKE. Where the row only has a
code, the name is the game's English text for it, the one the game shows:
currencies are named from their name (Currency.SKIP_5_MINUTES for MS2,
Currency.FAST_TRAVEL_FEATHERS for PTT), researches from their title and level
(Research.STRENGTH_TRAINING_L1). A row the game has no text of its own for
keeps its code (Currency.DC1 to DC80, the decoration catalysts, share one
name), and a blueprint or recipe research keeps the items file's note, group
and level (Research.BEEFSTORAGE_G193_L1). Where two rows
would get the same name, both carry their id (GlobalEffect.SPEED_BOOST_2,
GlobalEffect.SPEED_BOOST_11). Quests are named after what their first
condition counts, so most carry their id (QuestId.BUILDINGS_44).
Tables whose rows have no name column are named from the text the game shows
for them: gems (Gem.GEM_OF_THE_GLORIOUS_DEFENDER_L6, a unique one by its own
name), sceat skills, achievements (the series' name, the step as its level),
titles (Title.KNIGHT), crest layouts and main quests. Where the game has no
text, a designer note names the row (Horse.WARHORSE_STABLE1, as the game
names a horse only by its place in the travel dialog;
AllianceCrestLayout.FREE_1), and a crest colour, which has neither, is named
by its id and carries its hex colour (AllianceCrestColor.COLOR_1.color).
Enums the game names by a text give that name at run time, in any language:
Currency.SKIP_5_MINUTES.display_name("de"), with its text id as .text_id.
A member named from the English text has the same name in English; one named
from the items file's code may not (Unit.MEAD_RANGER_L6 is a "Valkyrie
ranger"). See Game texts.
Members are plain values¶
Members are plain ints (Currency members plain strs), so they go straight
into requests. Most also carry their row's fixed id and number columns, to
filter on without loading any game data:
from empire_core.gamedata import General, Tool, Unit
Unit.MEAD_RANGER_L6.role # "ranged"; also .level
General.TORIL.rarity_id # 4
[t for t in Tool if t.category == "Defence"]
Stats and costs are not baked in, as balance patches change them, and nothing
here downloads the game data. For the full row, index a loaded GameData
table with the member; see Game data.
Ids in models¶
Models type the ids they read with these enums, leniently: a field typed
EnumOrInt[Kingdom] holds the member for an id the enum has, and the plain int
for one it lacks (a client release newer than the enums), with a warning logged
once per id, so a packet never fails over it. EnumOrStr[Currency] does the
same for keys. Name a generated enum by its name in quotes
(EnumOrInt["QuestId"]) to load its module only when a value arrives.
Rewards, costs and goods are Collectables: the kind (a CollectableKind),
the amount, and the item the entry names, typed for the kinds that name one
(a Unit or Tool for units, a Currency, a BoosterId, a LootBox, ...):
from empire_core.gamedata import Collectable
rewards = Collectable.from_object({"U": [[664, 5]], "MS2": [1], "C1": [2000]})
[(reward.kind, reward.item, reward.amount) for reward in rewards]
# [(CollectableKind.UNITS, Unit.KINGSCROSSBOWMAN, 5),
# (CollectableKind.CURRENCY, Currency.SKIP_5_MINUTES, 1), (CollectableKind.COINS, None, 2000)]
An entry under a key the client has no type for is kept as
CollectableKind.OTHER, with its key and the entry as sent in value.
Packets read into the same type: a movement's goods and a travel
movement's loot, a battle report participant's loot, a spy report's
resources, the time skip a battle found and what an auto-skip cost
(auto_skip_paid) or refunded (auto_skip_refunded). So do the items'
rewards, through GameData.reward_list.
Keeping them current¶
ITEMS_VERSION is the items version the enums came from, and
is_current(game_data) says whether loaded data matches it; GameData.load()
logs a warning when it does not. Ids added since are not in the enums, but the
lookups by name cover them.
To regenerate from a checkout after a client update:
# downloads the current items and English texts
uv run python scripts/generate_gamedata_ids.py
# offline, with the texts the committed names came from
uv run python scripts/generate_gamedata_ids.py --items items_v786.03.json --texts scripts/gamedata_ids_texts.json
# exits 1 if the ids are out of date with the live texts (downloads them)
uv run python scripts/generate_gamedata_ids.py --check
# the same against the committed texts, offline for the texts
uv run python scripts/generate_gamedata_ids.py --check --texts scripts/gamedata_ids_texts.json
The names come from the game's texts, but the generator keeps only
the texts it used, in scripts/gamedata_ids_texts.json, next to it. A weekly
workflow regenerates the ids from the live items and texts and, when the ids
or those texts changed, opens a pull request with them, listing every member
renamed, removed or added. --diff-names names.md writes the same list
locally.
API: Generated ids