You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Expected. A Package author should be able to tune non-spell DBC-backed catalogues (factions, auction house rates, talents, skill-ability thresholds, and similar tables) through the same typed Package Delta pipeline the spell family already uses, with identifiers and references checked before any write.
Actual (at 87109e3c). Only the spell family has a Package Delta path today: casts, creature_ai, creatures, fixtures, gameobjects, globals, gossip, items, loot, quest, script, spellmeta, spell and trainers all live under module/src/package_import/, with no talents or catalogues family. The two DBC-loading importer runs, run (importer/src/dbc.rs#L167) and run_talents (importer/src/talent.rs#L141), do not stamp game_import_meta, so neither has a base-import owner a Package claim could replay against.
A proposal on this issue inventories every DBC-backed table outside the spell stream and names a v1 set: two update-only families, talents and catalogues, six claimable tables, and no Package Identifier Range, since every candidate table is mirrored by the client's own DBC and an inserted row would be unreachable. It also raises five open questions: whether an update-only v1 with no insert band is the right scope, what to do about game_skill_ability's positional (non-portable) primary key, whether the whole --dbc run should be one family or several, whether lyracore packages replay should grow a DBC path in the same change, and how a claim should check a spell reference against a family that lands in a separate base import.
Fix direction. A maintainer answers the five open questions on this issue; the proposed v1 set is the contract to build once they do.
This is an agent's proposal for the v1 Package-editable DBC set, awaiting maintainer approval on this issue before any implementation.
Inventory of DBC catalogues outside the spell stream
Two importer invocations load them. --dbc <Data> alone runs importer/src/dbc.rs::run and clears and reloads 16 tables in one pass. --dbc <Data> --talents runs importer/src/talent.rs and clears and reloads 2 more. Neither invocation stamps game_import_meta, so no table below has a base-import owner today. --family covers the --dump ETL only.
No table below is map-filtered. Each loads all its rows, so all are global catalogues, not World Import Scope. import-world.sh runs both invocations once per target database, so every World Shard and the Instance Pool holds a full copy. Realm-core holds accounts and no game catalogue.
corpse release, via game_graveyard_zone from the globals dump family
game_creature_family
CreatureFamily.dbc
--dbc
family_id u32
yes
pet food and tameable gates, not wired (188 rows)
game_lock
Lock.dbc
--dbc
id u64 positional
no
gameobject open gate, via by_lock
game_taxi_node
TaxiNodes.dbc
--dbc
id u32
yes
flight, plus a unique client_node_id inside the client taxi mask
game_taxi_path
TaxiPath.dbc
--dbc
id u32
yes
flight route and fare
game_taxi_path_node
TaxiPathNode.dbc
--dbc
id u32
yes
flight geometry
game_talent_tab
TalentTab.dbc
--dbc --talents
tab_id u32
yes
talent panes (27 rows)
game_talent
Talent.dbc
--dbc --talents
talent_id u32
yes
learn_talent, apply_learned_talents (432 rows)
"Positional" means the importer writes a 1-based counter over DBC record order, not the DBC's own id. A claim keyed on such an id is not portable between operators, because a different patch chain shifts every row. game_quest_objective shows the way out: it has an auto_inc storage key and its claims use the logical key (quest_entry, obj_index).
Proposed v1 set
One rule decides the whole set. Every catalogue here is mirrored by the client's own DBC, so a row a Package invents is unreachable or invisible on an unmodified client. v1 is therefore update-only. No table accepts an insert, no family owns a Package Identifier Range, and clear_package_range is a no-op. That removes the largest part of what the casts and trainers families cost. DeltaError::InsertNotSupported already exists for exactly this, from game_creature_cast.
Two Import Families, one per invocation that already exists. A family is the unit a base import clears, and the standalone --dbc run clears its 16 tables together, so it is one family. This follows the globals precedent in the --dump ETL.
every spell id is 0 or a game_spell row; required_talent_id is 0 or a game_talent row after the family lands; max_rank in 1..=5 and consistent with the rank_spell_* set
both rates in 0..=100, the bound auction_house_sql already enforces
tree_id and tab_id on game_talent, order_index on game_talent_tab, reputation_index on game_faction, faction on game_faction_template, and spell_id/skill_line on game_skill_ability stay out of the claimable set. Each one is the client's own view of the row. Changing it server-side desynchronizes the two halves and shows nothing to a player.
Two acceptance criteria need no new work. Table::parse is a closed catalogue, so an unsupported DBC table already fails with a refusal that names every claimable table. The licensing firewall in dbc.rs and pack_client.rs already refuses a committed .dbc, and a claim is a typed row artifact.
Size, against commit 94d5cb8 (2 families, 3 tables, 1,834 lines over 18 files, with bands, clear passes and inserts):
6 tables against 3, but no band and no insert path
module/src/package_import/{talents,catalogues}.rs
about 0.8x
setters and update_target only, no clear_package_range body
importer wiring
new, about 100 lines
two stamp_family calls, two run_package_stage calls, extend the --packages gate to --dbc and --dbc --talents. casts and trainers got this free because their --dump families already existed
the importer validates the three as one graph and refuses a malformed catalogue. A claim would have to repeat that validation inside the reducer. Node ids are also bounded by the client 256-bit taxi mask. High cost, low modder value
game_area
zone and subzone names come from the client's own AreaTable.dbc. A server-side rename shows nowhere
game_area_trigger
the client sends only trigger ids its own AreaTrigger.dbc holds. Moving a volume server-side splits the two views
game_graveyard
the point comes from WorldSafeLocs.dbc and its zone link comes from the globals dump family. The two halves would sit in different families. Revisit when globals gets a schema
game_char_base_info, game_race_info
character creation is gated by the client UI first. A combo the client will not offer is unreachable
positional primary key, and each needs a logical key before a claim is portable. game_lock is also only half a story: the other half is game_gameobject_template.lock_id in the gameobjects dump family
game_skill_line, game_creature_family
names and groupings are client-drawn, and the pet food and tameable gates are not wired. A claim would change nothing today
Spell.dbc headers and effects, DBC binaries, terrain, navigation, vmap, models
out of scope by this issue
Open questions
Update-only v1, no Package Identifier Range.talents and catalogues would be the first families with no band, so clear_package_range is a no-op for both. Recommendation: accept it. Every catalogue here is client-mirrored, so an invented row is unreachable. Give a table a band later, one table at a time, when it earns one.
game_skill_ability primary key.skill_ability_sql writes a positional counter, so its ids move when the operator's patch chain changes, and a claim keyed on one is not portable. Recommendation: in the same change, write the DBC's own row id instead, with a duplicate guard. If you prefer not to touch that importer path, drop game_skill_ability and the catalogues family carries three tables.
Family granularity. The standalone --dbc run clears 16 tables in one invocation, so it is one clear-and-reload unit. Recommendation: one family named catalogues. The alternative is a --family selector for the --dbc path, which is more importer work and buys nothing until a second DBC family exists.
lyracore packages replay reimports Spell.dbc only. A DBC family needs it to run --dbc and --dbc --talents too. Recommendation: extend replay_command in the CLI repo in the same change. Note the same gap already stops the items, quests, loot, casts and trainers families from replaying, because replay reimports the spell family alone. That gap deserves its own issue.
Cross-family references. A talent may point at a Package Spell, and the spell family and the talents family are separate base imports with no ordering rule between them. Recommendation: check spell_id against game_spell as the Shard holds it at apply time, the rule trainers already uses, and document that a Package replays the spell family first.
Proposed by Claude (model claude-opus-5) via Claude Code subagent.
changed the title [-]Package Deltas: support non-spell DBC catalogues[/-][+]Package Deltas cannot yet reach DBC catalogues outside the spell stream[/+]on Oct 8, 2026
Activity
Mathih13 commentedon Sep 2, 2026
This is an agent's proposal for the v1 Package-editable DBC set, awaiting maintainer approval on this issue before any implementation.
Inventory of DBC catalogues outside the spell stream
Two importer invocations load them.
--dbc <Data>alone runsimporter/src/dbc.rs::runand clears and reloads 16 tables in one pass.--dbc <Data> --talentsrunsimporter/src/talent.rsand clears and reloads 2 more. Neither invocation stampsgame_import_meta, so no table below has a base-import owner today.--familycovers the--dumpETL only.No table below is map-filtered. Each loads all its rows, so all are global catalogues, not World Import Scope.
import-world.shruns both invocations once per target database, so every World Shard and the Instance Pool holds a full copy. Realm-core holds accounts and no game catalogue.game_faction_templateFactionTemplate.dbc--dbcidu32faction.rs), auctioneer resolution,game_race_info.faction_templategame_factionFaction.dbc--dbcfaction_idu32SMSG_INITIALIZE_FACTIONS, reputationgame_auction_houseAuctionHouse.dbc--dbcidu32game_char_base_infoCharBaseInfo.dbc--dbcrace_classu16create_charactercombo gategame_race_infoChrRaces.dbc--dbcraceu8game_start_itemCharStartOutfit.dbc--dbcidu64 positionalgrant_starter_itemgame_skill_lineSkillLine.dbc--dbcskill_lineu32game_skill_abilitySkillLineAbility.dbc--dbcidu64 positionalgame_skill_availabilitySkillRaceClassInfo.dbc--dbcidu64 positionalgame_areaAreaTable.dbc--dbcidu32terrain::zone_id_atgame_area_triggerAreaTrigger.dbc--dbcidu32game_graveyardWorldSafeLocs.dbc--dbcidu32game_graveyard_zonefrom theglobalsdump familygame_creature_familyCreatureFamily.dbc--dbcfamily_idu32game_lockLock.dbc--dbcidu64 positionalby_lockgame_taxi_nodeTaxiNodes.dbc--dbcidu32client_node_idinside the client taxi maskgame_taxi_pathTaxiPath.dbc--dbcidu32game_taxi_path_nodeTaxiPathNode.dbc--dbcidu32game_talent_tabTalentTab.dbc--dbc --talentstab_idu32game_talentTalent.dbc--dbc --talentstalent_idu32learn_talent,apply_learned_talents(432 rows)"Positional" means the importer writes a 1-based counter over DBC record order, not the DBC's own id. A claim keyed on such an id is not portable between operators, because a different patch chain shifts every row.
game_quest_objectiveshows the way out: it has anauto_incstorage key and its claims use the logical key(quest_entry, obj_index).Proposed v1 set
One rule decides the whole set. Every catalogue here is mirrored by the client's own DBC, so a row a Package invents is unreachable or invisible on an unmodified client. v1 is therefore update-only. No table accepts an insert, no family owns a Package Identifier Range, and
clear_package_rangeis a no-op. That removes the largest part of what the casts and trainers families cost.DeltaError::InsertNotSupportedalready exists for exactly this, fromgame_creature_cast.Two Import Families, one per invocation that already exists. A family is the unit a base import clears, and the standalone
--dbcrun clears its 16 tables together, so it is one family. This follows theglobalsprecedent in the--dumpETL.talents--dbc <Data> --talentsgame_talentname,tier,column,max_rank,spell_id,grant_spell_id,rank_spell_2..5,required_talent_id,required_talent_rank,required_points_in_tree,required_spell_idgame_spellrow;required_talent_idis 0 or agame_talentrow after the family lands;max_rankin 1..=5 and consistent with therank_spell_*settalentsgame_talent_tabname,class_mask,race_maskclass_maskis one-hotcatalogues--dbc <Data>game_faction_templatefaction_group,friend_group,enemy_group,enemy_0..3,friend_0..3game_factionrowcataloguesgame_factionbase_standingcataloguesgame_skill_abilitymin_skill,acquire_method,gray,greengreen <= graycataloguesgame_auction_housedeposit_rate,consignment_rate,nameauction_house_sqlalready enforcestree_idandtab_idongame_talent,order_indexongame_talent_tab,reputation_indexongame_faction,factionongame_faction_template, andspell_id/skill_lineongame_skill_abilitystay out of the claimable set. Each one is the client's own view of the row. Changing it server-side desynchronizes the two halves and shows nothing to a player.Two acceptance criteria need no new work.
Table::parseis a closed catalogue, so an unsupported DBC table already fails with a refusal that names every claimable table. The licensing firewall indbc.rsandpack_client.rsalready refuses a committed.dbc, and a claim is a typed row artifact.Size, against commit 94d5cb8 (2 families, 3 tables, 1,834 lines over 18 files, with bands, clear passes and inserts):
lyracore-package-deltaschema, keys, testsmodule/src/package_import/{talents,catalogues}.rsupdate_targetonly, noclear_package_rangebodystamp_familycalls, tworun_package_stagecalls, extend the--packagesgate to--dbcand--dbc --talents. casts and trainers got this free because their--dumpfamilies already existedlyracore-clipackages replayExcluded from v1:
game_taxi_node,game_taxi_path,game_taxi_path_nodegame_areaAreaTable.dbc. A server-side rename shows nowheregame_area_triggerAreaTrigger.dbcholds. Moving a volume server-side splits the two viewsgame_graveyardWorldSafeLocs.dbcand its zone link comes from theglobalsdump family. The two halves would sit in different families. Revisit whenglobalsgets a schemagame_char_base_info,game_race_infogame_start_item,game_lock,game_skill_availabilitygame_lockis also only half a story: the other half isgame_gameobject_template.lock_idin thegameobjectsdump familygame_skill_line,game_creature_familySpell.dbcheaders and effects, DBC binaries, terrain, navigation, vmap, modelsOpen questions
talentsandcatalogueswould be the first families with no band, soclear_package_rangeis a no-op for both. Recommendation: accept it. Every catalogue here is client-mirrored, so an invented row is unreachable. Give a table a band later, one table at a time, when it earns one.game_skill_abilityprimary key.skill_ability_sqlwrites a positional counter, so its ids move when the operator's patch chain changes, and a claim keyed on one is not portable. Recommendation: in the same change, write the DBC's own row id instead, with a duplicate guard. If you prefer not to touch that importer path, dropgame_skill_abilityand thecataloguesfamily carries three tables.--dbcrun clears 16 tables in one invocation, so it is one clear-and-reload unit. Recommendation: one family namedcatalogues. The alternative is a--familyselector for the--dbcpath, which is more importer work and buys nothing until a second DBC family exists.lyracore packages replayreimportsSpell.dbconly. A DBC family needs it to run--dbcand--dbc --talentstoo. Recommendation: extendreplay_commandin the CLI repo in the same change. Note the same gap already stops the items, quests, loot, casts and trainers families from replaying, because replay reimports the spell family alone. That gap deserves its own issue.talentsfamily are separate base imports with no ordering rule between them. Recommendation: checkspell_idagainstgame_spellas the Shard holds it at apply time, the ruletrainersalready uses, and document that a Package replays the spell family first.Proposed by Claude (model claude-opus-5) via Claude Code subagent.
[-]Package Deltas: support non-spell DBC catalogues[/-][+]Package Deltas cannot yet reach DBC catalogues outside the spell stream[/+]