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
|
|
@ -371,7 +371,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
|||
harness.Character.Options.Replace(0x04000000u, 0x00948700u);
|
||||
harness.Character.MovementSkills.Update(200, 175);
|
||||
harness.Character.Spellbook.OnSpellLearned(42u);
|
||||
harness.InventoryState.Shortcuts.Replace(
|
||||
harness.InventoryState.Shortcuts.Load(
|
||||
[
|
||||
new ShortcutEntry(1, 0x80000001u, 0u),
|
||||
]);
|
||||
|
|
@ -506,6 +506,20 @@ public sealed class CurrentGameRuntimeAdapterTests
|
|||
static entry => entry.Kind == RuntimeTraceKind.Command
|
||||
&& (entry.Code >> 16)
|
||||
== (int)RuntimeCommandDomain.Social);
|
||||
Assert.True(harness.InventoryState.View.TryGetShortcut(
|
||||
2,
|
||||
out RuntimeShortcutSnapshot storedShortcut));
|
||||
Assert.Equal(0x80000001u, storedShortcut.ObjectId);
|
||||
Assert.True(harness.Character.View.TryGetFavorite(
|
||||
0,
|
||||
0,
|
||||
out uint storedFavorite));
|
||||
Assert.Equal(42u, storedFavorite);
|
||||
Assert.Equal(0x3FFEu, harness.Character.Spellbook.SpellbookFilters);
|
||||
Assert.True(harness.Character.View.TryGetDesiredComponent(
|
||||
0x68000001u,
|
||||
out uint storedDesired));
|
||||
Assert.Equal(10u, storedDesired);
|
||||
|
||||
int published = harness.Commands.Published.Count;
|
||||
RuntimeCommandResult stale = commands.Character.SetOptions1(
|
||||
|
|
@ -515,6 +529,101 @@ public sealed class CurrentGameRuntimeAdapterTests
|
|||
Assert.Equal(published, harness.Commands.Published.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GraphicalAndNoWindowJ4CommandsProduceIdenticalCanonicalState()
|
||||
{
|
||||
using var directEntities = new RuntimeEntityObjectLifetime();
|
||||
using var directInventory =
|
||||
new RuntimeInventoryState(directEntities);
|
||||
using var directCharacter = new RuntimeCharacterState();
|
||||
using var harness = new Harness();
|
||||
_ = harness.Runtime.Session.Start(harness.Runtime.Generation);
|
||||
RuntimeGenerationToken generation = harness.Runtime.Generation;
|
||||
IGameRuntimeCommands graphical = harness.Runtime;
|
||||
var directOutbound = new List<string>();
|
||||
var shortcut = new ShortcutEntry(2, 0x80000001u, 0u);
|
||||
|
||||
Assert.True(directInventory.TryAddShortcut(
|
||||
shortcut,
|
||||
() => directOutbound.Add("add-shortcut")));
|
||||
Assert.True(directCharacter.TryAddFavorite(
|
||||
0,
|
||||
0,
|
||||
42u,
|
||||
() => directOutbound.Add("add-favorite")));
|
||||
directCharacter.SetSpellbookFilter(
|
||||
0x3FFEu,
|
||||
() => directOutbound.Add("filter"));
|
||||
Assert.True(directCharacter.TrySetDesiredComponent(
|
||||
0x68000001u,
|
||||
10u,
|
||||
() => directOutbound.Add("desired")));
|
||||
|
||||
Assert.True(graphical.InventoryState.AddShortcut(
|
||||
generation,
|
||||
new RuntimeShortcutCommand(
|
||||
shortcut.Index,
|
||||
shortcut.ObjectId,
|
||||
shortcut.SpellId)).Accepted);
|
||||
Assert.True(graphical.Spellbook.AddFavorite(
|
||||
generation,
|
||||
0,
|
||||
0,
|
||||
42u).Accepted);
|
||||
Assert.True(graphical.Spellbook.SetFilter(
|
||||
generation,
|
||||
0x3FFEu).Accepted);
|
||||
Assert.True(graphical.Spellbook.SetDesiredComponent(
|
||||
generation,
|
||||
0x68000001u,
|
||||
10u).Accepted);
|
||||
|
||||
Assert.Equal(
|
||||
directInventory.View.Snapshot,
|
||||
harness.InventoryState.View.Snapshot);
|
||||
Assert.Equal(
|
||||
directCharacter.View.Snapshot,
|
||||
harness.Character.View.Snapshot);
|
||||
Assert.Equal(
|
||||
directInventory.Shortcuts.Items,
|
||||
harness.InventoryState.Shortcuts.Items);
|
||||
Assert.Equal(
|
||||
directCharacter.Spellbook.GetFavorites(0),
|
||||
harness.Character.Spellbook.GetFavorites(0));
|
||||
Assert.Equal(
|
||||
["add-shortcut", "add-favorite", "filter", "desired"],
|
||||
directOutbound);
|
||||
|
||||
Assert.True(directInventory.TryRemoveShortcut(
|
||||
2,
|
||||
() => directOutbound.Add("remove-shortcut")));
|
||||
Assert.True(directCharacter.TryRemoveFavorite(
|
||||
0,
|
||||
42u,
|
||||
() => directOutbound.Add("remove-favorite")));
|
||||
directCharacter.ClearDesiredComponents(
|
||||
() => directOutbound.Add("clear-desired"));
|
||||
Assert.True(graphical.InventoryState.RemoveShortcut(
|
||||
generation,
|
||||
2).Accepted);
|
||||
Assert.True(graphical.Spellbook.RemoveFavorite(
|
||||
generation,
|
||||
0,
|
||||
42u).Accepted);
|
||||
Assert.True(graphical.Spellbook.ClearDesiredComponents(
|
||||
generation).Accepted);
|
||||
|
||||
Assert.Equal(
|
||||
directInventory.View.Snapshot,
|
||||
harness.InventoryState.View.Snapshot);
|
||||
Assert.Equal(
|
||||
directCharacter.View.Snapshot,
|
||||
harness.Character.View.Snapshot);
|
||||
Assert.Empty(harness.InventoryState.Shortcuts.Items);
|
||||
Assert.Empty(harness.Character.Spellbook.GetFavorites(0));
|
||||
Assert.Empty(harness.Character.Spellbook.DesiredComponents);
|
||||
}
|
||||
|
||||
private static ClientObject Object(WorldSession.EntitySpawn spawn) => new()
|
||||
{
|
||||
ObjectId = spawn.Guid,
|
||||
|
|
@ -587,6 +696,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
|||
|
||||
_items = new ItemInteractionController(
|
||||
Objects,
|
||||
InventoryState.Transactions,
|
||||
() => PlayerGuid,
|
||||
sendUse: null,
|
||||
sendUseWithTarget: null,
|
||||
|
|
|
|||
|
|
@ -41,13 +41,32 @@ public sealed class RuntimeInventoryOwnershipTests
|
|||
"InteractionRetainedUiComposition.cs");
|
||||
string session = ReadSource("Net", "LiveSessionRuntimeFactory.cs");
|
||||
string shutdown = ReadSource("Rendering", "GameWindowLifetime.cs");
|
||||
string itemInteraction = ReadSource(
|
||||
"UI",
|
||||
"ItemInteractionController.cs");
|
||||
|
||||
Assert.Contains(
|
||||
"transactions: d.Inventory.Transactions",
|
||||
"d.Inventory.Transactions",
|
||||
ui,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"new InventoryTransactionState(d.Inventory.Objects)",
|
||||
ui,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"new InventoryTransactionState",
|
||||
itemInteraction,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"_ownsTransactions",
|
||||
itemInteraction,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
"OnShortcuts: _domain.Inventory.Shortcuts.Replace",
|
||||
"InventoryTransactionState transactions,",
|
||||
itemInteraction,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
"OnShortcuts: _domain.Inventory.Shortcuts.Load",
|
||||
session,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
|
|
@ -64,6 +83,73 @@ public sealed class RuntimeInventoryOwnershipTests
|
|||
"\"runtime entity/object lifetime\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToolbarBorrowsRuntimeShortcutManagerWithoutAProviderOrMirror()
|
||||
{
|
||||
string toolbar = ReadSource(
|
||||
"UI",
|
||||
"Layout",
|
||||
"ToolbarController.cs");
|
||||
string ui = ReadSource(
|
||||
"Composition",
|
||||
"InteractionRetainedUiComposition.cs");
|
||||
|
||||
Assert.Contains(
|
||||
"private readonly ShortcutStore _store;",
|
||||
toolbar,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
"_store = shortcuts ?? throw",
|
||||
toolbar,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"new ShortcutStore()",
|
||||
toolbar,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"Func<IReadOnlyList<ShortcutEntry>>",
|
||||
toolbar,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"_storeLoaded",
|
||||
toolbar,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains(
|
||||
"d.Inventory.Shortcuts,",
|
||||
ui,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpellUiDoesNotApplyASecondLocalCommandMutation()
|
||||
{
|
||||
string spellbook = ReadSource(
|
||||
"UI",
|
||||
"Layout",
|
||||
"SpellbookWindowController.cs");
|
||||
string spellcasting = ReadSource(
|
||||
"UI",
|
||||
"Layout",
|
||||
"SpellcastingUiController.cs");
|
||||
|
||||
Assert.DoesNotContain(
|
||||
"_spellbook.SetSpellbookFilters(filters);",
|
||||
spellbook,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"_spellbook.SetDesiredComponent(componentId, parsed);",
|
||||
spellbook,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"_spellbook.SetFavorite(",
|
||||
spellcasting,
|
||||
StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(
|
||||
"_spellbook.RemoveFavorite(",
|
||||
spellcasting,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string ReadSource(params string[] relative)
|
||||
{
|
||||
string root = FindRepositoryRoot();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue