package player import ( "crypto/rand" "encoding/hex" "sync" "time" ) // Session holds an authenticated player's session state. type Session struct { Token string PlayerID uint64 PlayerName string CreatedAt time.Time LastActive time.Time } // SessionManager manages active sessions. type SessionManager struct { mu sync.RWMutex sessions map[string]*Session // token -> session } // NewSessionManager creates a new session manager. func NewSessionManager() *SessionManager { return &SessionManager{ sessions: make(map[string]*Session), } } // Create generates a new session for the given player. func (sm *SessionManager) Create(playerID uint64, playerName string) *Session { token := generateToken() now := time.Now() s := &Session{ Token: token, PlayerID: playerID, PlayerName: playerName, CreatedAt: now, LastActive: now, } sm.mu.Lock() sm.sessions[token] = s sm.mu.Unlock() return s } // Get retrieves a session by token. Returns nil if not found. func (sm *SessionManager) Get(token string) *Session { sm.mu.RLock() defer sm.mu.RUnlock() s := sm.sessions[token] if s != nil { s.LastActive = time.Now() } return s } // Remove deletes a session. func (sm *SessionManager) Remove(token string) { sm.mu.Lock() delete(sm.sessions, token) sm.mu.Unlock() } // CleanupExpired removes sessions inactive for longer than the given duration. func (sm *SessionManager) CleanupExpired(maxIdle time.Duration) int { sm.mu.Lock() defer sm.mu.Unlock() cutoff := time.Now().Add(-maxIdle) removed := 0 for token, s := range sm.sessions { if s.LastActive.Before(cutoff) { delete(sm.sessions, token) removed++ } } return removed } func generateToken() string { b := make([]byte, 32) _, _ = rand.Read(b) return hex.EncodeToString(b) }