using System.Collections.Concurrent; using Yuanshen.Gameserver.Network.Session; using Yuanshen.Gameserver.Database; using Yuanshen.Gameserver.Game; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Yuanshen.Gameserver.Network; internal class NetSessionManager { private static readonly TimeSpan AutoSaveInterval = TimeSpan.FromSeconds(10); private readonly ConcurrentDictionary _sessions; private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly Yuanshen.Gameserver.Game.ConsoleBridge _consoleBridge; public NetSessionManager(ILogger logger, IServiceScopeFactory serviceScopeFactory, Yuanshen.Gameserver.Game.ConsoleBridge consoleBridge) { _sessions = new(); _logger = logger; _serviceScopeFactory = serviceScopeFactory; _consoleBridge = consoleBridge; } public async Task RunSessionAsync(long sessionId, INetworkUnit networkUnit) { try { await using AsyncServiceScope serviceScope = _serviceScopeFactory.CreateAsyncScope(); NetSession session = serviceScope.ServiceProvider.GetRequiredService(); _consoleBridge.Attach(serviceScope.ServiceProvider); PlayerPersistence persistence = serviceScope.ServiceProvider.GetRequiredService(); persistence.Bind(sessionId); using CancellationTokenSource sessionEnded = new(); Task autoSave = RunAutoSaveAsync(persistence, sessionEnded.Token); try { session.Establish(sessionId, networkUnit); await session.RunAsync(); } catch (OperationCanceledException) { } catch (Exception exception) { _logger.LogError("Exception occurred during handling a session, trace: {exception}", exception); } finally { await sessionEnded.CancelAsync(); await autoSave; if (await persistence.SaveAsync()) _logger.LogInformation("Player {uid} state persisted on disconnect", persistence.PlayerUid); persistence.Release(); _consoleBridge.Detach(serviceScope.ServiceProvider); } } catch (Exception exception) { _logger.LogError("Failed to start session {sessionId}: {exception}", sessionId, exception); } } private static async Task RunAutoSaveAsync(PlayerPersistence persistence, CancellationToken cancellationToken) { using PeriodicTimer timer = new(AutoSaveInterval); try { while (await timer.WaitForNextTickAsync(cancellationToken)) await persistence.SaveAsync(); } catch (OperationCanceledException) { } } public void Add(NetSession session) { _sessions[session.SessionId] = session; _logger.LogInformation("New connection from {endPoint}", session.EndPoint); } public bool TryRemove(NetSession session) { bool removed = _sessions.TryRemove(session.SessionId, out _); if (removed) { _logger.LogInformation("Client from {endPoint} disconnected", session.EndPoint); } return removed; } }