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
|
|
@ -3,6 +3,7 @@ using AcDream.App.Input;
|
|||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Net;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions;
|
||||
|
|
@ -20,7 +21,11 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
IRuntimeCombatCommands,
|
||||
IRuntimeMovementCommands,
|
||||
IRuntimeChatCommands,
|
||||
IRuntimePortalCommands
|
||||
IRuntimePortalCommands,
|
||||
IRuntimeInventoryStateCommands,
|
||||
IRuntimeSpellbookCommands,
|
||||
IRuntimeCharacterCommands,
|
||||
IRuntimeSocialCommands
|
||||
{
|
||||
private readonly LiveSessionController _session;
|
||||
private readonly LiveSessionHost _sessionHost;
|
||||
|
|
@ -276,6 +281,340 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
return Result(RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeShortcutCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (command.Index < 0
|
||||
|| (command.ObjectId == 0u && command.SpellId == 0u))
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.ObjectId);
|
||||
}
|
||||
|
||||
_commands.Publish(new AddShortcutRuntimeCmd(new ShortcutEntry(
|
||||
command.Index,
|
||||
command.ObjectId,
|
||||
command.SpellId)));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
command.ObjectId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult RemoveShortcut(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int index)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (index < 0)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected);
|
||||
}
|
||||
|
||||
_commands.Publish(new RemoveShortcutRuntimeCmd((uint)index));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.InventoryState,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AddFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
int position,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if ((uint)tabIndex >= 8u
|
||||
|| position < 0
|
||||
|| spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new AddFavoriteRuntimeCmd(
|
||||
spellId,
|
||||
position,
|
||||
tabIndex));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult RemoveFavorite(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
int tabIndex,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if ((uint)tabIndex >= 8u || spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new RemoveFavoriteRuntimeCmd(spellId, tabIndex));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetFilter(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint filters)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new SetSpellbookFilterRuntimeCmd(filters));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult ForgetSpell(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint spellId)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (spellId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
spellId);
|
||||
}
|
||||
|
||||
_commands.Publish(new ForgetSpellRuntimeCmd(spellId));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
spellId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetDesiredComponent(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint componentId,
|
||||
uint amount)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (componentId == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
componentId);
|
||||
}
|
||||
|
||||
_commands.Publish(new SetDesiredComponentRuntimeCmd(
|
||||
componentId,
|
||||
amount));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
componentId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult ClearDesiredComponents(
|
||||
RuntimeGenerationToken expectedGeneration)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new ClearDesiredComponentsRuntimeCmd());
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Spellbook,
|
||||
operation: 5,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Advance(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeAdvancementCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (command.StatId == 0u
|
||||
|| command.Cost == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
switch (command.Kind)
|
||||
{
|
||||
case RuntimeAdvancementKind.Attribute:
|
||||
_commands.Publish(new RaiseAttributeRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.Vital:
|
||||
_commands.Publish(new RaiseVitalRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.Skill:
|
||||
_commands.Publish(new RaiseSkillRuntimeCmd(
|
||||
command.StatId,
|
||||
command.Cost));
|
||||
break;
|
||||
case RuntimeAdvancementKind.TrainSkill
|
||||
when command.Cost <= uint.MaxValue:
|
||||
_commands.Publish(new TrainSkillRuntimeCmd(
|
||||
command.StatId,
|
||||
(uint)command.Cost));
|
||||
break;
|
||||
default:
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
(int)command.Kind,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
command.StatId);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetOptions1(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint options)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new SetCharacterOptionsRuntimeCmd(options));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Character,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeFriendCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
RuntimeCommandStatus status = RuntimeCommandStatus.Accepted;
|
||||
switch (command.Kind)
|
||||
{
|
||||
case RuntimeFriendCommandKind.Add
|
||||
when !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new AddFriendRuntimeCmd(command.Name));
|
||||
break;
|
||||
case RuntimeFriendCommandKind.Remove
|
||||
when command.CharacterId != 0u:
|
||||
_commands.Publish(new RemoveFriendRuntimeCmd(
|
||||
command.CharacterId));
|
||||
break;
|
||||
case RuntimeFriendCommandKind.Clear:
|
||||
_commands.Publish(new ClearFriendsRuntimeCmd());
|
||||
break;
|
||||
case RuntimeFriendCommandKind.RequestLegacyList:
|
||||
_commands.Publish(new RequestLegacyFriendsRuntimeCmd());
|
||||
break;
|
||||
default:
|
||||
status = RuntimeCommandStatus.Rejected;
|
||||
break;
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Social,
|
||||
(int)command.Kind,
|
||||
status,
|
||||
command.CharacterId,
|
||||
command.Name);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Execute(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
in RuntimeSquelchCommand command)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
RuntimeCommandStatus status = RuntimeCommandStatus.Accepted;
|
||||
switch (command.Scope)
|
||||
{
|
||||
case RuntimeSquelchScope.Character
|
||||
when command.CharacterId != 0u
|
||||
&& !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new ModifyCharacterSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.CharacterId,
|
||||
command.Name,
|
||||
command.MessageType));
|
||||
break;
|
||||
case RuntimeSquelchScope.Account
|
||||
when !string.IsNullOrWhiteSpace(command.Name):
|
||||
_commands.Publish(new ModifyAccountSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.Name));
|
||||
break;
|
||||
case RuntimeSquelchScope.Global:
|
||||
_commands.Publish(new ModifyGlobalSquelchRuntimeCmd(
|
||||
command.Add,
|
||||
command.MessageType));
|
||||
break;
|
||||
default:
|
||||
status = RuntimeCommandStatus.Rejected;
|
||||
break;
|
||||
}
|
||||
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Social,
|
||||
operation: 4 + (int)command.Scope,
|
||||
status,
|
||||
command.CharacterId,
|
||||
command.Name);
|
||||
}
|
||||
|
||||
private RuntimeCommandStatus Validate(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool requireWorld)
|
||||
|
|
@ -294,6 +633,17 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
uint objectId = 0u) =>
|
||||
new(status, _view.Generation, objectId);
|
||||
|
||||
private RuntimeCommandResult EmitResult(
|
||||
RuntimeCommandDomain domain,
|
||||
int operation,
|
||||
RuntimeCommandStatus status,
|
||||
uint objectId = 0u,
|
||||
string? text = null)
|
||||
{
|
||||
_events.EmitCommand(domain, operation, status, objectId, text);
|
||||
return Result(status, objectId);
|
||||
}
|
||||
|
||||
private RuntimeSessionStartResult RejectedStart(RuntimeCommandStatus status) =>
|
||||
new(
|
||||
status == RuntimeCommandStatus.StaleGeneration
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue