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:
parent
e321410770
commit
c9d25ade50
19 changed files with 684 additions and 117 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using AcDream.App.Settings;
|
|||
using AcDream.App.World;
|
||||
using AcDream.Content;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.Runtime.Session;
|
||||
using DatReaderWriter;
|
||||
using Silk.NET.Input;
|
||||
|
|
@ -323,17 +324,18 @@ public sealed class GameWindow :
|
|||
private AcDream.App.World.LiveEntityRuntime? _liveEntities;
|
||||
private AcDream.App.World.LiveEntityLivenessController? _liveEntityLiveness;
|
||||
|
||||
// Phase F.1-H.1 — client-side state classes fed by GameEventWiring.
|
||||
// Exposed publicly so plugins + UI panels can bind directly.
|
||||
public readonly AcDream.Core.Chat.ChatLog Chat = new();
|
||||
// Phase I.6 — runtime state for retail's TurbineChat (0xF7DE) global
|
||||
// chat rooms. Empty/disabled until the server fires
|
||||
// SetTurbineChatChannels (0x0295) shortly after EnterWorld.
|
||||
public readonly AcDream.Core.Chat.TurbineChatState TurbineChat = new();
|
||||
// J4.1: Runtime owns communication/social state. App, UI, plugins, and
|
||||
// live-session routing borrow these exact instances.
|
||||
private readonly RuntimeCommunicationState _runtimeCommunication = new();
|
||||
public AcDream.Core.Chat.ChatLog Chat => _runtimeCommunication.Chat;
|
||||
public AcDream.Core.Chat.TurbineChatState TurbineChat =>
|
||||
_runtimeCommunication.TurbineChat;
|
||||
public AcDream.Core.Social.FriendsState Friends =>
|
||||
_runtimeCommunication.Friends;
|
||||
public AcDream.Core.Social.SquelchState Squelch =>
|
||||
_runtimeCommunication.Squelch;
|
||||
public readonly AcDream.Core.Combat.CombatState Combat = new();
|
||||
public readonly AcDream.Core.Items.ItemManaState ItemMana = new();
|
||||
public readonly AcDream.Core.Social.FriendsState Friends = new();
|
||||
public readonly AcDream.Core.Social.SquelchState Squelch = new();
|
||||
private readonly DesiredComponentSnapshotState _desiredComponents = new();
|
||||
public IReadOnlyList<(uint Id, uint Amount)> DesiredComponents =>
|
||||
_desiredComponents.Items;
|
||||
|
|
@ -1208,7 +1210,7 @@ public sealed class GameWindow :
|
|||
hostInputCamera.CameraController,
|
||||
contentEffectsAudio.Audio?.Engine),
|
||||
_hostQuiescence,
|
||||
Chat,
|
||||
_runtimeCommunication,
|
||||
Combat,
|
||||
LocalPlayer,
|
||||
optionalDevTools,
|
||||
|
|
@ -1264,7 +1266,7 @@ public sealed class GameWindow :
|
|||
_runtimeEntityObjects.Objects,
|
||||
contentEffectsAudio.MagicCatalog,
|
||||
SpellBook,
|
||||
Chat,
|
||||
_runtimeCommunication,
|
||||
LocalPlayer,
|
||||
ItemMana,
|
||||
_stackSplitQuantity,
|
||||
|
|
@ -1409,13 +1411,10 @@ public sealed class GameWindow :
|
|||
_combatAttackOperations,
|
||||
Combat,
|
||||
_combatFeedback,
|
||||
Chat,
|
||||
_runtimeCommunication,
|
||||
LocalPlayer,
|
||||
SpellBook,
|
||||
ItemMana,
|
||||
Friends,
|
||||
Squelch,
|
||||
TurbineChat,
|
||||
_externalContainers,
|
||||
_portalTunnelFallback,
|
||||
Console.WriteLine),
|
||||
|
|
@ -1591,6 +1590,7 @@ public sealed class GameWindow :
|
|||
_equippedChildRenderer,
|
||||
_liveEntities,
|
||||
_runtimeEntityObjects,
|
||||
_runtimeCommunication,
|
||||
_renderSceneShadow,
|
||||
_livePresentationBindings,
|
||||
_entityEffectAdvance,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ using AcDream.Content;
|
|||
using AcDream.Core.Audio;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions.Input;
|
||||
using DatReaderWriter;
|
||||
|
|
@ -83,6 +84,7 @@ internal sealed record LiveShutdownRoots(
|
|||
EquippedChildRenderController? EquippedChildren,
|
||||
LiveEntityRuntime? LiveEntities,
|
||||
RuntimeEntityObjectLifetime EntityObjects,
|
||||
RuntimeCommunicationState Communication,
|
||||
RenderSceneShadowRuntime? RenderSceneShadow,
|
||||
LivePresentationRuntimeBindings? PresentationBindings,
|
||||
DeferredEntityEffectAdvanceSource EffectAdvance,
|
||||
|
|
@ -439,6 +441,10 @@ internal static class GameWindowShutdownManifest
|
|||
Hard("sky", () => render.Sky?.Dispose()),
|
||||
Hard("particles", () => render.Particles?.Dispose()),
|
||||
]),
|
||||
new ResourceShutdownStage("runtime communication state",
|
||||
[
|
||||
Hard("communication owner", live.Communication.Dispose),
|
||||
]),
|
||||
new ResourceShutdownStage("shared texture owners",
|
||||
[
|
||||
Hard("sampler cache", () => render.Samplers?.Dispose()),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using AcDream.Core.Chat;
|
|||
using AcDream.Core.Selection;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.Runtime.Session;
|
||||
using AcDream.UI.Abstractions;
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
LocalPlayerIdentityState playerIdentity,
|
||||
LiveEntityRuntime entities,
|
||||
RuntimeEntityObjectLifetime entityObjects,
|
||||
ChatLog chat,
|
||||
RuntimeCommunicationState communication,
|
||||
ILocalPlayerControllerSource playerController,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
IGameRuntimeClock clock,
|
||||
|
|
@ -50,7 +51,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
playerIdentity,
|
||||
entities,
|
||||
entityObjects,
|
||||
chat,
|
||||
communication,
|
||||
playerController,
|
||||
worldReveal,
|
||||
clock);
|
||||
|
|
@ -60,7 +61,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
|||
_events = new CurrentGameRuntimeEventAdapter(
|
||||
_view,
|
||||
entityObjects,
|
||||
chat);
|
||||
communication);
|
||||
_commands = new CurrentGameRuntimeCommandAdapter(
|
||||
session,
|
||||
sessionHost,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using AcDream.Core.Chat;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.App.Runtime;
|
||||
|
||||
|
|
@ -25,14 +25,16 @@ internal sealed class CurrentGameRuntimeEventAdapter
|
|||
: IRuntimeEventSource,
|
||||
ICurrentGameRuntimeEventSink,
|
||||
IRuntimeEntityObjectObserver,
|
||||
IRuntimeCommunicationObserver,
|
||||
IDisposable
|
||||
{
|
||||
private readonly CurrentGameRuntimeViewAdapter _view;
|
||||
private readonly RuntimeEntityObjectLifetime _entityObjects;
|
||||
private readonly ChatLog _chat;
|
||||
private readonly RuntimeCommunicationState _communication;
|
||||
private readonly object _observerGate = new();
|
||||
private IRuntimeEventObserver[] _observers = [];
|
||||
private IDisposable? _entityObjectSubscription;
|
||||
private IDisposable? _communicationSubscription;
|
||||
private bool _ownerEventsAttached;
|
||||
private bool _disposed;
|
||||
|
||||
|
|
@ -42,12 +44,13 @@ internal sealed class CurrentGameRuntimeEventAdapter
|
|||
public CurrentGameRuntimeEventAdapter(
|
||||
CurrentGameRuntimeViewAdapter view,
|
||||
RuntimeEntityObjectLifetime entityObjects,
|
||||
ChatLog chat)
|
||||
RuntimeCommunicationState communication)
|
||||
{
|
||||
_view = view ?? throw new ArgumentNullException(nameof(view));
|
||||
_entityObjects = entityObjects
|
||||
?? throw new ArgumentNullException(nameof(entityObjects));
|
||||
_chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
_communication = communication
|
||||
?? throw new ArgumentNullException(nameof(communication));
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IRuntimeEventObserver observer)
|
||||
|
|
@ -175,7 +178,7 @@ internal sealed class CurrentGameRuntimeEventAdapter
|
|||
private void AttachOwnerEvents()
|
||||
{
|
||||
_entityObjectSubscription = _entityObjects.Events.Subscribe(this);
|
||||
_chat.EntryAppended += OnChatEntry;
|
||||
_communicationSubscription = _communication.Events.Subscribe(this);
|
||||
_ownerEventsAttached = true;
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +186,8 @@ internal sealed class CurrentGameRuntimeEventAdapter
|
|||
{
|
||||
if (!_ownerEventsAttached)
|
||||
return;
|
||||
_chat.EntryAppended -= OnChatEntry;
|
||||
_communicationSubscription?.Dispose();
|
||||
_communicationSubscription = null;
|
||||
_entityObjectSubscription?.Dispose();
|
||||
_entityObjectSubscription = null;
|
||||
_ownerEventsAttached = false;
|
||||
|
|
@ -223,19 +227,13 @@ internal sealed class CurrentGameRuntimeEventAdapter
|
|||
}
|
||||
}
|
||||
|
||||
private void OnChatEntry(ChatEntry entry)
|
||||
public void OnChat(in RuntimeCommunicationEvent committed)
|
||||
{
|
||||
if (!TryObservers(out IRuntimeEventObserver[] observers))
|
||||
return;
|
||||
var delta = new RuntimeChatDelta(
|
||||
NextStamp(),
|
||||
new RuntimeChatEntry(
|
||||
_chat.Revision,
|
||||
entry.SenderGuid,
|
||||
(int)entry.Kind,
|
||||
entry.Sender,
|
||||
entry.Text,
|
||||
entry.ChannelName));
|
||||
committed.Entry);
|
||||
foreach (IRuntimeEventObserver observer in observers)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using AcDream.Core.Chat;
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Entities;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using AcDream.Runtime.Session;
|
||||
|
||||
namespace AcDream.App.Runtime;
|
||||
|
|
@ -20,11 +21,10 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
private readonly LocalPlayerIdentityState _playerIdentity;
|
||||
private readonly LiveEntityRuntime _entities;
|
||||
private readonly ClientObjectTable _objects;
|
||||
private readonly ChatLog _chat;
|
||||
private readonly IGameRuntimeClock _clock;
|
||||
private readonly IRuntimeEntityView _entityView;
|
||||
private readonly IRuntimeInventoryView _inventoryView;
|
||||
private readonly ChatView _chatView;
|
||||
private readonly IRuntimeChatView _chatView;
|
||||
private readonly MovementView _movementView;
|
||||
private readonly PortalView _portalView;
|
||||
private bool _active = true;
|
||||
|
|
@ -34,7 +34,7 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
LocalPlayerIdentityState playerIdentity,
|
||||
LiveEntityRuntime entities,
|
||||
RuntimeEntityObjectLifetime entityObjects,
|
||||
ChatLog chat,
|
||||
RuntimeCommunicationState communication,
|
||||
ILocalPlayerControllerSource playerController,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
IGameRuntimeClock clock)
|
||||
|
|
@ -45,12 +45,12 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
_entities = entities ?? throw new ArgumentNullException(nameof(entities));
|
||||
ArgumentNullException.ThrowIfNull(entityObjects);
|
||||
_objects = entityObjects.Objects;
|
||||
_chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
ArgumentNullException.ThrowIfNull(communication);
|
||||
_clock = clock ?? throw new ArgumentNullException(nameof(clock));
|
||||
|
||||
_entityView = entityObjects.EntityView;
|
||||
_inventoryView = entityObjects.InventoryView;
|
||||
_chatView = new ChatView(_chat);
|
||||
_chatView = communication.View;
|
||||
_movementView = new MovementView(
|
||||
playerController
|
||||
?? throw new ArgumentNullException(nameof(playerController)),
|
||||
|
|
@ -103,19 +103,13 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
|||
_entities.MaterializedCount,
|
||||
_objects.ObjectCount,
|
||||
_objects.ContainerCount,
|
||||
_chat.Revision,
|
||||
_chat.Count,
|
||||
_chatView.Revision,
|
||||
_chatView.Count,
|
||||
_movementView.Snapshot,
|
||||
_portalView.Snapshot);
|
||||
|
||||
internal void Deactivate() => _active = false;
|
||||
|
||||
private sealed class ChatView(ChatLog owner) : IRuntimeChatView
|
||||
{
|
||||
public long Revision => owner.Revision;
|
||||
public int Count => owner.Count;
|
||||
}
|
||||
|
||||
private sealed class MovementView(
|
||||
ILocalPlayerControllerSource owner,
|
||||
IGameRuntimeClock clock)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue