Files
tekton/scripts/managers/session_manager.gd
T

126 lines
3.9 KiB
GDScript

extends Node
# SessionManager - Proactive session refresh and expiry handling
# Monitors session health and refreshes tokens before expiry
signal session_refreshed()
signal session_expired()
# =============================================================================
# Configuration
# =============================================================================
const CHECK_INTERVAL: float = 60.0 # Check every 60s
const REFRESH_BEFORE_EXPIRY: float = 300.0 # Refresh when <5min to expiry
const MAX_RETRY_ATTEMPTS: int = 3
const BASE_RETRY_DELAY: float = 1.0 # 1s, 2s, 4s exponential backoff
# =============================================================================
# State
# =============================================================================
var _refresh_timer: float = 0.0
var _is_refreshing: bool = false
# =============================================================================
# Lifecycle
# =============================================================================
func _ready():
set_process(false)
# Start monitoring when session is available
if NakamaManager:
if NakamaManager.session and not NakamaManager.session.is_expired():
_start_monitoring()
func _process(delta: float) -> void:
_refresh_timer -= delta
if _refresh_timer <= 0.0:
_refresh_timer = CHECK_INTERVAL
_check_session_health()
# =============================================================================
# Public API
# =============================================================================
func start_monitoring() -> void:
"""Start monitoring session health. Call after successful login."""
_start_monitoring()
func stop_monitoring() -> void:
"""Stop monitoring session health."""
set_process(false)
# =============================================================================
# Internal
# =============================================================================
func _start_monitoring() -> void:
_refresh_timer = CHECK_INTERVAL
set_process(true)
print("[SessionManager] Monitoring started")
func _check_session_health() -> void:
if not NakamaManager or not NakamaManager.session:
return
if NakamaManager.session.is_expired():
print("[SessionManager] Session expired!")
session_expired.emit()
if EventBus:
EventBus.emit(EventBus.EVENT_SESSION_EXPIRED)
return
# Check if within refresh window
var time_to_expiry = NakamaManager.session.expires_at - Time.get_unix_time_from_system()
if time_to_expiry < REFRESH_BEFORE_EXPIRY:
print("[SessionManager] Session expiring soon (%.0fs), refreshing..." % time_to_expiry)
_refresh_session()
func _refresh_session() -> void:
if _is_refreshing:
return
_is_refreshing = true
var success = await _attempt_refresh_with_retry()
_is_refreshing = false
if success:
print("[SessionManager] Session refreshed successfully")
session_refreshed.emit()
if EventBus:
EventBus.emit(EventBus.EVENT_SESSION_REFRESHED)
else:
print("[SessionManager] Session refresh failed after all retries")
session_expired.emit()
if EventBus:
EventBus.emit(EventBus.EVENT_SESSION_EXPIRED)
func _attempt_refresh_with_retry() -> bool:
for attempt in range(MAX_RETRY_ATTEMPTS):
var delay = BASE_RETRY_DELAY * pow(2, attempt)
if attempt > 0:
print("[SessionManager] Retry attempt %d/%d after %.1fs" % [attempt + 1, MAX_RETRY_ATTEMPTS, delay])
await get_tree().create_timer(delay).timeout
var result = await _do_refresh()
if result:
return true
return false
func _do_refresh() -> bool:
if not NakamaManager or not NakamaManager.client or not NakamaManager.session:
return false
var refresh_result = await NakamaManager.client.session_refresh_async(NakamaManager.session)
if refresh_result.is_exception():
print("[SessionManager] Refresh error: ", refresh_result.get_exception().message)
return false
# Update the session in NakamaManager
NakamaManager.session = refresh_result
return true