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:
parent
e65119f0c6
commit
b5b230c860
21 changed files with 271 additions and 181 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System.Buffers.Binary;
|
||||
using AcDream.Core.Items;
|
||||
|
||||
namespace AcDream.Core.Net.Messages;
|
||||
|
||||
|
|
@ -98,20 +99,20 @@ public static class InventoryActions
|
|||
return body;
|
||||
}
|
||||
|
||||
/// <summary>Pin an item/spell to a quickbar slot. ShortCutData = Index(u32), ObjectId(u32),
|
||||
/// SpellId(u16), Layer(u16) — CONFIRMED across ACE/Chorizite/holtburger (action-bar deep-dive
|
||||
/// §131-145). For an ITEM: objectGuid = item guid, spellId = layer = 0.</summary>
|
||||
public static byte[] BuildAddShortcut(
|
||||
uint seq, uint index, uint objectGuid, ushort spellId, ushort layer)
|
||||
/// <summary>
|
||||
/// Pack retail <c>ShortCutData</c> for AddShortcut: signed index, object id,
|
||||
/// and raw 32-bit spell word. Retail uses the same three-u32 packer as
|
||||
/// <c>InventoryPlacement::Pack @ 0x005CABB0</c> through the shortcut vtable.
|
||||
/// </summary>
|
||||
public static byte[] BuildAddShortcut(uint seq, ShortcutEntry entry)
|
||||
{
|
||||
byte[] body = new byte[24];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), AddShortcutOpcode);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), index);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), objectGuid);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(20), spellId);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(22), layer);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(body.AsSpan(12), entry.Index);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), entry.ObjectId);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), entry.SpellId);
|
||||
return body;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <= 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue