feat(ui): preserve exact retail shortcut records

Carry signed index, object id, and raw spell word losslessly through PlayerDescription, session storage, drag mutation, and AddShortcut wire serialization while keeping gmToolbarUI object-only. Retire AP-103 and record the live ACE relog persistence gate.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 09:17:33 +02:00
parent e65119f0c6
commit b5b230c860
21 changed files with 271 additions and 181 deletions

View file

@ -197,18 +197,6 @@ public static class PlayerDescriptionParser
SpellLists8 = 0x00000400,
}
/// <summary>One shortcut bar entry. 16 bytes wire size.
/// holtburger <c>shortcuts.rs:13-34</c>. Named <c>ShortcutEntry</c>
/// (not <c>Shortcut</c>) to avoid a homograph with the
/// <see cref="CharacterOptionDataFlag.Shortcut"/> flag bit, which is
/// referenced from the same scope as instances of this type in the
/// trailer walker.</summary>
public readonly record struct ShortcutEntry(
uint Index,
uint ObjectGuid,
ushort SpellId,
ushort Layer);
/// <summary>One inventory entry — a guid plus a ContainerType
/// discriminator (0=NonContainer, 1=Container, 2=Foci). Holtburger
/// <c>events.rs:143-168</c> validates <c>ContainerType &lt;= 2</c>
@ -348,11 +336,10 @@ public static class PlayerDescriptionParser
if (count > 10_000) throw new FormatException("unreasonable shortcut count");
for (uint i = 0; i < count; i++)
{
uint idx = ReadU32(payload, ref pos);
uint guid = ReadU32(payload, ref pos);
ushort spellId = ReadU16(payload, ref pos);
ushort layer = ReadU16(payload, ref pos);
shortcuts.Add(new ShortcutEntry(idx, guid, spellId, layer));
int index = unchecked((int)ReadU32(payload, ref pos));
uint objectId = ReadU32(payload, ref pos);
uint spellId = ReadU32(payload, ref pos);
shortcuts.Add(new ShortcutEntry(index, objectId, spellId));
}
}