refactor(runtime): close canonical gameplay ownership
Unify the toolbar shortcut manager with Runtime inventory state, route retail-ordered shortcut and spellbook command effects through the canonical owners, and make retained controllers borrow those exact instances. Remove the item-interaction transaction fallback and add graphical/no-window parity plus failure-safe terminal ownership-ledger coverage. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
ce6fae7b38
commit
89e6b207f8
36 changed files with 1433 additions and 251 deletions
|
|
@ -115,7 +115,7 @@ public sealed class RuntimeCharacterStateTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposalRetriesObserverFailuresAndClearsBothOwners()
|
||||
public void DisposalReportsObserverFailuresAfterTerminalConvergence()
|
||||
{
|
||||
var state = new RuntimeCharacterState();
|
||||
state.Spellbook.OnSpellLearned(7u);
|
||||
|
|
@ -143,13 +143,13 @@ public sealed class RuntimeCharacterStateTests
|
|||
state.Dispose);
|
||||
|
||||
Assert.Equal(2, error.InnerExceptions.Count);
|
||||
Assert.False(state.IsDisposed);
|
||||
Assert.True(state.IsDisposed);
|
||||
Assert.False(state.Spellbook.Knows(7u));
|
||||
Assert.Null(state.LocalPlayer.Get(
|
||||
AcDream.Core.Player.LocalPlayerState.VitalKind.Health));
|
||||
Assert.True(state.CaptureOwnership().IsConverged);
|
||||
|
||||
state.Dispose();
|
||||
state.Dispose();
|
||||
|
||||
Assert.True(state.IsDisposed);
|
||||
}
|
||||
|
|
@ -254,4 +254,77 @@ public sealed class RuntimeCharacterStateTests
|
|||
Assert.True(first.View.Snapshot.SpellbookRevision > 0);
|
||||
Assert.Equal(0, second.View.Snapshot.SpellbookRevision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpellbookCommandsFollowRetailLocalAndOutboundOrder()
|
||||
{
|
||||
using var state = new RuntimeCharacterState();
|
||||
var order = new List<string>();
|
||||
state.Spellbook.SpellbookChanged += () => order.Add("local");
|
||||
state.Spellbook.DesiredComponentsChanged += () => order.Add("local");
|
||||
|
||||
Assert.True(state.TryAddFavorite(
|
||||
0,
|
||||
0,
|
||||
42u,
|
||||
() => order.Add("send")));
|
||||
Assert.Equal(["local", "send"], order);
|
||||
Assert.Equal([42u], state.Spellbook.GetFavorites(0));
|
||||
|
||||
order.Clear();
|
||||
state.SetSpellbookFilter(0x3FFEu, () => order.Add("send"));
|
||||
Assert.Equal(["local", "send"], order);
|
||||
Assert.Equal(0x3FFEu, state.Spellbook.SpellbookFilters);
|
||||
|
||||
order.Clear();
|
||||
Assert.True(state.TrySetDesiredComponent(
|
||||
0x68000001u,
|
||||
0u,
|
||||
() => order.Add("send")));
|
||||
Assert.Equal(["send", "local"], order);
|
||||
Assert.True(state.Spellbook.DesiredComponents.ContainsKey(
|
||||
0x68000001u));
|
||||
Assert.Equal(0u, state.Spellbook.DesiredComponents[0x68000001u]);
|
||||
|
||||
order.Clear();
|
||||
state.ClearDesiredComponents(() => order.Add("send"));
|
||||
Assert.Equal(["send", "local"], order);
|
||||
Assert.Empty(state.Spellbook.DesiredComponents);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => state.TrySetDesiredComponent(
|
||||
0x68000002u,
|
||||
10u,
|
||||
() => throw new InvalidOperationException("transport")));
|
||||
Assert.Equal(10u, state.Spellbook.DesiredComponents[0x68000002u]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidSpellbookCommandsDoNotPublishOrMutate()
|
||||
{
|
||||
using var state = new RuntimeCharacterState();
|
||||
int sends = 0;
|
||||
|
||||
Assert.False(state.TryAddFavorite(
|
||||
8,
|
||||
0,
|
||||
42u,
|
||||
() => sends++));
|
||||
Assert.False(state.TryRemoveFavorite(
|
||||
-1,
|
||||
42u,
|
||||
() => sends++));
|
||||
Assert.False(state.TrySetDesiredComponent(
|
||||
0u,
|
||||
1u,
|
||||
() => sends++));
|
||||
Assert.False(state.TrySetDesiredComponent(
|
||||
1u,
|
||||
5001u,
|
||||
() => sends++));
|
||||
|
||||
Assert.Equal(0, sends);
|
||||
Assert.Empty(state.Spellbook.GetFavorites(0));
|
||||
Assert.Empty(state.Spellbook.DesiredComponents);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue