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
84
src/AcDream.Core/Chat/ChatCommandTargetState.cs
Normal file
84
src/AcDream.Core/Chat/ChatCommandTargetState.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
namespace AcDream.Core.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Presentation-independent reply/retell targets derived from the committed
|
||||
/// chat transcript.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Incoming tells carry a non-zero sender GUID and update the reply target.
|
||||
/// Local outgoing tell echoes carry a zero sender GUID and place the target
|
||||
/// name in <see cref="ChatEntry.Sender"/>. This is the existing retail
|
||||
/// <c>@reply</c>/<c>@retell</c> behavior formerly owned by <c>ChatVM</c>.
|
||||
/// </remarks>
|
||||
public sealed class ChatCommandTargetState : IDisposable
|
||||
{
|
||||
private readonly ChatLog _chat;
|
||||
private readonly object _gate = new();
|
||||
private string? _lastIncomingTellSender;
|
||||
private string? _lastOutgoingTellTarget;
|
||||
private bool _disposed;
|
||||
|
||||
public ChatCommandTargetState(ChatLog chat)
|
||||
{
|
||||
_chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
_chat.EntryAppended += OnEntryAppended;
|
||||
}
|
||||
|
||||
public string? LastIncomingTellSender
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_gate)
|
||||
return _lastIncomingTellSender;
|
||||
}
|
||||
}
|
||||
|
||||
public string? LastOutgoingTellTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_gate)
|
||||
return _lastOutgoingTellTarget;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forget character-scoped command targets without clearing visible
|
||||
/// transcript history.
|
||||
/// </summary>
|
||||
public void ResetSession()
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
_lastIncomingTellSender = null;
|
||||
_lastOutgoingTellTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
_chat.EntryAppended -= OnEntryAppended;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEntryAppended(ChatEntry entry)
|
||||
{
|
||||
if (entry.Kind != ChatKind.Tell || string.IsNullOrEmpty(entry.Sender))
|
||||
return;
|
||||
|
||||
lock (_gate)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
if (entry.SenderGuid != 0u)
|
||||
_lastIncomingTellSender = entry.Sender;
|
||||
else
|
||||
_lastOutgoingTellTarget = entry.Sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue