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

@ -18,6 +18,7 @@ using AcDream.Core.Items;
using AcDream.Core.Player;
using AcDream.Core.Selection;
using AcDream.Core.Spells;
using AcDream.Runtime.Gameplay;
using AcDream.UI.Abstractions.Input;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Vitals;
@ -49,7 +50,7 @@ internal sealed record InteractionRetainedUiDependencies(
ClientObjectTable Objects,
MagicCatalog MagicCatalog,
Spellbook Spellbook,
ChatLog Chat,
RuntimeCommunicationState Communication,
LocalPlayerState LocalPlayer,
ItemManaState ItemMana,
StackSplitQuantityState StackSplitQuantity,
@ -317,7 +318,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
session.CurrentSession?.SendStackableSplitTo3D(item, amount),
selectedObjectId: () => d.Selection.SelectedObjectId ?? 0u,
stackSplitQuantity: d.StackSplitQuantity,
systemMessage: text => d.Chat.OnSystemMessage(text, 0x1Au),
systemMessage:
text => d.Communication.Chat.OnSystemMessage(text, 0x1Au),
sendPutItemInContainer: (item, container, placement) =>
session.CurrentSession?.SendPutItemInContainer(
item,
@ -400,7 +402,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
late.Session.CurrentSession?.SendCastUntargetedSpell(spellId),
sendTargeted: (target, spellId) =>
late.Session.CurrentSession?.SendCastTargetedSpell(target, spellId),
displayMessage: text => d.Chat.OnSystemMessage(text, 0x1Au),
displayMessage:
text => d.Communication.Chat.OnSystemMessage(text, 0x1Au),
incrementBusy: itemInteraction.IncrementBusyCount,
canSend: () => late.Session.IsInWorld);
checkpoint(InteractionRetainedUiCompositionPoint.MagicRuntimeCreated);
@ -477,7 +480,10 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
host.Root.Width = d.Window.Size.X;
host.Root.Height = d.Window.Size.Y;
var chat = new ChatVM(d.Chat, displayLimit: 200);
var chat = new ChatVM(
d.Communication.Chat,
displayLimit: 200,
commandTargets: d.Communication.CommandTargets);
AcDream.UI.Abstractions.Panels.Settings.SettingsStore? layoutStore =
d.Settings.LayoutStore;
RetailUiPersistenceBindings? persistence = layoutStore is null
@ -663,7 +669,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
late.Session.CurrentSession?.SendSetInscription(
item,
inscription),
text => d.Chat.OnSystemMessage(text, 0x1Au)),
text =>
d.Communication.Chat.OnSystemMessage(text, 0x1Au)),
StackSplitQuantity: d.StackSplitQuantity,
Plugins: d.UiRegistry,
Persistence: persistence,

View file

@ -27,6 +27,7 @@ using AcDream.Core.Social;
using AcDream.Core.Spells;
using AcDream.Core.World;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Gameplay;
using AcDream.Runtime.Session;
using Silk.NET.Windowing;
@ -84,13 +85,10 @@ internal sealed record SessionPlayerDependencies(
CombatAttackOperationsSlot CombatAttackOperations,
CombatState Combat,
CombatFeedbackSlot CombatFeedback,
ChatLog Chat,
RuntimeCommunicationState Communication,
LocalPlayerState LocalPlayer,
Spellbook Spellbook,
ItemManaState ItemMana,
FriendsState Friends,
SquelchState Squelch,
TurbineChatState TurbineChat,
ExternalContainerState ExternalContainers,
TransferableResourceSlot<PortalTunnelPresentation> PortalTunnelFallback,
Action<string> Log);
@ -798,8 +796,6 @@ internal sealed class SessionPlayerCompositionPhase
AcDream.UI.Abstractions.Panels.Vitals.VitalsVM? vitals =
d.SettingsDevTools.DevTools?.Vitals
?? interaction.RetainedUi?.Vitals;
AcDream.UI.Abstractions.Panels.Chat.ChatVM? retailChat =
interaction.RetainedUi?.Chat;
var sessionRuntimeFactory = new LiveSessionRuntimeFactory(
new LiveSessionPlayerRuntime(
d.PlayerIdentity,
@ -815,15 +811,11 @@ internal sealed class SessionPlayerCompositionPhase
d.Spellbook,
d.Combat,
d.ItemMana,
d.Friends,
d.Squelch,
d.TurbineChat,
d.ExternalContainers,
d.Chat),
d.Communication),
new LiveSessionUiRuntime(
interaction.RetainedUi?.Runtime,
vitals,
retailChat,
interaction.RetainedUi?.CharacterSheet,
interaction.RetainedUi?.Magic,
live.PaperdollPresenter),
@ -881,7 +873,7 @@ internal sealed class SessionPlayerCompositionPhase
interaction.ItemInteraction),
d.Log,
debugToast,
text => d.Chat.OnSystemMessage(text, 0x1Au));
text => d.Communication.Chat.OnSystemMessage(text, 0x1Au));
bindings.Adopt(
"live combat-mode commands",
d.CombatModeCommands.BindOwned(combatCommand));
@ -892,7 +884,7 @@ internal sealed class SessionPlayerCompositionPhase
d.PlayerIdentity,
live.LiveEntities,
d.EntityObjects,
d.Chat,
d.Communication,
d.PlayerController,
worldReveal,
d.UpdateClock,

View file

@ -6,6 +6,7 @@ using AcDream.App.Settings;
using AcDream.Core.Chat;
using AcDream.Core.Combat;
using AcDream.Core.Player;
using AcDream.Runtime.Gameplay;
using AcDream.UI.Abstractions.Input;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Debug;
@ -34,7 +35,7 @@ internal sealed record SettingsDevToolsDependencies(
RuntimeSettingsController Settings,
IRuntimeSettingsStartupTarget StartupTarget,
HostQuiescenceGate HostQuiescence,
ChatLog Chat,
RuntimeCommunicationState Communication,
CombatState Combat,
LocalPlayerState LocalPlayer,
SettingsDevToolsOptionalDependencies? DevTools,
@ -418,7 +419,10 @@ internal sealed class SettingsDevToolsCompositionPhase :
var chatLease = scope.Acquire(
"developer chat view model",
() => new ChatVM(_dependencies.Chat)
() => new ChatVM(
_dependencies.Communication.Chat,
commandTargets:
_dependencies.Communication.CommandTargets)
{
FpsProvider = () => optional.Facts.Fps,
PositionProvider = () => optional.Facts.PlayerPosition,