refactor(runtime): expose canonical gameplay state
Move character options and movement skills into the Runtime-owned character graph, expose borrowed inventory, character, and social views, and route retained UI state commands through generation-gated typed Runtime contracts. Preserve the existing synchronous wire path while deleting the App-owned option and skill mirrors and extending normalized parity checkpoints. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
9d0d9b07e0
commit
dcb61efb5a
34 changed files with 2076 additions and 103 deletions
|
|
@ -10,6 +10,7 @@ using AcDream.App.UI.Layout;
|
|||
using AcDream.App.World;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.UI.Abstractions;
|
||||
|
||||
namespace AcDream.App.Tests.Composition;
|
||||
|
|
@ -67,6 +68,44 @@ public sealed class InteractionUiRuntimeSourcesTests
|
|||
Assert.Throws<ObjectDisposedException>(() => source.Bind(query, interactions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GameRuntimeCommandsCaptureTheExactCurrentGeneration()
|
||||
{
|
||||
var source = new DeferredGameRuntimeStateCommands();
|
||||
var first = new RuntimeTarget(new RuntimeGenerationToken(7));
|
||||
var second = new RuntimeTarget(new RuntimeGenerationToken(9));
|
||||
|
||||
Assert.False(source.IsInWorld);
|
||||
Assert.Equal(
|
||||
RuntimeCommandStatus.Inactive,
|
||||
source.Advance(RuntimeAdvancementKind.Skill, 6u, 100u).Status);
|
||||
|
||||
IDisposable stale = source.Bind(first, first);
|
||||
Assert.True(source.IsInWorld);
|
||||
Assert.True(source.Advance(
|
||||
RuntimeAdvancementKind.Skill,
|
||||
6u,
|
||||
100u).Accepted);
|
||||
Assert.Equal(new RuntimeGenerationToken(7), first.LastGeneration);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
source.Bind(second, second));
|
||||
|
||||
stale.Dispose();
|
||||
using IDisposable current = source.Bind(second, second);
|
||||
stale.Dispose();
|
||||
Assert.True(source.AddFavorite(0, 2, 42u).Accepted);
|
||||
Assert.Equal(new RuntimeGenerationToken(9), second.LastGeneration);
|
||||
Assert.Equal(
|
||||
RuntimeCommandStatus.Rejected,
|
||||
source.RemoveShortcut(uint.MaxValue).Status);
|
||||
|
||||
source.Deactivate();
|
||||
Assert.Equal(
|
||||
RuntimeCommandStatus.Inactive,
|
||||
source.RemoveShortcut(3u).Status);
|
||||
Assert.Throws<ObjectDisposedException>(() => source.Bind(first, first));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadarNeverCachesAnUnboundBootstrapSnapshot()
|
||||
{
|
||||
|
|
@ -172,6 +211,109 @@ public sealed class InteractionUiRuntimeSourcesTests
|
|||
public ICommandBus Commands { get; } = new LiveCommandBus();
|
||||
}
|
||||
|
||||
private sealed class RuntimeTarget
|
||||
: IGameRuntimeView,
|
||||
IGameRuntimeCommands,
|
||||
IRuntimeInventoryStateCommands,
|
||||
IRuntimeSpellbookCommands,
|
||||
IRuntimeCharacterCommands
|
||||
{
|
||||
public RuntimeTarget(RuntimeGenerationToken generation)
|
||||
{
|
||||
Generation = generation;
|
||||
}
|
||||
|
||||
public RuntimeGenerationToken LastGeneration { get; private set; }
|
||||
public RuntimeGenerationToken Generation { get; }
|
||||
public RuntimeLifecycleSnapshot Lifecycle =>
|
||||
new(Generation, RuntimeLifecycleState.InWorld, 1u, true);
|
||||
public IGameRuntimeClock Clock => null!;
|
||||
public IRuntimeEntityView Entities => null!;
|
||||
public IRuntimeInventoryView Inventory => null!;
|
||||
public IRuntimeInventoryStateView InventoryState => null!;
|
||||
public IRuntimeCharacterView Character => null!;
|
||||
public IRuntimeSocialView Social => null!;
|
||||
public IRuntimeChatView Chat => null!;
|
||||
public IRuntimeMovementView Movement => null!;
|
||||
public IRuntimePortalView Portal => null!;
|
||||
public IRuntimeSessionCommands Session => null!;
|
||||
public IRuntimeSelectionCommands Selection => null!;
|
||||
public IRuntimeCombatCommands Combat => null!;
|
||||
IRuntimeMovementCommands IGameRuntimeCommands.Movement => null!;
|
||||
IRuntimeChatCommands IGameRuntimeCommands.Chat => null!;
|
||||
IRuntimePortalCommands IGameRuntimeCommands.Portal => null!;
|
||||
IRuntimeInventoryStateCommands IGameRuntimeCommands.InventoryState => this;
|
||||
IRuntimeSpellbookCommands IGameRuntimeCommands.Spellbook => this;
|
||||
IRuntimeCharacterCommands IGameRuntimeCommands.Character => this;
|
||||
IRuntimeSocialCommands IGameRuntimeCommands.Social => null!;
|
||||
|
||||
public RuntimeStateCheckpoint CaptureCheckpoint() => default;
|
||||
|
||||
public RuntimeCommandResult AddShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeShortcutCommand command) =>
|
||||
Accepted(expectedGeneration, command.ObjectId);
|
||||
|
||||
public RuntimeCommandResult RemoveShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int index) =>
|
||||
Accepted(expectedGeneration);
|
||||
|
||||
public RuntimeCommandResult AddFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
int position,
|
||||
uint spellId) =>
|
||||
Accepted(expectedGeneration, spellId);
|
||||
|
||||
public RuntimeCommandResult RemoveFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
uint spellId) =>
|
||||
Accepted(expectedGeneration, spellId);
|
||||
|
||||
public RuntimeCommandResult SetFilter(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint filters) =>
|
||||
Accepted(expectedGeneration);
|
||||
|
||||
public RuntimeCommandResult ForgetSpell(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint spellId) =>
|
||||
Accepted(expectedGeneration, spellId);
|
||||
|
||||
public RuntimeCommandResult SetDesiredComponent(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint componentId,
|
||||
uint amount) =>
|
||||
Accepted(expectedGeneration, componentId);
|
||||
|
||||
public RuntimeCommandResult ClearDesiredComponents(
|
||||
RuntimeGenerationToken expectedGeneration) =>
|
||||
Accepted(expectedGeneration);
|
||||
|
||||
public RuntimeCommandResult Advance(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeAdvancementCommand command) =>
|
||||
Accepted(expectedGeneration, command.StatId);
|
||||
|
||||
public RuntimeCommandResult SetOptions1(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint options) =>
|
||||
Accepted(expectedGeneration);
|
||||
|
||||
private RuntimeCommandResult Accepted(
|
||||
RuntimeGenerationToken generation,
|
||||
uint objectId = 0u)
|
||||
{
|
||||
LastGeneration = generation;
|
||||
return new RuntimeCommandResult(
|
||||
RuntimeCommandStatus.Accepted,
|
||||
generation,
|
||||
objectId);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SelectionQuery : IRetainedUiSelectionQuery
|
||||
{
|
||||
public bool ShouldShowHealth(uint serverGuid) => true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue