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