126 lines
3.9 KiB
GDScript
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
|