package controller import ( "errors" socketio "github.com/googollee/go-socket.io" "github.com/hyperzlib/isekai-remote-playback/context" "github.com/hyperzlib/isekai-remote-playback/events" "github.com/hyperzlib/isekai-remote-playback/store" "github.com/hyperzlib/isekai-remote-playback/utils" "log" ) type ServerSocketContext struct { IsAuth bool } type ServerSocketController struct { SocketController appsConfig utils.ApplicationsConfig } func NewServerSocketController(basePath string, ctx *context.ServerContext) (*ServerSocketController, error) { appConfig, err := utils.GetApplicationsConfig() if err != nil { panic("Cannot load applications config: " + err.Error()) } instance := &ServerSocketController{ SocketController: SocketController{ basePath: basePath, sockets: ctx.Sockets, events: ctx.EventSource, context: ctx, storeModel: store.NewStoreModel(), }, appsConfig: appConfig, } err = instance.Initialize() if err != nil { return nil, err } return instance, nil } func (c *ServerSocketController) Initialize() error { server := c.sockets server.OnConnect(c.basePath, func(s socketio.Conn) error { s.SetContext(&ServerSocketContext{}) log.Println("Server connected: " + s.RemoteAddr().String()) /*time.AfterFunc(5*time.Second, func() { // 关闭5秒内为登录成功的链接 ctx := GetContext[ServerSocketContext](s) if !ctx.IsAuth { log.Println("Server auth timeout: " + s.RemoteAddr().String()) s.Emit("error", "ERR::AUTH_TIMEOUT") time.AfterFunc(1000, func() { s.Close() }) } })*/ return nil }) server.OnEvent(c.basePath, "auth:login", c.AppLogin) server.OnEvent(c.basePath, "user:update", c.UpdateUser) server.OnEvent(c.basePath, "user:player:bind", c.UserBindPlayer) server.OnError(c.basePath, func(s socketio.Conn, e error) { log.Println("Socket error:", e) }) c.events.AddListener("bindUser", c.OnBindUser) return nil } // CheckAuth 检测登录 func (c *ServerSocketController) CheckAuth(s socketio.Conn) bool { ctx := GetContext[ServerSocketContext](s) if !ctx.IsAuth { s.Emit("error", "ERR::NEED_AUTH") return false } return true } func (c *ServerSocketController) AppLogin(s socketio.Conn, appId string, token string) { if appId == "" || token == "" { s.Emit("error", "ERR::AUTH_PARAM_INVALID") s.Close() return } appConfig, ok := c.appsConfig[appId] if !ok { s.Emit("error", "ERR::AUTH_APP_NOT_EXISTS") s.Close() return } if token != appConfig.Token { s.Emit("error", "ERR::AUTH_APP_TOKEN_INVALID") s.Close() return } // 设置登录状态 ctx := GetContext[ServerSocketContext](s) ctx.IsAuth = true s.Emit("auth:update", true) log.Println("Server auth success: " + s.RemoteAddr().String()) } func (c *ServerSocketController) UpdateUser(s socketio.Conn, user string, displayName string) { if !c.CheckAuth(s) { return } if user == "" { s.Emit("error:user:update", "ERR::PARAMS_INVALID", user) } userInfo := c.storeModel.GetUserInfo(user) if userInfo == nil { userInfo = new(store.UserInfo) } if displayName != "" { userInfo.DisplayName = displayName } else { userInfo.DisplayName = user } type PlayerInfoResponse struct { Name string `json:"name"` Token string `json:"token"` IsOnline bool `json:"online"` } newBindingPlayers := make([]string, 0) boundPlayers := make([]*PlayerInfoResponse, 0) for _, token := range userInfo.BoundPlayer { playerInfo := c.storeModel.GetPlayerInfo(token) if playerInfo != nil { newBindingPlayers = append(newBindingPlayers, token) resPlayerInfo := new(PlayerInfoResponse) resPlayerInfo.Name = playerInfo.Name resPlayerInfo.Token = utils.AddAsterisks(token, 10, 10) resPlayerInfo.IsOnline = playerInfo.IsOnline boundPlayers = append(boundPlayers, resPlayerInfo) } } userInfo.BoundPlayer = newBindingPlayers // 保存用户信息 c.storeModel.SetUserInfo(user, userInfo) s.Emit("user:updated", user, boundPlayers) } func (c *ServerSocketController) UserBindPlayer(s socketio.Conn, user string, code string) { if !c.CheckAuth(s) { return } if user == "" || code == "" { s.Emit("error:user:player:bind", "ERR::PARAMS_INVALID", user, code) } instance := clientSocketControllerInstance _, err := instance.BindPlayer(user, code) if err != nil { if errors.Is(err, ErrParamsInvalid) { s.Emit("error:user:player:bind", "ERR::PARAMS_INVALID", user, code) } else if errors.Is(err, ErrPlayerNotExists) { s.Emit("error:user:player:bind", "ERR::PLAYER_NOT_EXISTS", user, code) } else { log.Println("Cannot bind user: ", err.Error()) s.Emit("error:user:player:bind", "ERR::UNKNOWN", user, code) } } } // OnBindUser 播放器绑定用户 func (c *ServerSocketController) OnBindUser(eventObj ...interface{}) { event, err := utils.GetEvent[events.BindUserEvent](eventObj) if err != nil { log.Panicln("Cannot get event in OnBindUser: ", err.Error()) } playerInfo := c.storeModel.GetPlayerInfo(event.Token) if playerInfo == nil { log.Println("Cannot find player info for token '" + event.Token + "' while bind player") return } // 广播客户端连接的信息 c.sockets.BroadcastToNamespace(c.basePath, "user:player:bound", event.Token, playerInfo.Name) }