fix(interaction): close selection lifecycle review gaps
Bind queued actions and pending inventory requests to exact live incarnations, separate optimistic placement from authoritative responses, and serialize retail-style inventory ownership across UI surfaces. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
d2bb5af453
commit
5acc3f01cf
23 changed files with 2635 additions and 168 deletions
|
|
@ -50,11 +50,19 @@ public readonly record struct ClientObjectPlacement(
|
|||
/// <c>ACCWeenieObject::ServerSaysMoveItem @ 0x0058DBB0</c> snapshots and
|
||||
/// publishes both complete placements after applying the new state.
|
||||
/// </summary>
|
||||
public enum ClientObjectMoveOrigin
|
||||
{
|
||||
LocalProjection,
|
||||
ServerRollbackProjection,
|
||||
AuthoritativeResponse,
|
||||
}
|
||||
|
||||
public readonly record struct ClientObjectMove(
|
||||
uint ItemId,
|
||||
ClientObject? Item,
|
||||
ClientObjectPlacement Previous,
|
||||
ClientObjectPlacement Current);
|
||||
ClientObjectPlacement Current,
|
||||
ClientObjectMoveOrigin Origin);
|
||||
|
||||
public enum ClientObjectRemovalReason
|
||||
{
|
||||
|
|
@ -119,7 +127,10 @@ public sealed class ClientObjectTable
|
|||
|
||||
/// <summary>
|
||||
/// Fires after a complete placement notice (moved between packs,
|
||||
/// equipped, unequipped, dropped on ground). <see cref="ClientObjectMove.Item"/>
|
||||
/// equipped, unequipped, dropped on ground). <see cref="ClientObjectMove.Origin"/>
|
||||
/// distinguishes an immediate local projection from ACE's authoritative
|
||||
/// response; transaction owners must complete only on the latter.
|
||||
/// <see cref="ClientObjectMove.Item"/>
|
||||
/// is null when retail publishes the notice before CreateObject resolves
|
||||
/// the GUID; the old/new placement remains authoritative notice data.
|
||||
/// </summary>
|
||||
|
|
@ -168,6 +179,13 @@ public sealed class ClientObjectTable
|
|||
/// <summary>Fires when an object's properties are updated (typically after Appraise).</summary>
|
||||
public event Action<ClientObject>? ObjectUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Fires only for an authoritative SetStackSize response. Inventory request
|
||||
/// owners use this narrower seam instead of treating an unrelated appraisal
|
||||
/// or property update as the response to a merge, split, give, or drop.
|
||||
/// </summary>
|
||||
public event Action<ClientObject>? StackSizeUpdated;
|
||||
|
||||
/// <summary>Fires after all session object and pending-move state is flushed.</summary>
|
||||
public event Action? Cleared;
|
||||
|
||||
|
|
@ -242,7 +260,8 @@ public sealed class ClientObjectTable
|
|||
newSlot,
|
||||
item.WielderId,
|
||||
newEquipLocation),
|
||||
containerTypeHint);
|
||||
containerTypeHint,
|
||||
ClientObjectMoveOrigin.LocalProjection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -270,13 +289,15 @@ public sealed class ClientObjectTable
|
|||
newSlot,
|
||||
newWielderId,
|
||||
newEquipLocation),
|
||||
containerTypeHint);
|
||||
containerTypeHint,
|
||||
ClientObjectMoveOrigin.AuthoritativeResponse);
|
||||
}
|
||||
|
||||
private bool ApplyPlacement(
|
||||
ClientObject item,
|
||||
ClientObjectPlacement current,
|
||||
uint? containerTypeHint = null)
|
||||
uint? containerTypeHint,
|
||||
ClientObjectMoveOrigin origin)
|
||||
{
|
||||
ClientObjectPlacement previous = ClientObjectPlacement.From(item);
|
||||
List<uint>? changedContainers = RemoveFromOtherContainerIndexes(
|
||||
|
|
@ -289,18 +310,19 @@ public sealed class ClientObjectTable
|
|||
if (containerTypeHint is { } hint)
|
||||
item.ContainerTypeHint = hint;
|
||||
Reindex(item, previous.ContainerId);
|
||||
PublishPlacementChange(item, previous);
|
||||
PublishPlacementChange(item, previous, origin);
|
||||
PublishContainerContentsChanges(changedContainers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PublishPlacementChange(
|
||||
ClientObject item,
|
||||
ClientObjectPlacement previous)
|
||||
ClientObjectPlacement previous,
|
||||
ClientObjectMoveOrigin origin)
|
||||
{
|
||||
ClientObjectPlacement current = ClientObjectPlacement.From(item);
|
||||
UpdateEquipmentIndex(item.ObjectId, previous, current);
|
||||
ObjectMoved?.Invoke(new ClientObjectMove(item.ObjectId, item, previous, current));
|
||||
ObjectMoved?.Invoke(new ClientObjectMove(item.ObjectId, item, previous, current, origin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -337,7 +359,8 @@ public sealed class ClientObjectTable
|
|||
newContainerId,
|
||||
newSlot,
|
||||
newWielderId,
|
||||
newEquipLocation)));
|
||||
newEquipLocation),
|
||||
ClientObjectMoveOrigin.AuthoritativeResponse));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -364,14 +387,17 @@ public sealed class ClientObjectTable
|
|||
itemId,
|
||||
Item: null,
|
||||
Previous: default,
|
||||
Current: placement));
|
||||
Current: placement,
|
||||
ClientObjectMoveOrigin.AuthoritativeResponse));
|
||||
WieldConfirmed?.Invoke(itemId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ApplyPlacement(
|
||||
item,
|
||||
placement))
|
||||
placement,
|
||||
containerTypeHint: null,
|
||||
ClientObjectMoveOrigin.AuthoritativeResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -434,7 +460,7 @@ public sealed class ClientObjectTable
|
|||
}
|
||||
else item.ContainerSlot = newSlot;
|
||||
|
||||
PublishPlacementChange(item, previous);
|
||||
PublishPlacementChange(item, previous, ClientObjectMoveOrigin.LocalProjection);
|
||||
PublishContainerContentsChanges(changedContainers);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -485,12 +511,12 @@ public sealed class ClientObjectTable
|
|||
if (!_pendingMoves.TryGetValue(itemId, out var pre)) return false;
|
||||
_pendingMoves.Remove(itemId);
|
||||
ClientObjectPlacement placement = pre.placement;
|
||||
if (!ApplyServerMove(
|
||||
itemId,
|
||||
placement.ContainerId,
|
||||
placement.WielderId,
|
||||
placement.ContainerSlot,
|
||||
placement.EquipLocation))
|
||||
if (!_objects.TryGetValue(itemId, out ClientObject? item)
|
||||
|| !ApplyPlacement(
|
||||
item,
|
||||
placement,
|
||||
containerTypeHint: null,
|
||||
ClientObjectMoveOrigin.ServerRollbackProjection))
|
||||
return false;
|
||||
MoveRolledBack?.Invoke(_objects[itemId]);
|
||||
return true;
|
||||
|
|
@ -654,6 +680,7 @@ public sealed class ClientObjectTable
|
|||
item.StackSize = stackSize;
|
||||
item.Value = value;
|
||||
ObjectUpdated?.Invoke(item);
|
||||
StackSizeUpdated?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue