refactor(runtime): own magic and player state

Move the coupled Spellbook and LocalPlayerState into one Runtime-owned character graph, route content, live-session, retained UI, reset, and shutdown through that exact owner, and delete the duplicate desired-component snapshot from inventory state. Preserve synchronous retail update and reset ordering while adding independent-instance and retryable-failure coverage.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-26 08:39:02 +02:00
parent d362172620
commit d02a12ceac
16 changed files with 418 additions and 96 deletions

View file

@ -42,8 +42,7 @@ internal sealed record LiveSessionPlayerRuntime(
internal sealed record LiveSessionDomainRuntime(
RuntimeEntityObjectLifetime EntityObjects,
LocalPlayerState LocalPlayer,
Spellbook Spellbook,
RuntimeCharacterState Character,
CombatState Combat,
RuntimeInventoryState Inventory,
RuntimeCommunicationState Communication);
@ -170,12 +169,12 @@ internal sealed class LiveSessionRuntimeFactory
InventoryTransactions = _domain.Inventory.ResetTransactions,
SelectionPresentation = _world.SelectionScene.Reset,
ObjectTable = _domain.EntityObjects.ClearObjects,
Spellbook = _domain.Spellbook.Clear,
Spellbook = _domain.Character.ResetSpellbook,
MagicRuntime = () => _ui.Magic?.Reset(),
CombatAttack = _interaction.CombatAttack.ResetSession,
CombatState = _domain.Combat.Clear,
ItemMana = _domain.Inventory.ResetItemMana,
LocalPlayer = _domain.LocalPlayer.Clear,
LocalPlayer = _domain.Character.ResetLocalPlayer,
Friends = _domain.Communication.ResetFriends,
Squelch = _domain.Communication.ResetSquelch,
TurbineChat = _domain.Communication.ResetNegotiatedChannels,
@ -256,7 +255,6 @@ internal sealed class LiveSessionRuntimeFactory
private LiveInventorySessionBindings CreateInventoryBindings() => new(
_domain.Inventory.Objects,
_domain.LocalPlayer,
PlayerGuid: () => _player.Identity.ServerGuid,
OnShortcuts: _domain.Inventory.Shortcuts.Replace,
OnUseDone: error =>
@ -265,7 +263,6 @@ internal sealed class LiveSessionRuntimeFactory
_domain.Inventory.Transactions.CompleteUse(error);
},
_domain.Inventory.ItemMana,
OnDesiredComponents: _domain.Inventory.DesiredComponents.Replace,
ExternalContainers: _domain.Inventory.ExternalContainers,
OnAppraisal: appraisal =>
{
@ -281,7 +278,7 @@ internal sealed class LiveSessionRuntimeFactory
var skillCreditResolver = new LiveSkillCreditResolver(skillTable);
return new LiveCharacterSessionBindings(
_domain.Combat,
_domain.Spellbook,
_domain.Character,
ResolveSkillFormulaBonus: skillCreditResolver.Resolve,
OnSkillsUpdated: (runSkill, jumpSkill) =>
{
@ -337,7 +334,7 @@ internal sealed class LiveSessionRuntimeFactory
?? "unknown",
CurrentPosition: () => _player.Controller.Controller?.CellPosition,
LastOutsideCorpsePosition: () =>
_domain.LocalPlayer.GetPosition(0x0Eu),
_domain.Character.LocalPlayer.GetPosition(0x0Eu),
ShowConfirmation: (message, completed) =>
_ui.RetailUi?.ShowConfirmation(message, completed),
Suicide: session.SendSuicide,
@ -372,7 +369,6 @@ internal sealed class LiveSessionRuntimeFactory
ClearDesiredComponents: () =>
{
session.SendClearDesiredComponents();
_domain.Inventory.DesiredComponents.Clear();
},
HasOpenVendor: () => false,
FillComponentBuyList: (_, _) => { }),