refactor(runtime): own communication and social state

Construct chat history, negotiated channels, friends, squelch, and reply targets in one presentation-independent Runtime owner. Make live routing, retained UI, devtools, and current-runtime projections borrow the exact instances, preserve reconnect reset semantics, and publish failure-isolated reentrant-safe chat commits.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-26 07:50:16 +02:00
parent e321410770
commit c9d25ade50
19 changed files with 684 additions and 117 deletions

View file

@ -24,6 +24,7 @@ using AcDream.Core.Spells;
using AcDream.Core.World;
using AcDream.Content;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Gameplay;
using AcDream.Runtime.Session;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Vitals;
@ -47,16 +48,12 @@ internal sealed record LiveSessionDomainRuntime(
Spellbook Spellbook,
CombatState Combat,
ItemManaState ItemMana,
FriendsState Friends,
SquelchState Squelch,
TurbineChatState TurbineChat,
ExternalContainerState ExternalContainers,
ChatLog Chat);
RuntimeCommunicationState Communication);
internal sealed record LiveSessionUiRuntime(
RetailUiRuntime? RetailUi,
VitalsVM? Vitals,
ChatVM? RetailChat,
CharacterSheetProvider? CharacterSheet,
MagicRuntime? Magic,
PaperdollFramePresenter? Paperdoll);
@ -140,7 +137,7 @@ internal sealed class LiveSessionRuntimeFactory
Selection: new(
SetPlayerIdentity: id => _player.Identity.ServerGuid = id,
SetVitalsIdentity: id => _ui.Vitals?.SetLocalPlayerGuid(id),
SetChatIdentity: _domain.Chat.SetLocalPlayerGuid,
SetChatIdentity: _domain.Communication.Chat.SetLocalPlayerGuid,
MarkPersistent: _world.WorldState.MarkPersistent,
SetVanishProbeIdentity: id => EntityVanishProbe.PlayerGuid = id,
ClearCombat: _domain.Combat.Clear),
@ -151,11 +148,11 @@ internal sealed class LiveSessionRuntimeFactory
LoadCharacterSettings: _interaction.Settings.LoadCharacterContext,
ArmPlayerModeAutoEntry: _interaction.PlayerModeAutoEntry.Arm),
Connecting: (host, port, user) =>
_domain.Chat.OnSystemMessage(
_domain.Communication.Chat.OnSystemMessage(
$"connecting to {host}:{port} as {user}",
chatType: 1),
Connected: () =>
_domain.Chat.OnSystemMessage(
_domain.Communication.Chat.OnSystemMessage(
"connected — character list received",
chatType: 1)),
connectOptions);
@ -167,7 +164,7 @@ internal sealed class LiveSessionRuntimeFactory
PlayerPresentation = ResetPlayerPresentation,
TeleportTransit = _world.Teleport.ResetSession,
SessionDialogs = () => _ui.RetailUi?.ResetSessionTransientUi(),
ChatCommandTargets = () => _ui.RetailChat?.ResetSessionTargets(),
ChatCommandTargets = _domain.Communication.ResetCommandTargets,
SettingsCharacterContext =
_interaction.Settings.RestoreDefaultCharacterContext,
EquippedChildren = _world.EquippedChildren.Clear,
@ -181,9 +178,9 @@ internal sealed class LiveSessionRuntimeFactory
CombatState = _domain.Combat.Clear,
ItemMana = _domain.ItemMana.Clear,
LocalPlayer = _domain.LocalPlayer.Clear,
Friends = _domain.Friends.Clear,
Squelch = _domain.Squelch.Clear,
TurbineChat = _domain.TurbineChat.Reset,
Friends = _domain.Communication.ResetFriends,
Squelch = _domain.Communication.ResetSquelch,
TurbineChat = _domain.Communication.ResetNegotiatedChannels,
ParticleVisibility = _world.ParticleVisibility.Reset,
InboundEventFifo = _world.InboundEvents.Clear,
LiveLiveness = _world.Liveness.Clear,
@ -218,7 +215,7 @@ internal sealed class LiveSessionRuntimeFactory
// default comes from PlayerModule::PlayerModule @ 0x005D51F0.
_player.Identity.ServerGuid = 0u;
_ui.Vitals?.SetLocalPlayerGuid(0u);
_domain.Chat.ResetSessionIdentity();
_domain.Communication.ResetChatIdentity();
EntityVanishProbe.PlayerGuid = 0u;
_interaction.Settings.ResetActiveCharacterKey();
_player.CharacterOptions.Reset();
@ -254,10 +251,10 @@ internal sealed class LiveSessionRuntimeFactory
CreateInventoryBindings(),
CreateCharacterBindings(skillTable),
new LiveSocialSessionBindings(
_domain.Chat,
_domain.TurbineChat,
_domain.Friends,
_domain.Squelch));
_domain.Communication.Chat,
_domain.Communication.TurbineChat,
_domain.Communication.Friends,
_domain.Communication.Squelch));
}
private LiveInventorySessionBindings CreateInventoryBindings() => new(
@ -331,8 +328,10 @@ internal sealed class LiveSessionRuntimeFactory
ToggleUiLock: () =>
_interaction.Settings.SetUiLocked(
!_interaction.Settings.Gameplay.LockUI),
ShowSystemMessage: text => _domain.Chat.OnSystemMessage(text, 0u),
ShowWeenieError: code => _domain.Chat.OnWeenieError(code, null),
ShowSystemMessage:
text => _domain.Communication.Chat.OnSystemMessage(text, 0u),
ShowWeenieError:
code => _domain.Communication.Chat.OnWeenieError(code, null),
PlayerPublicWeenieBitfield: () =>
_domain.EntityObjects.Objects.Get(_player.Identity.ServerGuid)?
.PublicWeenieBitfield,
@ -346,7 +345,7 @@ internal sealed class LiveSessionRuntimeFactory
ShowConfirmation: (message, completed) =>
_ui.RetailUi?.ShowConfirmation(message, completed),
Suicide: session.SendSuicide,
ClearChat: _ => _domain.Chat.Clear(),
ClearChat: _ => _domain.Communication.Chat.Clear(),
SaveUi: name => _ui.RetailUi?.SaveNamedLayout(name),
LoadUi: name => _ui.RetailUi?.RestoreNamedLayout(name),
SaveAutoUi: () => _ui.RetailUi?.SaveLayout(),
@ -363,16 +362,17 @@ internal sealed class LiveSessionRuntimeFactory
ClearConsent: session.SendClearConsent,
RemoveConsent: session.SendRemoveConsent,
SendEmote: session.SendEmote,
_domain.Friends,
_domain.Communication.Friends,
AddFriend: session.SendAddFriend,
RemoveFriend: session.SendRemoveFriend,
ClearFriends: session.SendClearFriends,
RequestLegacyFriends: session.SendLegacyFriendsListRequest,
_domain.Squelch,
_domain.Communication.Squelch,
ModifyCharacterSquelch: session.SendModifyCharacterSquelch,
ModifyAccountSquelch: session.SendModifyAccountSquelch,
ModifyGlobalSquelch: session.SendModifyGlobalSquelch,
LastTeller: () => _ui.RetailChat?.LastIncomingTellSender,
LastTeller: () =>
_domain.Communication.CommandTargets.LastIncomingTellSender,
ClearDesiredComponents: () =>
{
session.SendClearDesiredComponents();
@ -380,8 +380,8 @@ internal sealed class LiveSessionRuntimeFactory
},
HasOpenVendor: () => false,
FillComponentBuyList: (_, _) => { }),
_domain.Chat,
_domain.TurbineChat,
_domain.Communication.Chat,
_domain.Communication.TurbineChat,
PlayerGuid: () => _player.Identity.ServerGuid,
SendTalk: session.SendTalk,
SendTell: session.SendTell,