basic summaries for all pair classes, create partial classes and condense models into a single file
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using LightlessSync.API.Data.Enum;
|
||||
using LightlessSync.API.Data.Extensions;
|
||||
@@ -11,12 +10,14 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LightlessSync.PlayerData.Pairs;
|
||||
|
||||
/// <summary>
|
||||
/// creates, tracks, and removes pair handlers
|
||||
/// </summary>
|
||||
public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
private readonly object _gate = new();
|
||||
private readonly Dictionary<string, IPairHandlerAdapter> _identToHandler = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<IPairHandlerAdapter, HashSet<PairUniqueIdentifier>> _handlerToPairs = new();
|
||||
private readonly Dictionary<string, CancellationTokenSource> _waitingRequests = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, PairHandlerEntry> _entriesByIdent = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<IPairHandlerAdapter, PairHandlerEntry> _entriesByHandler = new();
|
||||
|
||||
private readonly IPairHandlerAdapterFactory _handlerFactory;
|
||||
private readonly PairManager _pairManager;
|
||||
@@ -24,7 +25,6 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
private readonly ILogger<PairHandlerRegistry> _logger;
|
||||
|
||||
private readonly TimeSpan _deletionGracePeriod = TimeSpan.FromMinutes(5);
|
||||
private readonly TimeSpan _waitForHandlerGracePeriod = TimeSpan.FromMinutes(2);
|
||||
|
||||
public PairHandlerRegistry(
|
||||
IPairHandlerAdapterFactory handlerFactory,
|
||||
@@ -42,7 +42,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
return _handlerToPairs.Keys.Count(handler => handler.IsVisible);
|
||||
return _entriesByHandler.Keys.Count(handler => handler.IsVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
return _identToHandler.TryGetValue(ident, out var handler) && handler.IsVisible;
|
||||
return _entriesByIdent.TryGetValue(ident, out var entry) && entry.Handler.IsVisible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,16 +64,10 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
IPairHandlerAdapter handler;
|
||||
lock (_gate)
|
||||
{
|
||||
handler = GetOrAddHandler(registration.CharacterIdent);
|
||||
var entry = GetOrCreateEntry(registration.CharacterIdent);
|
||||
handler = entry.Handler;
|
||||
handler.ScheduledForDeletion = false;
|
||||
|
||||
if (!_handlerToPairs.TryGetValue(handler, out var set))
|
||||
{
|
||||
set = new HashSet<PairUniqueIdentifier>();
|
||||
_handlerToPairs[handler] = set;
|
||||
}
|
||||
|
||||
set.Add(registration.PairIdent);
|
||||
entry.AddPair(registration.PairIdent);
|
||||
}
|
||||
|
||||
ApplyPauseStateForHandler(handler);
|
||||
@@ -109,25 +103,23 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
|
||||
lock (_gate)
|
||||
{
|
||||
if (!_identToHandler.TryGetValue(registration.CharacterIdent, out handler))
|
||||
if (!_entriesByIdent.TryGetValue(registration.CharacterIdent, out var entry))
|
||||
{
|
||||
return PairOperationResult<PairUniqueIdentifier>.Fail($"Ident {registration.CharacterIdent} not registered.");
|
||||
}
|
||||
|
||||
if (_handlerToPairs.TryGetValue(handler, out var set))
|
||||
handler = entry.Handler;
|
||||
entry.RemovePair(registration.PairIdent);
|
||||
if (entry.PairCount == 0)
|
||||
{
|
||||
set.Remove(registration.PairIdent);
|
||||
if (set.Count == 0)
|
||||
if (forceDisposal)
|
||||
{
|
||||
if (forceDisposal)
|
||||
{
|
||||
shouldDisposeImmediately = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldScheduleRemoval = true;
|
||||
handler.ScheduledForDeletion = true;
|
||||
}
|
||||
shouldDisposeImmediately = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldScheduleRemoval = true;
|
||||
handler.ScheduledForDeletion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,13 +146,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
return PairOperationResult.Fail($"Character data received without ident for {registration.PairIdent.UserId}.");
|
||||
}
|
||||
|
||||
IPairHandlerAdapter? handler;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(registration.CharacterIdent, out handler);
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
if (!TryGetHandler(registration.CharacterIdent, out var handler) || handler is null)
|
||||
{
|
||||
var registerResult = RegisterOnlinePair(registration);
|
||||
if (!registerResult.Success)
|
||||
@@ -168,30 +154,19 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
return PairOperationResult.Fail(registerResult.Error);
|
||||
}
|
||||
|
||||
lock (_gate)
|
||||
if (!TryGetHandler(registration.CharacterIdent, out handler) || handler is null)
|
||||
{
|
||||
_identToHandler.TryGetValue(registration.CharacterIdent, out handler);
|
||||
return PairOperationResult.Fail($"Handler not ready for {registration.PairIdent.UserId}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
{
|
||||
return PairOperationResult.Fail($"Handler not ready for {registration.PairIdent.UserId}.");
|
||||
}
|
||||
|
||||
handler.ApplyData(dto.CharaData);
|
||||
return PairOperationResult.Ok();
|
||||
}
|
||||
|
||||
public PairOperationResult ApplyLastReceivedData(PairUniqueIdentifier pairIdent, string ident, bool forced = false)
|
||||
{
|
||||
IPairHandlerAdapter? handler;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(ident, out handler);
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
if (!TryGetHandler(ident, out var handler) || handler is null)
|
||||
{
|
||||
return PairOperationResult.Fail($"Cannot reapply data: handler for {pairIdent.UserId} not found.");
|
||||
}
|
||||
@@ -202,13 +177,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
|
||||
public PairOperationResult SetUploading(PairUniqueIdentifier pairIdent, string ident, bool uploading)
|
||||
{
|
||||
IPairHandlerAdapter? handler;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(ident, out handler);
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
if (!TryGetHandler(ident, out var handler) || handler is null)
|
||||
{
|
||||
return PairOperationResult.Fail($"Cannot set uploading for {pairIdent.UserId}: handler not found.");
|
||||
}
|
||||
@@ -219,44 +188,31 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
|
||||
public PairOperationResult SetPausedState(PairUniqueIdentifier pairIdent, string ident, bool paused)
|
||||
{
|
||||
IPairHandlerAdapter? handler;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(ident, out handler);
|
||||
}
|
||||
|
||||
if (handler is null)
|
||||
if (!TryGetHandler(ident, out var handler) || handler is null)
|
||||
{
|
||||
return PairOperationResult.Fail($"Cannot update pause state for {pairIdent.UserId}: handler not found.");
|
||||
}
|
||||
|
||||
_ = paused; // value reflected in pair manager already
|
||||
// Recalculate pause state against all registered pairs to ensure consistency across contexts.
|
||||
ApplyPauseStateForHandler(handler);
|
||||
return PairOperationResult.Ok();
|
||||
}
|
||||
|
||||
public PairOperationResult<IReadOnlyList<(PairUniqueIdentifier Ident, PairConnection Pair)>> GetPairConnections(string ident)
|
||||
{
|
||||
IPairHandlerAdapter? handler;
|
||||
HashSet<PairUniqueIdentifier>? identifiers = null;
|
||||
|
||||
PairHandlerEntry? entry;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(ident, out handler);
|
||||
if (handler is not null)
|
||||
{
|
||||
_handlerToPairs.TryGetValue(handler, out identifiers);
|
||||
}
|
||||
_entriesByIdent.TryGetValue(ident, out entry);
|
||||
}
|
||||
|
||||
if (handler is null || identifiers is null)
|
||||
if (entry is null)
|
||||
{
|
||||
return PairOperationResult<IReadOnlyList<(PairUniqueIdentifier Ident, PairConnection Pair)>>.Fail($"No handler registered for {ident}.");
|
||||
}
|
||||
|
||||
var list = new List<(PairUniqueIdentifier, PairConnection)>();
|
||||
foreach (var pairIdent in identifiers)
|
||||
foreach (var pairIdent in entry.SnapshotPairs())
|
||||
{
|
||||
var result = _pairManager.GetPair(pairIdent.UserId);
|
||||
if (result.Success)
|
||||
@@ -279,8 +235,8 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
var success = _identToHandler.TryGetValue(ident, out var resolved);
|
||||
handler = resolved;
|
||||
var success = _entriesByIdent.TryGetValue(ident, out var entry);
|
||||
handler = entry?.Handler;
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -289,7 +245,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
return _identToHandler.Values.Distinct().ToList();
|
||||
return _entriesByHandler.Keys.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,9 +253,9 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (_handlerToPairs.TryGetValue(handler, out var pairs))
|
||||
if (_entriesByHandler.TryGetValue(handler, out var entry))
|
||||
{
|
||||
return pairs.ToList();
|
||||
return entry.SnapshotPairs();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,17 +286,9 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
List<IPairHandlerAdapter> handlers;
|
||||
lock (_gate)
|
||||
{
|
||||
handlers = _identToHandler.Values.Distinct().ToList();
|
||||
_identToHandler.Clear();
|
||||
_handlerToPairs.Clear();
|
||||
|
||||
foreach (var pending in _waitingRequests.Values)
|
||||
{
|
||||
pending.Cancel();
|
||||
pending.Dispose();
|
||||
}
|
||||
|
||||
_waitingRequests.Clear();
|
||||
handlers = _entriesByHandler.Keys.ToList();
|
||||
_entriesByIdent.Clear();
|
||||
_entriesByHandler.Clear();
|
||||
}
|
||||
|
||||
foreach (var handler in handlers)
|
||||
@@ -364,14 +312,9 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
List<IPairHandlerAdapter> handlers;
|
||||
lock (_gate)
|
||||
{
|
||||
handlers = _identToHandler.Values.Distinct().ToList();
|
||||
_identToHandler.Clear();
|
||||
_handlerToPairs.Clear();
|
||||
foreach (var kv in _waitingRequests.Values)
|
||||
{
|
||||
kv.Cancel();
|
||||
}
|
||||
_waitingRequests.Clear();
|
||||
handlers = _entriesByHandler.Keys.ToList();
|
||||
_entriesByIdent.Clear();
|
||||
_entriesByHandler.Clear();
|
||||
}
|
||||
|
||||
foreach (var handler in handlers)
|
||||
@@ -380,46 +323,23 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private IPairHandlerAdapter GetOrAddHandler(string ident)
|
||||
private PairHandlerEntry GetOrCreateEntry(string ident)
|
||||
{
|
||||
if (_identToHandler.TryGetValue(ident, out var handler))
|
||||
if (_entriesByIdent.TryGetValue(ident, out var entry))
|
||||
{
|
||||
return handler;
|
||||
return entry;
|
||||
}
|
||||
|
||||
handler = _handlerFactory.Create(ident);
|
||||
_identToHandler[ident] = handler;
|
||||
_handlerToPairs[handler] = new HashSet<PairUniqueIdentifier>();
|
||||
return handler;
|
||||
}
|
||||
|
||||
private void EnsureInitialized(IPairHandlerAdapter handler)
|
||||
{
|
||||
if (handler.Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
handler.Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to initialize handler for {Ident}", handler.Ident);
|
||||
}
|
||||
var handler = _handlerFactory.Create(ident);
|
||||
entry = new PairHandlerEntry(ident, handler);
|
||||
_entriesByIdent[ident] = entry;
|
||||
_entriesByHandler[handler] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
private async Task RemoveAfterGracePeriodAsync(IPairHandlerAdapter handler)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(_deletionGracePeriod).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await Task.Delay(_deletionGracePeriod).ConfigureAwait(false);
|
||||
|
||||
if (TryFinalizeHandlerRemoval(handler))
|
||||
{
|
||||
@@ -431,63 +351,15 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (!_handlerToPairs.TryGetValue(handler, out var set) || set.Count > 0)
|
||||
if (!_entriesByHandler.TryGetValue(handler, out var entry) || entry.HasPairs)
|
||||
{
|
||||
handler.ScheduledForDeletion = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
_handlerToPairs.Remove(handler);
|
||||
_identToHandler.Remove(handler.Ident);
|
||||
|
||||
if (_waitingRequests.TryGetValue(handler.Ident, out var cts))
|
||||
{
|
||||
cts.Cancel();
|
||||
cts.Dispose();
|
||||
_waitingRequests.Remove(handler.Ident);
|
||||
}
|
||||
|
||||
_entriesByHandler.Remove(handler);
|
||||
_entriesByIdent.Remove(entry.Ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitThenApplyDataAsync(PairRegistration registration, OnlineUserCharaDataDto dto, CancellationTokenSource cts)
|
||||
{
|
||||
var token = cts.Token;
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
IPairHandlerAdapter? handler;
|
||||
lock (_gate)
|
||||
{
|
||||
_identToHandler.TryGetValue(registration.CharacterIdent!, out handler);
|
||||
}
|
||||
|
||||
if (handler is not null && handler.Initialized)
|
||||
{
|
||||
handler.ApplyData(dto.CharaData);
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(500), token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// expected
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (_waitingRequests.TryGetValue(registration.CharacterIdent!, out var existing) && existing == cts)
|
||||
{
|
||||
_waitingRequests.Remove(registration.CharacterIdent!);
|
||||
}
|
||||
}
|
||||
|
||||
cts.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user