You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.2 KiB
Go

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)
}