diff --git a/api/operation.go b/api/operation.go index 801f992..72b9a0f 100644 --- a/api/operation.go +++ b/api/operation.go @@ -5,9 +5,10 @@ import ( "fmt" "github.com/golang/protobuf/proto" "reflect" + "strings" ) -const opOffset int32 = 13578 +const OpOffset int32 = 13578 // 注册消息类型 var protoInstances = []proto.Message{ @@ -21,6 +22,7 @@ var protoInstances = []proto.Message{ var ( prototypeOpMap = make(map[reflect.Type]int32) opPrototypeMap = make(map[int32]reflect.Type) // TODO array + opProtoNameMap = make(map[int32][2]string) // opInstanceMap = make(map[int]any) ) @@ -28,9 +30,17 @@ func init() { for i, defaultInstance := range protoInstances { op := int32(i) t := reflect.TypeOf(defaultInstance) - prototypeOpMap[t] = op + opOffset + prototypeOpMap[t] = op + OpOffset opPrototypeMap[op] = t // opInstanceMap[i] = defaultInstance + + pkg := t.Elem().PkgPath() + idx := strings.LastIndex(pkg, "/") + if idx != -1 { + pkg = pkg[idx+1:] + } + name := t.Elem().Name() + opProtoNameMap[op+OpOffset] = [2]string{pkg, name} } } @@ -44,10 +54,15 @@ func GetProtoOp(msg any) (int32, error) { } func NewProtoInstance(op int32) (proto.Message, error) { - prototype := opPrototypeMap[op-opOffset] + prototype := opPrototypeMap[op-OpOffset] if prototype == nil { return nil, errors.New(fmt.Sprintf("op :%d not registry!", op)) } val := reflect.New(prototype.Elem()) return val.Interface().(proto.Message), nil } + +// GetOpNameMap 获取编号和消息 +func GetOpNameMap() map[int32][2]string { + return opProtoNameMap +} diff --git a/api/poker.pb.go b/api/poker.pb.go index 5c8967b..5e2a9dc 100644 --- a/api/poker.pb.go +++ b/api/poker.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.17.3 +// protoc v3.21.12 // source: poker.proto package api diff --git a/etc/config.toml b/etc/config.toml index c482214..38414d4 100644 --- a/etc/config.toml +++ b/etc/config.toml @@ -5,10 +5,11 @@ addr = ":9999" [auth] aesTokenKey = "9Nz3Y6DES3msAFndz4QsJAECUIFkf+KKaRa+jRnNALk=" ignoreUrl = [ - "/ws", - "/auth/captcha", - "/auth/authorize", - "/user/findByName", + "/api/conn/ws", + "/api/conn/opMap", + "/api/auth/captcha", + "/api/auth/authorize", + "/api/user/findByName", ] # 需要输入图形验证码的url captchaUrl = [ diff --git a/internal/server/server.go b/internal/server/server.go index f9e6869..c8c51dd 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,6 +2,8 @@ package server import ( "github.com/gin-gonic/gin" + "net/http" + "texas-poker-bk/api" "texas-poker-bk/internal/service" ) @@ -13,14 +15,20 @@ func NewServer() *gin.Engine { // session storage server.Use(SessionStore("golang-tech-stack")) + base := server.Group("/api") + conn := base.Group("/conn") + // 消息映射关系 TODO 生成 json 放在前端 + conn.GET("/opMap", func(ctx *gin.Context) { + ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"offset": api.OpOffset, "opMap": api.GetOpNameMap()}}) + }) // websocket - server.GET("/ws", Upgrade) + conn.GET("/ws", Upgrade) - auth := server.Group("/auth") + auth := base.Group("/auth") auth.GET("/captcha", service.Captcha) // 验证码 auth.POST("/authorize", service.Authorize) // 登录或注册认证 - user := server.Group("/user") + user := base.Group("/user") user.GET("/findByName", service.FindUserByName) return server diff --git a/internal/server/ws.go b/internal/server/ws.go index 349891b..ba707c2 100644 --- a/internal/server/ws.go +++ b/internal/server/ws.go @@ -83,24 +83,34 @@ func handleNetClient(client *session.NetClient) { client.Close("api body unmarshal fail! " + err.Error()) } // TODO queue channel -> msg -> handler + var called bool + var res proto.Message + var resErr error handleNetClient := NetClientHandlers[wrap.Op] - if handleNetClient != nil { - handleNetClient(client, msg) - continue + if called = handleNetClient != nil; called { + res, resErr = handleNetClient(client, msg) + // continue + } else { + // account process + handlerNetAccount := NetAccountHandlers[wrap.Op] + if called = handlerNetAccount != nil; called { + // TODO account check + res, resErr = handlerNetAccount(client.Account, msg) + // continue + } else { + // player process + handlerPlayer := PlayerHandlers[wrap.Op] + if called = handlerPlayer != nil; called { + // TODO player check + res, resErr = handlerPlayer(nil, msg) + } + } } - // account process - handlerNetAccount := NetAccountHandlers[wrap.Op] - if handlerNetAccount != nil { - // TODO account check - handlerNetAccount(client.Account, msg) - continue - } - // player process - handlerPlayer := PlayerHandlers[wrap.Op] - if handlerPlayer != nil { - // TODO player check - handlerPlayer(nil, msg) - continue + // TODO 处理请求对应消息响应 + fmt.Println(called, res, resErr) + // 写响应 + if res != nil { + client.WriteSeq(wrap.Seq, res) } // log not found handler wrap.Op diff --git a/internal/server/wsroute.go b/internal/server/wsroute.go index b17d872..fe3c73d 100644 --- a/internal/server/wsroute.go +++ b/internal/server/wsroute.go @@ -5,13 +5,29 @@ import ( "reflect" "texas-poker-bk/api" "texas-poker-bk/internal/service" - "texas-poker-bk/internal/session" ) -var NetClientHandlers = make(map[int32]func(*session.NetClient, proto.Message)) -var NetAccountHandlers = make(map[int32]func(*session.NetAccount, proto.Message)) -var PlayerHandlers = make(map[int32]func(*session.Player, proto.Message)) +var NetClientHandlers = make(map[int32]func(*session.NetClient, proto.Message) (proto.Message, error)) +var NetAccountHandlers = make(map[int32]func(*session.NetAccount, proto.Message) (proto.Message, error)) +var PlayerHandlers = make(map[int32]func(*session.Player, proto.Message) (proto.Message, error)) + +func init() { + HandleNetClientMsg(&api.ReqIdentity{}, func(client *session.NetClient, msg *api.ReqIdentity) (proto.Message, error) { + subject, err := service.DecodeSubject(msg.Token) + if err != nil { + client.Close("authorize failed!") + return nil, nil + } + account := &session.NetAccount{Id: subject.Id, UserName: subject.Name, Client: client} + client.Account = account + + // response + res := &api.ResIdentity{Status: 200, Msg: "ok"} + // client.Write(res) + return res, nil + }) +} func checkExistsTypeHandler(op int32, err error) { if err != nil { @@ -23,42 +39,26 @@ func checkExistsTypeHandler(op int32, err error) { } } -func HandleNetClientMsg[T proto.Message](msg T, f func(*session.NetClient, T)) { +func HandleNetClientMsg[T proto.Message](msg T, f func(*session.NetClient, T) (proto.Message, error)) { op, err := api.GetProtoOp(msg) checkExistsTypeHandler(op, err) - NetClientHandlers[op] = func(client *session.NetClient, msg proto.Message) { - f(client, msg.(T)) + NetClientHandlers[op] = func(client *session.NetClient, msg proto.Message) (proto.Message, error) { + return f(client, msg.(T)) } } -func HandleNetAccountMsg[T proto.Message](msg T, f func(*session.NetAccount, T)) { +func HandleNetAccountMsg[T proto.Message](msg T, f func(*session.NetAccount, T) (proto.Message, error)) { op, err := api.GetProtoOp(msg) checkExistsTypeHandler(op, err) - NetAccountHandlers[op] = func(account *session.NetAccount, msg proto.Message) { - f(account, msg.(T)) + NetAccountHandlers[op] = func(account *session.NetAccount, msg proto.Message) (proto.Message, error) { + return f(account, msg.(T)) } } -func HandleNetPlayerMsg[T proto.Message](msg T, f func(*session.Player, T)) { +func HandleNetPlayerMsg[T proto.Message](msg T, f func(*session.Player, T) (proto.Message, error)) { op, err := api.GetProtoOp(msg) checkExistsTypeHandler(op, err) - PlayerHandlers[op] = func(player *session.Player, msg proto.Message) { - f(player, msg.(T)) + PlayerHandlers[op] = func(player *session.Player, msg proto.Message) (proto.Message, error) { + return f(player, msg.(T)) } } - -func init() { - HandleNetClientMsg(&api.ReqIdentity{}, func(client *session.NetClient, msg *api.ReqIdentity) { - subject, err := service.DecodeSubject(msg.Token) - if err != nil { - client.Close("authorize failed!") - return - } - account := &session.NetAccount{Id: subject.Id, UserName: subject.Name} - client.Account = account - - // response - res := &api.ResIdentity{Status: 200, Msg: "ok"} - client.Write(res) - }) -} diff --git a/internal/service/auth.go b/internal/service/auth.go index 84223c4..2ad9200 100644 --- a/internal/service/auth.go +++ b/internal/service/auth.go @@ -3,6 +3,7 @@ package service import ( "encoding/base64" "encoding/json" + "fmt" "github.com/gin-gonic/gin" "net/http" "texas-poker-bk/internal/conf" @@ -21,6 +22,14 @@ var ( aesTokenKeyBytes []byte // token aes 加密 key ) +func init() { + keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey) + if err != nil { + panic(err) + } + aesTokenKeyBytes = keyBytes +} + // Authorize 登录或注册 func Authorize(ctx *gin.Context) { username := ctx.PostForm("username") @@ -29,7 +38,7 @@ func Authorize(ctx *gin.Context) { //username := ctx.Param("username") //password := ctx.Param("password") if username == "" || password == "" { - ctx.JSON(http.StatusBadRequest, gin.H{"api": "用户名或密码不能为空"}) + ctx.JSON(http.StatusBadRequest, gin.H{"msg": "用户名或密码不能为空"}) return } user := userDao.FindUserByName(username) @@ -39,7 +48,7 @@ func Authorize(ctx *gin.Context) { } else { // 校验密码 if user.Password != password { - ctx.JSON(http.StatusUnauthorized, gin.H{"api": "用户名或密码有误"}) + ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "用户名或密码有误"}) return } } @@ -47,7 +56,7 @@ func Authorize(ctx *gin.Context) { sub := &session.Subject{Id: user.Id, Name: user.Username, Time: time.Now().UnixMilli()} token, err := EncodeSubject(sub) if err != nil { - ctx.JSON(http.StatusUnauthorized, gin.H{"api": err.Error()}) + ctx.JSON(http.StatusUnauthorized, gin.H{"msg": err.Error()}) return } ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"token": token}}) @@ -65,14 +74,6 @@ func registerUser(username string, password string) *entity.User { return user } -func init() { - keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey) - if err != nil { - panic(err) - } - aesTokenKeyBytes = keyBytes -} - // SubjectAuthFilter 认证过滤器 func SubjectAuthFilter(ctx *gin.Context) { @@ -88,14 +89,16 @@ func SubjectAuthFilter(ctx *gin.Context) { auth := ctx.GetHeader("Authorization") if auth == "" { - ctx.JSON(http.StatusUnauthorized, gin.H{"api": "请登录后进行操作"}) + ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "请登录后进行操作"}) ctx.Abort() + return } defer func() { // 捕获aes解析错误 if r := recover(); r != nil { - ctx.JSON(http.StatusUnauthorized, gin.H{"api": "认证失败,请重新登录"}) + fmt.Println("request error:", r) + ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "认证失败,请重新登录"}) ctx.Abort() } }() @@ -103,7 +106,7 @@ func SubjectAuthFilter(ctx *gin.Context) { // 这里返回 error,不然后捕获后续执行 handler 的 panic subject, err := DecodeSubject(auth) if err != nil { - ctx.JSON(http.StatusUnauthorized, gin.H{"api": "认证失败,请重新登录"}) + ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "认证失败,请重新登录"}) ctx.Abort() } else { // 设置token用户到请求上下文 diff --git a/internal/service/user.go b/internal/service/user.go index 53ce594..21faea3 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -27,12 +27,12 @@ func FindUserByName(ctx *gin.Context) { user := userDao.FindUserByName(username) if user == nil { ctx.JSON(http.StatusOK, gin.H{ - "api": "not found", + "msg": "not found", }) return } ctx.JSON(http.StatusOK, gin.H{ - "api": "ok", + "msg": "ok", "data": user, }) } diff --git a/internal/session/net_client.go b/internal/session/net_client.go index 8fff079..cc7c8ff 100644 --- a/internal/session/net_client.go +++ b/internal/session/net_client.go @@ -22,12 +22,16 @@ func (c *NetClient) Close(reason string) { } func (c *NetClient) Write(msg proto.Message) { + c.WriteSeq(0, msg) +} + +func (c *NetClient) WriteSeq(seq int32, msg proto.Message) { op, err := api.GetProtoOp(msg) if err != nil { return } bytes, err := proto.Marshal(msg) - wrap := &api.ProtoWrap{Ver: 1, Op: op, Seq: 1, Body: bytes} + wrap := &api.ProtoWrap{Ver: 1, Op: op, Seq: seq, Body: bytes} wrapBytes, err := proto.Marshal(wrap) err = c.Conn.WriteMessage(websocket.BinaryMessage, wrapBytes) }