node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,289 @@
package events
import (
"os"
"seanime/internal/util"
"seanime/internal/util/result"
"sync"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/gorilla/websocket"
"github.com/rs/zerolog"
)
type WSEventManagerInterface interface {
SendEvent(t string, payload interface{})
SendEventTo(clientId string, t string, payload interface{}, noLog ...bool)
SubscribeToClientEvents(id string) *ClientEventSubscriber
SubscribeToClientNativePlayerEvents(id string) *ClientEventSubscriber
SubscribeToClientNakamaEvents(id string) *ClientEventSubscriber
UnsubscribeFromClientEvents(id string)
}
type GlobalWSEventManagerWrapper struct {
WSEventManager WSEventManagerInterface
}
var GlobalWSEventManager *GlobalWSEventManagerWrapper
func (w *GlobalWSEventManagerWrapper) SendEvent(t string, payload interface{}) {
if w.WSEventManager == nil {
return
}
w.WSEventManager.SendEvent(t, payload)
}
func (w *GlobalWSEventManagerWrapper) SendEventTo(clientId string, t string, payload interface{}, noLog ...bool) {
if w.WSEventManager == nil {
return
}
w.WSEventManager.SendEventTo(clientId, t, payload, noLog...)
}
type (
// WSEventManager holds the websocket connection instance.
// It is attached to the App instance, so it is available to other handlers.
WSEventManager struct {
Conns []*WSConn
Logger *zerolog.Logger
hasHadConnection bool
mu sync.Mutex
eventMu sync.RWMutex
clientEventSubscribers *result.Map[string, *ClientEventSubscriber]
clientNativePlayerEventSubscribers *result.Map[string, *ClientEventSubscriber]
nakamaEventSubscribers *result.Map[string, *ClientEventSubscriber]
}
ClientEventSubscriber struct {
Channel chan *WebsocketClientEvent
mu sync.RWMutex
closed bool
}
WSConn struct {
ID string
Conn *websocket.Conn
}
WSEvent struct {
Type string `json:"type"`
Payload interface{} `json:"payload"`
}
)
// NewWSEventManager creates a new WSEventManager instance for App.
func NewWSEventManager(logger *zerolog.Logger) *WSEventManager {
ret := &WSEventManager{
Logger: logger,
Conns: make([]*WSConn, 0),
clientEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
clientNativePlayerEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
nakamaEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
}
GlobalWSEventManager = &GlobalWSEventManagerWrapper{
WSEventManager: ret,
}
return ret
}
// ExitIfNoConnsAsDesktopSidecar monitors the websocket connection as a desktop sidecar.
// It checks for a connection every 5 seconds. If a connection is lost, it starts a countdown a waits for 15 seconds.
// If a connection is not established within 15 seconds, it will exit the app.
func (m *WSEventManager) ExitIfNoConnsAsDesktopSidecar() {
go func() {
defer util.HandlePanicInModuleThen("events/ExitIfNoConnsAsDesktopSidecar", func() {})
m.Logger.Info().Msg("ws: Monitoring connection as desktop sidecar")
// Create a ticker to check connection every 5 seconds
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
// Track connection loss time
var connectionLostTime time.Time
exitTimeout := 10 * time.Second
for range ticker.C {
// Check WebSocket connection status
if len(m.Conns) == 0 && m.hasHadConnection {
// If not connected and first detection of connection loss
if connectionLostTime.IsZero() {
m.Logger.Warn().Msg("ws: No connection detected. Starting countdown...")
connectionLostTime = time.Now()
}
// Check if connection has been lost for more than 15 seconds
if time.Since(connectionLostTime) > exitTimeout {
m.Logger.Warn().Msg("ws: No connection detected for 10 seconds. Exiting...")
os.Exit(1)
}
} else {
// Connection is active, reset connection lost time
connectionLostTime = time.Time{}
}
}
}()
}
func (m *WSEventManager) AddConn(id string, conn *websocket.Conn) {
m.hasHadConnection = true
m.Conns = append(m.Conns, &WSConn{
ID: id,
Conn: conn,
})
}
func (m *WSEventManager) RemoveConn(id string) {
for i, conn := range m.Conns {
if conn.ID == id {
m.Conns = append(m.Conns[:i], m.Conns[i+1:]...)
break
}
}
}
// SendEvent sends a websocket event to the client.
func (m *WSEventManager) SendEvent(t string, payload interface{}) {
m.mu.Lock()
defer m.mu.Unlock()
// If there's no connection, do nothing
//if m.Conn == nil {
// return
//}
if t != PlaybackManagerProgressPlaybackState && payload == nil {
m.Logger.Trace().Str("type", t).Msg("ws: Sending message")
}
for _, conn := range m.Conns {
err := conn.Conn.WriteJSON(WSEvent{
Type: t,
Payload: payload,
})
if err != nil {
// Note: NaN error coming from [progress_tracking.go]
//m.Logger.Err(err).Msg("ws: Failed to send message")
}
//m.Logger.Trace().Str("type", t).Msg("ws: Sent message")
}
//err := m.Conn.WriteJSON(WSEvent{
// Type: t,
// Payload: payload,
//})
//if err != nil {
// m.Logger.Err(err).Msg("ws: Failed to send message")
//}
//m.Logger.Trace().Str("type", t).Msg("ws: Sent message")
}
// SendEventTo sends a websocket event to the specified client.
func (m *WSEventManager) SendEventTo(clientId string, t string, payload interface{}, noLog ...bool) {
m.mu.Lock()
defer m.mu.Unlock()
for _, conn := range m.Conns {
if conn.ID == clientId {
if t != "pong" {
if len(noLog) == 0 || !noLog[0] {
truncated := spew.Sprint(payload)
if len(truncated) > 500 {
truncated = truncated[:500] + "..."
}
m.Logger.Trace().Str("to", clientId).Str("type", t).Str("payload", truncated).Msg("ws: Sending message")
}
}
_ = conn.Conn.WriteJSON(WSEvent{
Type: t,
Payload: payload,
})
}
}
}
func (m *WSEventManager) SendStringTo(clientId string, s string) {
m.mu.Lock()
defer m.mu.Unlock()
for _, conn := range m.Conns {
if conn.ID == clientId {
_ = conn.Conn.WriteMessage(websocket.TextMessage, []byte(s))
}
}
}
func (m *WSEventManager) OnClientEvent(event *WebsocketClientEvent) {
m.eventMu.RLock()
defer m.eventMu.RUnlock()
onEvent := func(key string, subscriber *ClientEventSubscriber) bool {
go func() {
defer util.HandlePanicInModuleThen("events/OnClientEvent/clientNativePlayerEventSubscribers", func() {})
subscriber.mu.RLock()
defer subscriber.mu.RUnlock()
if !subscriber.closed {
select {
case subscriber.Channel <- event:
default:
// Channel is blocked, skip sending
m.Logger.Warn().Msg("ws: Client event channel is blocked, event dropped")
}
}
}()
return true
}
switch event.Type {
case NativePlayerEventType:
m.clientNativePlayerEventSubscribers.Range(onEvent)
case NakamaEventType:
m.nakamaEventSubscribers.Range(onEvent)
default:
m.clientEventSubscribers.Range(onEvent)
}
}
func (m *WSEventManager) SubscribeToClientEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 900),
}
m.clientEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) SubscribeToClientNativePlayerEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 100),
}
m.clientNativePlayerEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) SubscribeToClientNakamaEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 100),
}
m.nakamaEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) UnsubscribeFromClientEvents(id string) {
m.eventMu.Lock()
defer m.eventMu.Unlock()
defer func() {
if r := recover(); r != nil {
m.Logger.Warn().Msg("ws: Failed to unsubscribe from client events")
}
}()
subscriber, ok := m.clientEventSubscribers.Get(id)
if !ok {
subscriber, ok = m.clientNativePlayerEventSubscribers.Get(id)
}
if ok {
subscriber.mu.Lock()
defer subscriber.mu.Unlock()
subscriber.closed = true
m.clientEventSubscribers.Delete(id)
close(subscriber.Channel)
}
}