node build fixed
This commit is contained in:
646
seanime-2.9.10/internal/nakama/nakama.go
Normal file
646
seanime-2.9.10/internal/nakama/nakama.go
Normal file
@@ -0,0 +1,646 @@
|
||||
package nakama
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"seanime/internal/database/models"
|
||||
debrid_client "seanime/internal/debrid/client"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/library/playbackmanager"
|
||||
"seanime/internal/platforms/platform"
|
||||
"seanime/internal/torrentstream"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/result"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/imroc/req/v3"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
serverHost string
|
||||
serverPort int
|
||||
username string
|
||||
logger *zerolog.Logger
|
||||
settings *models.NakamaSettings
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
platform platform.Platform
|
||||
playbackManager *playbackmanager.PlaybackManager
|
||||
torrentstreamRepository *torrentstream.Repository
|
||||
debridClientRepository *debrid_client.Repository
|
||||
peerId string
|
||||
|
||||
// Host connections (when acting as host)
|
||||
peerConnections *result.Map[string, *PeerConnection]
|
||||
|
||||
// Host connection (when connecting to a host)
|
||||
hostConnection *HostConnection
|
||||
hostConnectionCtx context.Context
|
||||
hostConnectionCancel context.CancelFunc
|
||||
hostMu sync.RWMutex
|
||||
reconnecting bool // Flag to prevent multiple concurrent reconnection attempts
|
||||
|
||||
// Connection management
|
||||
cancel context.CancelFunc
|
||||
ctx context.Context
|
||||
|
||||
// Message handlers
|
||||
messageHandlers map[MessageType]func(*Message, string) error
|
||||
handlerMu sync.RWMutex
|
||||
|
||||
// Cleanup functions
|
||||
cleanups []func()
|
||||
|
||||
reqClient *req.Client
|
||||
watchPartyManager *WatchPartyManager
|
||||
|
||||
previousPath string // latest file streamed by the peer - real path on the host
|
||||
}
|
||||
|
||||
type NewManagerOptions struct {
|
||||
Logger *zerolog.Logger
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
PlaybackManager *playbackmanager.PlaybackManager
|
||||
TorrentstreamRepository *torrentstream.Repository
|
||||
DebridClientRepository *debrid_client.Repository
|
||||
Platform platform.Platform
|
||||
ServerHost string
|
||||
ServerPort int
|
||||
}
|
||||
|
||||
type ConnectionType string
|
||||
|
||||
const (
|
||||
ConnectionTypeHost ConnectionType = "host"
|
||||
ConnectionTypePeer ConnectionType = "peer"
|
||||
)
|
||||
|
||||
// MessageType represents the type of message being sent
|
||||
type MessageType string
|
||||
|
||||
const (
|
||||
MessageTypeAuth MessageType = "auth"
|
||||
MessageTypeAuthReply MessageType = "auth_reply"
|
||||
MessageTypePing MessageType = "ping"
|
||||
MessageTypePong MessageType = "pong"
|
||||
MessageTypeError MessageType = "error"
|
||||
|
||||
MessageTypeCustom MessageType = "custom"
|
||||
)
|
||||
|
||||
// Message represents a message sent between Nakama instances
|
||||
type Message struct {
|
||||
Type MessageType `json:"type"`
|
||||
Payload interface{} `json:"payload"`
|
||||
RequestID string `json:"requestId,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// PeerConnection represents a connection from a peer to this host
|
||||
type PeerConnection struct {
|
||||
ID string // Internal connection ID (websocket)
|
||||
PeerId string // UUID generated by the peer (primary identifier)
|
||||
Username string // Display name (kept for UI purposes)
|
||||
Conn *websocket.Conn
|
||||
ConnectionType ConnectionType
|
||||
Authenticated bool
|
||||
LastPing time.Time
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// HostConnection represents this instance's connection to a host
|
||||
type HostConnection struct {
|
||||
URL string
|
||||
PeerId string // UUID generated by this peer instance
|
||||
Username string
|
||||
Conn *websocket.Conn
|
||||
Authenticated bool
|
||||
LastPing time.Time
|
||||
reconnectTimer *time.Timer
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NakamaEvent represents events sent to the client
|
||||
type NakamaEvent struct {
|
||||
Type string `json:"type"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// AuthPayload represents authentication data
|
||||
type AuthPayload struct {
|
||||
Password string `json:"password"`
|
||||
PeerId string `json:"peerId"` // UUID generated by the peer
|
||||
}
|
||||
|
||||
// AuthReplyPayload represents authentication response
|
||||
type AuthReplyPayload struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Username string `json:"username"`
|
||||
PeerId string `json:"peerId"` // Echo back the peer's UUID
|
||||
}
|
||||
|
||||
// ErrorPayload represents error messages
|
||||
type ErrorPayload struct {
|
||||
Message string `json:"message"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// HostConnectionStatus represents the status of the host connection
|
||||
type HostConnectionStatus struct {
|
||||
Connected bool `json:"connected"`
|
||||
Authenticated bool `json:"authenticated"`
|
||||
URL string `json:"url"`
|
||||
LastPing time.Time `json:"lastPing"`
|
||||
PeerId string `json:"peerId"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// NakamaStatus represents the overall status of Nakama connections
|
||||
type NakamaStatus struct {
|
||||
IsHost bool `json:"isHost"`
|
||||
ConnectedPeers []string `json:"connectedPeers"`
|
||||
IsConnectedToHost bool `json:"isConnectedToHost"`
|
||||
HostConnectionStatus *HostConnectionStatus `json:"hostConnectionStatus"`
|
||||
CurrentWatchPartySession *WatchPartySession `json:"currentWatchPartySession"`
|
||||
}
|
||||
|
||||
// MessageResponse represents a response to message sending requests
|
||||
type MessageResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ClientEvent struct {
|
||||
Type string `json:"type"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
func NewManager(opts *NewManagerOptions) *Manager {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
m := &Manager{
|
||||
username: "",
|
||||
logger: opts.Logger,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
playbackManager: opts.PlaybackManager,
|
||||
peerConnections: result.NewResultMap[string, *PeerConnection](),
|
||||
platform: opts.Platform,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
messageHandlers: make(map[MessageType]func(*Message, string) error),
|
||||
cleanups: make([]func(), 0),
|
||||
reqClient: req.C(),
|
||||
serverHost: opts.ServerHost,
|
||||
serverPort: opts.ServerPort,
|
||||
settings: &models.NakamaSettings{},
|
||||
torrentstreamRepository: opts.TorrentstreamRepository,
|
||||
debridClientRepository: opts.DebridClientRepository,
|
||||
previousPath: "",
|
||||
}
|
||||
|
||||
m.watchPartyManager = NewWatchPartyManager(m)
|
||||
|
||||
// Register default message handlers
|
||||
m.registerDefaultHandlers()
|
||||
|
||||
eventListener := m.wsEventManager.SubscribeToClientEvents("nakama")
|
||||
go func() {
|
||||
for event := range eventListener.Channel {
|
||||
if event.Type == events.NakamaStatusRequested {
|
||||
currSession, _ := m.GetWatchPartyManager().GetCurrentSession()
|
||||
status := &NakamaStatus{
|
||||
IsHost: m.IsHost(),
|
||||
ConnectedPeers: m.GetConnectedPeers(),
|
||||
IsConnectedToHost: m.IsConnectedToHost(),
|
||||
HostConnectionStatus: m.GetHostConnectionStatus(),
|
||||
CurrentWatchPartySession: currSession,
|
||||
}
|
||||
m.wsEventManager.SendEvent(events.NakamaStatus, status)
|
||||
}
|
||||
|
||||
if event.Type == events.NakamaWatchPartyEnableRelayMode {
|
||||
var payload WatchPartyEnableRelayModePayload
|
||||
marshaledPayload, err := json.Marshal(event.Payload)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("nakama: Failed to marshal watch party enable relay mode payload")
|
||||
continue
|
||||
}
|
||||
err = json.Unmarshal(marshaledPayload, &payload)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("nakama: Failed to unmarshal watch party enable relay mode payload")
|
||||
continue
|
||||
}
|
||||
m.GetWatchPartyManager().EnableRelayMode(payload.PeerId)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manager) SetSettings(settings *models.NakamaSettings) {
|
||||
var previousSettings *models.NakamaSettings
|
||||
if m.settings != nil {
|
||||
previousSettings = &[]models.NakamaSettings{*m.settings}[0]
|
||||
}
|
||||
|
||||
// If the host password has changed, stop host service
|
||||
// This will cause a restart of the host service
|
||||
disconnectAsHost := false
|
||||
if m.settings != nil && m.settings.HostPassword != settings.HostPassword {
|
||||
disconnectAsHost = true
|
||||
m.stopHostServices()
|
||||
}
|
||||
|
||||
m.settings = settings
|
||||
m.username = cmp.Or(settings.Username, "Peer_"+util.RandomStringWithAlphabet(8, "bcdefhijklmnopqrstuvwxyz0123456789"))
|
||||
m.logger.Debug().Bool("isHost", settings.IsHost).Str("username", m.username).Str("remoteURL", settings.RemoteServerURL).Msg("nakama: Settings updated")
|
||||
|
||||
if previousSettings == nil || previousSettings.IsHost != settings.IsHost || previousSettings.Enabled != settings.Enabled || disconnectAsHost {
|
||||
// Determine if we should stop host services
|
||||
shouldStopHost := m.IsHost() && (!settings.Enabled || // Nakama disabled
|
||||
!settings.IsHost || // Switching to peer mode
|
||||
disconnectAsHost) // Password changed (requires restart)
|
||||
|
||||
// Determine if we should start host services
|
||||
shouldStartHost := settings.IsHost && settings.Enabled
|
||||
|
||||
// Always stop first if needed, then start
|
||||
if shouldStopHost {
|
||||
m.stopHostServices()
|
||||
}
|
||||
if shouldStartHost {
|
||||
m.startHostServices()
|
||||
}
|
||||
}
|
||||
|
||||
if previousSettings == nil || previousSettings.RemoteServerURL != settings.RemoteServerURL || previousSettings.RemoteServerPassword != settings.RemoteServerPassword || previousSettings.Enabled != settings.Enabled {
|
||||
// Determine if we should disconnect from current host
|
||||
shouldDisconnect := m.IsConnectedToHost() && (!settings.Enabled || // Nakama disabled
|
||||
settings.IsHost || // Switching to host mode
|
||||
settings.RemoteServerURL == "" || // No remote URL
|
||||
settings.RemoteServerPassword == "" || // No password
|
||||
(previousSettings != nil && previousSettings.RemoteServerURL != settings.RemoteServerURL) || // URL changed
|
||||
(previousSettings != nil && previousSettings.RemoteServerPassword != settings.RemoteServerPassword)) // Password changed
|
||||
|
||||
// Determine if we should connect to a host
|
||||
shouldConnect := !settings.IsHost &&
|
||||
settings.Enabled &&
|
||||
settings.RemoteServerURL != "" &&
|
||||
settings.RemoteServerPassword != ""
|
||||
|
||||
// Always disconnect first if needed, then connect
|
||||
if shouldDisconnect {
|
||||
m.disconnectFromHost()
|
||||
}
|
||||
if shouldConnect {
|
||||
m.connectToHost()
|
||||
}
|
||||
}
|
||||
|
||||
// if previousSettings == nil || previousSettings.Username != settings.Username {
|
||||
// m.SendMessage(MessageTypeCustom, map[string]interface{}{
|
||||
// "type": "nakama_username_changed",
|
||||
// "username": settings.Username,
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
func (m *Manager) GetHostBaseServerURL() string {
|
||||
url := m.settings.RemoteServerURL
|
||||
if strings.HasSuffix(url, "/") {
|
||||
url = strings.TrimSuffix(url, "/")
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
func (m *Manager) IsHost() bool {
|
||||
return m.settings.IsHost
|
||||
}
|
||||
|
||||
func (m *Manager) GetHostConnection() (*HostConnection, bool) {
|
||||
m.hostMu.RLock()
|
||||
defer m.hostMu.RUnlock()
|
||||
return m.hostConnection, m.hostConnection != nil
|
||||
}
|
||||
|
||||
// GetWatchPartyManager returns the watch party manager
|
||||
func (m *Manager) GetWatchPartyManager() *WatchPartyManager {
|
||||
return m.watchPartyManager
|
||||
}
|
||||
|
||||
// Cleanup stops all connections and services
|
||||
func (m *Manager) Cleanup() {
|
||||
m.logger.Debug().Msg("nakama: Cleaning up")
|
||||
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
}
|
||||
|
||||
// Cancel any ongoing host connection attempts
|
||||
m.hostMu.Lock()
|
||||
if m.hostConnectionCancel != nil {
|
||||
m.hostConnectionCancel()
|
||||
m.hostConnectionCancel = nil
|
||||
}
|
||||
m.hostMu.Unlock()
|
||||
|
||||
// Cleanup host connections
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
conn.Close()
|
||||
return true
|
||||
})
|
||||
m.peerConnections.Clear()
|
||||
|
||||
// Cleanup client connection
|
||||
m.hostMu.Lock()
|
||||
if m.hostConnection != nil {
|
||||
m.hostConnection.Close()
|
||||
m.hostConnection = nil
|
||||
}
|
||||
m.hostMu.Unlock()
|
||||
|
||||
// Run cleanup functions
|
||||
for _, cleanup := range m.cleanups {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterMessageHandler registers a custom message handler
|
||||
func (m *Manager) RegisterMessageHandler(msgType MessageType, handler func(*Message, string) error) {
|
||||
m.handlerMu.Lock()
|
||||
defer m.handlerMu.Unlock()
|
||||
m.messageHandlers[msgType] = handler
|
||||
}
|
||||
|
||||
// SendMessage sends a message to all connected peers (when acting as host)
|
||||
func (m *Manager) SendMessage(msgType MessageType, payload interface{}) error {
|
||||
if !m.settings.IsHost {
|
||||
return errors.New("not acting as host")
|
||||
}
|
||||
|
||||
message := &Message{
|
||||
Type: msgType,
|
||||
Payload: payload,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
var lastError error
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
if err := conn.SendMessage(message); err != nil {
|
||||
m.logger.Error().Err(err).Str("peerId", conn.PeerId).Msg("nakama: Failed to send message to peer")
|
||||
lastError = err
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return lastError
|
||||
}
|
||||
|
||||
// SendMessageToPeer sends a message to a specific peer by their PeerID
|
||||
func (m *Manager) SendMessageToPeer(peerID string, msgType MessageType, payload interface{}) error {
|
||||
if !m.settings.IsHost {
|
||||
return errors.New("only hosts can send messages to peers")
|
||||
}
|
||||
|
||||
// Find peer by PeerID
|
||||
var targetConn *PeerConnection
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
if conn.PeerId == peerID {
|
||||
targetConn = conn
|
||||
return false // Stop iteration
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if targetConn == nil {
|
||||
return errors.New("peer not found: " + peerID)
|
||||
}
|
||||
|
||||
message := &Message{
|
||||
Type: msgType,
|
||||
Payload: payload,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
return targetConn.SendMessage(message)
|
||||
}
|
||||
|
||||
// SendMessageToHost sends a message to the host (when acting as peer)
|
||||
func (m *Manager) SendMessageToHost(msgType MessageType, payload interface{}) error {
|
||||
m.hostMu.RLock()
|
||||
defer m.hostMu.RUnlock()
|
||||
|
||||
if m.hostConnection == nil || !m.hostConnection.Authenticated {
|
||||
return errors.New("not connected to host")
|
||||
}
|
||||
|
||||
message := &Message{
|
||||
Type: msgType,
|
||||
Payload: payload,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
return m.hostConnection.SendMessage(message)
|
||||
}
|
||||
|
||||
// GetConnectedPeers returns a list of connected peer IDs
|
||||
func (m *Manager) GetConnectedPeers() []string {
|
||||
if !m.settings.IsHost {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
peers := make([]string, 0)
|
||||
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
if conn.Authenticated {
|
||||
// Use PeerID as the primary identifier
|
||||
peerDisplayName := conn.Username
|
||||
if peerDisplayName == "" {
|
||||
peerDisplayName = "Unknown"
|
||||
}
|
||||
// Format: "Username (PeerID_short)"
|
||||
// peers = append(peers, fmt.Sprintf("%s (%s)", peerDisplayName, conn.PeerId[:8]))
|
||||
peers = append(peers, fmt.Sprintf("%s", peerDisplayName))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
// IsConnectedToHost returns whether this instance is connected to a host
|
||||
func (m *Manager) IsConnectedToHost() bool {
|
||||
m.hostMu.RLock()
|
||||
defer m.hostMu.RUnlock()
|
||||
return m.hostConnection != nil && m.hostConnection.Authenticated
|
||||
}
|
||||
|
||||
// GetHostConnectionStatus returns the status of the host connection
|
||||
func (m *Manager) GetHostConnectionStatus() *HostConnectionStatus {
|
||||
m.hostMu.RLock()
|
||||
defer m.hostMu.RUnlock()
|
||||
|
||||
if m.hostConnection == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &HostConnectionStatus{
|
||||
Connected: m.hostConnection != nil,
|
||||
Authenticated: m.hostConnection != nil && m.hostConnection.Authenticated,
|
||||
URL: m.hostConnection.URL,
|
||||
LastPing: m.hostConnection.LastPing,
|
||||
PeerId: m.hostConnection.PeerId,
|
||||
Username: m.hostConnection.Username,
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) SendMessage(message *Message) error {
|
||||
pc.mu.Lock()
|
||||
defer pc.mu.Unlock()
|
||||
return pc.Conn.WriteJSON(message)
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) Close() {
|
||||
pc.mu.Lock()
|
||||
defer pc.mu.Unlock()
|
||||
_ = pc.Conn.Close()
|
||||
}
|
||||
|
||||
func (hc *HostConnection) SendMessage(message *Message) error {
|
||||
hc.mu.Lock()
|
||||
defer hc.mu.Unlock()
|
||||
return hc.Conn.WriteJSON(message)
|
||||
}
|
||||
|
||||
func (hc *HostConnection) Close() {
|
||||
hc.mu.Lock()
|
||||
defer hc.mu.Unlock()
|
||||
if hc.reconnectTimer != nil {
|
||||
hc.reconnectTimer.Stop()
|
||||
}
|
||||
_ = hc.Conn.Close()
|
||||
}
|
||||
|
||||
// Helper function to generate connection IDs
|
||||
func generateConnectionID() string {
|
||||
return fmt.Sprintf("conn_%d", time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// ReconnectToHost attempts to reconnect to the host
|
||||
func (m *Manager) ReconnectToHost() error {
|
||||
if m.settings == nil || m.settings.RemoteServerURL == "" || m.settings.RemoteServerPassword == "" {
|
||||
return errors.New("no host connection configured")
|
||||
}
|
||||
|
||||
// Check if already reconnecting
|
||||
m.hostMu.Lock()
|
||||
if m.reconnecting {
|
||||
m.hostMu.Unlock()
|
||||
return errors.New("reconnection already in progress")
|
||||
}
|
||||
m.hostMu.Unlock()
|
||||
|
||||
m.logger.Info().Msg("nakama: Manual reconnection to host requested")
|
||||
|
||||
// Disconnect current connection if exists
|
||||
m.disconnectFromHost()
|
||||
|
||||
// Wait a moment before reconnecting
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Reconnect
|
||||
m.connectToHost()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveStaleConnections removes connections that haven't responded to ping in a while
|
||||
func (m *Manager) RemoveStaleConnections() {
|
||||
if !m.settings.IsHost {
|
||||
return
|
||||
}
|
||||
|
||||
staleThreshold := 90 * time.Second // Consider connections stale after 90 seconds of no ping
|
||||
now := time.Now()
|
||||
|
||||
var staleConnections []string
|
||||
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
conn.mu.RLock()
|
||||
lastPing := conn.LastPing
|
||||
authenticated := conn.Authenticated
|
||||
conn.mu.RUnlock()
|
||||
|
||||
// Only check authenticated connections
|
||||
if !authenticated {
|
||||
return true
|
||||
}
|
||||
|
||||
// If LastPing is zero, use connection time as reference
|
||||
if lastPing.IsZero() {
|
||||
lastPing = now.Add(-staleThreshold - time.Minute)
|
||||
}
|
||||
|
||||
if now.Sub(lastPing) > staleThreshold {
|
||||
staleConnections = append(staleConnections, id)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Remove stale connections
|
||||
for _, id := range staleConnections {
|
||||
if conn, exists := m.peerConnections.Get(id); exists {
|
||||
// Double-check to avoid race conditions
|
||||
conn.mu.RLock()
|
||||
lastPing := conn.LastPing
|
||||
if lastPing.IsZero() {
|
||||
lastPing = now.Add(-staleThreshold - time.Minute)
|
||||
}
|
||||
isStale := now.Sub(lastPing) > staleThreshold
|
||||
conn.mu.RUnlock()
|
||||
|
||||
if isStale {
|
||||
m.logger.Info().Str("peerId", conn.PeerId).Str("internalConnID", id).Msg("nakama: Removing stale peer connection")
|
||||
|
||||
// Remove from map first to prevent re-addition
|
||||
m.peerConnections.Delete(id)
|
||||
|
||||
// Remove peer from watch party if they were participating
|
||||
m.watchPartyManager.HandlePeerDisconnected(conn.PeerId)
|
||||
|
||||
// Then close the connection (this will trigger the defer cleanup in handlePeerConnection)
|
||||
conn.Close()
|
||||
|
||||
// Send event about peer disconnection
|
||||
m.wsEventManager.SendEvent(events.NakamaPeerDisconnected, map[string]interface{}{
|
||||
"peerId": conn.PeerId,
|
||||
"reason": "stale_connection",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(staleConnections) > 0 {
|
||||
m.logger.Info().Int("count", len(staleConnections)).Msg("nakama: Removed stale peer connections")
|
||||
}
|
||||
}
|
||||
|
||||
// FindPeerByPeerID finds a peer connection by their PeerID
|
||||
func (m *Manager) FindPeerByPeerID(peerID string) (*PeerConnection, bool) {
|
||||
var found *PeerConnection
|
||||
m.peerConnections.Range(func(id string, conn *PeerConnection) bool {
|
||||
if conn.PeerId == peerID {
|
||||
found = conn
|
||||
return false // Stop iteration
|
||||
}
|
||||
return true
|
||||
})
|
||||
return found, found != nil
|
||||
}
|
||||
Reference in New Issue
Block a user