refactor(runtime): own inventory transaction state
Move the retail one-request-at-a-time gate, shared use busy references, external-container state, item mana, shortcuts, and desired-component snapshots into one Runtime-owned graph over J3's exact ClientObjectTable. Retained UI and session routing now borrow that owner; reset and shutdown preserve the existing order while failure/reentrancy tests protect the transaction boundary. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
595d5e6b01
commit
011efbeaa7
18 changed files with 1337 additions and 406 deletions
|
|
@ -0,0 +1,128 @@
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Gameplay;
|
||||
|
||||
public sealed class RuntimeInventoryStateTests
|
||||
{
|
||||
[Fact]
|
||||
public void OwnsOneGameplayGraphOverExactEntityObjectTable()
|
||||
{
|
||||
using var entities = new RuntimeEntityObjectLifetime();
|
||||
using var inventory = new RuntimeInventoryState(entities);
|
||||
IReadOnlyList<ShortcutEntry> shortcuts =
|
||||
[new ShortcutEntry(2, 0x50000001u, 0u)];
|
||||
IReadOnlyList<(uint Id, uint Amount)> components =
|
||||
[(0x68000001u, 12u)];
|
||||
|
||||
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
||||
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
||||
inventory.ItemMana.OnQueryItemManaResponse(0x50000001u, 0.75f, true);
|
||||
inventory.Shortcuts.Replace(shortcuts);
|
||||
inventory.DesiredComponents.Replace(components);
|
||||
|
||||
Assert.Same(entities.Objects, inventory.Objects);
|
||||
Assert.Equal(0x70000001u, inventory.ExternalContainers.CurrentContainerId);
|
||||
Assert.Equal(0.75f, inventory.ItemMana.GetManaPercent(0x50000001u));
|
||||
Assert.Same(shortcuts, inventory.Shortcuts.Items);
|
||||
Assert.Same(components, inventory.DesiredComponents.Items);
|
||||
Assert.Equal(1, inventory.Shortcuts.Revision);
|
||||
Assert.Equal(1, inventory.DesiredComponents.Revision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetOperationsClearOnlyTheirOwnedLifetimeGroup()
|
||||
{
|
||||
using var entities = new RuntimeEntityObjectLifetime();
|
||||
using var inventory = new RuntimeInventoryState(entities);
|
||||
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
||||
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
||||
inventory.ItemMana.OnQueryItemManaResponse(0x50000001u, 0.75f, true);
|
||||
inventory.Shortcuts.Replace([new ShortcutEntry(1, 2u, 3u)]);
|
||||
inventory.DesiredComponents.Replace([(4u, 5u)]);
|
||||
inventory.Transactions.IncrementBusyCount();
|
||||
|
||||
inventory.ResetExternalContainer();
|
||||
Assert.Equal(0u, inventory.ExternalContainers.CurrentContainerId);
|
||||
Assert.True(inventory.ItemMana.HasMana(0x50000001u));
|
||||
Assert.NotEmpty(inventory.Shortcuts.Items);
|
||||
Assert.Equal(1, inventory.Transactions.BusyCount);
|
||||
|
||||
inventory.ResetTransactions();
|
||||
inventory.ResetItemMana();
|
||||
inventory.ResetPlayerSnapshots();
|
||||
|
||||
Assert.Equal(0, inventory.Transactions.BusyCount);
|
||||
Assert.False(inventory.ItemMana.HasMana(0x50000001u));
|
||||
Assert.Empty(inventory.Shortcuts.Items);
|
||||
Assert.Empty(inventory.DesiredComponents.Items);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndependentRuntimeInstancesNeverShareState()
|
||||
{
|
||||
using var firstEntities = new RuntimeEntityObjectLifetime();
|
||||
using var secondEntities = new RuntimeEntityObjectLifetime();
|
||||
using var first = new RuntimeInventoryState(firstEntities);
|
||||
using var second = new RuntimeInventoryState(secondEntities);
|
||||
|
||||
first.Shortcuts.Replace([new ShortcutEntry(1, 2u, 3u)]);
|
||||
first.Transactions.IncrementBusyCount();
|
||||
|
||||
Assert.NotSame(first.Objects, second.Objects);
|
||||
Assert.Single(first.Shortcuts.Items);
|
||||
Assert.Empty(second.Shortcuts.Items);
|
||||
Assert.Equal(1, first.Transactions.BusyCount);
|
||||
Assert.Equal(0, second.Transactions.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposeDetachesTransactionsAndClearsOwnedState()
|
||||
{
|
||||
using var entities = new RuntimeEntityObjectLifetime();
|
||||
var inventory = new RuntimeInventoryState(entities);
|
||||
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
||||
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
||||
inventory.ItemMana.OnQueryItemManaResponse(0x50000001u, 0.5f, true);
|
||||
inventory.Shortcuts.Replace([new ShortcutEntry(1, 2u, 3u)]);
|
||||
inventory.DesiredComponents.Replace([(4u, 5u)]);
|
||||
|
||||
inventory.Dispose();
|
||||
inventory.Dispose();
|
||||
|
||||
Assert.True(inventory.IsDisposed);
|
||||
Assert.True(inventory.Transactions.IsDisposed);
|
||||
Assert.Equal(0u, inventory.ExternalContainers.CurrentContainerId);
|
||||
Assert.False(inventory.ItemMana.HasMana(0x50000001u));
|
||||
Assert.Empty(inventory.Shortcuts.Items);
|
||||
Assert.Empty(inventory.DesiredComponents.Items);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposalRetriesATransientOwnerObserverFailureToConvergence()
|
||||
{
|
||||
using var entities = new RuntimeEntityObjectLifetime();
|
||||
var inventory = new RuntimeInventoryState(entities);
|
||||
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
||||
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
||||
bool fail = true;
|
||||
inventory.ExternalContainers.Changed += _ =>
|
||||
{
|
||||
if (fail)
|
||||
{
|
||||
fail = false;
|
||||
throw new InvalidOperationException("transient");
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Throws<AggregateException>(inventory.Dispose);
|
||||
Assert.False(inventory.IsDisposed);
|
||||
Assert.True(inventory.Transactions.IsDisposed);
|
||||
|
||||
inventory.Dispose();
|
||||
|
||||
Assert.True(inventory.IsDisposed);
|
||||
Assert.Equal(0u, inventory.ExternalContainers.CurrentContainerId);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue