ModernUO/Projects/UOContent/Systems/Gateway/GatewaySessionStore.cs
2026-04-09 16:05:47 -06:00

91 lines
2.5 KiB
C#

using System;
using System.Collections.Generic;
using Server.Logging;
namespace Server.Systems.Gateway;
/// <summary>
/// Stores sessions pushed from the gateway via SignalR.
/// Accessed only on the game thread -- no concurrency needed.
/// When a client sends 0x91, the login handler checks here first (O(1) lookup)
/// before falling back to network validation.
/// </summary>
public static class GatewaySessionStore
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(GatewaySessionStore));
private static readonly Dictionary<int, PushedSession> _sessions = new();
private static TimerExecutionToken _cleanupTimer;
public static void Configure()
{
if (!GatewayConfig.Enabled || !GatewayConfig.SignalREnabled)
{
return;
}
// Cleanup expired sessions every 60 seconds
Timer.StartTimer(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60), Cleanup, out _cleanupTimer);
}
/// <summary>
/// Adds a pushed session. Called on the game thread via Core.LoopContext.Post.
/// </summary>
public static void Add(PushedSession session)
{
_sessions[session.AuthId] = session;
logger.Debug("Cached pushed session AuthId=0x{AuthId:X8} for '{Username}'", session.AuthId, session.Username);
}
/// <summary>
/// Tries to consume a session (one-time use). Returns true if found and not expired.
/// Called on the game thread from the login handler.
/// </summary>
public static bool TryConsume(int authId, out PushedSession session)
{
if (_sessions.Remove(authId, out session!))
{
if (session.ExpiresAt > DateTime.UtcNow)
{
return true;
}
// Expired
session = default!;
}
return false;
}
private static void Cleanup()
{
var now = DateTime.UtcNow;
var expired = new List<int>();
foreach (var (authId, session) in _sessions)
{
if (session.ExpiresAt < now)
{
expired.Add(authId);
}
}
foreach (var authId in expired)
{
_sessions.Remove(authId);
}
if (expired.Count > 0)
{
logger.Debug("Cleaned up {Count} expired pushed sessions", expired.Count);
}
}
}
public record PushedSession(
int AuthId,
Guid GameAccountId,
string Username,
string AccessLevel,
string ClientIP,
DateTime ExpiresAt
);