# Slice J4.5 — retail local gameplay-command ordering ## Scope This note fixes the command/local-state ordering used while J4.5 removes the last graphical-only shortcut and spellbook mirrors. It does not add a new gameplay algorithm. It preserves the existing packet builders and moves the retail `PlayerModule` mutation onto the one Runtime-owned state object. Oracle: - `docs/research/named-retail/acclient_2013_pseudo_c.txt` - `docs/research/named-retail/acclient.h` The existing wire ports remain: - `AcDream.Core.Net.Messages.ClientCommandRequests` - `AcDream.Core.Net.WorldSession` - `AcDream.App.Net.LiveSessionCommandRouter` ## Retail pseudocode ### Toolbar shortcut add Retail anchors: - `gmToolbarUI::AddShortcut @ 0x004BD9A0` - `PlayerModule::AddShortCut @ 0x005D29A0` - `ShortCutManager::AddShortCut @ 0x005D5790` ```text if slot is valid and the toolbar accepts the item: create ShortCutData(slot, objectId, spellId) send Event_AddShortCut(data) playerModule.AddShortCut(data) ShortCutManager.AddShortCut(data): if data.index is outside [0, 18): return false if the slot already owns a record: replace all three record fields in place else: allocate and install one record return true ``` ### Toolbar shortcut remove Retail anchors: - `gmToolbarUI::RemoveShortcut @ 0x004BD450` - `PlayerModule::RemoveShortCut @ 0x005D29E0` - `ShortCutManager::RemoveShortCut @ 0x005D5690` ```text find the slot containing the selected shortcut flush the visible slot if this is a persisted removal: send Event_RemoveShortCut(slot) playerModule.RemoveShortCut(slot) ShortCutManager.RemoveShortCut(slot): if slot is outside [0, 18): return destroy the record if present set the slot to empty ``` ### Favorite spell add/remove Retail anchors: - spell-menu handler at `0x004C64C0` - `PlayerModule::AddSpellFavorite @ 0x005D43E0` - `PlayerModule::RemoveSpellFavorite @ 0x005D4910` ```text add: if tab is in [0, 8): insert spellId at position in that tab send Event_AddSpellFavorite(spellId, position, tab) remove: if tab is in [0, 8): remove spellId from that tab send Event_RemoveSpellFavorite(spellId, tab) ``` Unlike toolbar shortcuts, favorite-spell state changes locally before the outbound event. ### Spellbook filter Retail anchors: - `gmSpellbookUI::UpdateFilter @ 0x0048B5E0` ```text derive the changed school/level bit derive the next filter bitfield from the button state if next differs from current: playerModule.SetSpellbookFilter(next) send Event_SpellbookFilterEvent(next) rebuild the visible spell list scroll to row zero ``` ### Desired component level Retail anchors: - `gmSpellComponentUI::ListenToElementMessage @ 0x0048A420` - `PlayerModule::SetDesiredCompLevel @ 0x005D4940` ```text parse the edited amount if amount is in [0, 5000]: send Event_SetDesiredComponentLevel(componentId, amount) playerModule.SetDesiredCompLevel(componentId, amount) else: restore the current value in the editor PlayerModule.SetDesiredCompLevel(componentId, amount): if amount is outside [0, 5000]: return false lazily create the desired-component table replace an existing value or add a new value return true ``` Retail retains an explicit zero-valued entry. J4.5 preserves that distinction; only the clear-list command destroys the complete desired-component table. ### Clear desired components Retail anchors: - chat-command handler at `0x0056FE4A` - `PlayerModule::ClearDesiredCompList @ 0x005D2A00` ```text send Event_SetDesiredComponentLevel(INVALID_DID, -1) destroy the complete desired-component table set the table pointer to null publish the spell-component UI update notice ``` ## J4.5 integration consequence The graphical toolbar, retained spell UI, plugins, and future no-window host must all mutate the same Runtime-owned `ShortcutStore` and `Spellbook`. Graphical controllers may initiate the operation, but they may not retain or reconstruct another mutable slot/favorite/component collection. Commands stay synchronous; no additional frame or background queue is introduced.