├── .gitignore ├── LICENSE ├── README.md ├── agent ├── README.md ├── client │ └── main.go ├── main.go └── service │ ├── frame.go │ └── service.go ├── doc └── img │ ├── event.png │ ├── users.png │ ├── 组件图.png │ ├── 认证.png │ └── 通信图.png ├── docker ├── README.md ├── compose │ ├── README.md │ └── infrastructure │ │ └── docker-compose.yml ├── data │ ├── README.md │ ├── bind9 │ │ ├── ifunbow.com.zone │ │ └── named.conf │ └── nginx │ │ └── conf.d │ │ ├── default.conf │ │ ├── drone.conf │ │ ├── gogs.conf │ │ └── tpapi.conf └── dockerfile │ ├── README.md │ └── etcd │ ├── Dockerfile │ ├── etcd │ └── etcdctl ├── frontapi └── README.md ├── game ├── README.md ├── gamer │ ├── card.go │ ├── card_test.go │ ├── const.go │ ├── error.go │ ├── game.go │ ├── player.go │ ├── robot.go │ ├── room.go │ ├── seat.go │ └── table.go ├── main.go ├── pb │ ├── compile.sh │ ├── game.pb.go │ └── game.proto └── service │ ├── endpoints.go │ ├── error.go │ ├── frame.go │ ├── grpcairfone.go │ ├── instrumenting.go │ ├── logging.go │ ├── service.go │ ├── transportgrpc.go │ └── ucclient.go └── usercenter ├── README.md ├── center ├── account.go ├── accountinfo.go ├── error.go ├── user.go └── usermanager.go ├── client └── grpc.go ├── cockroach └── repository.go ├── kafka └── kafka.go ├── main.go ├── pb ├── compile.sh ├── user.pb.go └── user.proto └── service ├── endpoints.go ├── error.go ├── instrumenting.go ├── logging.go ├── service.go ├── transportgrpc.go └── transporthttp.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Go template 3 | # Binaries for programs and plugins 4 | *.exe 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 16 | .glide/ 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 yiv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yivgame 2 | Yivgame是用go语言基于go-kit写的一套微服务架构游戏服务器方案,它不是一个框架,是一整套游戏服务器实例,每个模块只保留了一份示例代码实现。除了游戏服务器(长连接),还包含针对前端和后台运营的API接口服务器,运营后台的界面会使用Angular实现。 3 | 除了服务器本身之外,还会涉及docker部署的详细配置。 4 | 5 | ## 特性 6 | * 微服务架构 7 | * 客户端与游戏服务器通过 grpc 双向流(bidirectional streaming)实现透传 8 | * 客户端与服务端websocket通信 9 | * 实现 http endpoints 和 websocket endpoints 度量衡和日志 10 | ## 设计实践 11 | ### 微服务架构 12 | * 通过微服务架构,将传统的游戏服务器拆分成了不同的微服务 13 | * 不同的微服务器可通过grpc进行同步通信和通过kafka进行异步通信 14 | ### 领域驱动模型 15 | * 为了实现软件不同层次的解耦,各服务统一都参照领域驱动模型进行设计 16 | * 将微服务的软件结构由内至外分成游戏领域业务层、用例层、接口层和设施依赖层 17 | * 各层之间严格遵守由外向内的单向依赖 18 | 19 | 业务层主要实现游戏或服务器的是核心逻辑,不关心外部实现,对文件系统、数据库等的依赖,业务层使用interface定义接口,由依赖层实现接口方法,并在main中通过依赖注入的方式传递给业务层调用。所以业务层除了引用一些基本的标准库外,几乎不引用第三方包。 20 | ### 事件驱动模型与Kafka 21 | * 整个微服务系统之间的核心通信方式是grpc同步调用和以kafka作为流平台的异步事件通信 22 | * 所有微服务内的被关注的活动都会以产生事件的方式发布到kafka,具有不同关注点的消费者分别订阅各自感兴趣的事件,kafka将事件推送给消费者,消费者做相以的事件处理和响应 23 | ### 事件驱动模型与数据分析 24 | * 以前做游戏数据分析的时候,都是通过联表查询 25 | * 使用事件驱动模型后,可把我们关注的玩家行为都作为事件记录下来,为不同的事件设计不同的属性,实现极具扩展性的数据分析 26 | ![图](doc/img/event.png) 27 | ![图](doc/img/users.png) 28 | 29 | ### 工程目录结构 30 | * 由于由于go-kit实现的微服务架构,所以在目录结构上尽量与go-kit的官方示例保持一致 31 | * 由于领域驱动模型是分层的,所以在设计工程目录结构的时候很自然的会把内层包的目录包含在外层内,由于我比较喜欢大多数go工程的偏平目录结构,所以没有严格按着领域层次来设计目录结构,反而是把不同层次包的目录放在了同级的目录下面,对我来说,这样显得直观、简单一些 32 | 33 | ### 无全局变量 34 | * 为了让软件在代码逻辑上面更加清晰,严格避免全局变量 35 | ### 数据缓存、数据存盘与Kafka 36 | * 所以玩家数据直接保存在服务内存中,便于直接进行数据处理 37 | * 玩家数据的修改通过WAL方式写入kafka,再由存盘服务异步地写入数据库 38 | * 由于使用了WAL方式,则玩家数据的redo和undo则很容易实现 39 | ### NewSQL CockroachDB 40 | * 数据持久化使用支持分布式事务的关系数据库CockroachDB 41 | * 使用CockroachDB可以很轻松的实现水平扩展、故障容错和自动恢复、负载均衡 42 | 43 | 我从v1.0开始使用CockroachDB,从v1.0到v1.0.6,CockroachDB在特定情况和压力下,一直存在崩溃的问题,自从v1.1发布,崩溃问题没再出现,但是性能一直没有大的改善。因为yivgame的数据几乎都存在内存中,只有存盘的时候需要写db,所以对整个yivgame系统来说,不存在db性能瓶颈。 44 | ## 模型 45 | ### 通信图 46 | ![通信图](doc/img/通信图.png) 47 | * 通信方式 48 | * HTTP:http为作短连接,主要用于后台运营系统的通信,另外,游戏中涉及到强交互的数据通信部分,也可以用http来通信 49 | * WebSocket:客户端使用cocos creator开发,长连接通信支持WebSocket,WebSocket主要用于游戏中实时和强交互的难通信 50 | * GRPC:基于HTTP/2协议GRPC,可以实现在一个socket连接上进行多stream通信,是go微服务生态中比较通用的通信方式 51 | * 数据格式 52 | * JSON:由于json格式的自解释性,主要将它作在游戏中短连接和后台运营系统接口的数据交换 53 | * Protobuf:主要用于客户端与服务端websocket间和微务间的数据交换 54 | 55 | ### 服务组件图 56 | ![服务组件图](doc/img/组件图.png) 57 | * Agent:主要用于客户端的接入,它直接将数据报文透传、转发到后端微服务,是一个傻网关、薄网关,几乎不参与业务逻辑和编解码业务数据,所以其代码逻辑相对简单,也极易进行水平扩展 58 | * UserCenter:所有玩家数据集中在 user center 进行管理,由 user center 负责游戏数据的读写删改查,它提供grpc接口供apigate、game server等其它需要请求玩家数据微服务使用 59 | * Game Server:主要负责游戏业务逻辑的处理 60 | ### 身份认证与鉴权 61 | ![图](doc/img/认证.png) 62 | * 服务与服务之间使用jwt进行身份认证 63 | * 通过API gateway实现单点登陆 64 | * 全局认证,每服务鉴权(do authentication globally, and authorization in every microservice) 65 | ## 设施依赖 66 | * docker:所有依赖设施服务和游戏实例通过docker社区版进行布署 67 | * rockcoach:作为持久化数据库 68 | * kafka:作为message queue和stream platform 69 | * etcd:用于服务发现 70 | * gogs:使用gogs进行版本管理 71 | * bind9:域名服务器,通过切换域名解析实现开发、测试网络的无缝切换 72 | ## go-kit generator 73 | * [yiv/gk](https://github.com/yiv/gk): go-kit 代码生成器是一把手电钻,go-kit目前发现的唯一的不爽就是写一个服务太啰嗦,它设计了一套很优雅的服务输出方式,但为了写一个服务接口,每个都要写一整套endpoint、set和transport,代码模式都是相同的,这一大坨大坨的代码基本相似,写多了就会很烦燥,感觉在做重复的工作,而且极易出错,找了几遍都没有发现完全符合go-kit官方示例的generator,最后选中了[kujtimiihoxha/gk](https://github.com/kujtimiihoxha/gk),但它还没有很多地方并不完善,也不完全适用于我,于是fork下来自己改,通过它自动生成代码,在写服务接口的时候可以美减少60%的重复代码,更重要的是,它极大地降低了出错的概率。 74 | 75 | ## 系统环境 76 | * Ubuntu Server 16.04 77 | 78 | ## 参考 79 | * [gonet/2](https://gonet2.github.io/): yivgame从gonet吸取了很多设计,如使用stream进行透传、引入kafka等 80 | * [go-kit](https://github.com/go-kit/kit): yivgame基于go-kit开发 81 | * [goddd](https://github.com/marcusolsson/goddd): 一个用go写的基于领域模型的样例APP 82 | * [Practical Persistence in Go: Organising Database Access](http://www.alexedwards.net/blog/organising-database-access) 83 | * [The Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) 84 | * [Applying The Clean Architecture to Go applications](http://manuel.kiessling.net/2012/09/28/applying-the-clean-architecture-to-go-applications/) 85 | * [一篇文章读懂分层架构](https://zhuanlan.zhihu.com/p/40353581) 86 | 87 | ## 关于设计的一些思考 88 | * 系统的复杂性只会转移,不会消失,直白简单背后都是脏活累活,简单都是有成本的,要么降低性能、要么回避一些特性如扩展性,要么由其它人来做,使用 go-kit 的好处是它看起来不那么简单,把设计目标直接体现在代码里,学习使用 go-kit 有助于提高软件设计能力 89 | * go-kit 不适合追求易上手、短平快的目标,它使用分层来分离关注,必定引入复杂性,代码看起来可能穿插啰嗦,但是关注分离的设计有助于逻辑解耦 90 | * go-kit 始于 service interface,始于关注业务领域,http 或是 grpc 只是对外发布的方式,放在最后处理 91 | * 不追求写法上的自由,要追求软件适应性的自由,自由不是说我想怎么写就怎么写,代码要有规范,团队要统一编程风格,才便于沟通,go-kit 不自由,因为他定义了自己软件的设计范式,导致的结果就是用它写出来的服务,看起来都长得差不多。牛逼到把代码写得像幅画,还能编译运行,只适合玩,不适合拿来做工程。 92 | * 微服务调用的编解码和通讯,引入的延时成本大概为 2 毫秒,国内互 Ping 延时通常为 40ms 93 | * 无论是框架还是语言,都只是工具,挑一个业界优秀的、趁手的、合适的,用好用熟用深,没必要把时间花在争论和纠结最好的几个当中哪个是才是最好的,即使工具是最好的,用得不好也是烂,关键是学习和掌握它们的设计精髓,才能万变不离其宗 94 | -------------------------------------------------------------------------------- /agent/README.md: -------------------------------------------------------------------------------- 1 | # agent 2 | -------------------------------------------------------------------------------- /agent/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net/url" 8 | "strconv" 9 | 10 | "github.com/gogo/protobuf/proto" 11 | "github.com/gorilla/websocket" 12 | 13 | "github.com/yiv/yivgame/game/pb" 14 | ) 15 | 16 | var ( 17 | agentAddr string = ":10050" 18 | ) 19 | 20 | type client struct { 21 | sk *websocket.Conn 22 | uid int64 23 | token string 24 | roomClass int32 25 | player *pb.Player 26 | } 27 | 28 | func newClient(uid int64) (c *client) { 29 | c = &client{ 30 | sk: newWebSocket(), 31 | uid: uid, 32 | token: strconv.FormatInt(uid, 10), 33 | roomClass: 3, 34 | } 35 | 36 | return 37 | } 38 | 39 | func (c *client) recv(errChan chan error) { 40 | for { 41 | _, message, err := c.sk.ReadMessage() 42 | if err != nil { 43 | log.Println("read:", err) 44 | errChan <- err 45 | return 46 | } 47 | code, pbBytes := splitBytes(message) 48 | if code == 10018 { 49 | res := &pb.GeRes{} 50 | proto.Unmarshal(pbBytes, res) 51 | fmt.Printf("[返回]发消息[code: %v]\n", res.Code) 52 | } else if code == 20019 { 53 | res := &pb.ChatMsg{} 54 | proto.Unmarshal(pbBytes, res) 55 | fmt.Printf("[通知]信息[发者: %v,编号:%v]\n", res.Sid, res.Mid) 56 | } else { 57 | res := &pb.GeRes{} 58 | proto.Unmarshal(pbBytes, res) 59 | fmt.Printf("[返回]未知[code: %v]\n", res.Code) 60 | } 61 | fmt.Printf("\n 17=消息\n\n") 62 | } 63 | } 64 | func (c *client) send(errChan chan error) { 65 | var cmdCode uint32 66 | fmt.Println("=======命令行启动=====") 67 | for { 68 | fmt.Scanln(&cmdCode) 69 | 70 | if cmdCode == 100 { 71 | c.sk.Close() 72 | continue 73 | } else if cmdCode == 99 { 74 | c.enterTable() 75 | continue 76 | } else { 77 | cmdCode += 10000 78 | } 79 | 80 | fmt.Printf("输入命令: %v\n", cmdCode) 81 | err := c.sk.WriteMessage(websocket.BinaryMessage, toBytes(cmdCode, nil)) 82 | if err != nil { 83 | fmt.Println("命令发送错误:", err) 84 | errChan <- err 85 | return 86 | } 87 | } 88 | 89 | } 90 | 91 | func newWebSocket() *websocket.Conn { 92 | u := url.URL{Scheme: "ws", Host: agentAddr, Path: "/ws"} 93 | log.Printf("connecting to %s", u.String()) 94 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) 95 | if err != nil { 96 | log.Fatal("dial:", err) 97 | } 98 | fmt.Println("socket connected ") 99 | return c 100 | } 101 | func splitBytes(payload []byte) (code uint32, pbBytes []byte) { 102 | code = uint32(payload[0])<<24 | uint32(payload[1])<<16 | uint32(payload[2])<<8 | uint32(payload[3]) 103 | pbBytes = payload[4:] 104 | return 105 | } 106 | func toBytes(i uint32, pbBytes []byte) (payload []byte) { 107 | payload = append(payload, byte(i>>24), byte(i>>16), byte(i>>8), byte(i)) 108 | payload = append(payload, pbBytes...) 109 | return 110 | } 111 | 112 | var ( 113 | uid = flag.Int("uid", 1, "the client uid") 114 | ) 115 | 116 | var ( 117 | userMap = map[int]int64{ 118 | 1: 953685341995009, 119 | 2: 2232870895617, 120 | } 121 | ) 122 | 123 | func main() { 124 | flag.Parse() 125 | uid := userMap[*uid] 126 | errChan := make(chan error) 127 | cli := newClient(uid) 128 | go cli.recv(errChan) 129 | go cli.send(errChan) 130 | fmt.Println("terminate by err :", <-errChan) 131 | } 132 | -------------------------------------------------------------------------------- /agent/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/http" 7 | "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | 12 | "github.com/prometheus/client_golang/prometheus/promhttp" 13 | "google.golang.org/grpc" 14 | 15 | "github.com/go-kit/kit/log" 16 | "github.com/go-kit/kit/log/level" 17 | 18 | "github.com/yiv/yivgame/agent/service" 19 | "github.com/yiv/yivgame/game/pb" 20 | ) 21 | 22 | var ( 23 | webSocketAddr = flag.String("websocket.addr", ":10050", "game agent webSocket address") 24 | gameServer = flag.String("grpc.gameserver", ":10020", "game server gRPC server address") 25 | debugAddr = flag.String("debug.addr", ":10051", "Debug and metrics listen address") 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | // Logging domain. 32 | var logger log.Logger 33 | { 34 | logger = log.NewLogfmtLogger(os.Stdout) 35 | logger = level.NewFilter(logger, level.AllowDebug()) 36 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 37 | logger = log.With(logger, "caller", log.DefaultCaller) 38 | } 39 | logger.Log("msg", "hello") 40 | defer logger.Log("msg", "goodbye") 41 | 42 | // Mechanical domain. 43 | errc := make(chan error) 44 | // Interrupt handler. 45 | go func() { 46 | c := make(chan os.Signal) 47 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 48 | errc <- fmt.Errorf("%s", <-c) 49 | }() 50 | 51 | // gRPC transport. 52 | conn, err := grpc.Dial(*gameServer, grpc.WithInsecure()) 53 | if err != nil { 54 | errc <- err 55 | return 56 | } 57 | defer conn.Close() 58 | client := pb.NewGameServiceClient(conn) 59 | 60 | // Business domain. 61 | agentService := service.NewAgentService(client, logger) 62 | 63 | // webSocket transport. 64 | go func() { 65 | logger := log.With(logger, "transport", "webSocket") 66 | m := http.NewServeMux() 67 | m.HandleFunc("/", agentService.WebSocketServer) 68 | logger.Log("addr", *webSocketAddr) 69 | errc <- http.ListenAndServe(*webSocketAddr, m) 70 | }() 71 | 72 | // Debug listener. 73 | go func() { 74 | logger := log.With(logger, "transport", "debug") 75 | 76 | m := http.NewServeMux() 77 | m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) 78 | m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) 79 | m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) 80 | m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) 81 | m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) 82 | m.Handle("/metrics", promhttp.Handler()) 83 | 84 | logger.Log("addr", *debugAddr) 85 | errc <- http.ListenAndServe(*debugAddr, m) 86 | }() 87 | logger.Log("terminated", <-errc) 88 | } 89 | -------------------------------------------------------------------------------- /agent/service/frame.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/yiv/yivgame/game/pb" 5 | ) 6 | 7 | func code2bytes(i uint32) (b []byte) { 8 | b = append(b, byte(i>>24), byte(i>>16), byte(i>>8), byte(i)) 9 | return 10 | } 11 | func bytes2code(b []byte) (i uint32) { 12 | i = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) 13 | return 14 | } 15 | 16 | func toframe(i uint32, pbBytes []byte) *pb.Frame { 17 | payload := append(code2bytes(i), pbBytes...) 18 | return &pb.Frame{Payload: payload} 19 | } 20 | func frameCode(f *pb.Frame) uint32 { 21 | payload := f.Payload 22 | return bytes2code(payload[0:4]) 23 | } 24 | func framePBbytes(f *pb.Frame) []byte { 25 | payload := f.Payload 26 | return payload[4:] 27 | } 28 | -------------------------------------------------------------------------------- /agent/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | "sync" 9 | 10 | "github.com/gorilla/websocket" 11 | "github.com/satori/go.uuid" 12 | 13 | "github.com/go-kit/kit/log" 14 | "github.com/go-kit/kit/log/level" 15 | 16 | "github.com/yiv/yivgame/game/pb" 17 | ) 18 | 19 | const ( 20 | WebSocketReadDeadline int = 15 //秒 21 | ) 22 | 23 | var ( 24 | ErrReadWebSocket = errors.New("err on read data from webSocket") 25 | ErrWriteWebSocket = errors.New("err on write data to webSocket") 26 | ErrReadRPCStream = errors.New("err on read data from rpc stream") 27 | ErrWriteRPCStream = errors.New("err on write data to rpc stream") 28 | ) 29 | 30 | type Session struct { 31 | id string //会话随机ID 32 | logger log.Logger 33 | as *AgentService 34 | conn *websocket.Conn //webSocket连接 35 | stream pb.GameService_StreamClient //rpc stream,每会话每stream 36 | wg sync.WaitGroup 37 | dieChan chan struct{} 38 | } 39 | 40 | func NewSession(id string, conn *websocket.Conn, stream pb.GameService_StreamClient, service *AgentService, logger log.Logger) *Session { 41 | s := &Session{ 42 | id: id, 43 | as: service, 44 | logger: logger, 45 | conn: conn, 46 | stream: stream, 47 | dieChan: make(chan struct{}), 48 | } 49 | return s 50 | } 51 | 52 | func (s *Session) ForwardToClient(payload []byte) (err error) { 53 | err = s.conn.WriteMessage(websocket.BinaryMessage, payload) 54 | level.Debug(s.logger).Log("id", s.id, "msg", "forward server data to client") 55 | if err != nil { 56 | level.Error(s.logger).Log("err", err.Error(), "msg", "webSocket write err") 57 | } 58 | return 59 | } 60 | func (s *Session) ForwardToServer(f *pb.Frame) (err error) { 61 | //转发到服务器 62 | err = s.stream.Send(f) 63 | level.Debug(s.logger).Log("id", s.id, "msg", "forward client data to server") 64 | if err != nil { 65 | level.Error(s.logger).Log("err", err.Error(), "msg", "stream send err") 66 | } 67 | return 68 | } 69 | func (s *Session) HandleClient() { 70 | errChan := make(chan error) 71 | go func() { 72 | s.wg.Add(1) 73 | defer s.wg.Done() 74 | //处理来自客户端的数据 75 | for { 76 | //s.conn.SetReadDeadline(time.Now().Add(time.Duration(WebSocketReadDeadline) * time.Second)) 77 | _, bytes, err := s.conn.ReadMessage() 78 | if err != nil { 79 | level.Error(s.logger).Log("id", s.id, "err", err.Error(), "msg", "session webSocket read data err") 80 | errChan <- ErrReadWebSocket 81 | return 82 | } 83 | err = s.ForwardToServer(&pb.Frame{Payload: bytes}) 84 | if err != nil { 85 | errChan <- ErrWriteRPCStream 86 | return 87 | } 88 | select { 89 | case <-s.dieChan: 90 | return 91 | default: 92 | } 93 | 94 | } 95 | }() 96 | 97 | go func() { 98 | s.wg.Add(1) 99 | defer s.wg.Done() 100 | //处理来自服务器的数据 101 | for { 102 | f, err := s.stream.Recv() 103 | if err != nil { 104 | level.Error(s.logger).Log("err", err.Error(), "msg", "stream recv err") 105 | errChan <- ErrReadRPCStream 106 | return 107 | } 108 | err = s.ForwardToClient(f.Payload) 109 | if err != nil { 110 | errChan <- ErrWriteWebSocket 111 | return 112 | } 113 | select { 114 | case <-s.dieChan: 115 | return 116 | default: 117 | 118 | } 119 | } 120 | 121 | }() 122 | s.as.closeSession(s.id, <-errChan) 123 | } 124 | 125 | type AgentService struct { 126 | mtx sync.RWMutex 127 | SessDieChan chan int64 128 | Sessions map[string]*Session 129 | logger log.Logger 130 | gameServerClient pb.GameServiceClient 131 | } 132 | 133 | func NewAgentService(gameServerClient pb.GameServiceClient, logger log.Logger) AgentService { 134 | level.Info(logger).Log("msg", "start new agent service") 135 | 136 | agent := AgentService{ 137 | gameServerClient: gameServerClient, 138 | logger: logger, 139 | Sessions: make(map[string]*Session), 140 | } 141 | return agent 142 | } 143 | 144 | func (a AgentService) WebSocketServer(w http.ResponseWriter, r *http.Request) { 145 | 146 | var upgrader = websocket.Upgrader{ 147 | CheckOrigin: func(r *http.Request) bool { return true }, 148 | } 149 | conn, err := upgrader.Upgrade(w, r, nil) 150 | if err != nil { 151 | level.Error(a.logger).Log("err", err.Error(), "msg", "err on create webSocket for session") 152 | return 153 | } 154 | stream, err := a.gameServerClient.Stream(context.Background()) 155 | if err != nil { 156 | conn.Close() 157 | level.Error(a.logger).Log("err", err.Error(), "msg", "err on create rpc stream for session") 158 | return 159 | } 160 | a.mtx.Lock() 161 | defer a.mtx.Unlock() 162 | id := uuid.NewV4().String() 163 | a.Sessions[id] = NewSession(id, conn, stream, &a, a.logger) 164 | go a.Sessions[id].HandleClient() 165 | fmt.Println("start new session") 166 | } 167 | 168 | func (a AgentService) closeSession(id string, err error) { 169 | a.mtx.Lock() 170 | defer a.mtx.Unlock() 171 | bf := len(a.Sessions) 172 | ss := a.Sessions[id] 173 | ss.conn.Close() 174 | ss.stream.CloseSend() 175 | close(ss.dieChan) 176 | ss.wg.Wait() 177 | delete(a.Sessions, id) 178 | af := len(a.Sessions) 179 | level.Error(a.logger).Log("id", id, "before", bf, "after", af, "err", err.Error(), "msg", "close session") 180 | } 181 | -------------------------------------------------------------------------------- /doc/img/event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/doc/img/event.png -------------------------------------------------------------------------------- /doc/img/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/doc/img/users.png -------------------------------------------------------------------------------- /doc/img/组件图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/doc/img/组件图.png -------------------------------------------------------------------------------- /doc/img/认证.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/doc/img/认证.png -------------------------------------------------------------------------------- /doc/img/通信图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/doc/img/通信图.png -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # docker 2 | 所有服务使用docker部署 3 | -------------------------------------------------------------------------------- /docker/compose/README.md: -------------------------------------------------------------------------------- 1 | # docker-compose 2 | 所有服务器使用docker容器布署,所有容器使用docker-compose进行编排 3 | * infrastructure:该目录下存放的docker-compose文件主要负责yivgame微服务架构的依赖设施容器编排 4 | * game:该目录存放的docker-compose文件负责yivgame各微服务的容器编排 -------------------------------------------------------------------------------- /docker/compose/infrastructure/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | image: nginx:1.12-alpine 5 | volumes: 6 | - /docker/data/www/service:/var/www/service 7 | - /docker/data/nginx/conf.d:/etc/nginx/conf.d 8 | ports: 9 | - 192.168.1.200:80:80 10 | dns: 11 | - "192.168.1.56" 12 | restart: always 13 | mysql: 14 | image: mysql:5.7 15 | ports: 16 | - 3306:3306 17 | environment: 18 | MYSQL_ROOT_PASSWORD: 123456 19 | volumes: 20 | - /docker/data/mysql/:/var/lib/mysql 21 | restart: always 22 | mongo: 23 | image: mongo:3.4.4 24 | ports: 25 | - 27017:27017 26 | volumes: 27 | - /docker/data/mongo/:/data/db 28 | restart: always 29 | gogs: 30 | image: gogs/gogs 31 | volumes: 32 | - /docker/data/gogs/gogs:/data/gogs 33 | - /docker/data/gogs/git:/data/git 34 | ports: 35 | - "10022:22" 36 | - "10080:3000" 37 | dns: 38 | - "192.168.1.56" 39 | restart: always 40 | drone-server: 41 | image: drone/drone:0.8.0 42 | ports: 43 | - 8000:8000 44 | volumes: 45 | - /var/lib/drone:/var/lib/drone/ 46 | restart: always 47 | environment: 48 | - DRONE_OPEN=true 49 | - DRONE_HOST=192.168.1.200 50 | - DRONE_GOGS=true 51 | - DRONE_GOGS_URL=http://192.168.1.200:10080 52 | - DRONE_SECRET=3a029f04d76d32e79367c4b3255dda4d 53 | 54 | drone-agent: 55 | image: drone/drone:0.8.0 56 | command: agent 57 | restart: always 58 | depends_on: 59 | - drone-server 60 | volumes: 61 | - /var/run/docker.sock:/var/run/docker.sock 62 | environment: 63 | - DRONE_HOST=192.168.1.200 64 | - DRONE_GOGS=true 65 | - DRONE_GOGS_URL=http://192.168.1.200:10080 66 | - DRONE_SERVER=ws://192.168.1.200:8000/ws/broker 67 | - DRONE_SECRET=3a029f04d76d32e79367c4b3255dda4d 68 | cockroach1: 69 | image: cockroachdb/cockroach:v1.0.3 70 | hostname: cockroach1 71 | ports: 72 | - "26257:26257" 73 | - "8080:8080" 74 | volumes: 75 | - /docker/data/cockroach/roach1:/cockroach/cockroach-data 76 | command: start --insecure 77 | networks: 78 | - cocknetwork 79 | restart: always 80 | cockroach2: 81 | image: cockroachdb/cockroach:v1.0.3 82 | hostname: cockroach2 83 | volumes: 84 | - /docker/data/cockroach/roach2:/cockroach/cockroach-data 85 | command: start --insecure --join=cockroach1 86 | networks: 87 | - cocknetwork 88 | restart: always 89 | cockroach3: 90 | image: cockroachdb/cockroach:v1.0.3 91 | hostname: cockroach3 92 | volumes: 93 | - /docker/data/cockroach/roach3:/cockroach/cockroach-data 94 | command: start --insecure --join=cockroach1 95 | networks: 96 | - cocknetwork 97 | restart: always 98 | etcd1: 99 | image: edwin/etcd:3.2.4 100 | hostname: etcd1 101 | ports: 102 | - "2379:2379" 103 | - "2380:2380" 104 | volumes: 105 | - /docker/data/etcd/etcd1:/etcd-data 106 | restart: always 107 | environment: 108 | - ETCD_NAME=etcd1 109 | - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.168.1.200:2380 110 | - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380 111 | - ETCD_ADVERTISE_CLIENT_URLS=http://192.168.1.200:2379 112 | - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 113 | - ETCD_INITIAL_CLUSTER=etcd1=http://192.168.1.200:2380 114 | - ETCD_DATA_DIR=/etcd-data 115 | - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster-1 116 | - ETCD_INITIAL_CLUSTER_STATE=new 117 | prometheus: 118 | image: prom/prometheus 119 | volumes: 120 | - /docker/data/prometheus:/etc/prometheus 121 | ports: 122 | - "9090:9090" 123 | restart: always 124 | opentracing: 125 | image: jaegertracing/all-in-one 126 | ports: 127 | - "16686:16686" 128 | - "5775/udp:5775/udp" 129 | restart: always 130 | zookeeper: 131 | image: wurstmeister/zookeeper 132 | ports: 133 | - "2181:2181" 134 | restart: always 135 | kafka: 136 | image: wurstmeister/kafka 137 | ports: 138 | - "9092:9092" 139 | environment: 140 | KAFKA_ADVERTISED_HOST_NAME: 192.168.1.200 141 | # KAFKA_CREATE_TOPICS: "test:1:1" 142 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 143 | volumes: 144 | - /var/run/docker.sock:/var/run/docker.sock 145 | restart: always 146 | networks: 147 | cocknetwork: 148 | driver: bridge -------------------------------------------------------------------------------- /docker/data/README.md: -------------------------------------------------------------------------------- 1 | # data directory 2 | 所有容器的数据目录都重定到data目下进行持久保存 -------------------------------------------------------------------------------- /docker/data/bind9/ifunbow.com.zone: -------------------------------------------------------------------------------- 1 | $TTL 600 2 | yivgame.com. IN SOA ns1.yivgame.com. admin.yivgame.com. ( 3 | 2013040101 4 | 1H 5 | 5M 6 | 2D 7 | 6H ) 8 | IN NS ns1 9 | ns1 IN A 192.168.1.56 10 | git IN A 192.168.1.200 11 | drone IN A 192.168.1.200 12 | db IN A 192.168.1.200 13 | tpapi IN A 192.168.1.200 14 | gameagent IN A 192.168.1.51 15 | test IN A 192.168.1.200 16 | kafka IN A 192.168.1.200 17 | etcd IN A 192.168.1.200 18 | h5 IN A 192.168.1.200 19 | 20 | -------------------------------------------------------------------------------- /docker/data/bind9/named.conf: -------------------------------------------------------------------------------- 1 | options { 2 | directory "/var/bind"; 3 | 4 | // Specify a list of CIDR masks which should be allowed to issue recursive 5 | // queries to the DNS server. Do NOT specify 0.0.0.0/0 here; see above. 6 | allow-recursion { 7 | 192.168.1/24; 8 | }; 9 | 10 | // If you want this resolver to itself resolve via means of another recursive 11 | // resolver, uncomment this block and specify the IP addresses of the desired 12 | // upstream resolvers. 13 | //forwarders { 14 | // 8.8.8.8; 15 | // 192.168.1.1; 16 | //}; 17 | 18 | // By default the resolver will attempt to perform recursive resolution itself 19 | // if the forwarders are unavailable. If you want this resolver to fail outright 20 | // if the upstream resolvers are unavailable, uncomment this directive. 21 | //forward only; 22 | 23 | // Configure the IPs to listen on here. 24 | //listen-on { 127.0.0.1; }; 25 | //listen-on-v6 { none; }; 26 | 27 | // If you have problems and are behind a firewall: 28 | //query-source address * port 53; 29 | 30 | pid-file "/var/run/named/named.pid"; 31 | 32 | // Removing this block will cause BIND to revert to its default behaviour 33 | // of allowing zone transfers to any host (!). There is no need to allow zone 34 | // transfers when operating as a recursive resolver. 35 | //allow-transfer { none; }; 36 | }; 37 | 38 | // Briefly, a zone which has been declared delegation-only will be effectively 39 | // limited to containing NS RRs for subdomains, but no actual data beyond its 40 | // own apex (for example, its SOA RR and apex NS RRset). This can be used to 41 | // filter out "wildcard" or "synthesized" data from NAT boxes or from 42 | // authoritative name servers whose undelegated (in-zone) data is of no 43 | // interest. 44 | // See http://www.isc.org/products/BIND/delegation-only.html for more info 45 | 46 | //zone "COM" { type delegation-only; }; 47 | //zone "NET" { type delegation-only; }; 48 | 49 | zone "." IN { 50 | type hint; 51 | file "named.ca"; 52 | }; 53 | 54 | zone "localhost" IN { 55 | type master; 56 | file "pri/localhost.zone"; 57 | allow-update { none; }; 58 | notify no; 59 | }; 60 | 61 | zone "127.in-addr.arpa" IN { 62 | type master; 63 | file "pri/127.zone"; 64 | allow-update { none; }; 65 | notify no; 66 | }; 67 | 68 | zone "yivgame.com" IN { 69 | type master; 70 | file "pri/yivgame.com.zone"; 71 | }; -------------------------------------------------------------------------------- /docker/data/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 809; 3 | server_name xx.yivgame.com; 4 | 5 | #charset koi8-r; 6 | #access_log /var/log/nginx/log/host.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | index index.html index.htm; 11 | } 12 | 13 | #error_page 404 /404.html; 14 | 15 | # redirect server error pages to the static page /50x.html 16 | # 17 | error_page 500 502 503 504 /50x.html; 18 | location = /50x.html { 19 | root /usr/share/nginx/html; 20 | } 21 | 22 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 23 | # 24 | #location ~ \.php$ { 25 | # proxy_pass http://127.0.0.1; 26 | #} 27 | 28 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 29 | # 30 | #location ~ \.php$ { 31 | # root html; 32 | # fastcgi_pass 127.0.0.1:9000; 33 | # fastcgi_index index.php; 34 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 35 | # include fastcgi_params; 36 | #} 37 | 38 | # deny access to .htaccess files, if Apache's document root 39 | # concurs with nginx's one 40 | # 41 | #location ~ /\.ht { 42 | # deny all; 43 | #} 44 | } 45 | -------------------------------------------------------------------------------- /docker/data/nginx/conf.d/drone.conf: -------------------------------------------------------------------------------- 1 | map $http_upgrade $connection_upgrade { 2 | default upgrade; 3 | '' close; 4 | } 5 | 6 | server { 7 | listen 80; 8 | server_name drone.yivgame.com; 9 | 10 | location / { 11 | proxy_set_header X-Forwarded-For $remote_addr; 12 | proxy_set_header X-Forwarded-Proto $scheme; 13 | proxy_set_header Host $http_host; 14 | 15 | proxy_pass http://192.168.1.200:8000; 16 | proxy_redirect off; 17 | proxy_http_version 1.1; 18 | proxy_buffering off; 19 | 20 | chunked_transfer_encoding off; 21 | } 22 | 23 | location ~* /ws { 24 | proxy_pass http://192.168.1.200:8000; 25 | proxy_http_version 1.1; 26 | proxy_set_header Upgrade $http_upgrade; 27 | proxy_set_header Connection "upgrade"; 28 | proxy_read_timeout 86400; 29 | proxy_set_header X-Forwarded-For $remote_addr; 30 | proxy_set_header X-Forwarded-Proto $scheme; 31 | proxy_set_header Host $http_host; 32 | } 33 | } -------------------------------------------------------------------------------- /docker/data/nginx/conf.d/gogs.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name git.yivgame.com; 4 | 5 | #charset koi8-r; 6 | #access_log /var/log/nginx/log/host.access.log main; 7 | client_max_body_size 100m; 8 | 9 | location / { 10 | #root /usr/share/nginx/html; 11 | # index index.html index.htm; 12 | proxy_set_header X-Real-IP $remote_addr; 13 | proxy_set_header X-Forwarded-For $remote_addr; 14 | proxy_set_header Host $host; 15 | proxy_pass http://192.168.1.200:10080; 16 | } 17 | 18 | #error_page 404 /404.html; 19 | 20 | # redirect server error pages to the static page /50x.html 21 | # 22 | error_page 500 502 503 504 /50x.html; 23 | location = /50x.html { 24 | root /usr/share/nginx/html; 25 | } 26 | 27 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 28 | # 29 | #location ~ \.php$ { 30 | # proxy_pass http://127.0.0.1; 31 | #} 32 | 33 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 34 | # 35 | #location ~ \.php$ { 36 | # root html; 37 | # fastcgi_pass 127.0.0.1:9000; 38 | # fastcgi_index index.php; 39 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 40 | # include fastcgi_params; 41 | #} 42 | 43 | # deny access to .htaccess files, if Apache's document root 44 | # concurs with nginx's one 45 | # 46 | #location ~ /\.ht { 47 | # deny all; 48 | #} 49 | } 50 | -------------------------------------------------------------------------------- /docker/data/nginx/conf.d/tpapi.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name tpapi.yivgame.com; 4 | 5 | #charset koi8-r; 6 | #access_log /var/log/nginx/log/host.access.log main; 7 | client_max_body_size 20m; 8 | 9 | location / { 10 | #root /usr/share/nginx/html; 11 | # index index.html index.htm; 12 | add_header Access-Control-Allow-Origin *; 13 | proxy_set_header X-Real-IP $remote_addr; 14 | proxy_set_header X-Forwarded-For $remote_addr; 15 | proxy_set_header Host $host; 16 | proxy_pass http://192.168.1.51:10070/; 17 | } 18 | 19 | #error_page 404 /404.html; 20 | 21 | # redirect server error pages to the static page /50x.html 22 | # 23 | error_page 500 502 503 504 /50x.html; 24 | location = /50x.html { 25 | root /usr/share/nginx/html; 26 | } 27 | 28 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 29 | # 30 | #location ~ \.php$ { 31 | # proxy_pass http://127.0.0.1; 32 | #} 33 | 34 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 35 | # 36 | #location ~ \.php$ { 37 | # root html; 38 | # fastcgi_pass 127.0.0.1:9000; 39 | # fastcgi_index index.php; 40 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 41 | # include fastcgi_params; 42 | #} 43 | 44 | # deny access to .htaccess files, if Apache's document root 45 | # concurs with nginx's one 46 | # 47 | #location ~ /\.ht { 48 | # deny all; 49 | #} 50 | } 51 | -------------------------------------------------------------------------------- /docker/dockerfile/README.md: -------------------------------------------------------------------------------- 1 | # dockerfile 2 | 对于一些在容器hub中找不到的docker镜像,需要手动通过dockerfile创建 -------------------------------------------------------------------------------- /docker/dockerfile/etcd/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM aarch64/ubuntu:16.04 2 | 3 | ADD etcd /usr/local/bin/ 4 | ADD etcdctl /usr/local/bin/ 5 | #ADD var/etcd /var/etcd 6 | #ADD var/lib/etcd /var/lib/etcd 7 | 8 | EXPOSE 2379 2380 9 | 10 | # Define default command. 11 | CMD ["/usr/local/bin/etcd"] -------------------------------------------------------------------------------- /docker/dockerfile/etcd/etcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/docker/dockerfile/etcd/etcd -------------------------------------------------------------------------------- /docker/dockerfile/etcd/etcdctl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/docker/dockerfile/etcd/etcdctl -------------------------------------------------------------------------------- /frontapi/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/frontapi/README.md -------------------------------------------------------------------------------- /game/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/game/README.md -------------------------------------------------------------------------------- /game/gamer/card.go: -------------------------------------------------------------------------------- 1 | package gamer 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "time" 7 | ) 8 | 9 | type Cards []Card 10 | type Card byte 11 | type CardColor byte 12 | type CardValue byte 13 | type CardsType byte 14 | 15 | const ( 16 | HighCard CardsType = iota + 1 //高牌 17 | Pair //对子 18 | Flush //同花 19 | Straight //顺子 20 | StraightFlush //同花顺 21 | ThreeOfAKind //豹子 22 | ) 23 | 24 | const ( 25 | Heart CardColor = iota + 1 //红桃 26 | Spade //黑桃 27 | Club //梅花 28 | Diamond //方块 29 | ) 30 | 31 | func (cds Cards) ToBytes() (cards []byte) { 32 | for _, card := range cds { 33 | cards = append(cards, byte(card)) 34 | } 35 | return 36 | } 37 | func (cds Cards) Equal(cards Cards) bool { 38 | if cds.GetType() == cards.GetType() { 39 | //牌型相同 40 | cType := cds.GetType() 41 | if cType == ThreeOfAKind { 42 | //豹子的牌值需相等 43 | if cds[0].equal(cards[0]) { 44 | return true 45 | } 46 | } else if cType == StraightFlush || cType == Straight || cType == Flush || cType == HighCard { 47 | //三张牌依次相等 48 | bc := cds 49 | lc := cards 50 | sort.Sort(bc) 51 | sort.Sort(lc) 52 | if bc[0].equal(lc[0]) && bc[1].equal(lc[1]) && bc[2].equal(lc[2]) { 53 | return true 54 | } 55 | } else if cType == Pair { 56 | //对子牌和单牌需相等 57 | bp, bs := cds.splitPair() 58 | lp, ls := cards.splitPair() 59 | if bp.equal(lp) && bs.equal(ls) { 60 | return true 61 | } 62 | } 63 | } 64 | return false 65 | } 66 | 67 | func (cds Cards) BiggerThan(cards Cards) bool { 68 | if cds.GetType() > cards.GetType() { 69 | //牌型更大 70 | return true 71 | } else if cds.GetType() == cards.GetType() { 72 | //牌型相同 73 | cType := cds.GetType() 74 | if cType == ThreeOfAKind { 75 | //豹子 76 | if cds[0].biggerThan(cards[0]) { 77 | //牌值更大 78 | return true 79 | } 80 | } else if cType == StraightFlush || cType == Straight || cType == Flush || cType == HighCard { 81 | //依次比较已排序的牌 82 | bc := cds 83 | lc := cards 84 | sort.Sort(bc) 85 | sort.Sort(lc) 86 | if bc[0].biggerThan(lc[0]) { 87 | return true 88 | } else if bc[1].biggerThan(lc[1]) { 89 | return true 90 | } else if bc[2].biggerThan(lc[2]) { 91 | return true 92 | } 93 | } else if cType == Pair { 94 | bp, bs := cds.splitPair() 95 | lp, ls := cards.splitPair() 96 | if bp.biggerThan(lp) { 97 | return true 98 | } else if bp.equal(lp) { 99 | if bs.biggerThan(ls) { 100 | return true 101 | } 102 | } 103 | } 104 | } 105 | return false 106 | } 107 | func (cds Cards) GetType() CardsType { 108 | if cds.isThreeOfAKind() { 109 | return ThreeOfAKind 110 | } else if cds.isStraightFlush() { 111 | return StraightFlush 112 | } else if cds.isStraight() { 113 | return Straight 114 | } else if cds.isFlush() { 115 | return Flush 116 | } else if cds.isPair() { 117 | return Pair 118 | } else { 119 | return HighCard 120 | } 121 | } 122 | 123 | //splitPair 取出对子的对子牌和单牌 124 | func (cds Cards) splitPair() (pairCard, singleCard Card) { 125 | p1 := cds[0:2] 126 | p2 := cds[1:3] 127 | if p1.isAllSameValue() || p2.isAllSameValue() { 128 | return cds[0], cds[2] 129 | } 130 | return cds[2], cds[0] 131 | } 132 | 133 | //isPair 是否对子 134 | func (cds Cards) isPair() bool { 135 | p1 := cds[0:2] 136 | p2 := cds[1:3] 137 | if cds.isThreeOfAKind() { 138 | return false 139 | } 140 | if p1.isAllSameValue() || p2.isAllSameValue() { 141 | return true 142 | } 143 | return false 144 | } 145 | 146 | //isFlush 是否同花 147 | func (cds Cards) isFlush() bool { 148 | if cds.isAllSeq() { 149 | return false 150 | } 151 | if !cds.isAllSameColor() { 152 | return false 153 | } 154 | 155 | return true 156 | } 157 | 158 | //isStraight 是否顺子 159 | func (cds Cards) isStraight() bool { 160 | if cds.isAllSameColor() { 161 | return false 162 | } 163 | if cds.isA23() || cds.isAllSeq() { 164 | return true 165 | } 166 | 167 | return false 168 | } 169 | 170 | //isStraightFlush 是否同花顺 171 | func (cds Cards) isStraightFlush() bool { 172 | if (cds.isAllSeq() || cds.isA23()) && cds.isAllSameColor() { 173 | return true 174 | } 175 | return false 176 | } 177 | 178 | //isThreeOfAKind 是否豹子 179 | func (cds Cards) isThreeOfAKind() bool { 180 | return cds.isAllSameValue() 181 | } 182 | func (cds Cards) isAllSameValue() bool { 183 | tv, _ := cds[0].splitCard() 184 | for _, card := range cds { 185 | cv, _ := card.splitCard() 186 | if tv != cv { 187 | return false 188 | } 189 | } 190 | return true 191 | } 192 | 193 | //isAllSameSeq 是否所有连牌 194 | func (cds Cards) isAllSeq() bool { 195 | cards := cds 196 | sort.Sort(cards) 197 | for i := 0; i < len(cards)-1; i++ { 198 | b, _ := cards[i].splitCard() 199 | l, _ := cards[i+1].splitCard() 200 | if (l + 1) != b { 201 | return false 202 | } 203 | } 204 | return true 205 | } 206 | 207 | //isAllSameColor 是否花色相同 208 | func (cds Cards) isAllSameColor() bool { 209 | 210 | _, c := cds[0].splitCard() 211 | for _, card := range cds { 212 | _, cc := card.splitCard() 213 | if c != cc { 214 | return false 215 | } 216 | } 217 | 218 | return true 219 | } 220 | func (cds Cards) isA23() bool { 221 | cards := cds 222 | sort.Sort(cards) 223 | var cvs []CardValue 224 | for _, card := range cards { 225 | cv, _ := card.splitCard() 226 | cvs = append(cvs, cv) 227 | } 228 | if cvs[0] == 0x2 && cvs[1] == 0x3 && cvs[2] == 0xe { 229 | return true 230 | } 231 | return false 232 | } 233 | 234 | //inCards 判断单牌是否在牌组中 235 | func (cds Cards) inCards(c Card) bool { 236 | for _, card := range cds { 237 | if c == card { 238 | return true 239 | } 240 | } 241 | return false 242 | } 243 | 244 | //Len 245 | func (cds Cards) Len() int { 246 | return len(cds) 247 | } 248 | 249 | //Swap 250 | func (cds Cards) Swap(i, j int) { 251 | cds[i], cds[j] = cds[j], cds[i] 252 | } 253 | 254 | //Less 255 | func (cds Cards) Less(i, j int) bool { 256 | vi, _ := cds[i].splitCard() 257 | vj, _ := cds[j].splitCard() 258 | return vi > vj 259 | } 260 | 261 | //filter 返回从牌组中去掉指定牌后的牌组 262 | func (cds Cards) diff(cards Cards) (newCards Cards) { 263 | for _, card := range cds { 264 | if !cards.inCards(card) { 265 | newCards = append(newCards, card) 266 | } 267 | } 268 | return 269 | } 270 | func comebineCard(color CardColor, value CardValue) (card Card) { 271 | x := byte(color<<4) | 0 272 | x = byte(value) | x 273 | card = Card(x) 274 | return 275 | } 276 | 277 | type CardDealer struct { 278 | deck Cards 279 | } 280 | 281 | func NewCardDealer() *CardDealer { 282 | deck := Cards{} 283 | for color := Heart; color <= Diamond; color++ { 284 | for value := CardValue(2); value <= CardValue(0xe); value++ { 285 | deck = append(deck, comebineCard(color, value)) 286 | } 287 | } 288 | de := &CardDealer{} 289 | de.deck = deck 290 | return de 291 | } 292 | 293 | //Shuffle 洗牌 294 | func (de *CardDealer) Shuffle() { 295 | de.deck = nil 296 | for color := Heart; color <= Diamond; color++ { 297 | for value := CardValue(2); value <= CardValue(0xe); value++ { 298 | de.deck = append(de.deck, comebineCard(color, value)) 299 | } 300 | } 301 | } 302 | 303 | //NextSuit 获取下一组牌 304 | func (de *CardDealer) NextSuit() (cards Cards) { 305 | rand.Seed(time.Now().UnixNano()) 306 | for { 307 | p := rand.Intn(len(de.deck)) 308 | if cards.inCards(de.deck[p]) { 309 | continue 310 | } 311 | cards = append(cards, de.deck[p]) 312 | if len(cards) >= 3 { 313 | break 314 | } 315 | } 316 | newDeck := de.deck.diff(cards) 317 | de.deck = newDeck 318 | return 319 | } 320 | 321 | //splitCard 获取牌的牌值和花色 322 | func (c Card) splitCard() (cv CardValue, cc CardColor) { 323 | cv = CardValue(byte(c) & 0x0F) 324 | cc = CardColor(byte(c) >> 4) 325 | return 326 | } 327 | 328 | //biggerThan 比单牌牌值大小 329 | func (c Card) biggerThan(card Card) bool { 330 | bv, _ := c.splitCard() 331 | lv, _ := card.splitCard() 332 | if bv > lv { 333 | return true 334 | } 335 | return false 336 | } 337 | 338 | //equal 判断两单牌牌值是否相同 339 | func (c Card) equal(card Card) bool { 340 | bv, _ := c.splitCard() 341 | lv, _ := card.splitCard() 342 | if bv == lv { 343 | return true 344 | } 345 | return false 346 | } 347 | -------------------------------------------------------------------------------- /game/gamer/card_test.go: -------------------------------------------------------------------------------- 1 | package gamer 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "testing" 7 | ) 8 | 9 | var t2s = map[CardsType]string{ 10 | HighCard: "高牌", 11 | Pair: "对子", 12 | Flush: "同花", 13 | Straight: "顺子", 14 | StraightFlush: "同花顺", 15 | ThreeOfAKind: "豹子", 16 | } 17 | 18 | func TestComebineCard(t *testing.T) { 19 | de := NewCardDealer() 20 | fmt.Println(len(de.deck)) 21 | for _, v := range de.deck { 22 | fmt.Printf("%x ", v) 23 | } 24 | } 25 | 26 | func TestNextSuit(t *testing.T) { 27 | de := NewCardDealer() 28 | cards := de.NextSuit() 29 | for _, v := range cards { 30 | fmt.Printf("%x ", v) 31 | } 32 | fmt.Println("len", len(de.deck)) 33 | } 34 | func TestIsThreeOfAKind(t *testing.T) { 35 | c1 := Cards{0x11, 0x21, 0x31} 36 | fmt.Println("c1: ", c1.isThreeOfAKind()) 37 | c2 := Cards{0x13, 0x21, 0x31} 38 | fmt.Println("c2: ", c2.isThreeOfAKind()) 39 | } 40 | func TestSort(t *testing.T) { 41 | de := NewCardDealer() 42 | cards := de.NextSuit() 43 | for _, v := range cards { 44 | fmt.Printf("%x ", v) 45 | } 46 | fmt.Println() 47 | sort.Sort(cards) 48 | for _, v := range cards { 49 | fmt.Printf("%x ", v) 50 | } 51 | } 52 | func TestIsStraight(t *testing.T) { 53 | c1 := Cards{0x16, 0x27, 0x38} 54 | fmt.Println("c1: ", c1.isStraight()) 55 | c2 := Cards{0x13, 0x22, 0x3e} 56 | fmt.Println("c2: ", c2.isStraight()) 57 | } 58 | func TestIsFlush(t *testing.T) { 59 | c1 := Cards{0x14, 0x15, 0x16} 60 | fmt.Println("c1: ", c1.isFlush()) 61 | c2 := Cards{0x13, 0x11, 0x11} 62 | fmt.Println("c2: ", c2.isFlush()) 63 | } 64 | func TestIsStraightFlush(t *testing.T) { 65 | c1 := Cards{0x13, 0x12, 0x1e} 66 | fmt.Println("c1: ", c1.isStraightFlush()) 67 | c2 := Cards{0x13, 0x14, 0x15} 68 | fmt.Println("c2: ", c2.isStraightFlush()) 69 | } 70 | 71 | func TestIsPair(t *testing.T) { 72 | c1 := Cards{0x16, 0x26, 0x34} 73 | fmt.Println("c1: ", c1.isPair()) 74 | c2 := Cards{0x13, 0x21, 0x31} 75 | fmt.Println("c2: ", c2.isPair()) 76 | } 77 | func TestIsA23(t *testing.T) { 78 | c1 := Cards{0x11, 0x21, 0x31} 79 | fmt.Println("c1: ", c1.isA23()) 80 | c2 := Cards{0x1e, 0x23, 0x42} 81 | fmt.Println("c2: ", c2.isA23()) 82 | } 83 | func TestGetType(t *testing.T) { 84 | 85 | c1 := Cards{0x11, 0x21, 0x31} 86 | fmt.Println("c1: ", t2s[c1.GetType()]) 87 | c2 := Cards{0x1e, 0x13, 0x12} 88 | fmt.Println("c2: ", t2s[c2.GetType()]) 89 | c3 := Cards{0x1e, 0x23, 0x42} 90 | fmt.Println("c2: ", t2s[c3.GetType()]) 91 | c4 := Cards{0x1e, 0x13, 0x15} 92 | fmt.Println("c2: ", t2s[c4.GetType()]) 93 | c5 := Cards{0x1a, 0x2e, 0x3e} 94 | fmt.Println("c2: ", t2s[c5.GetType()]) 95 | c6 := Cards{0x17, 0x2e, 0x15} 96 | fmt.Println("c2: ", t2s[c6.GetType()]) 97 | } 98 | func TestBiggerThan(t *testing.T) { 99 | //豹子比较 100 | a1 := Cards{0x12, 0x22, 0x32} 101 | a2 := Cards{0x13, 0x23, 0x33} 102 | fmt.Printf("a1>a2: %v, a1=a2: %v, a1a2: %v, a1=a2: %v, a1a2: %v, a1=a2: %v, a1a2: %v, a1=a2: %v, a1a2: %v, a1=a2: %v, a1a2: %v, a1=a2: %v, a1= 1000 { 42 | return nil, ErrorRoomTableExceed 43 | } 44 | id := TableId(r.CountTable() + 1) 45 | tbOption := TableOptions{ 46 | TableId: id, 47 | RoomClass: r.RoomClass, 48 | BootBet: r.BootBet, 49 | } 50 | t = NewTable(tbOption, logger, userCenter) 51 | r.Tables[id] = t 52 | return 53 | } 54 | 55 | //CountTable 计算开桌局数 56 | func (r *Room) CountTable() int { 57 | return len(r.Tables) 58 | } 59 | -------------------------------------------------------------------------------- /game/gamer/seat.go: -------------------------------------------------------------------------------- 1 | package gamer 2 | 3 | type ( 4 | Seat int //Seat 玩家在牌桌上的座位号 5 | ) 6 | 7 | func (s Seat) Next() (seat Seat) { 8 | if s < 5 { 9 | return Seat(s + 1) 10 | } 11 | return Seat1 12 | } 13 | -------------------------------------------------------------------------------- /game/gamer/table.go: -------------------------------------------------------------------------------- 1 | package gamer 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/go-kit/kit/log/level" 7 | ) 8 | 9 | type ( 10 | TableId int //TableId 桌号 11 | SeatCode int //SeatCode 由游戏代码、房间代码、牌桌代码和座位号构成 12 | ) 13 | 14 | type UserCenter interface { 15 | GetInfo(id UserID) (info *PlayerInfo, err error) 16 | } 17 | 18 | type TableOptions struct { 19 | TableId TableId //桌号 20 | RoomClass RoomClass //所属房间等级 21 | BootBet int64 //底注金币数 22 | } 23 | type Table struct { 24 | mtx sync.RWMutex 25 | //牌桌配置 26 | TableOptions 27 | 28 | //玩家管理 29 | Seats map[Seat]UserID 30 | Players map[UserID]*Player 31 | //牌局状态 32 | Status int 33 | dealer *CardDealer //发牌机 34 | //外部依赖 35 | UCenter UserCenter 36 | logger Logger 37 | } 38 | 39 | //NewTable 新建牌桌 40 | func NewTable(option TableOptions, logger Logger, userCenter UserCenter) (t *Table) { 41 | t = &Table{ 42 | TableOptions: option, 43 | Seats: make(map[Seat]UserID), 44 | Players: make(map[UserID]*Player), 45 | logger: logger, 46 | dealer: NewCardDealer(), 47 | UCenter: userCenter, 48 | } 49 | return 50 | } 51 | 52 | //AddFriend 邀请加好友 53 | func (t *Table) SendChat(id UserID, mid int32, msg string) (err error) { 54 | var p *Player 55 | if p = t.getPlayer(id); p == nil { 56 | return ErrorNotOnTable 57 | } 58 | go p.airfone.ReplySendChat() 59 | level.Debug(t.logger).Log("SendChat", id) 60 | for _, pl := range t.Players { 61 | if pl.UserID != p.UserID { 62 | go pl.airfone.PlayerSendChat(p.Seat, mid, msg) 63 | } 64 | 65 | } 66 | return 67 | } 68 | 69 | // getPlayer 通过ID获取牌桌上的玩家 70 | func (t *Table) getPlayer(id UserID) (p *Player) { 71 | if pl, ok := t.Players[id]; ok { 72 | p = pl 73 | return 74 | } 75 | return 76 | } 77 | -------------------------------------------------------------------------------- /game/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "net/http/pprof" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | "time" 14 | 15 | stdopentracing "github.com/opentracing/opentracing-go" 16 | zipkin "github.com/openzipkin/zipkin-go-opentracing" 17 | stdprometheus "github.com/prometheus/client_golang/prometheus" 18 | "github.com/prometheus/client_golang/prometheus/promhttp" 19 | "google.golang.org/grpc" 20 | 21 | "github.com/go-kit/kit/endpoint" 22 | "github.com/go-kit/kit/log" 23 | "github.com/go-kit/kit/log/level" 24 | "github.com/go-kit/kit/metrics" 25 | "github.com/go-kit/kit/metrics/prometheus" 26 | ketcd "github.com/go-kit/kit/sd/etcd" 27 | "github.com/go-kit/kit/tracing/opentracing" 28 | 29 | "github.com/yiv/yivgame/game/gamer" 30 | "github.com/yiv/yivgame/game/pb" 31 | "github.com/yiv/yivgame/game/service" 32 | ) 33 | 34 | var ( 35 | grpcAddr = flag.String("grpc.addr", ":10020", "gRPC (HTTP) listen address") 36 | debugAddr = flag.String("debug.addr", ":10021", "Debug and metrics listen address") 37 | zipkinAddr = flag.String("zipkin.addr", "", "Enable Zipkin tracing via a Zipkin HTTP Collector endpoint") 38 | etcdAddr = flag.String("etcd.addr", "http://etcd.yivgame.com:2379", "Consul agent address") 39 | serviceName = flag.String("service.name", "gameserver", "the name of this service in service discovery") 40 | userCenter = flag.String("userCenter.name", "usercenter", "the name of this service in service discovery") 41 | retryMax = flag.Int("retry.max", 3, "per-request retries to different instances") 42 | retryTimeout = flag.Duration("retry.timeout", 500*time.Millisecond, "per-request timeout, including retries") 43 | //first class room config 44 | frBootbet = flag.Int64("fr.bb", 100, "boot bet coins of first class room") 45 | ) 46 | 47 | func main() { 48 | flag.Parse() 49 | 50 | // Logging domain. 51 | var logger log.Logger 52 | { 53 | logger = log.NewLogfmtLogger(os.Stdout) 54 | logger = level.NewFilter(logger, level.AllowDebug()) 55 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 56 | logger = log.With(logger, "caller", log.DefaultCaller) 57 | } 58 | logger.Log("msg", "hello") 59 | defer logger.Log("msg", "goodbye") 60 | 61 | // Mechanical domain. 62 | errc := make(chan error) 63 | // Interrupt handler. 64 | go func() { 65 | c := make(chan os.Signal) 66 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 67 | errc <- fmt.Errorf("%s", <-c) 68 | }() 69 | 70 | // Service discovery domain 71 | ctx := context.Background() 72 | etcdClient, err := ketcd.NewClient(ctx, []string{*etcdAddr}, ketcd.ClientOptions{}) 73 | if err != nil { 74 | panic(err) 75 | } 76 | registrar := ketcd.NewRegistrar(etcdClient, ketcd.Service{Key: *serviceName, Value: *grpcAddr}, logger) 77 | registrar.Register() 78 | defer registrar.Deregister() 79 | 80 | // Metrics domain. 81 | var ( 82 | requestCount metrics.Counter 83 | requestLatency metrics.Histogram 84 | fieldKeys []string 85 | ) 86 | { 87 | // Business level metrics. 88 | fieldKeys = []string{"method", "error"} 89 | requestCount = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ 90 | Namespace: "gameserver", 91 | Name: "request_count", 92 | Help: "Number of requests received.", 93 | }, fieldKeys) 94 | requestLatency = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 95 | Namespace: "usercenter", 96 | Name: "request_latency_microseconds", 97 | Help: "Total duration of requests in microseconds.", 98 | }, fieldKeys) 99 | } 100 | var duration metrics.Histogram 101 | { 102 | // Transport level metrics. 103 | duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 104 | Namespace: "gameserver", 105 | Name: "request_duration_ns", 106 | Help: "Request duration in nanoseconds.", 107 | }, []string{"method", "success"}) 108 | } 109 | 110 | // Tracing domain. 111 | var tracer stdopentracing.Tracer 112 | { 113 | 114 | logger := log.With(logger, "tracer", "ZipkinHTTP") 115 | logger.Log("addr", *zipkinAddr) 116 | 117 | // endpoint typically looks like: http://zipkinhost:9411/api/v1/spans 118 | collector, err := zipkin.NewHTTPCollector(*zipkinAddr) 119 | if err != nil { 120 | logger.Log("err", err) 121 | os.Exit(1) 122 | } 123 | defer collector.Close() 124 | 125 | tracer, err = zipkin.NewTracer( 126 | zipkin.NewRecorder(collector, false, "localhost:10003", "addsvc"), 127 | ) 128 | if err != nil { 129 | logger.Log("err", err) 130 | os.Exit(1) 131 | } 132 | 133 | } 134 | // Business domain. 135 | uc, err := service.NewUserCenter(*userCenter, []string{*etcdAddr}, *retryMax, *retryTimeout, logger) 136 | if err != nil { 137 | errc <- err 138 | return 139 | } 140 | firRoomOp := gamer.RoomOptions{ 141 | RoomClass: gamer.FirstClassRoom, 142 | BootBet: *frBootbet, 143 | } 144 | var gameService service.GameService 145 | { 146 | logger := log.With(logger, "service", "gameService") 147 | gameService = service.NewGameService(firRoomOp, uc, logger) 148 | gameService = service.ServiceLoggingMiddleware(logger)(gameService) 149 | gameService = service.ServiceInstrumentingMiddleware(requestCount, requestLatency)(gameService) 150 | } 151 | // Endpoint domain. 152 | var sendChatEndpoint endpoint.Endpoint 153 | { 154 | method := "sendChat" 155 | duration := duration.With("method", method) 156 | logger := log.With(logger, "method", method) 157 | sendChatEndpoint = service.MakeSendChatEndpoint(gameService) 158 | sendChatEndpoint = opentracing.TraceServer(tracer, method)(sendChatEndpoint) 159 | sendChatEndpoint = service.EndpointInstrumentingMiddleware(duration)(sendChatEndpoint) 160 | sendChatEndpoint = service.EndpointLoggingMiddleware(logger)(sendChatEndpoint) 161 | } 162 | endpoints := service.Endpoints{ 163 | Logger: logger, 164 | SendChatEndpoint: sendChatEndpoint, 165 | } 166 | 167 | // gRPC transport. 168 | go func() { 169 | logger := log.With(logger, "transport", "gRPC") 170 | 171 | ln, err := net.Listen("tcp", *grpcAddr) 172 | if err != nil { 173 | errc <- err 174 | return 175 | } 176 | grpcServer := grpc.NewServer() 177 | grpcHandler := service.MakeGRPCHandler(endpoints, tracer, logger) 178 | pb.RegisterGameServiceServer(grpcServer, grpcHandler) 179 | 180 | logger.Log("addr", *grpcAddr) 181 | errc <- grpcServer.Serve(ln) 182 | }() 183 | 184 | // Debug listener. 185 | go func() { 186 | logger := log.With(logger, "transport", "debug") 187 | 188 | m := http.NewServeMux() 189 | m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) 190 | m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) 191 | m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) 192 | m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) 193 | m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) 194 | m.Handle("/metrics", promhttp.Handler()) 195 | 196 | logger.Log("addr", *debugAddr) 197 | errc <- http.ListenAndServe(*debugAddr, m) 198 | }() 199 | logger.Log("terminated", <-errc) 200 | } 201 | -------------------------------------------------------------------------------- /game/pb/compile.sh: -------------------------------------------------------------------------------- 1 | protoc game.proto --go_out=plugins=grpc:.; -------------------------------------------------------------------------------- /game/pb/game.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: game.proto 3 | 4 | /* 5 | Package pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | game.proto 9 | 10 | It has these top-level messages: 11 | Frame 12 | EnterTableReq 13 | SitdownReq 14 | BetReq 15 | DuelReq 16 | FriendReq 17 | GiftReq 18 | ChatReq 19 | DelegateReq 20 | GeRes 21 | RevealRes 22 | Player 23 | Table 24 | PlayerSeat 25 | Reveal 26 | GeCall 27 | GiveUp 28 | Duel 29 | Bet 30 | DuelResult 31 | FriendAdd 32 | FriendAddResult 33 | SendGift 34 | GameOver 35 | TableResult 36 | CardDeal 37 | ChatMsg 38 | NetInfo 39 | */ 40 | package pb 41 | 42 | import proto "github.com/golang/protobuf/proto" 43 | import fmt "fmt" 44 | import math "math" 45 | 46 | import ( 47 | context "golang.org/x/net/context" 48 | grpc "google.golang.org/grpc" 49 | ) 50 | 51 | // Reference imports to suppress errors if they are not otherwise used. 52 | var _ = proto.Marshal 53 | var _ = fmt.Errorf 54 | var _ = math.Inf 55 | 56 | // This is a compile-time assertion to ensure that this generated file 57 | // is compatible with the proto package it is being compiled against. 58 | // A compilation error at this line likely means your copy of the 59 | // proto package needs to be updated. 60 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 61 | 62 | // 双向流包 63 | type Frame struct { 64 | Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` 65 | } 66 | 67 | func (m *Frame) Reset() { *m = Frame{} } 68 | func (m *Frame) String() string { return proto.CompactTextString(m) } 69 | func (*Frame) ProtoMessage() {} 70 | func (*Frame) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 71 | 72 | func (m *Frame) GetPayload() []byte { 73 | if m != nil { 74 | return m.Payload 75 | } 76 | return nil 77 | } 78 | 79 | // 双向流请求包 80 | type EnterTableReq struct { 81 | Uid int64 `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty"` 82 | // 登陆令牌 83 | Token string `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` 84 | // 房间等级 85 | RoomClass int32 `protobuf:"varint,3,opt,name=roomClass" json:"roomClass,omitempty"` 86 | } 87 | 88 | func (m *EnterTableReq) Reset() { *m = EnterTableReq{} } 89 | func (m *EnterTableReq) String() string { return proto.CompactTextString(m) } 90 | func (*EnterTableReq) ProtoMessage() {} 91 | func (*EnterTableReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 92 | 93 | func (m *EnterTableReq) GetUid() int64 { 94 | if m != nil { 95 | return m.Uid 96 | } 97 | return 0 98 | } 99 | 100 | func (m *EnterTableReq) GetToken() string { 101 | if m != nil { 102 | return m.Token 103 | } 104 | return "" 105 | } 106 | 107 | func (m *EnterTableReq) GetRoomClass() int32 { 108 | if m != nil { 109 | return m.RoomClass 110 | } 111 | return 0 112 | } 113 | 114 | type SitdownReq struct { 115 | // 座位号 116 | Seat int32 `protobuf:"varint,1,opt,name=seat" json:"seat,omitempty"` 117 | } 118 | 119 | func (m *SitdownReq) Reset() { *m = SitdownReq{} } 120 | func (m *SitdownReq) String() string { return proto.CompactTextString(m) } 121 | func (*SitdownReq) ProtoMessage() {} 122 | func (*SitdownReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 123 | 124 | func (m *SitdownReq) GetSeat() int32 { 125 | if m != nil { 126 | return m.Seat 127 | } 128 | return 0 129 | } 130 | 131 | type BetReq struct { 132 | // 下注金币数量 133 | Coins int64 `protobuf:"varint,1,opt,name=coins" json:"coins,omitempty"` 134 | } 135 | 136 | func (m *BetReq) Reset() { *m = BetReq{} } 137 | func (m *BetReq) String() string { return proto.CompactTextString(m) } 138 | func (*BetReq) ProtoMessage() {} 139 | func (*BetReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 140 | 141 | func (m *BetReq) GetCoins() int64 { 142 | if m != nil { 143 | return m.Coins 144 | } 145 | return 0 146 | } 147 | 148 | type DuelReq struct { 149 | // 被比牌玩家ID 150 | Rival int64 `protobuf:"varint,1,opt,name=rival" json:"rival,omitempty"` 151 | } 152 | 153 | func (m *DuelReq) Reset() { *m = DuelReq{} } 154 | func (m *DuelReq) String() string { return proto.CompactTextString(m) } 155 | func (*DuelReq) ProtoMessage() {} 156 | func (*DuelReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 157 | 158 | func (m *DuelReq) GetRival() int64 { 159 | if m != nil { 160 | return m.Rival 161 | } 162 | return 0 163 | } 164 | 165 | type FriendReq struct { 166 | // 被加好友ID 167 | ToId int64 `protobuf:"varint,1,opt,name=toId" json:"toId,omitempty"` 168 | } 169 | 170 | func (m *FriendReq) Reset() { *m = FriendReq{} } 171 | func (m *FriendReq) String() string { return proto.CompactTextString(m) } 172 | func (*FriendReq) ProtoMessage() {} 173 | func (*FriendReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 174 | 175 | func (m *FriendReq) GetToId() int64 { 176 | if m != nil { 177 | return m.ToId 178 | } 179 | return 0 180 | } 181 | 182 | type GiftReq struct { 183 | // 被加好友ID 184 | ToId int64 `protobuf:"varint,1,opt,name=toId" json:"toId,omitempty"` 185 | // 礼物ID 186 | GiftId int32 `protobuf:"varint,2,opt,name=giftId" json:"giftId,omitempty"` 187 | // 礼物数量 188 | Amount int32 `protobuf:"varint,3,opt,name=amount" json:"amount,omitempty"` 189 | } 190 | 191 | func (m *GiftReq) Reset() { *m = GiftReq{} } 192 | func (m *GiftReq) String() string { return proto.CompactTextString(m) } 193 | func (*GiftReq) ProtoMessage() {} 194 | func (*GiftReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } 195 | 196 | func (m *GiftReq) GetToId() int64 { 197 | if m != nil { 198 | return m.ToId 199 | } 200 | return 0 201 | } 202 | 203 | func (m *GiftReq) GetGiftId() int32 { 204 | if m != nil { 205 | return m.GiftId 206 | } 207 | return 0 208 | } 209 | 210 | func (m *GiftReq) GetAmount() int32 { 211 | if m != nil { 212 | return m.Amount 213 | } 214 | return 0 215 | } 216 | 217 | type ChatReq struct { 218 | // 消息类型或消息编码 219 | Mid int32 `protobuf:"varint,1,opt,name=mid" json:"mid,omitempty"` 220 | // 自定义消息内容 221 | Msg string `protobuf:"bytes,2,opt,name=msg" json:"msg,omitempty"` 222 | } 223 | 224 | func (m *ChatReq) Reset() { *m = ChatReq{} } 225 | func (m *ChatReq) String() string { return proto.CompactTextString(m) } 226 | func (*ChatReq) ProtoMessage() {} 227 | func (*ChatReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } 228 | 229 | func (m *ChatReq) GetMid() int32 { 230 | if m != nil { 231 | return m.Mid 232 | } 233 | return 0 234 | } 235 | 236 | func (m *ChatReq) GetMsg() string { 237 | if m != nil { 238 | return m.Msg 239 | } 240 | return "" 241 | } 242 | 243 | type DelegateReq struct { 244 | // 托管操作[1:自动弃牌,2:自动跟注] 245 | Method int32 `protobuf:"varint,1,opt,name=method" json:"method,omitempty"` 246 | Action bool `protobuf:"varint,2,opt,name=action" json:"action,omitempty"` 247 | } 248 | 249 | func (m *DelegateReq) Reset() { *m = DelegateReq{} } 250 | func (m *DelegateReq) String() string { return proto.CompactTextString(m) } 251 | func (*DelegateReq) ProtoMessage() {} 252 | func (*DelegateReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } 253 | 254 | func (m *DelegateReq) GetMethod() int32 { 255 | if m != nil { 256 | return m.Method 257 | } 258 | return 0 259 | } 260 | 261 | func (m *DelegateReq) GetAction() bool { 262 | if m != nil { 263 | return m.Action 264 | } 265 | return false 266 | } 267 | 268 | type GeRes struct { 269 | Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"` 270 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 271 | } 272 | 273 | func (m *GeRes) Reset() { *m = GeRes{} } 274 | func (m *GeRes) String() string { return proto.CompactTextString(m) } 275 | func (*GeRes) ProtoMessage() {} 276 | func (*GeRes) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } 277 | 278 | func (m *GeRes) GetCode() int32 { 279 | if m != nil { 280 | return m.Code 281 | } 282 | return 0 283 | } 284 | 285 | func (m *GeRes) GetCallSeat() int32 { 286 | if m != nil { 287 | return m.CallSeat 288 | } 289 | return 0 290 | } 291 | 292 | type RevealRes struct { 293 | Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"` 294 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 295 | Cards []byte `protobuf:"bytes,3,opt,name=cards,proto3" json:"cards,omitempty"` 296 | // 牌型[1:高牌,2:对子,3:同花,4:顺子,5:同花顺,6:豹子] 297 | CardType int32 `protobuf:"varint,4,opt,name=cardType" json:"cardType,omitempty"` 298 | } 299 | 300 | func (m *RevealRes) Reset() { *m = RevealRes{} } 301 | func (m *RevealRes) String() string { return proto.CompactTextString(m) } 302 | func (*RevealRes) ProtoMessage() {} 303 | func (*RevealRes) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } 304 | 305 | func (m *RevealRes) GetCode() int32 { 306 | if m != nil { 307 | return m.Code 308 | } 309 | return 0 310 | } 311 | 312 | func (m *RevealRes) GetCallSeat() int32 { 313 | if m != nil { 314 | return m.CallSeat 315 | } 316 | return 0 317 | } 318 | 319 | func (m *RevealRes) GetCards() []byte { 320 | if m != nil { 321 | return m.Cards 322 | } 323 | return nil 324 | } 325 | 326 | func (m *RevealRes) GetCardType() int32 { 327 | if m != nil { 328 | return m.CardType 329 | } 330 | return 0 331 | } 332 | 333 | type Player struct { 334 | // 玩家ID 335 | Uid int64 `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty"` 336 | // 玩家座标 337 | SeatCode int32 `protobuf:"varint,2,opt,name=seatCode" json:"seatCode,omitempty"` 338 | // 是否未看牌 339 | Blind bool `protobuf:"varint,3,opt,name=blind" json:"blind,omitempty"` 340 | // 牌桌座位号 341 | Seat int32 `protobuf:"varint,4,opt,name=seat" json:"seat,omitempty"` 342 | // 金币数量 343 | Coin int64 `protobuf:"varint,5,opt,name=coin" json:"coin,omitempty"` 344 | // 昵称 345 | Nick string `protobuf:"bytes,6,opt,name=nick" json:"nick,omitempty"` 346 | // 头像路径 347 | Avatar string `protobuf:"bytes,7,opt,name=avatar" json:"avatar,omitempty"` 348 | // 玩家牌 349 | Cards []byte `protobuf:"bytes,8,opt,name=cards,proto3" json:"cards,omitempty"` 350 | // 本局下注金币 351 | BetCoin int64 `protobuf:"varint,9,opt,name=betCoin" json:"betCoin,omitempty"` 352 | // 玩家特征 353 | Character int32 `protobuf:"varint,10,opt,name=character" json:"character,omitempty"` 354 | // 玩家当前牌局状态 355 | Status int32 `protobuf:"varint,11,opt,name=status" json:"status,omitempty"` 356 | } 357 | 358 | func (m *Player) Reset() { *m = Player{} } 359 | func (m *Player) String() string { return proto.CompactTextString(m) } 360 | func (*Player) ProtoMessage() {} 361 | func (*Player) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } 362 | 363 | func (m *Player) GetUid() int64 { 364 | if m != nil { 365 | return m.Uid 366 | } 367 | return 0 368 | } 369 | 370 | func (m *Player) GetSeatCode() int32 { 371 | if m != nil { 372 | return m.SeatCode 373 | } 374 | return 0 375 | } 376 | 377 | func (m *Player) GetBlind() bool { 378 | if m != nil { 379 | return m.Blind 380 | } 381 | return false 382 | } 383 | 384 | func (m *Player) GetSeat() int32 { 385 | if m != nil { 386 | return m.Seat 387 | } 388 | return 0 389 | } 390 | 391 | func (m *Player) GetCoin() int64 { 392 | if m != nil { 393 | return m.Coin 394 | } 395 | return 0 396 | } 397 | 398 | func (m *Player) GetNick() string { 399 | if m != nil { 400 | return m.Nick 401 | } 402 | return "" 403 | } 404 | 405 | func (m *Player) GetAvatar() string { 406 | if m != nil { 407 | return m.Avatar 408 | } 409 | return "" 410 | } 411 | 412 | func (m *Player) GetCards() []byte { 413 | if m != nil { 414 | return m.Cards 415 | } 416 | return nil 417 | } 418 | 419 | func (m *Player) GetBetCoin() int64 { 420 | if m != nil { 421 | return m.BetCoin 422 | } 423 | return 0 424 | } 425 | 426 | func (m *Player) GetCharacter() int32 { 427 | if m != nil { 428 | return m.Character 429 | } 430 | return 0 431 | } 432 | 433 | func (m *Player) GetStatus() int32 { 434 | if m != nil { 435 | return m.Status 436 | } 437 | return 0 438 | } 439 | 440 | type Table struct { 441 | // 桌号 442 | TableId int32 `protobuf:"varint,1,opt,name=tableId" json:"tableId,omitempty"` 443 | // 当前叫牌座号 444 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 445 | // 庄家座号 446 | BankerSeat int32 `protobuf:"varint,3,opt,name=bankerSeat" json:"bankerSeat,omitempty"` 447 | // 总注池 448 | Jackpot int64 `protobuf:"varint,4,opt,name=jackpot" json:"jackpot,omitempty"` 449 | // 当局轮数 450 | Round int32 `protobuf:"varint,5,opt,name=round" json:"round,omitempty"` 451 | // 当轮下注值 452 | RoundBet int64 `protobuf:"varint,6,opt,name=roundBet" json:"roundBet,omitempty"` 453 | // 牌局状态,0:等待,1:读秒准备中,3:进行中 454 | Status int32 `protobuf:"varint,7,opt,name=status" json:"status,omitempty"` 455 | // 桌内玩家列表 456 | Players []*Player `protobuf:"bytes,8,rep,name=players" json:"players,omitempty"` 457 | // 返回码 458 | Code int32 `protobuf:"varint,9,opt,name=code" json:"code,omitempty"` 459 | } 460 | 461 | func (m *Table) Reset() { *m = Table{} } 462 | func (m *Table) String() string { return proto.CompactTextString(m) } 463 | func (*Table) ProtoMessage() {} 464 | func (*Table) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } 465 | 466 | func (m *Table) GetTableId() int32 { 467 | if m != nil { 468 | return m.TableId 469 | } 470 | return 0 471 | } 472 | 473 | func (m *Table) GetCallSeat() int32 { 474 | if m != nil { 475 | return m.CallSeat 476 | } 477 | return 0 478 | } 479 | 480 | func (m *Table) GetBankerSeat() int32 { 481 | if m != nil { 482 | return m.BankerSeat 483 | } 484 | return 0 485 | } 486 | 487 | func (m *Table) GetJackpot() int64 { 488 | if m != nil { 489 | return m.Jackpot 490 | } 491 | return 0 492 | } 493 | 494 | func (m *Table) GetRound() int32 { 495 | if m != nil { 496 | return m.Round 497 | } 498 | return 0 499 | } 500 | 501 | func (m *Table) GetRoundBet() int64 { 502 | if m != nil { 503 | return m.RoundBet 504 | } 505 | return 0 506 | } 507 | 508 | func (m *Table) GetStatus() int32 { 509 | if m != nil { 510 | return m.Status 511 | } 512 | return 0 513 | } 514 | 515 | func (m *Table) GetPlayers() []*Player { 516 | if m != nil { 517 | return m.Players 518 | } 519 | return nil 520 | } 521 | 522 | func (m *Table) GetCode() int32 { 523 | if m != nil { 524 | return m.Code 525 | } 526 | return 0 527 | } 528 | 529 | type PlayerSeat struct { 530 | // 玩家ID 531 | Uid int64 `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty"` 532 | // 玩家座号 533 | Seat int32 `protobuf:"varint,2,opt,name=seat" json:"seat,omitempty"` 534 | CallSeat int32 `protobuf:"varint,3,opt,name=callSeat" json:"callSeat,omitempty"` 535 | } 536 | 537 | func (m *PlayerSeat) Reset() { *m = PlayerSeat{} } 538 | func (m *PlayerSeat) String() string { return proto.CompactTextString(m) } 539 | func (*PlayerSeat) ProtoMessage() {} 540 | func (*PlayerSeat) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } 541 | 542 | func (m *PlayerSeat) GetUid() int64 { 543 | if m != nil { 544 | return m.Uid 545 | } 546 | return 0 547 | } 548 | 549 | func (m *PlayerSeat) GetSeat() int32 { 550 | if m != nil { 551 | return m.Seat 552 | } 553 | return 0 554 | } 555 | 556 | func (m *PlayerSeat) GetCallSeat() int32 { 557 | if m != nil { 558 | return m.CallSeat 559 | } 560 | return 0 561 | } 562 | 563 | type Reveal struct { 564 | // 座号 565 | Seat int32 `protobuf:"varint,1,opt,name=seat" json:"seat,omitempty"` 566 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 567 | Cards []byte `protobuf:"bytes,3,opt,name=cards,proto3" json:"cards,omitempty"` 568 | // 牌型[1:高牌,2:对子,3:同花,4:顺子,5:同花顺,6:豹子] 569 | CardType int32 `protobuf:"varint,4,opt,name=cardType" json:"cardType,omitempty"` 570 | } 571 | 572 | func (m *Reveal) Reset() { *m = Reveal{} } 573 | func (m *Reveal) String() string { return proto.CompactTextString(m) } 574 | func (*Reveal) ProtoMessage() {} 575 | func (*Reveal) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } 576 | 577 | func (m *Reveal) GetSeat() int32 { 578 | if m != nil { 579 | return m.Seat 580 | } 581 | return 0 582 | } 583 | 584 | func (m *Reveal) GetCallSeat() int32 { 585 | if m != nil { 586 | return m.CallSeat 587 | } 588 | return 0 589 | } 590 | 591 | func (m *Reveal) GetCards() []byte { 592 | if m != nil { 593 | return m.Cards 594 | } 595 | return nil 596 | } 597 | 598 | func (m *Reveal) GetCardType() int32 { 599 | if m != nil { 600 | return m.CardType 601 | } 602 | return 0 603 | } 604 | 605 | type GeCall struct { 606 | // 座号 607 | Seat int32 `protobuf:"varint,1,opt,name=seat" json:"seat,omitempty"` 608 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 609 | } 610 | 611 | func (m *GeCall) Reset() { *m = GeCall{} } 612 | func (m *GeCall) String() string { return proto.CompactTextString(m) } 613 | func (*GeCall) ProtoMessage() {} 614 | func (*GeCall) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } 615 | 616 | func (m *GeCall) GetSeat() int32 { 617 | if m != nil { 618 | return m.Seat 619 | } 620 | return 0 621 | } 622 | 623 | func (m *GeCall) GetCallSeat() int32 { 624 | if m != nil { 625 | return m.CallSeat 626 | } 627 | return 0 628 | } 629 | 630 | type GiveUp struct { 631 | // 座号 632 | Seat int32 `protobuf:"varint,1,opt,name=seat" json:"seat,omitempty"` 633 | // 下一个叫牌座号 634 | CallSeat int32 `protobuf:"varint,2,opt,name=callSeat" json:"callSeat,omitempty"` 635 | } 636 | 637 | func (m *GiveUp) Reset() { *m = GiveUp{} } 638 | func (m *GiveUp) String() string { return proto.CompactTextString(m) } 639 | func (*GiveUp) ProtoMessage() {} 640 | func (*GiveUp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } 641 | 642 | func (m *GiveUp) GetSeat() int32 { 643 | if m != nil { 644 | return m.Seat 645 | } 646 | return 0 647 | } 648 | 649 | func (m *GiveUp) GetCallSeat() int32 { 650 | if m != nil { 651 | return m.CallSeat 652 | } 653 | return 0 654 | } 655 | 656 | type Duel struct { 657 | // 比牌发起者、拒绝者或接受者座号(根据协议定) 658 | FromSeat int32 `protobuf:"varint,1,opt,name=fromSeat" json:"fromSeat,omitempty"` 659 | // 比牌发起者、拒绝者或接受者座号(根据协议定) 660 | ToSeat int32 `protobuf:"varint,2,opt,name=toSeat" json:"toSeat,omitempty"` 661 | // 下一个叫牌座号 662 | CallSeat int32 `protobuf:"varint,3,opt,name=callSeat" json:"callSeat,omitempty"` 663 | } 664 | 665 | func (m *Duel) Reset() { *m = Duel{} } 666 | func (m *Duel) String() string { return proto.CompactTextString(m) } 667 | func (*Duel) ProtoMessage() {} 668 | func (*Duel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } 669 | 670 | func (m *Duel) GetFromSeat() int32 { 671 | if m != nil { 672 | return m.FromSeat 673 | } 674 | return 0 675 | } 676 | 677 | func (m *Duel) GetToSeat() int32 { 678 | if m != nil { 679 | return m.ToSeat 680 | } 681 | return 0 682 | } 683 | 684 | func (m *Duel) GetCallSeat() int32 { 685 | if m != nil { 686 | return m.CallSeat 687 | } 688 | return 0 689 | } 690 | 691 | type Bet struct { 692 | // 下注玩家座号 693 | Seat int32 `protobuf:"varint,1,opt,name=seat" json:"seat,omitempty"` 694 | // 下注金币数量 695 | Coins int64 `protobuf:"varint,2,opt,name=coins" json:"coins,omitempty"` 696 | // 下一个叫牌座号 697 | CallSeat int32 `protobuf:"varint,3,opt,name=callSeat" json:"callSeat,omitempty"` 698 | } 699 | 700 | func (m *Bet) Reset() { *m = Bet{} } 701 | func (m *Bet) String() string { return proto.CompactTextString(m) } 702 | func (*Bet) ProtoMessage() {} 703 | func (*Bet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } 704 | 705 | func (m *Bet) GetSeat() int32 { 706 | if m != nil { 707 | return m.Seat 708 | } 709 | return 0 710 | } 711 | 712 | func (m *Bet) GetCoins() int64 { 713 | if m != nil { 714 | return m.Coins 715 | } 716 | return 0 717 | } 718 | 719 | func (m *Bet) GetCallSeat() int32 { 720 | if m != nil { 721 | return m.CallSeat 722 | } 723 | return 0 724 | } 725 | 726 | type DuelResult struct { 727 | // 比牌发起者座号 728 | FromSeat int32 `protobuf:"varint,1,opt,name=fromSeat" json:"fromSeat,omitempty"` 729 | // 被比牌者座号 730 | ToSeat int32 `protobuf:"varint,2,opt,name=toSeat" json:"toSeat,omitempty"` 731 | // 胜者座号 732 | WinSeat int32 `protobuf:"varint,3,opt,name=winSeat" json:"winSeat,omitempty"` 733 | // 比牌二者的牌组 734 | DuelCards map[int32][]byte `protobuf:"bytes,4,rep,name=duelCards" json:"duelCards,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"` 735 | // 下一个叫牌座号 736 | CallSeat int32 `protobuf:"varint,5,opt,name=callSeat" json:"callSeat,omitempty"` 737 | } 738 | 739 | func (m *DuelResult) Reset() { *m = DuelResult{} } 740 | func (m *DuelResult) String() string { return proto.CompactTextString(m) } 741 | func (*DuelResult) ProtoMessage() {} 742 | func (*DuelResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } 743 | 744 | func (m *DuelResult) GetFromSeat() int32 { 745 | if m != nil { 746 | return m.FromSeat 747 | } 748 | return 0 749 | } 750 | 751 | func (m *DuelResult) GetToSeat() int32 { 752 | if m != nil { 753 | return m.ToSeat 754 | } 755 | return 0 756 | } 757 | 758 | func (m *DuelResult) GetWinSeat() int32 { 759 | if m != nil { 760 | return m.WinSeat 761 | } 762 | return 0 763 | } 764 | 765 | func (m *DuelResult) GetDuelCards() map[int32][]byte { 766 | if m != nil { 767 | return m.DuelCards 768 | } 769 | return nil 770 | } 771 | 772 | func (m *DuelResult) GetCallSeat() int32 { 773 | if m != nil { 774 | return m.CallSeat 775 | } 776 | return 0 777 | } 778 | 779 | type FriendAdd struct { 780 | // 发起、拒绝或接受加好友玩家座号(根据协议定) 781 | FromId int64 `protobuf:"varint,1,opt,name=fromId" json:"fromId,omitempty"` 782 | // 发起、拒绝或接受加好友玩家座号(根据协议定) 783 | ToId int64 `protobuf:"varint,2,opt,name=toId" json:"toId,omitempty"` 784 | } 785 | 786 | func (m *FriendAdd) Reset() { *m = FriendAdd{} } 787 | func (m *FriendAdd) String() string { return proto.CompactTextString(m) } 788 | func (*FriendAdd) ProtoMessage() {} 789 | func (*FriendAdd) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } 790 | 791 | func (m *FriendAdd) GetFromId() int64 { 792 | if m != nil { 793 | return m.FromId 794 | } 795 | return 0 796 | } 797 | 798 | func (m *FriendAdd) GetToId() int64 { 799 | if m != nil { 800 | return m.ToId 801 | } 802 | return 0 803 | } 804 | 805 | type FriendAddResult struct { 806 | // 发起加好友玩家座号 807 | FromId int64 `protobuf:"varint,1,opt,name=fromId" json:"fromId,omitempty"` 808 | // 被加好友玩家座号 809 | ToId int64 `protobuf:"varint,2,opt,name=toId" json:"toId,omitempty"` 810 | // 加友好是否接受 811 | Accept bool `protobuf:"varint,3,opt,name=accept" json:"accept,omitempty"` 812 | } 813 | 814 | func (m *FriendAddResult) Reset() { *m = FriendAddResult{} } 815 | func (m *FriendAddResult) String() string { return proto.CompactTextString(m) } 816 | func (*FriendAddResult) ProtoMessage() {} 817 | func (*FriendAddResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } 818 | 819 | func (m *FriendAddResult) GetFromId() int64 { 820 | if m != nil { 821 | return m.FromId 822 | } 823 | return 0 824 | } 825 | 826 | func (m *FriendAddResult) GetToId() int64 { 827 | if m != nil { 828 | return m.ToId 829 | } 830 | return 0 831 | } 832 | 833 | func (m *FriendAddResult) GetAccept() bool { 834 | if m != nil { 835 | return m.Accept 836 | } 837 | return false 838 | } 839 | 840 | type SendGift struct { 841 | // 送礼物玩家座号 842 | FromId int64 `protobuf:"varint,1,opt,name=fromId" json:"fromId,omitempty"` 843 | // 被送礼物玩家座号 844 | ToId int64 `protobuf:"varint,2,opt,name=toId" json:"toId,omitempty"` 845 | // 礼物编号 846 | GiftId int32 `protobuf:"varint,3,opt,name=giftId" json:"giftId,omitempty"` 847 | // 礼物数量 848 | Amount int32 `protobuf:"varint,4,opt,name=amount" json:"amount,omitempty"` 849 | } 850 | 851 | func (m *SendGift) Reset() { *m = SendGift{} } 852 | func (m *SendGift) String() string { return proto.CompactTextString(m) } 853 | func (*SendGift) ProtoMessage() {} 854 | func (*SendGift) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } 855 | 856 | func (m *SendGift) GetFromId() int64 { 857 | if m != nil { 858 | return m.FromId 859 | } 860 | return 0 861 | } 862 | 863 | func (m *SendGift) GetToId() int64 { 864 | if m != nil { 865 | return m.ToId 866 | } 867 | return 0 868 | } 869 | 870 | func (m *SendGift) GetGiftId() int32 { 871 | if m != nil { 872 | return m.GiftId 873 | } 874 | return 0 875 | } 876 | 877 | func (m *SendGift) GetAmount() int32 { 878 | if m != nil { 879 | return m.Amount 880 | } 881 | return 0 882 | } 883 | 884 | type GameOver struct { 885 | // 胜者座号 886 | WinUid int64 `protobuf:"varint,1,opt,name=winUid" json:"winUid,omitempty"` 887 | // 税后金币 888 | Result map[string]*TableResult `protobuf:"bytes,2,rep,name=result" json:"result,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` 889 | } 890 | 891 | func (m *GameOver) Reset() { *m = GameOver{} } 892 | func (m *GameOver) String() string { return proto.CompactTextString(m) } 893 | func (*GameOver) ProtoMessage() {} 894 | func (*GameOver) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } 895 | 896 | func (m *GameOver) GetWinUid() int64 { 897 | if m != nil { 898 | return m.WinUid 899 | } 900 | return 0 901 | } 902 | 903 | func (m *GameOver) GetResult() map[string]*TableResult { 904 | if m != nil { 905 | return m.Result 906 | } 907 | return nil 908 | } 909 | 910 | type TableResult struct { 911 | BetCoins int64 `protobuf:"varint,1,opt,name=betCoins" json:"betCoins,omitempty"` 912 | Cards []byte `protobuf:"bytes,2,opt,name=cards,proto3" json:"cards,omitempty"` 913 | CardsType int32 `protobuf:"varint,3,opt,name=cardsType" json:"cardsType,omitempty"` 914 | GreetType int32 `protobuf:"varint,4,opt,name=greetType" json:"greetType,omitempty"` 915 | } 916 | 917 | func (m *TableResult) Reset() { *m = TableResult{} } 918 | func (m *TableResult) String() string { return proto.CompactTextString(m) } 919 | func (*TableResult) ProtoMessage() {} 920 | func (*TableResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } 921 | 922 | func (m *TableResult) GetBetCoins() int64 { 923 | if m != nil { 924 | return m.BetCoins 925 | } 926 | return 0 927 | } 928 | 929 | func (m *TableResult) GetCards() []byte { 930 | if m != nil { 931 | return m.Cards 932 | } 933 | return nil 934 | } 935 | 936 | func (m *TableResult) GetCardsType() int32 { 937 | if m != nil { 938 | return m.CardsType 939 | } 940 | return 0 941 | } 942 | 943 | func (m *TableResult) GetGreetType() int32 { 944 | if m != nil { 945 | return m.GreetType 946 | } 947 | return 0 948 | } 949 | 950 | type CardDeal struct { 951 | // 下一个叫牌座号 952 | CallSeat int32 `protobuf:"varint,1,opt,name=callSeat" json:"callSeat,omitempty"` 953 | BankerSeat int32 `protobuf:"varint,2,opt,name=bankerSeat" json:"bankerSeat,omitempty"` 954 | } 955 | 956 | func (m *CardDeal) Reset() { *m = CardDeal{} } 957 | func (m *CardDeal) String() string { return proto.CompactTextString(m) } 958 | func (*CardDeal) ProtoMessage() {} 959 | func (*CardDeal) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } 960 | 961 | func (m *CardDeal) GetCallSeat() int32 { 962 | if m != nil { 963 | return m.CallSeat 964 | } 965 | return 0 966 | } 967 | 968 | func (m *CardDeal) GetBankerSeat() int32 { 969 | if m != nil { 970 | return m.BankerSeat 971 | } 972 | return 0 973 | } 974 | 975 | type ChatMsg struct { 976 | // 座号 977 | Sid int32 `protobuf:"varint,1,opt,name=sid" json:"sid,omitempty"` 978 | // 消息类型或消息编码 979 | Mid int32 `protobuf:"varint,2,opt,name=mid" json:"mid,omitempty"` 980 | // 自定义消息内容 981 | Msg string `protobuf:"bytes,3,opt,name=msg" json:"msg,omitempty"` 982 | } 983 | 984 | func (m *ChatMsg) Reset() { *m = ChatMsg{} } 985 | func (m *ChatMsg) String() string { return proto.CompactTextString(m) } 986 | func (*ChatMsg) ProtoMessage() {} 987 | func (*ChatMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } 988 | 989 | func (m *ChatMsg) GetSid() int32 { 990 | if m != nil { 991 | return m.Sid 992 | } 993 | return 0 994 | } 995 | 996 | func (m *ChatMsg) GetMid() int32 { 997 | if m != nil { 998 | return m.Mid 999 | } 1000 | return 0 1001 | } 1002 | 1003 | func (m *ChatMsg) GetMsg() string { 1004 | if m != nil { 1005 | return m.Msg 1006 | } 1007 | return "" 1008 | } 1009 | 1010 | type NetInfo struct { 1011 | // 座号 1012 | Sid int32 `protobuf:"varint,1,opt,name=sid" json:"sid,omitempty"` 1013 | // 网络状态[在线/掉线] 1014 | Status bool `protobuf:"varint,2,opt,name=status" json:"status,omitempty"` 1015 | } 1016 | 1017 | func (m *NetInfo) Reset() { *m = NetInfo{} } 1018 | func (m *NetInfo) String() string { return proto.CompactTextString(m) } 1019 | func (*NetInfo) ProtoMessage() {} 1020 | func (*NetInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } 1021 | 1022 | func (m *NetInfo) GetSid() int32 { 1023 | if m != nil { 1024 | return m.Sid 1025 | } 1026 | return 0 1027 | } 1028 | 1029 | func (m *NetInfo) GetStatus() bool { 1030 | if m != nil { 1031 | return m.Status 1032 | } 1033 | return false 1034 | } 1035 | 1036 | func init() { 1037 | proto.RegisterType((*Frame)(nil), "pb.Frame") 1038 | proto.RegisterType((*EnterTableReq)(nil), "pb.EnterTableReq") 1039 | proto.RegisterType((*SitdownReq)(nil), "pb.SitdownReq") 1040 | proto.RegisterType((*BetReq)(nil), "pb.BetReq") 1041 | proto.RegisterType((*DuelReq)(nil), "pb.DuelReq") 1042 | proto.RegisterType((*FriendReq)(nil), "pb.FriendReq") 1043 | proto.RegisterType((*GiftReq)(nil), "pb.GiftReq") 1044 | proto.RegisterType((*ChatReq)(nil), "pb.ChatReq") 1045 | proto.RegisterType((*DelegateReq)(nil), "pb.DelegateReq") 1046 | proto.RegisterType((*GeRes)(nil), "pb.GeRes") 1047 | proto.RegisterType((*RevealRes)(nil), "pb.RevealRes") 1048 | proto.RegisterType((*Player)(nil), "pb.Player") 1049 | proto.RegisterType((*Table)(nil), "pb.Table") 1050 | proto.RegisterType((*PlayerSeat)(nil), "pb.PlayerSeat") 1051 | proto.RegisterType((*Reveal)(nil), "pb.Reveal") 1052 | proto.RegisterType((*GeCall)(nil), "pb.GeCall") 1053 | proto.RegisterType((*GiveUp)(nil), "pb.GiveUp") 1054 | proto.RegisterType((*Duel)(nil), "pb.Duel") 1055 | proto.RegisterType((*Bet)(nil), "pb.Bet") 1056 | proto.RegisterType((*DuelResult)(nil), "pb.DuelResult") 1057 | proto.RegisterType((*FriendAdd)(nil), "pb.FriendAdd") 1058 | proto.RegisterType((*FriendAddResult)(nil), "pb.FriendAddResult") 1059 | proto.RegisterType((*SendGift)(nil), "pb.SendGift") 1060 | proto.RegisterType((*GameOver)(nil), "pb.GameOver") 1061 | proto.RegisterType((*TableResult)(nil), "pb.TableResult") 1062 | proto.RegisterType((*CardDeal)(nil), "pb.CardDeal") 1063 | proto.RegisterType((*ChatMsg)(nil), "pb.ChatMsg") 1064 | proto.RegisterType((*NetInfo)(nil), "pb.NetInfo") 1065 | } 1066 | 1067 | // Reference imports to suppress errors if they are not otherwise used. 1068 | var _ context.Context 1069 | var _ grpc.ClientConn 1070 | 1071 | // This is a compile-time assertion to ensure that this generated file 1072 | // is compatible with the grpc package it is being compiled against. 1073 | const _ = grpc.SupportPackageIsVersion4 1074 | 1075 | // Client API for GameService service 1076 | 1077 | type GameServiceClient interface { 1078 | Stream(ctx context.Context, opts ...grpc.CallOption) (GameService_StreamClient, error) 1079 | } 1080 | 1081 | type gameServiceClient struct { 1082 | cc *grpc.ClientConn 1083 | } 1084 | 1085 | func NewGameServiceClient(cc *grpc.ClientConn) GameServiceClient { 1086 | return &gameServiceClient{cc} 1087 | } 1088 | 1089 | func (c *gameServiceClient) Stream(ctx context.Context, opts ...grpc.CallOption) (GameService_StreamClient, error) { 1090 | stream, err := grpc.NewClientStream(ctx, &_GameService_serviceDesc.Streams[0], c.cc, "/pb.GameService/Stream", opts...) 1091 | if err != nil { 1092 | return nil, err 1093 | } 1094 | x := &gameServiceStreamClient{stream} 1095 | return x, nil 1096 | } 1097 | 1098 | type GameService_StreamClient interface { 1099 | Send(*Frame) error 1100 | Recv() (*Frame, error) 1101 | grpc.ClientStream 1102 | } 1103 | 1104 | type gameServiceStreamClient struct { 1105 | grpc.ClientStream 1106 | } 1107 | 1108 | func (x *gameServiceStreamClient) Send(m *Frame) error { 1109 | return x.ClientStream.SendMsg(m) 1110 | } 1111 | 1112 | func (x *gameServiceStreamClient) Recv() (*Frame, error) { 1113 | m := new(Frame) 1114 | if err := x.ClientStream.RecvMsg(m); err != nil { 1115 | return nil, err 1116 | } 1117 | return m, nil 1118 | } 1119 | 1120 | // Server API for GameService service 1121 | 1122 | type GameServiceServer interface { 1123 | Stream(GameService_StreamServer) error 1124 | } 1125 | 1126 | func RegisterGameServiceServer(s *grpc.Server, srv GameServiceServer) { 1127 | s.RegisterService(&_GameService_serviceDesc, srv) 1128 | } 1129 | 1130 | func _GameService_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { 1131 | return srv.(GameServiceServer).Stream(&gameServiceStreamServer{stream}) 1132 | } 1133 | 1134 | type GameService_StreamServer interface { 1135 | Send(*Frame) error 1136 | Recv() (*Frame, error) 1137 | grpc.ServerStream 1138 | } 1139 | 1140 | type gameServiceStreamServer struct { 1141 | grpc.ServerStream 1142 | } 1143 | 1144 | func (x *gameServiceStreamServer) Send(m *Frame) error { 1145 | return x.ServerStream.SendMsg(m) 1146 | } 1147 | 1148 | func (x *gameServiceStreamServer) Recv() (*Frame, error) { 1149 | m := new(Frame) 1150 | if err := x.ServerStream.RecvMsg(m); err != nil { 1151 | return nil, err 1152 | } 1153 | return m, nil 1154 | } 1155 | 1156 | var _GameService_serviceDesc = grpc.ServiceDesc{ 1157 | ServiceName: "pb.GameService", 1158 | HandlerType: (*GameServiceServer)(nil), 1159 | Methods: []grpc.MethodDesc{}, 1160 | Streams: []grpc.StreamDesc{ 1161 | { 1162 | StreamName: "Stream", 1163 | Handler: _GameService_Stream_Handler, 1164 | ServerStreams: true, 1165 | ClientStreams: true, 1166 | }, 1167 | }, 1168 | Metadata: "game.proto", 1169 | } 1170 | 1171 | func init() { proto.RegisterFile("game.proto", fileDescriptor0) } 1172 | 1173 | var fileDescriptor0 = []byte{ 1174 | // 991 bytes of a gzipped FileDescriptorProto 1175 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4b, 0x6f, 0xdb, 0x46, 1176 | 0x10, 0x06, 0x49, 0x91, 0x94, 0x46, 0x6e, 0x53, 0x10, 0x41, 0x40, 0x08, 0x6d, 0xe2, 0x12, 0x2d, 1177 | 0xa0, 0x4b, 0x8d, 0xd4, 0x39, 0x24, 0xe8, 0x03, 0x45, 0x23, 0xc7, 0x86, 0x5b, 0x24, 0x2d, 0xa8, 1178 | 0xb8, 0xf7, 0x15, 0x39, 0x92, 0x19, 0xf1, 0xa1, 0x2e, 0x57, 0x32, 0x84, 0xde, 0xfb, 0x3b, 0xfa, 1179 | 0x17, 0x7b, 0xee, 0xa5, 0x98, 0x7d, 0x90, 0xeb, 0xd4, 0x31, 0x60, 0xa3, 0xb7, 0xf9, 0x96, 0x3b, 1180 | 0xb3, 0x3b, 0xf3, 0x0d, 0xbf, 0x1d, 0x80, 0x15, 0xab, 0xf0, 0x68, 0xc3, 0x1b, 0xd1, 0x44, 0xee, 1181 | 0x66, 0x91, 0x7c, 0x0e, 0xfe, 0x29, 0x67, 0x15, 0x46, 0x31, 0x84, 0x1b, 0xb6, 0x2f, 0x1b, 0x96, 1182 | 0xc7, 0xce, 0xa1, 0x33, 0x3d, 0x48, 0x0d, 0x4c, 0x2e, 0xe0, 0xa3, 0x57, 0xb5, 0x40, 0xfe, 0x96, 1183 | 0x2d, 0x4a, 0x4c, 0xf1, 0xf7, 0xe8, 0x13, 0xf0, 0xb6, 0x85, 0xda, 0xe6, 0xa5, 0x64, 0x46, 0x0f, 1184 | 0xc1, 0x17, 0xcd, 0x1a, 0xeb, 0xd8, 0x3d, 0x74, 0xa6, 0xa3, 0x54, 0x81, 0xe8, 0x53, 0x18, 0xf1, 1185 | 0xa6, 0xa9, 0x66, 0x25, 0x6b, 0xdb, 0xd8, 0x3b, 0x74, 0xa6, 0x7e, 0xda, 0x2f, 0x24, 0x87, 0x00, 1186 | 0xf3, 0x42, 0xe4, 0xcd, 0x55, 0x4d, 0x31, 0x23, 0x18, 0xb4, 0xc8, 0x84, 0x0c, 0xea, 0xa7, 0xd2, 1187 | 0x4e, 0x1e, 0x43, 0xf0, 0x12, 0x05, 0x7d, 0x7d, 0x08, 0x7e, 0xd6, 0x14, 0x75, 0xab, 0xcf, 0x54, 1188 | 0x20, 0x79, 0x02, 0xe1, 0xc9, 0x16, 0x4b, 0xbd, 0x81, 0x17, 0x3b, 0x56, 0x9a, 0x0d, 0x12, 0x24, 1189 | 0x4f, 0x60, 0x74, 0xca, 0x0b, 0xac, 0x73, 0x7d, 0x82, 0x68, 0xce, 0xcd, 0xb5, 0xa5, 0x9d, 0xbc, 1190 | 0x86, 0xf0, 0xac, 0x58, 0x8a, 0x0f, 0x7c, 0x8e, 0x1e, 0x41, 0xb0, 0x2a, 0x96, 0xe2, 0x3c, 0x97, 1191 | 0x79, 0xf9, 0xa9, 0x46, 0xb4, 0xce, 0xaa, 0x66, 0x5b, 0x0b, 0x9d, 0x95, 0x46, 0xc9, 0x57, 0x10, 1192 | 0xce, 0x2e, 0x99, 0xd0, 0x35, 0xaa, 0x74, 0x8d, 0xfc, 0x94, 0x4c, 0xb9, 0xd2, 0xae, 0x74, 0x85, 1193 | 0xc8, 0x4c, 0xbe, 0x87, 0xf1, 0x09, 0x96, 0xb8, 0x62, 0x42, 0x96, 0xf5, 0x11, 0x04, 0x15, 0x8a, 1194 | 0xcb, 0xc6, 0x78, 0x69, 0x24, 0x4f, 0xcb, 0x44, 0xd1, 0xa8, 0xea, 0x0e, 0x53, 0x8d, 0x92, 0xe7, 1195 | 0xe0, 0x9f, 0x61, 0x8a, 0x2d, 0x5d, 0x3d, 0x6b, 0x72, 0x34, 0xb5, 0x23, 0x3b, 0x9a, 0xc0, 0x30, 1196 | 0x63, 0x65, 0x39, 0xa7, 0x9a, 0xaa, 0xcb, 0x77, 0x38, 0xa9, 0x60, 0x94, 0xe2, 0x0e, 0x59, 0x79, 1197 | 0x0f, 0x67, 0x49, 0x05, 0xe3, 0xb9, 0x22, 0xf4, 0x20, 0x55, 0x40, 0x79, 0xf0, 0xfc, 0xed, 0x7e, 1198 | 0x83, 0xf1, 0xc0, 0x78, 0x28, 0x9c, 0xfc, 0xe9, 0x42, 0xf0, 0x6b, 0xc9, 0xf6, 0xc8, 0x6f, 0xe8, 1199 | 0x9c, 0x09, 0x0c, 0x89, 0xeb, 0x19, 0x5d, 0x41, 0x1f, 0x65, 0x30, 0x1d, 0xb5, 0x28, 0x8b, 0x3a, 1200 | 0x97, 0x47, 0x0d, 0x53, 0x05, 0xba, 0x4e, 0x19, 0xf4, 0x9d, 0xa2, 0x92, 0x28, 0xea, 0xd8, 0x57, 1201 | 0xe4, 0x91, 0x4d, 0x6b, 0x75, 0x91, 0xad, 0xe3, 0x40, 0x16, 0x5c, 0xda, 0xb2, 0x94, 0x3b, 0x26, 1202 | 0x18, 0x8f, 0x43, 0xb9, 0xaa, 0x51, 0x9f, 0xd4, 0xd0, 0x4e, 0x2a, 0x86, 0x70, 0x81, 0x62, 0x46, 1203 | 0x81, 0x47, 0x32, 0xb0, 0x81, 0xd4, 0xd9, 0xd9, 0x25, 0xe3, 0x2c, 0x13, 0xc8, 0x63, 0x50, 0x9d, 1204 | 0xdd, 0x2d, 0xd0, 0x29, 0xad, 0x60, 0x62, 0xdb, 0xc6, 0x63, 0x45, 0xa4, 0x42, 0xc9, 0x3f, 0x0e, 1205 | 0xf8, 0xf2, 0x27, 0xa2, 0xc8, 0x82, 0x8c, 0x73, 0xc3, 0xb5, 0x81, 0xb7, 0x96, 0xfe, 0x31, 0xc0, 1206 | 0x82, 0xd5, 0x6b, 0xe4, 0xf2, 0xab, 0x6a, 0x3d, 0x6b, 0x85, 0xa2, 0xbe, 0x63, 0xd9, 0x7a, 0xd3, 1207 | 0xa8, 0xe2, 0x78, 0xa9, 0x81, 0xf2, 0xf7, 0x68, 0xb6, 0x75, 0x2e, 0x0b, 0xe4, 0xa7, 0x0a, 0xd0, 1208 | 0x59, 0xd2, 0x78, 0x89, 0x42, 0x56, 0xc9, 0x4b, 0x3b, 0x6c, 0xe5, 0x10, 0xda, 0x39, 0x44, 0x5f, 1209 | 0x40, 0xb8, 0x91, 0x5c, 0x52, 0xad, 0xbc, 0xe9, 0xf8, 0x18, 0x8e, 0x36, 0x8b, 0x23, 0x45, 0x6f, 1210 | 0x6a, 0x3e, 0x75, 0x4d, 0x35, 0xea, 0x9b, 0x2a, 0x79, 0x03, 0xa0, 0xb6, 0xc9, 0xbb, 0xfe, 0xb7, 1211 | 0x13, 0x0c, 0xaf, 0xae, 0xc5, 0xab, 0x5d, 0x0d, 0xef, 0xbd, 0x2e, 0x7e, 0x07, 0x81, 0xea, 0xe2, 1212 | 0x9b, 0xb4, 0xe3, 0x7f, 0x6e, 0xe1, 0x17, 0x10, 0x9c, 0xe1, 0x8c, 0x95, 0x77, 0x3e, 0x4b, 0x7a, 1213 | 0x16, 0x3b, 0xbc, 0xd8, 0xdc, 0xd9, 0xf3, 0x37, 0x18, 0x90, 0xba, 0xd1, 0x9e, 0x25, 0x6f, 0xaa, 1214 | 0x79, 0xef, 0xdb, 0x61, 0x62, 0x49, 0x34, 0x96, 0xb7, 0x46, 0xb7, 0xd6, 0xed, 0x67, 0xf0, 0x88, 1215 | 0xe0, 0x9b, 0xae, 0xd3, 0xc9, 0xac, 0x6b, 0xc9, 0xec, 0xad, 0xc1, 0xfe, 0x76, 0x00, 0x94, 0x06, 1216 | 0xb7, 0xdb, 0x52, 0xdc, 0xeb, 0xae, 0x31, 0x84, 0x57, 0x45, 0x6d, 0x45, 0x37, 0x30, 0xfa, 0x16, 1217 | 0x46, 0xf9, 0x16, 0xcb, 0x99, 0xe4, 0x6a, 0x20, 0xbb, 0xed, 0x33, 0xea, 0xb6, 0xfe, 0x40, 0x69, 1218 | 0xca, 0xef, 0xaf, 0x6a, 0xc1, 0xf7, 0x69, 0xbf, 0xff, 0xda, 0xad, 0xfd, 0xeb, 0xb7, 0x9e, 0x7c, 1219 | 0x07, 0x1f, 0x5f, 0x77, 0xa4, 0x76, 0x5c, 0xe3, 0xde, 0xc8, 0xf5, 0x1a, 0xf7, 0x54, 0x8b, 0x1d, 1220 | 0x2b, 0xb7, 0x4a, 0x95, 0x0e, 0x52, 0x05, 0xbe, 0x71, 0x5f, 0x38, 0xc9, 0x73, 0xf3, 0xaa, 0xfc, 1221 | 0x98, 0x4b, 0x71, 0xa6, 0x0c, 0xbb, 0x87, 0x43, 0xa3, 0xee, 0x39, 0x71, 0xad, 0xd7, 0xe6, 0x02, 1222 | 0x1e, 0x74, 0x8e, 0xba, 0x60, 0x77, 0x70, 0x57, 0xef, 0x40, 0x86, 0x1b, 0xa1, 0xf5, 0x50, 0xa3, 1223 | 0x64, 0x09, 0xc3, 0x39, 0xd6, 0x39, 0x3d, 0x64, 0x77, 0x8d, 0xa7, 0x5f, 0x37, 0xef, 0x03, 0xaf, 1224 | 0xdb, 0xe0, 0xda, 0xeb, 0xf6, 0x97, 0x03, 0xc3, 0x33, 0x56, 0xe1, 0x2f, 0x3b, 0xa5, 0x71, 0x57, 1225 | 0x45, 0x7d, 0xd1, 0xfd, 0xc2, 0x1a, 0x45, 0x4f, 0x21, 0xe0, 0x32, 0xb5, 0xd8, 0x95, 0x84, 0xc5, 1226 | 0x44, 0x98, 0xf1, 0x3a, 0x52, 0x59, 0x2b, 0xae, 0xf4, 0xbe, 0xc9, 0x4f, 0x30, 0xb6, 0x96, 0x6d, 1227 | 0x26, 0x46, 0x8a, 0x89, 0x2f, 0x6d, 0x26, 0xc6, 0xc7, 0x0f, 0x28, 0xa2, 0x9e, 0x45, 0xc8, 0xcd, 1228 | 0xa6, 0xe6, 0x0f, 0x18, 0x5b, 0x5f, 0xa8, 0x07, 0xb4, 0x62, 0x9b, 0xc9, 0xa1, 0xc3, 0xbd, 0x08, 1229 | 0xb8, 0xb6, 0x08, 0x90, 0xb0, 0x93, 0x21, 0x55, 0x40, 0x8f, 0x2c, 0xdd, 0x02, 0x7d, 0x5d, 0x71, 1230 | 0x44, 0x61, 0x69, 0x44, 0xbf, 0x90, 0x9c, 0xc2, 0x90, 0x3a, 0xea, 0x84, 0x24, 0xc9, 0xee, 0x3e, 1231 | 0xe7, 0x56, 0x19, 0x77, 0xdf, 0x97, 0xf1, 0xe4, 0x07, 0x35, 0x45, 0xbc, 0x6e, 0x57, 0x54, 0x8c, 1232 | 0xb6, 0x9f, 0x22, 0x5a, 0x3d, 0x45, 0x14, 0x66, 0x1e, 0xb1, 0xe7, 0x0a, 0xaf, 0x9f, 0x2b, 0x9e, 1233 | 0x41, 0xf8, 0x06, 0xc5, 0x79, 0xbd, 0x6c, 0x6e, 0x08, 0xd0, 0x0b, 0xbb, 0x9e, 0x26, 0x14, 0x3a, 1234 | 0xfe, 0x1a, 0xc6, 0x44, 0xd3, 0x1c, 0xf9, 0xae, 0xc8, 0x30, 0x4a, 0x20, 0x98, 0x0b, 0x8e, 0xac, 1235 | 0x8a, 0x46, 0x54, 0x6f, 0x39, 0x23, 0x4e, 0x7a, 0x73, 0xea, 0x3c, 0x75, 0x16, 0x81, 0x1c, 0x23, 1236 | 0x9f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x42, 0x55, 0xb2, 0x1e, 0x54, 0x0a, 0x00, 0x00, 1237 | } 1238 | -------------------------------------------------------------------------------- /game/pb/game.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | service GameService { 6 | rpc Stream (stream Frame) returns (stream Frame); // 透传消息, 双向流 7 | } 8 | //双向流包 9 | message Frame { 10 | bytes payload = 1; 11 | } 12 | //双向流请求包 13 | message EnterTableReq { 14 | int64 uid = 1; 15 | //登陆令牌 16 | string token = 2; 17 | //房间等级 18 | int32 roomClass = 3; 19 | } 20 | message SitdownReq { 21 | //座位号 22 | int32 seat = 1; 23 | } 24 | message BetReq { 25 | //下注金币数量 26 | int64 coins = 1; 27 | } 28 | message DuelReq { 29 | //被比牌玩家ID 30 | int64 rival = 1; 31 | } 32 | message FriendReq { 33 | //被加好友ID 34 | int64 toId = 1; 35 | } 36 | message GiftReq { 37 | //被加好友ID 38 | int64 toId = 1; 39 | //礼物ID 40 | int32 giftId = 2; 41 | //礼物数量 42 | int32 amount = 3; 43 | } 44 | message ChatReq { 45 | //消息类型或消息编码 46 | int32 mid = 1; 47 | //自定义消息内容 48 | string msg = 2; 49 | } 50 | 51 | message DelegateReq { 52 | //托管操作[1:自动弃牌,2:自动跟注] 53 | int32 method = 1; 54 | bool action = 2; 55 | } 56 | //双向流返回包 57 | 58 | message GeRes { 59 | int32 code = 1; 60 | int32 callSeat = 2; 61 | } 62 | message RevealRes { 63 | int32 code = 1; 64 | int32 callSeat = 2; 65 | bytes cards = 3; 66 | //牌型[1:高牌,2:对子,3:同花,4:顺子,5:同花顺,6:豹子] 67 | int32 cardType = 4; 68 | } 69 | message Player { 70 | //玩家ID 71 | int64 uid = 1; 72 | //玩家座标 73 | int32 seatCode = 2; 74 | //是否未看牌 75 | bool blind = 3; 76 | //牌桌座位号 77 | int32 seat = 4; 78 | //金币数量 79 | int64 coin = 5; 80 | //昵称 81 | string nick = 6; 82 | //头像路径 83 | string avatar = 7; 84 | //玩家牌 85 | bytes cards = 8; 86 | //本局下注金币 87 | int64 betCoin = 9; 88 | //玩家特征 89 | int32 character = 10; 90 | //玩家当前牌局状态 91 | int32 status = 11; 92 | } 93 | message Table { 94 | //桌号 95 | int32 tableId = 1; 96 | //当前叫牌座号 97 | int32 callSeat = 2; 98 | //庄家座号 99 | int32 bankerSeat = 3; 100 | //总注池 101 | int64 jackpot = 4; 102 | //当局轮数 103 | int32 round = 5; 104 | //当轮下注值 105 | int64 roundBet = 6; 106 | //牌局状态,0:等待,1:读秒准备中,3:进行中 107 | int32 status = 7; 108 | //桌内玩家列表 109 | repeated Player players = 8; 110 | //返回码 111 | int32 code = 9; 112 | } 113 | message PlayerSeat { 114 | //玩家ID 115 | int64 uid = 1; 116 | //玩家座号 117 | int32 seat = 2; 118 | int32 callSeat = 3; 119 | } 120 | message Reveal { 121 | //座号 122 | int32 seat = 1; 123 | int32 callSeat = 2; 124 | bytes cards = 3; 125 | //牌型[1:高牌,2:对子,3:同花,4:顺子,5:同花顺,6:豹子] 126 | int32 cardType = 4; 127 | } 128 | message GeCall { 129 | //座号 130 | int32 seat = 1; 131 | int32 callSeat = 2; 132 | } 133 | message GiveUp { 134 | //座号 135 | int32 seat = 1; 136 | //下一个叫牌座号 137 | int32 callSeat = 2; 138 | } 139 | message Duel { 140 | //比牌发起者、拒绝者或接受者座号(根据协议定) 141 | int32 fromSeat = 1; 142 | //比牌发起者、拒绝者或接受者座号(根据协议定) 143 | int32 toSeat = 2; 144 | //下一个叫牌座号 145 | int32 callSeat = 3; 146 | } 147 | message Bet { 148 | //下注玩家座号 149 | int32 seat = 1; 150 | //下注金币数量 151 | int64 coins = 2; 152 | //下一个叫牌座号 153 | int32 callSeat = 3; 154 | } 155 | message DuelResult { 156 | //比牌发起者座号 157 | int32 fromSeat = 1; 158 | //被比牌者座号 159 | int32 toSeat = 2; 160 | //胜者座号 161 | int32 winSeat = 3; 162 | //比牌二者的牌组 163 | map duelCards = 4; 164 | //下一个叫牌座号 165 | int32 callSeat = 5; 166 | } 167 | message FriendAdd { 168 | //发起、拒绝或接受加好友玩家座号(根据协议定) 169 | int64 fromId = 1; 170 | //发起、拒绝或接受加好友玩家座号(根据协议定) 171 | int64 toId = 2; 172 | } 173 | message FriendAddResult { 174 | //发起加好友玩家座号 175 | int64 fromId = 1; 176 | //被加好友玩家座号 177 | int64 toId = 2; 178 | //加友好是否接受 179 | bool accept = 3; 180 | } 181 | message SendGift { 182 | //送礼物玩家座号 183 | int64 fromId = 1; 184 | //被送礼物玩家座号 185 | int64 toId = 2; 186 | //礼物编号 187 | int32 giftId = 3; 188 | //礼物数量 189 | int32 amount = 4; 190 | } 191 | message GameOver { 192 | //胜者座号 193 | int64 winUid = 1; 194 | //税后金币 195 | map result = 2; 196 | } 197 | message TableResult { 198 | int64 betCoins = 1; 199 | bytes cards = 2; 200 | int32 cardsType = 3; 201 | int32 greetType = 4; 202 | } 203 | message CardDeal { 204 | //下一个叫牌座号 205 | int32 callSeat = 1; 206 | int32 bankerSeat = 2; 207 | } 208 | message ChatMsg { 209 | //座号 210 | int32 sid = 1; 211 | //消息类型或消息编码 212 | int32 mid = 2; 213 | //自定义消息内容 214 | string msg = 3; 215 | } 216 | message NetInfo { 217 | //座号 218 | int32 sid = 1; 219 | //网络状态[在线/掉线] 220 | bool status = 2; 221 | } 222 | -------------------------------------------------------------------------------- /game/service/endpoints.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | "github.com/go-kit/kit/metrics" 11 | 12 | "github.com/yiv/yivgame/game/gamer" 13 | ) 14 | 15 | type Endpoints struct { 16 | Logger log.Logger 17 | SendChatEndpoint endpoint.Endpoint 18 | } 19 | 20 | type sendChatReq struct { 21 | Uid int64 `json:"uid"` 22 | Mid int32 `json:"mid"` 23 | Msg string `json:"msg"` 24 | } 25 | type sendChatRes struct { 26 | Err error `json:"err"` 27 | } 28 | 29 | func MakeSendChatEndpoint(s GameService) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 31 | req := request.(sendChatReq) 32 | err = s.SendChat(ctx, gamer.UserID(req.Uid), req.Mid, req.Msg) 33 | return sendChatRes{Err: err}, err 34 | } 35 | } 36 | 37 | func EndpointInstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware { 38 | return func(next endpoint.Endpoint) endpoint.Endpoint { 39 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 40 | defer func(begin time.Time) { 41 | duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds()) 42 | }(time.Now()) 43 | return next(ctx, request) 44 | 45 | } 46 | } 47 | } 48 | 49 | // EndpointLoggingMiddleware returns an endpoint middleware that logs the 50 | // duration of each invocation, and the resulting error, if any. 51 | func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware { 52 | return func(next endpoint.Endpoint) endpoint.Endpoint { 53 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 54 | defer func(begin time.Time) { 55 | logger.Log("error", err, "took", time.Since(begin)) 56 | }(time.Now()) 57 | return next(ctx, request) 58 | 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /game/service/error.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrorInvalidToken = errors.New("invalid token") 7 | ErrorNotOnTable = errors.New("not on table") 8 | ErrorBadRequest = errors.New("seat not empty") 9 | ErrorBadFrame = errors.New("bad frame") 10 | ErrorInvalidProtocol = errors.New("invalid protocol") 11 | ErrorClientDisconnected = errors.New("client end disconnected") 12 | ) 13 | 14 | type ErrCode int32 15 | 16 | var ( 17 | //客户端错误 18 | BadRequest ErrCode = 400 19 | Unauthorized ErrCode = 401 20 | Forbidden ErrCode = 403 21 | BadFrame ErrCode = 460 22 | InvalidProtocol ErrCode = 461 23 | //服务器错误 24 | InternalServerError ErrCode = 500 25 | 26 | UnknowError ErrCode = 521 27 | ) 28 | -------------------------------------------------------------------------------- /game/service/frame.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/yiv/yivgame/game/pb" 5 | ) 6 | 7 | func code2bytes(i uint32) (b []byte) { 8 | b = append(b, byte(i>>24), byte(i>>16), byte(i>>8), byte(i)) 9 | return 10 | } 11 | func bytes2code(b []byte) (i uint32) { 12 | i = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) 13 | return 14 | } 15 | 16 | func frameCode(f *pb.Frame) uint32 { 17 | payload := f.Payload 18 | return bytes2code(payload[0:4]) 19 | } 20 | func framePBbytes(f *pb.Frame) []byte { 21 | payload := f.Payload 22 | return payload[4:] 23 | } 24 | func toframe(i uint32, pbBytes []byte) *pb.Frame { 25 | payload := append(code2bytes(i), pbBytes...) 26 | return &pb.Frame{Payload: payload} 27 | } 28 | -------------------------------------------------------------------------------- /game/service/grpcairfone.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/gogo/protobuf/proto" 5 | 6 | "github.com/go-kit/kit/log" 7 | "github.com/go-kit/kit/log/level" 8 | 9 | "github.com/yiv/yivgame/game/gamer" 10 | "github.com/yiv/yivgame/game/pb" 11 | ) 12 | 13 | const ( 14 | CmdEnter uint32 = 10000 //登入 15 | CmdSendChat uint32 = 10017 //发聊天消息 16 | ) 17 | const ( 18 | InformPlayerSendChat uint32 = 20019 //通知玩家发聊天消息 19 | ) 20 | 21 | const ( 22 | ReportClientOffline uint32 = 40001 23 | ) 24 | 25 | type airfone struct { 26 | logger log.Logger 27 | stream pb.GameService_StreamServer 28 | } 29 | 30 | func NewAirfone(stream pb.GameService_StreamServer, logger log.Logger) (af airfone) { 31 | af = airfone{ 32 | logger: logger, 33 | stream: stream, 34 | } 35 | return 36 | } 37 | 38 | //请求返回 39 | func (a airfone) ReplySendChat() (err error) { 40 | r := &pb.GeRes{Code: 200} 41 | err = a.send(CmdSendChat, r) 42 | a.errCheck(err, "ReplySendChat") 43 | 44 | return 45 | } 46 | 47 | //通知 48 | func (a airfone) PlayerSendChat(sid gamer.Seat, mid int32, msg string) (err error) { 49 | r := &pb.ChatMsg{Sid: int32(sid), Mid: int32(mid), Msg: msg} 50 | err = a.send(InformPlayerSendChat, r) 51 | a.errCheck(err, "PlayerSendChat") 52 | return 53 | 54 | } 55 | func (a airfone) send(code uint32, pb proto.Message) (err error) { 56 | var pbBytes []byte 57 | if pb != nil { 58 | pbBytes, err = proto.Marshal(pb) 59 | if err != nil { 60 | level.Error(a.logger).Log("code", code, "err", err.Error(), "msg", "airfone Marshal err") 61 | return err 62 | } 63 | } 64 | f := toframe(code, pbBytes) 65 | err = a.stream.Send(f) 66 | if err != nil { 67 | level.Warn(a.logger).Log("code", code, "err", err.Error(), "msg", "airfone send err") 68 | } 69 | return 70 | } 71 | 72 | func (a airfone) errCheck(err error, method string) { 73 | if err != nil { 74 | level.Warn(a.logger).Log("err", err.Error(), "msg", method) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /game/service/instrumenting.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-kit/kit/metrics" 9 | "github.com/yiv/yivgame/game/gamer" 10 | ) 11 | 12 | type serviceInstrumentingMiddleware struct { 13 | requestCount metrics.Counter 14 | requestLatency metrics.Histogram 15 | next GameService 16 | } 17 | 18 | func ServiceInstrumentingMiddleware(requestCount metrics.Counter, requestLatency metrics.Histogram) Middleware { 19 | return func(next GameService) GameService { 20 | return serviceInstrumentingMiddleware{ 21 | requestCount: requestCount, 22 | requestLatency: requestLatency, 23 | next: next, 24 | } 25 | } 26 | } 27 | 28 | func (mw serviceInstrumentingMiddleware) SendChat(ctx context.Context, id gamer.UserID, mid int32, msg string) (err error) { 29 | defer func(begin time.Time) { 30 | lvs := []string{"method", "SendChat", "error", fmt.Sprint(err != nil)} 31 | mw.requestCount.With(lvs...).Add(1) 32 | mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 33 | }(time.Now()) 34 | return mw.next.SendChat(ctx, id, mid, msg) 35 | } 36 | -------------------------------------------------------------------------------- /game/service/logging.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/go-kit/kit/log" 8 | 9 | "github.com/yiv/yivgame/game/gamer" 10 | ) 11 | 12 | func ServiceLoggingMiddleware(logger log.Logger) Middleware { 13 | return func(next GameService) GameService { 14 | return serviceLoggingMiddleware{ 15 | logger: logger, 16 | next: next, 17 | } 18 | } 19 | } 20 | 21 | type serviceLoggingMiddleware struct { 22 | logger log.Logger 23 | next GameService 24 | } 25 | 26 | func (mw serviceLoggingMiddleware) SendChat(ctx context.Context, id gamer.UserID, mid int32, msg string) (err error) { 27 | defer func(begin time.Time) { 28 | mw.logger.Log( 29 | "method", "SendChat", 30 | "id", id, 31 | "mid", mid, 32 | "msg", msg, 33 | "err", err, 34 | ) 35 | }(time.Now()) 36 | return mw.next.SendChat(ctx, id, mid, msg) 37 | } 38 | -------------------------------------------------------------------------------- /game/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/go-kit/kit/log" 8 | "github.com/go-kit/kit/log/level" 9 | 10 | "github.com/yiv/yivgame/game/gamer" 11 | ) 12 | 13 | type Middleware func(GameService) GameService 14 | 15 | type GameService interface { 16 | SendChat(ctx context.Context, id gamer.UserID, mid int32, msg string) (err error) 17 | } 18 | 19 | type gameService struct { 20 | logger log.Logger 21 | userCenter gamer.UserCenter 22 | playerTableMap map[gamer.UserID]*gamer.Table //用于映身已登陆玩家的匹配房间,方便指令的路由,在玩家退出时清除映身, 23 | rooms map[gamer.RoomClass]*gamer.Room 24 | } 25 | 26 | func NewGameService(fir gamer.RoomOptions, uc *userCenter, logger log.Logger) GameService { 27 | g := gameService{ 28 | playerTableMap: make(map[gamer.UserID]*gamer.Table), 29 | rooms: make(map[gamer.RoomClass]*gamer.Room), 30 | userCenter: uc, 31 | logger: logger, 32 | } 33 | g.rooms[gamer.FirstClassRoom] = gamer.NewRoom(fir, logger, uc) 34 | return g 35 | } 36 | 37 | func (g gameService) SendChat(ctx context.Context, id gamer.UserID, mid int32, msg string) (err error) { 38 | if id <= 0 { 39 | return ErrorBadRequest 40 | } 41 | var t *gamer.Table 42 | if t = g.getPlayerTable(id); t == nil { 43 | return ErrorNotOnTable 44 | } 45 | defer g.recoveryTablePanic(t) 46 | return t.SendChat(id, mid, msg) 47 | } 48 | 49 | func (g gameService) recoveryTablePanic(t *gamer.Table) (err error) { 50 | if e := recover(); e != nil { 51 | err = fmt.Errorf("recover table panic err: %v", e) 52 | level.Error(g.logger).Log("table", fmt.Sprintf("%v", t), "PANIC", err) 53 | delete(g.rooms[t.RoomClass].Tables, t.TableId) 54 | } 55 | return 56 | } 57 | func (g gameService) getPlayerTable(id gamer.UserID) (t *gamer.Table) { 58 | if tb, ok := g.playerTableMap[id]; !ok { 59 | //映射表找不到玩家 60 | return nil 61 | } else { 62 | return tb 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /game/service/transportgrpc.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/gogo/protobuf/proto" 8 | stdopentracing "github.com/opentracing/opentracing-go" 9 | oldcontext "golang.org/x/net/context" 10 | 11 | "github.com/go-kit/kit/log" 12 | "github.com/go-kit/kit/log/level" 13 | "github.com/go-kit/kit/tracing/opentracing" 14 | kitgrpc "github.com/go-kit/kit/transport/grpc" 15 | 16 | "github.com/yiv/yivgame/game/pb" 17 | ) 18 | 19 | type grpcHandler struct { 20 | logger log.Logger 21 | sendChat kitgrpc.Handler 22 | } 23 | 24 | func MakeGRPCHandler(endpoints Endpoints, tracer stdopentracing.Tracer, logger log.Logger) pb.GameServiceServer { 25 | options := []kitgrpc.ServerOption{ 26 | kitgrpc.ServerErrorLogger(logger), 27 | } 28 | return &grpcHandler{ 29 | logger: logger, 30 | sendChat: kitgrpc.NewServer( 31 | endpoints.SendChatEndpoint, 32 | DecodeGRPCSendChatReq, 33 | EncodeGRPCSendChatRes, 34 | append(options, kitgrpc.ServerBefore(opentracing.GRPCToContext(tracer, "sendChat", logger)))..., 35 | ), 36 | } 37 | 38 | } 39 | 40 | //sendChat 41 | func DecodeGRPCSendChatReq(_ context.Context, request interface{}) (interface{}, error) { 42 | req := request.(sendChatReq) 43 | return req, nil 44 | } 45 | func EncodeGRPCSendChatRes(_ context.Context, response interface{}) (interface{}, error) { 46 | res := response.(sendChatRes) 47 | return nil, res.Err 48 | } 49 | 50 | //Stream 实现grpc双向流的stream方法 51 | func (g *grpcHandler) Stream(stream pb.GameService_StreamServer) (err error) { 52 | var userId int64 53 | dieCH := make(chan struct{}) 54 | defer func() { 55 | close(dieCH) 56 | }() 57 | recvChan := g.goRecv(stream, dieCH) 58 | for { 59 | select { 60 | case frame, ok := <-recvChan: // frames from agent 61 | if !ok { // EOF 62 | level.Info(g.logger).Log("FUNC", "Stream", "msg", "stream receive err, exit send loop") 63 | if err = g.cmdRoute(&userId, ReportClientOffline, nil, stream); err != nil { 64 | level.Info(g.logger).Log("err", err.Error(), "msg", "err when report client stream disconnected") 65 | return 66 | } 67 | return ErrorClientDisconnected 68 | } 69 | code := frameCode(frame) 70 | req := framePBbytes(frame) 71 | err = g.cmdRoute(&userId, code, req, stream) 72 | if err != nil { 73 | errCode := g.ErrToCode(err) 74 | f := g.ErrCodeToFrame(code, errCode) 75 | if err = stream.Send(f); err != nil { 76 | level.Info(g.logger).Log("code", code, "err", err.Error(), "msg", "stream err on send err response") 77 | return err 78 | } 79 | } 80 | } 81 | } 82 | return nil 83 | } 84 | 85 | //为 86 | func (g *grpcHandler) goRecv(stream pb.GameService_StreamServer, dieCH chan struct{}) chan *pb.Frame { 87 | recvChan := make(chan *pb.Frame, 1) 88 | go func() { 89 | defer func() { 90 | close(recvChan) 91 | }() 92 | for { 93 | frame, err := stream.Recv() 94 | if err == io.EOF { 95 | level.Error(g.logger).Log("FUNC", "goRecv", "err", err.Error(), "msg", "stream receive err, exit recv loop") 96 | return 97 | } 98 | if err != nil { 99 | level.Error(g.logger).Log("FUNC", "goRecv", "err", err.Error(), "msg", "stream receive err, , exit recv loop") 100 | return 101 | } 102 | //level.Debug(g.logger).Log("frame", frame.Payload, "msg", "stream received") 103 | select { 104 | case recvChan <- frame: 105 | case <-dieCH: 106 | } 107 | } 108 | }() 109 | return recvChan 110 | } 111 | 112 | //cmdRoute 对收到命令代码进行路由 113 | func (g *grpcHandler) cmdRoute(userId *int64, code uint32, req []byte, stream pb.GameService_StreamServer) error { 114 | level.Debug(g.logger).Log("CMD", "cmdRoute", "userId", *userId, "code", code) 115 | ctx := oldcontext.Background() 116 | 117 | switch code { 118 | case CmdEnter: 119 | request := &pb.EnterTableReq{} 120 | //记录当前登陆的玩家ID,作为对本stream已验证的标记 121 | *userId = request.Uid 122 | 123 | return nil 124 | //牌局命令 125 | if *userId <= 0 { 126 | //连接没有经过鉴权 127 | level.Debug(g.logger).Log("CMD", "cmdRoute", "userId", *userId, "code", code, "msg", "stream unauthorized") 128 | return ErrorInvalidToken 129 | } 130 | 131 | switch code { 132 | case CmdSendChat: 133 | request := &pb.ChatReq{} 134 | proto.Unmarshal(req, request) 135 | _, _, err := g.sendChat.ServeGRPC(ctx, sendChatReq{Uid: *userId, Mid: request.Mid, Msg: request.Msg}) 136 | if err != nil { 137 | level.Error(g.logger).Log("userId", *userId, "err", err.Error(), "msg", "sendGift serve err") 138 | return err 139 | } 140 | return nil 141 | default: 142 | return ErrorInvalidProtocol 143 | } 144 | return nil 145 | } 146 | return ErrorInvalidProtocol 147 | } 148 | func (g *grpcHandler) ErrToCode(err error) ErrCode { 149 | switch err { 150 | case ErrorBadFrame: 151 | return BadFrame 152 | case ErrorInvalidProtocol: 153 | return InvalidProtocol 154 | case ErrorInvalidToken: 155 | return Unauthorized 156 | } 157 | return UnknowError 158 | } 159 | 160 | func (g *grpcHandler) ErrCodeToFrame(code uint32, errCode ErrCode) *pb.Frame { 161 | var pbBytes []byte 162 | switch code { 163 | default: 164 | pbBytes, _ = proto.Marshal(&pb.GeRes{Code: int32(errCode)}) 165 | } 166 | return toframe(code, pbBytes) 167 | } 168 | func (g *grpcHandler) invalidProtocol(code uint32) *pb.Frame { 169 | level.Error(g.logger).Log("code", code, "msg", "protocol code invalid") 170 | pbBytes, _ := proto.Marshal(&pb.GeRes{Code: int32(InvalidProtocol)}) 171 | return toframe(code, pbBytes) 172 | } 173 | -------------------------------------------------------------------------------- /game/service/ucclient.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "time" 7 | 8 | stdopentracing "github.com/opentracing/opentracing-go" 9 | "google.golang.org/grpc" 10 | 11 | "github.com/go-kit/kit/endpoint" 12 | "github.com/go-kit/kit/log" 13 | "github.com/go-kit/kit/log/level" 14 | "github.com/go-kit/kit/sd" 15 | ketcd "github.com/go-kit/kit/sd/etcd" 16 | "github.com/go-kit/kit/sd/lb" 17 | 18 | "fmt" 19 | "github.com/yiv/yivgame/game/gamer" 20 | ucgrpccli "github.com/yiv/yivgame/usercenter/client" 21 | "github.com/yiv/yivgame/usercenter/service" 22 | ) 23 | 24 | type userCenter struct { 25 | logger log.Logger 26 | endpoints service.Endpoints 27 | } 28 | 29 | func (u *userCenter) GetInfo(id gamer.UserID) (info *gamer.PlayerInfo, err error) { 30 | var ctx = context.Background() 31 | user, err := u.endpoints.GetUserInfo(ctx, int64(id)) 32 | if err != nil { 33 | level.Error(u.logger).Log("userCenter", "GetInfo", "id", id, "err", err.Error()) 34 | return nil, err 35 | } 36 | var friends []gamer.UserID 37 | for _, f := range user.Friends { 38 | friends = append(friends, gamer.UserID(f)) 39 | } 40 | info = &gamer.PlayerInfo{ 41 | Token: user.Token, 42 | SeatCode: gamer.SeatCode(user.Online), 43 | Coin: user.Coin, 44 | Gem: user.Gem, 45 | Nick: user.Nick, 46 | Avatar: user.Avatar, 47 | Friends: friends, 48 | Character: user.Others["character"], 49 | } 50 | return 51 | } 52 | 53 | func NewUserCenter(serviceName string, etcdAddr []string, retryMax int, retryTimeout time.Duration, logger log.Logger) (uc *userCenter, err error) { 54 | var ctx = context.Background() 55 | tracer := stdopentracing.GlobalTracer() // no-op 56 | client, err := ketcd.NewClient(ctx, etcdAddr, ketcd.ClientOptions{}) 57 | if err != nil { 58 | level.Error(logger).Log("userCenter", "NewUserCenter", "err", err.Error()) 59 | return nil, fmt.Errorf("NewClient err : %s", err.Error()) 60 | } 61 | instancer, err := ketcd.NewInstancer(client, serviceName, logger) 62 | 63 | endpoints := service.Endpoints{} 64 | { 65 | factory := factoryFor(service.MakeGetUserInfoEndpoint, tracer, logger) 66 | endpointer := sd.NewEndpointer(instancer, factory, logger) 67 | balancer := lb.NewRoundRobin(endpointer) 68 | retry := lb.Retry(retryMax, retryTimeout, balancer) 69 | endpoints.GetUserInfoEndpoint = retry 70 | } 71 | uc = &userCenter{ 72 | logger: logger, 73 | endpoints: endpoints, 74 | } 75 | return 76 | } 77 | func factoryFor(makeEndpoint func(service.Service) endpoint.Endpoint, tracer stdopentracing.Tracer, logger log.Logger) sd.Factory { 78 | return func(instance string) (endpoint.Endpoint, io.Closer, error) { 79 | conn, err := grpc.Dial(instance, grpc.WithInsecure()) 80 | if err != nil { 81 | level.Error(logger).Log("userCenter", "factoryFor", "err", err.Error()) 82 | return nil, nil, err 83 | } 84 | svr := ucgrpccli.New(conn, tracer, logger) 85 | ep := makeEndpoint(svr) 86 | 87 | return ep, conn, nil 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /usercenter/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yiv/yivnet/9be9f3c53055c443bcab2bb80e9479f48d6326a0/usercenter/README.md -------------------------------------------------------------------------------- /usercenter/center/account.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | type Account struct { 4 | Id int64 5 | Uid int64 6 | Unionid string 7 | Uuid string 8 | Username string 9 | Password string 10 | Nick string 11 | Gender bool 12 | Addr string 13 | Avatar string 14 | Isguest bool 15 | Condays int32 16 | Signdate int64 17 | Vipsigndate int64 18 | Status bool 19 | Mtime int64 20 | Ctime int64 21 | Bankpwd string 22 | Forbid string 23 | Imsi string 24 | Imei string 25 | Mac string 26 | Did string 27 | Psystem string 28 | Pmodel string 29 | Others map[string]int32 30 | } 31 | -------------------------------------------------------------------------------- /usercenter/center/accountinfo.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | type AccountInfo struct { 4 | Accounts_uid int64 5 | Token string 6 | Coin int64 7 | Gem int32 8 | Bank int64 9 | Growth int32 10 | Level int32 11 | Viptype int32 12 | Vipexpiry int64 13 | Voucher int32 14 | Online int32 15 | Props map[string]int32 16 | Gifts map[string]int32 17 | Medals map[string]int32 18 | Friends []int64 19 | Tags []string 20 | Records map[string]int32 21 | } 22 | -------------------------------------------------------------------------------- /usercenter/center/error.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | ErrUserExist = errors.New("user exist") 9 | ErrNotFound = errors.New("not found") 10 | ErrAmountMustBigThanZero = errors.New("amount must big than zero") 11 | ErrCoinNotEnough = errors.New("coin not enough") 12 | ErrGemNotEnough = errors.New("gem not enough") 13 | ErrGiftNotEnough = errors.New("gift not enough") 14 | ErrBankNotEnough = errors.New("bank balance not enough") 15 | ErrPwdEmpty = errors.New("password can not be empty") 16 | ErrPwdNotSet = errors.New("password not set yet") 17 | ErrPwdWrong = errors.New("wrong password") 18 | ErrNickEmpty = errors.New("nick can not be empty") 19 | ErrSetValueExist = errors.New("can not use the same value") 20 | ) 21 | -------------------------------------------------------------------------------- /usercenter/center/user.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type User struct { 8 | Account 9 | AccountInfo 10 | sync.RWMutex 11 | MQ MQRepository 12 | } 13 | 14 | type DBRepository interface { 15 | FindUserById(id int64) (*User, error) 16 | } 17 | type MQRepository interface { 18 | SaveUser(user *User) (err error) 19 | } 20 | -------------------------------------------------------------------------------- /usercenter/center/usermanager.go: -------------------------------------------------------------------------------- 1 | package center 2 | 3 | type UserMap map[int64]*User 4 | 5 | type UserManager struct { 6 | users UserMap 7 | dbRepository DBRepository 8 | mqRepository MQRepository 9 | } 10 | 11 | func NewUserManager(dbRepository DBRepository, mqRepository MQRepository) *UserManager { 12 | um := &UserManager{} 13 | umap := make(UserMap) 14 | um.users = umap 15 | um.dbRepository = dbRepository 16 | um.mqRepository = mqRepository 17 | return um 18 | } 19 | 20 | func (um *UserManager) AddUser(uid int64, user *User, mq MQRepository) error { 21 | if _, ok := um.users[uid]; ok { 22 | return ErrUserExist 23 | } 24 | user.MQ = mq 25 | um.users[uid] = user 26 | return nil 27 | } 28 | 29 | func (um *UserManager) RemoveUser(uid int64) error { 30 | return nil 31 | } 32 | 33 | func (um *UserManager) GetUser(uid int64) (user *User, err error) { 34 | user, ok := um.users[uid] 35 | if ok { 36 | return user, nil 37 | } 38 | user, err = um.dbRepository.FindUserById(uid) 39 | if err != nil { 40 | return nil, err 41 | } 42 | if user == nil { 43 | return nil, ErrNotFound 44 | } 45 | err = um.AddUser(uid, user, um.mqRepository) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return user, nil 50 | } 51 | func (um *UserManager) IsUserExist(uid int64) bool { 52 | if _, ok := um.users[uid]; ok { 53 | return true 54 | } 55 | return false 56 | } 57 | -------------------------------------------------------------------------------- /usercenter/client/grpc.go: -------------------------------------------------------------------------------- 1 | // Package grpc provides a gRPC client for the add service. 2 | package grpc 3 | 4 | import ( 5 | _ "time" 6 | 7 | jujuratelimit "github.com/juju/ratelimit" 8 | stdopentracing "github.com/opentracing/opentracing-go" 9 | //"github.com/sony/gobreaker" 10 | "google.golang.org/grpc" 11 | 12 | //"github.com/go-kit/kit/circuitbreaker" 13 | "github.com/go-kit/kit/endpoint" 14 | "github.com/go-kit/kit/log" 15 | "github.com/go-kit/kit/ratelimit" 16 | "github.com/go-kit/kit/tracing/opentracing" 17 | grpctransport "github.com/go-kit/kit/transport/grpc" 18 | 19 | "github.com/yiv/yivgame/usercenter/pb" 20 | "github.com/yiv/yivgame/usercenter/service" 21 | ) 22 | 23 | func New(conn *grpc.ClientConn, tracer stdopentracing.Tracer, logger log.Logger) service.Service { 24 | 25 | limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) 26 | 27 | var getUserInfoEndpoint endpoint.Endpoint 28 | { 29 | getUserInfoEndpoint = grpctransport.NewClient( 30 | conn, 31 | "pb.User", 32 | "GetUserInfo", 33 | service.EncodeGRPCGetUserInfoReq, 34 | service.DecodeGRPCGetUserInfoRes, 35 | pb.UserInfo{}, 36 | grpctransport.ClientBefore(opentracing.ContextToGRPC(tracer, logger)), 37 | ).Endpoint() 38 | getUserInfoEndpoint = opentracing.TraceClient(tracer, "GetUserInfo")(getUserInfoEndpoint) 39 | getUserInfoEndpoint = limiter(getUserInfoEndpoint) 40 | } 41 | 42 | return service.Endpoints{ 43 | GetUserInfoEndpoint: getUserInfoEndpoint, 44 | } 45 | 46 | } 47 | 48 | //func makeEndpoint(serviceName, method string, conn *grpc.ClientConn, grpcReply interface{}, tracer stdopentracing.Tracer, logger log.Logger, limiter endpoint.Middleware, enc grpctransport.EncodeRequestFunc, dec grpctransport.DecodeResponseFunc) endpoint.Endpoint { 49 | // var endpoint endpoint.Endpoint 50 | // endpoint = grpctransport.NewClient( 51 | // conn, 52 | // serviceName, 53 | // method, 54 | // enc, 55 | // dec, 56 | // grpcReply, 57 | // grpctransport.ClientBefore(opentracing.ToGRPCRequest(tracer, logger)), 58 | // ).Endpoint() 59 | // endpoint = opentracing.TraceClient(tracer, method)(endpoint) 60 | // endpoint = limiter(endpoint) 61 | // //endpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ 62 | // //Name: method, 63 | // //Timeout: 1 * time.Second, 64 | // //}))(endpoint) 65 | // return endpoint 66 | //} 67 | -------------------------------------------------------------------------------- /usercenter/cockroach/repository.go: -------------------------------------------------------------------------------- 1 | package cockroach 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/jmoiron/sqlx" 8 | _ "github.com/lib/pq" 9 | 10 | "github.com/go-kit/kit/log" 11 | "github.com/go-kit/kit/log/level" 12 | 13 | "github.com/yiv/yivgame/usercenter/center" 14 | ) 15 | 16 | const ( 17 | DefalutCoins int64 = 100000 18 | DefaultGems int32 = 500 19 | ) 20 | 21 | type DbRepo struct { 22 | Conn *sqlx.DB 23 | logger log.Logger 24 | } 25 | 26 | func NewDbUserRepo(dataSourceName string, logger log.Logger) (center.DBRepository, error) { 27 | conn, err := sqlx.Open("postgres", dataSourceName) 28 | if err != nil { 29 | return nil, err 30 | } 31 | dbRepo := new(DbRepo) 32 | dbRepo.Conn = conn 33 | dbRepo.logger = logger 34 | return dbRepo, nil 35 | } 36 | 37 | func (repo *DbRepo) FindUserById(uid int64) (user *center.User, err error) { 38 | user = new(center.User) 39 | err = repo.Conn.Get(user, "select accounts.id,accounts.uid, accounts.unionid, accounts.uuid , "+ 40 | "accounts.username, accounts.password, accounts.nick, accounts.gender, accounts.addr , accounts.avatar, "+ 41 | "accounts.isguest, accounts.condays, accounts.signdate, accounts.vipsigndate ,accounts.status, accounts.mtime, "+ 42 | "accounts.ctime, accounts.bankpwd , accounts.forbid, accounts.imsi, accounts.imei, accounts.mac, "+ 43 | "accounts.did , accounts.psystem, accounts.pmodel, account_info.coin, account_info.gem, account_info.bank, "+ 44 | "account_info.growth, account_info.level,account_info.viptype, account_info.vipexpiry,account_info.voucher, account_info.token,"+ 45 | "account_info.online from accounts inner join account_info on accounts.uid = account_info.accounts_uid "+ 46 | "where accounts.uid = $1", uid) 47 | if err != nil { 48 | level.Error(repo.logger).Log("err", err.Error(), "uid", uid) 49 | return nil, err 50 | } 51 | type StringField struct { 52 | Others string 53 | Props string 54 | Gifts string 55 | Friends string 56 | Records string 57 | Tags string 58 | Medals string 59 | } 60 | s := new(StringField) 61 | err = repo.Conn.Get(s, "select accounts.others,account_info.props,account_info.gifts, account_info.friends, account_info.records, "+ 62 | "account_info.tags,account_info.medals from accounts inner join account_info on accounts.uid = account_info.accounts_uid where accounts.uid= $1", uid) 63 | if err != nil { 64 | level.Error(repo.logger).Log("err", err.Error(), "uid", uid) 65 | return nil, err 66 | } 67 | err = json.Unmarshal([]byte(s.Others), &user.Others) 68 | if err != nil { 69 | level.Error(repo.logger).Log("err", err.Error(), "Others", s.Others, "msg", "Unmarshal Others") 70 | return nil, fmt.Errorf("Props json decode err : %s", err.Error()) 71 | } 72 | err = json.Unmarshal([]byte(s.Props), &user.Props) 73 | if err != nil { 74 | level.Error(repo.logger).Log("err", err.Error(), "Props", s.Props, "msg", "Unmarshal Props") 75 | return nil, fmt.Errorf("Props json decode err : %s", err.Error()) 76 | } 77 | err = json.Unmarshal([]byte(s.Gifts), &user.Gifts) 78 | if err != nil { 79 | level.Error(repo.logger).Log("err", err.Error(), "Gifts", s.Gifts, "msg", "Unmarshal Gifts") 80 | return nil, fmt.Errorf("Gifts json decode err : %s", err.Error()) 81 | } 82 | err = json.Unmarshal([]byte(s.Friends), &user.Friends) 83 | if err != nil { 84 | level.Error(repo.logger).Log("err", err.Error(), "Friends", s.Friends, "msg", "Unmarshal Friends") 85 | return nil, fmt.Errorf("Friends json decode err : %s", err.Error()) 86 | } 87 | err = json.Unmarshal([]byte(s.Records), &user.Records) 88 | if err != nil { 89 | level.Error(repo.logger).Log("err", err.Error(), "Records", s.Records, "msg", "Unmarshal Records") 90 | return nil, fmt.Errorf("Records json decode err : %s", err.Error()) 91 | } 92 | err = json.Unmarshal([]byte(s.Tags), &user.Tags) 93 | if err != nil { 94 | level.Error(repo.logger).Log("err", err.Error(), "Tags", s.Tags, "msg", "Unmarshal Tags") 95 | return nil, fmt.Errorf("Tags json decode err : %s", err.Error()) 96 | } 97 | err = json.Unmarshal([]byte(s.Medals), &user.Medals) 98 | if err != nil { 99 | level.Error(repo.logger).Log("err", err.Error(), "Medals", s.Medals, "msg", "Unmarshal Medals") 100 | return nil, fmt.Errorf("Medals json decode err : %s", err.Error()) 101 | } 102 | return user, nil 103 | } 104 | -------------------------------------------------------------------------------- /usercenter/kafka/kafka.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "github.com/Shopify/sarama" 5 | "github.com/gogo/protobuf/proto" 6 | 7 | "github.com/go-kit/kit/log" 8 | "github.com/go-kit/kit/log/level" 9 | 10 | "github.com/yiv/yivgame/usercenter/pb" 11 | "github.com/yiv/yivgame/usercenter/center" 12 | ) 13 | 14 | const ( 15 | UserCenterPersistenceTopic string = "UserCenterPersistence" 16 | ) 17 | 18 | type MQRepo struct { 19 | asyncProducer sarama.AsyncProducer 20 | logger log.Logger 21 | } 22 | 23 | func NewMQRepo(addrs []string, logger log.Logger) (center.MQRepository, error) { 24 | 25 | mqRepo := new(MQRepo) 26 | mqRepo.logger = logger 27 | producer, err := sarama.NewAsyncProducer(addrs, nil) 28 | if err != nil { 29 | level.Error(logger).Log("err", err.Error(), "msg", "error occur on create new kafka producer") 30 | return nil, err 31 | } 32 | mqRepo.asyncProducer = producer 33 | return mqRepo, nil 34 | } 35 | func (r *MQRepo) SaveUser(user *center.User) (err error) { 36 | u := &pb.UserInfo{ 37 | Uid: user.Uid, 38 | Unionid: user.Unionid, 39 | Uuid: user.Uuid, 40 | Username: user.Username, 41 | Password: user.Password, 42 | Nick: user.Nick, 43 | Gender: user.Gender, 44 | Addr: user.Addr, 45 | Avatar: user.Avatar, 46 | Isguest: user.Isguest, 47 | Condays: user.Condays, 48 | Signdate: user.Signdate, 49 | Vipsigndate: user.Vipsigndate, 50 | Status: user.Status, 51 | Mtime: user.Mtime, 52 | Ctime: user.Ctime, 53 | Token: user.Token, 54 | Bankpwd: user.Bankpwd, 55 | Forbid: user.Forbid, 56 | Imsi: user.Imsi, 57 | Imei: user.Imei, 58 | Mac: user.Mac, 59 | Did: user.Did, 60 | Psystem: user.Psystem, 61 | Pmodel: user.Pmodel, 62 | Others: user.Others, 63 | Coin: user.Coin, 64 | Gem: user.Gem, 65 | Bank: user.Bank, 66 | Growth: user.Growth, 67 | Level: user.Level, 68 | Viptype: user.Viptype, 69 | Vipexpiry: user.Vipexpiry, 70 | Voucher: user.Voucher, 71 | Online: user.Online, 72 | Props: user.Props, 73 | Gifts: user.Gifts, 74 | Medals: user.Medals, 75 | Friends: user.Friends, 76 | Tags: user.Tags, 77 | Records: user.Records, 78 | } 79 | pbBytes, err := proto.Marshal(u) 80 | if err != nil { 81 | level.Error(r.logger).Log("err", err.Error(), "msg", "error occur on Marshal message for kafka producer") 82 | return err 83 | } 84 | _ = &sarama.ProducerMessage{ 85 | Topic: UserCenterPersistenceTopic, 86 | Partition: 0, 87 | Key: nil, 88 | Value: sarama.ByteEncoder(pbBytes), 89 | } 90 | //r.asyncProducer.Input() <- r.newProducerMessage(int32(u.Uid), pbBytes) 91 | return 92 | } 93 | -------------------------------------------------------------------------------- /usercenter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "net/http/pprof" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | 14 | stdopentracing "github.com/opentracing/opentracing-go" 15 | zipkin "github.com/openzipkin/zipkin-go-opentracing" 16 | stdprometheus "github.com/prometheus/client_golang/prometheus" 17 | "google.golang.org/grpc" 18 | 19 | "github.com/go-kit/kit/endpoint" 20 | "github.com/go-kit/kit/log" 21 | "github.com/go-kit/kit/metrics" 22 | ketcd "github.com/go-kit/kit/sd/etcd" 23 | 24 | "github.com/go-kit/kit/metrics/prometheus" 25 | "github.com/go-kit/kit/tracing/opentracing" 26 | "github.com/prometheus/client_golang/prometheus/promhttp" 27 | "github.com/yiv/yivgame/usercenter/cockroach" 28 | "github.com/yiv/yivgame/usercenter/kafka" 29 | "github.com/yiv/yivgame/usercenter/pb" 30 | "github.com/yiv/yivgame/usercenter/service" 31 | ) 32 | 33 | var ( 34 | grpcAddr = flag.String("grpc.addr", ":10000", "gRPC listen address") 35 | debugAddr = flag.String("debug.addr", ":10001", "Debug and metrics listen address") 36 | etcdAddr = flag.String("etcd.addr", "http://etcd.yivgame.com:2379", "Consul agent address") 37 | kafkaAddr = flag.String("kafka.addr", "kafka.yivgame.com:9092", "kafka address") 38 | zipkinAddr = flag.String("zipkin.addr", "", "Enable Zipkin tracing via a Zipkin HTTP Collector endpoint") 39 | serviceName = flag.String("service.name", "usercenter", "the name of this service in service discovery") 40 | ) 41 | 42 | func main() { 43 | flag.Parse() 44 | 45 | // Logging domain. 46 | var logger log.Logger 47 | { 48 | logger = log.NewLogfmtLogger(os.Stdout) 49 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 50 | logger = log.With(logger, "caller", log.DefaultCaller) 51 | } 52 | logger.Log("msg", "hello") 53 | defer logger.Log("msg", "goodbye") 54 | 55 | // Mechanical domain. 56 | errc := make(chan error) 57 | 58 | // Interrupt handler. 59 | go func() { 60 | c := make(chan os.Signal) 61 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 62 | errc <- fmt.Errorf("%s", <-c) 63 | }() 64 | 65 | // Service discovery domain 66 | ctx := context.Background() 67 | etcdClient, err := ketcd.NewClient(ctx, []string{*etcdAddr}, ketcd.ClientOptions{}) 68 | if err != nil { 69 | panic(err) 70 | } 71 | registrar := ketcd.NewRegistrar(etcdClient, ketcd.Service{Key: *serviceName, Value: *grpcAddr}, logger) 72 | registrar.Register() 73 | defer registrar.Deregister() 74 | 75 | // Tracing domain. 76 | var tracer stdopentracing.Tracer 77 | { 78 | 79 | logger := log.With(logger, "tracer", "ZipkinHTTP") 80 | logger.Log("addr", *zipkinAddr) 81 | 82 | // endpoint typically looks like: http://zipkinhost:9411/api/v1/spans 83 | collector, err := zipkin.NewHTTPCollector(*zipkinAddr) 84 | if err != nil { 85 | logger.Log("err", err) 86 | os.Exit(1) 87 | } 88 | defer collector.Close() 89 | 90 | tracer, err = zipkin.NewTracer( 91 | zipkin.NewRecorder(collector, false, "localhost:10003", "addsvc"), 92 | ) 93 | if err != nil { 94 | logger.Log("err", err) 95 | os.Exit(1) 96 | } 97 | 98 | } 99 | 100 | // Metrics domain. 101 | var ( 102 | requestCount metrics.Counter 103 | requestLatency metrics.Histogram 104 | fieldKeys []string 105 | ) 106 | { 107 | // Business level metrics. 108 | fieldKeys = []string{"method", "error"} 109 | requestCount = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ 110 | Namespace: "usercenter", 111 | Name: "request_count", 112 | Help: "Number of requests received.", 113 | }, fieldKeys) 114 | requestLatency = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 115 | Namespace: "usercenter", 116 | Name: "request_latency_microseconds", 117 | Help: "Total duration of requests in microseconds.", 118 | }, fieldKeys) 119 | } 120 | var duration metrics.Histogram 121 | { 122 | // Transport level metrics. 123 | duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 124 | Namespace: "usercenter", 125 | Name: "request_duration_ns", 126 | Help: "Request duration in nanoseconds.", 127 | }, []string{"method", "success"}) 128 | } 129 | 130 | // Setup repositories 131 | dblogger := log.With(logger, "db", "cockroach") 132 | dbRepo, err := cockroach.NewDbUserRepo("postgresql://edwin@db.yivgame.com:26257/tp_user?sslmode=disable", dblogger) 133 | if err != nil { 134 | logger.Log("cockroach connect err: ", err) 135 | os.Exit(1) 136 | } 137 | mqlogger := log.With(logger, "mq", "kafka") 138 | mqRepo, err := kafka.NewMQRepo([]string{*kafkaAddr}, mqlogger) 139 | if err != nil { 140 | logger.Log("kafka connect err: ", err) 141 | os.Exit(1) 142 | } 143 | 144 | // Business domain. 145 | var basicService service.Service 146 | { 147 | basicService = service.NewBasicService(dbRepo, mqRepo, logger) 148 | basicService = service.ServiceLoggingMiddleware(logger)(basicService) 149 | basicService = service.ServiceInstrumentingMiddleware(requestCount, requestLatency)(basicService) 150 | } 151 | 152 | var getUserInfoEndpoint endpoint.Endpoint 153 | { 154 | method := "getUserInfo" 155 | duration := duration.With("method", method) 156 | logger := log.With(logger, "method", method) 157 | getUserInfoEndpoint = service.MakeGetUserInfoEndpoint(basicService) 158 | getUserInfoEndpoint = opentracing.TraceServer(tracer, method)(getUserInfoEndpoint) 159 | getUserInfoEndpoint = service.EndpointInstrumentingMiddleware(duration)(getUserInfoEndpoint) 160 | getUserInfoEndpoint = service.EndpointLoggingMiddleware(logger)(getUserInfoEndpoint) 161 | } 162 | endpoints := service.Endpoints{ 163 | GetUserInfoEndpoint: getUserInfoEndpoint, 164 | } 165 | 166 | // gRPC transport. 167 | go func() { 168 | logger := log.With(logger, "transport", "gRPC") 169 | 170 | ln, err := net.Listen("tcp", *grpcAddr) 171 | if err != nil { 172 | errc <- err 173 | return 174 | } 175 | grpcHandler := service.MakeGRPCHandler(endpoints, tracer, logger) 176 | s := grpc.NewServer() 177 | pb.RegisterUserServer(s, grpcHandler) 178 | 179 | logger.Log("addr", *grpcAddr) 180 | errc <- s.Serve(ln) 181 | }() 182 | 183 | // Debug listener. 184 | go func() { 185 | logger := log.With(logger, "transport", "debug") 186 | 187 | m := http.NewServeMux() 188 | m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) 189 | m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) 190 | m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) 191 | m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) 192 | m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) 193 | m.Handle("/metrics", promhttp.Handler()) 194 | 195 | logger.Log("addr", *debugAddr) 196 | errc <- http.ListenAndServe(*debugAddr, m) 197 | }() 198 | logger.Log("terminated", <-errc) 199 | } 200 | -------------------------------------------------------------------------------- /usercenter/pb/compile.sh: -------------------------------------------------------------------------------- 1 | protoc user.proto --go_out=plugins=grpc:. -------------------------------------------------------------------------------- /usercenter/pb/user.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: user.proto 3 | 4 | /* 5 | Package pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | user.proto 9 | 10 | It has these top-level messages: 11 | UserInfo 12 | UserId 13 | */ 14 | package pb 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | 20 | import ( 21 | context "golang.org/x/net/context" 22 | grpc "google.golang.org/grpc" 23 | ) 24 | 25 | // Reference imports to suppress errors if they are not otherwise used. 26 | var _ = proto.Marshal 27 | var _ = fmt.Errorf 28 | var _ = math.Inf 29 | 30 | // This is a compile-time assertion to ensure that this generated file 31 | // is compatible with the proto package it is being compiled against. 32 | // A compilation error at this line likely means your copy of the 33 | // proto package needs to be updated. 34 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 35 | 36 | type UserInfo struct { 37 | Uid int64 `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty"` 38 | Unionid string `protobuf:"bytes,2,opt,name=unionid" json:"unionid,omitempty"` 39 | Uuid string `protobuf:"bytes,3,opt,name=uuid" json:"uuid,omitempty"` 40 | Username string `protobuf:"bytes,4,opt,name=username" json:"username,omitempty"` 41 | Password string `protobuf:"bytes,5,opt,name=password" json:"password,omitempty"` 42 | Nick string `protobuf:"bytes,6,opt,name=nick" json:"nick,omitempty"` 43 | Gender bool `protobuf:"varint,7,opt,name=gender" json:"gender,omitempty"` 44 | Addr string `protobuf:"bytes,8,opt,name=addr" json:"addr,omitempty"` 45 | Avatar string `protobuf:"bytes,9,opt,name=avatar" json:"avatar,omitempty"` 46 | Isguest bool `protobuf:"varint,10,opt,name=isguest" json:"isguest,omitempty"` 47 | Condays int32 `protobuf:"varint,11,opt,name=condays" json:"condays,omitempty"` 48 | Signdate int64 `protobuf:"varint,12,opt,name=signdate" json:"signdate,omitempty"` 49 | Vipsigndate int64 `protobuf:"varint,13,opt,name=vipsigndate" json:"vipsigndate,omitempty"` 50 | Status bool `protobuf:"varint,14,opt,name=status" json:"status,omitempty"` 51 | Mtime int64 `protobuf:"varint,15,opt,name=mtime" json:"mtime,omitempty"` 52 | Ctime int64 `protobuf:"varint,16,opt,name=ctime" json:"ctime,omitempty"` 53 | Token string `protobuf:"bytes,17,opt,name=token" json:"token,omitempty"` 54 | Bankpwd string `protobuf:"bytes,18,opt,name=bankpwd" json:"bankpwd,omitempty"` 55 | Forbid string `protobuf:"bytes,19,opt,name=forbid" json:"forbid,omitempty"` 56 | Imsi string `protobuf:"bytes,20,opt,name=imsi" json:"imsi,omitempty"` 57 | Imei string `protobuf:"bytes,21,opt,name=imei" json:"imei,omitempty"` 58 | Mac string `protobuf:"bytes,22,opt,name=mac" json:"mac,omitempty"` 59 | Did string `protobuf:"bytes,23,opt,name=did" json:"did,omitempty"` 60 | Psystem string `protobuf:"bytes,24,opt,name=psystem" json:"psystem,omitempty"` 61 | Pmodel string `protobuf:"bytes,25,opt,name=pmodel" json:"pmodel,omitempty"` 62 | Others map[string]int32 `protobuf:"bytes,26,rep,name=others" json:"others,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` 63 | Coin int64 `protobuf:"varint,27,opt,name=coin" json:"coin,omitempty"` 64 | Gem int32 `protobuf:"varint,28,opt,name=gem" json:"gem,omitempty"` 65 | Bank int64 `protobuf:"varint,29,opt,name=bank" json:"bank,omitempty"` 66 | Growth int32 `protobuf:"varint,30,opt,name=growth" json:"growth,omitempty"` 67 | Level int32 `protobuf:"varint,31,opt,name=level" json:"level,omitempty"` 68 | Viptype int32 `protobuf:"varint,32,opt,name=viptype" json:"viptype,omitempty"` 69 | Vipexpiry int64 `protobuf:"varint,33,opt,name=vipexpiry" json:"vipexpiry,omitempty"` 70 | Voucher int32 `protobuf:"varint,34,opt,name=voucher" json:"voucher,omitempty"` 71 | Online int32 `protobuf:"varint,35,opt,name=online" json:"online,omitempty"` 72 | Props map[string]int32 `protobuf:"bytes,36,rep,name=props" json:"props,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` 73 | Gifts map[string]int32 `protobuf:"bytes,37,rep,name=gifts" json:"gifts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` 74 | Medals map[string]int32 `protobuf:"bytes,38,rep,name=medals" json:"medals,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` 75 | Friends []int64 `protobuf:"varint,39,rep,packed,name=friends" json:"friends,omitempty"` 76 | Tags []string `protobuf:"bytes,40,rep,name=tags" json:"tags,omitempty"` 77 | Records map[string]int32 `protobuf:"bytes,41,rep,name=records" json:"records,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` 78 | Err string `protobuf:"bytes,99,opt,name=err" json:"err,omitempty"` 79 | } 80 | 81 | func (m *UserInfo) Reset() { *m = UserInfo{} } 82 | func (m *UserInfo) String() string { return proto.CompactTextString(m) } 83 | func (*UserInfo) ProtoMessage() {} 84 | func (*UserInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 85 | 86 | func (m *UserInfo) GetUid() int64 { 87 | if m != nil { 88 | return m.Uid 89 | } 90 | return 0 91 | } 92 | 93 | func (m *UserInfo) GetUnionid() string { 94 | if m != nil { 95 | return m.Unionid 96 | } 97 | return "" 98 | } 99 | 100 | func (m *UserInfo) GetUuid() string { 101 | if m != nil { 102 | return m.Uuid 103 | } 104 | return "" 105 | } 106 | 107 | func (m *UserInfo) GetUsername() string { 108 | if m != nil { 109 | return m.Username 110 | } 111 | return "" 112 | } 113 | 114 | func (m *UserInfo) GetPassword() string { 115 | if m != nil { 116 | return m.Password 117 | } 118 | return "" 119 | } 120 | 121 | func (m *UserInfo) GetNick() string { 122 | if m != nil { 123 | return m.Nick 124 | } 125 | return "" 126 | } 127 | 128 | func (m *UserInfo) GetGender() bool { 129 | if m != nil { 130 | return m.Gender 131 | } 132 | return false 133 | } 134 | 135 | func (m *UserInfo) GetAddr() string { 136 | if m != nil { 137 | return m.Addr 138 | } 139 | return "" 140 | } 141 | 142 | func (m *UserInfo) GetAvatar() string { 143 | if m != nil { 144 | return m.Avatar 145 | } 146 | return "" 147 | } 148 | 149 | func (m *UserInfo) GetIsguest() bool { 150 | if m != nil { 151 | return m.Isguest 152 | } 153 | return false 154 | } 155 | 156 | func (m *UserInfo) GetCondays() int32 { 157 | if m != nil { 158 | return m.Condays 159 | } 160 | return 0 161 | } 162 | 163 | func (m *UserInfo) GetSigndate() int64 { 164 | if m != nil { 165 | return m.Signdate 166 | } 167 | return 0 168 | } 169 | 170 | func (m *UserInfo) GetVipsigndate() int64 { 171 | if m != nil { 172 | return m.Vipsigndate 173 | } 174 | return 0 175 | } 176 | 177 | func (m *UserInfo) GetStatus() bool { 178 | if m != nil { 179 | return m.Status 180 | } 181 | return false 182 | } 183 | 184 | func (m *UserInfo) GetMtime() int64 { 185 | if m != nil { 186 | return m.Mtime 187 | } 188 | return 0 189 | } 190 | 191 | func (m *UserInfo) GetCtime() int64 { 192 | if m != nil { 193 | return m.Ctime 194 | } 195 | return 0 196 | } 197 | 198 | func (m *UserInfo) GetToken() string { 199 | if m != nil { 200 | return m.Token 201 | } 202 | return "" 203 | } 204 | 205 | func (m *UserInfo) GetBankpwd() string { 206 | if m != nil { 207 | return m.Bankpwd 208 | } 209 | return "" 210 | } 211 | 212 | func (m *UserInfo) GetForbid() string { 213 | if m != nil { 214 | return m.Forbid 215 | } 216 | return "" 217 | } 218 | 219 | func (m *UserInfo) GetImsi() string { 220 | if m != nil { 221 | return m.Imsi 222 | } 223 | return "" 224 | } 225 | 226 | func (m *UserInfo) GetImei() string { 227 | if m != nil { 228 | return m.Imei 229 | } 230 | return "" 231 | } 232 | 233 | func (m *UserInfo) GetMac() string { 234 | if m != nil { 235 | return m.Mac 236 | } 237 | return "" 238 | } 239 | 240 | func (m *UserInfo) GetDid() string { 241 | if m != nil { 242 | return m.Did 243 | } 244 | return "" 245 | } 246 | 247 | func (m *UserInfo) GetPsystem() string { 248 | if m != nil { 249 | return m.Psystem 250 | } 251 | return "" 252 | } 253 | 254 | func (m *UserInfo) GetPmodel() string { 255 | if m != nil { 256 | return m.Pmodel 257 | } 258 | return "" 259 | } 260 | 261 | func (m *UserInfo) GetOthers() map[string]int32 { 262 | if m != nil { 263 | return m.Others 264 | } 265 | return nil 266 | } 267 | 268 | func (m *UserInfo) GetCoin() int64 { 269 | if m != nil { 270 | return m.Coin 271 | } 272 | return 0 273 | } 274 | 275 | func (m *UserInfo) GetGem() int32 { 276 | if m != nil { 277 | return m.Gem 278 | } 279 | return 0 280 | } 281 | 282 | func (m *UserInfo) GetBank() int64 { 283 | if m != nil { 284 | return m.Bank 285 | } 286 | return 0 287 | } 288 | 289 | func (m *UserInfo) GetGrowth() int32 { 290 | if m != nil { 291 | return m.Growth 292 | } 293 | return 0 294 | } 295 | 296 | func (m *UserInfo) GetLevel() int32 { 297 | if m != nil { 298 | return m.Level 299 | } 300 | return 0 301 | } 302 | 303 | func (m *UserInfo) GetViptype() int32 { 304 | if m != nil { 305 | return m.Viptype 306 | } 307 | return 0 308 | } 309 | 310 | func (m *UserInfo) GetVipexpiry() int64 { 311 | if m != nil { 312 | return m.Vipexpiry 313 | } 314 | return 0 315 | } 316 | 317 | func (m *UserInfo) GetVoucher() int32 { 318 | if m != nil { 319 | return m.Voucher 320 | } 321 | return 0 322 | } 323 | 324 | func (m *UserInfo) GetOnline() int32 { 325 | if m != nil { 326 | return m.Online 327 | } 328 | return 0 329 | } 330 | 331 | func (m *UserInfo) GetProps() map[string]int32 { 332 | if m != nil { 333 | return m.Props 334 | } 335 | return nil 336 | } 337 | 338 | func (m *UserInfo) GetGifts() map[string]int32 { 339 | if m != nil { 340 | return m.Gifts 341 | } 342 | return nil 343 | } 344 | 345 | func (m *UserInfo) GetMedals() map[string]int32 { 346 | if m != nil { 347 | return m.Medals 348 | } 349 | return nil 350 | } 351 | 352 | func (m *UserInfo) GetFriends() []int64 { 353 | if m != nil { 354 | return m.Friends 355 | } 356 | return nil 357 | } 358 | 359 | func (m *UserInfo) GetTags() []string { 360 | if m != nil { 361 | return m.Tags 362 | } 363 | return nil 364 | } 365 | 366 | func (m *UserInfo) GetRecords() map[string]int32 { 367 | if m != nil { 368 | return m.Records 369 | } 370 | return nil 371 | } 372 | 373 | func (m *UserInfo) GetErr() string { 374 | if m != nil { 375 | return m.Err 376 | } 377 | return "" 378 | } 379 | 380 | type UserId struct { 381 | Uid int64 `protobuf:"varint,1,opt,name=uid" json:"uid,omitempty"` 382 | } 383 | 384 | func (m *UserId) Reset() { *m = UserId{} } 385 | func (m *UserId) String() string { return proto.CompactTextString(m) } 386 | func (*UserId) ProtoMessage() {} 387 | func (*UserId) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 388 | 389 | func (m *UserId) GetUid() int64 { 390 | if m != nil { 391 | return m.Uid 392 | } 393 | return 0 394 | } 395 | 396 | func init() { 397 | proto.RegisterType((*UserInfo)(nil), "pb.UserInfo") 398 | proto.RegisterType((*UserId)(nil), "pb.UserId") 399 | } 400 | 401 | // Reference imports to suppress errors if they are not otherwise used. 402 | var _ context.Context 403 | var _ grpc.ClientConn 404 | 405 | // This is a compile-time assertion to ensure that this generated file 406 | // is compatible with the grpc package it is being compiled against. 407 | const _ = grpc.SupportPackageIsVersion4 408 | 409 | // Client API for User service 410 | 411 | type UserClient interface { 412 | GetUserInfo(ctx context.Context, in *UserId, opts ...grpc.CallOption) (*UserInfo, error) 413 | } 414 | 415 | type userClient struct { 416 | cc *grpc.ClientConn 417 | } 418 | 419 | func NewUserClient(cc *grpc.ClientConn) UserClient { 420 | return &userClient{cc} 421 | } 422 | 423 | func (c *userClient) GetUserInfo(ctx context.Context, in *UserId, opts ...grpc.CallOption) (*UserInfo, error) { 424 | out := new(UserInfo) 425 | err := grpc.Invoke(ctx, "/pb.User/GetUserInfo", in, out, c.cc, opts...) 426 | if err != nil { 427 | return nil, err 428 | } 429 | return out, nil 430 | } 431 | 432 | // Server API for User service 433 | 434 | type UserServer interface { 435 | GetUserInfo(context.Context, *UserId) (*UserInfo, error) 436 | } 437 | 438 | func RegisterUserServer(s *grpc.Server, srv UserServer) { 439 | s.RegisterService(&_User_serviceDesc, srv) 440 | } 441 | 442 | func _User_GetUserInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 443 | in := new(UserId) 444 | if err := dec(in); err != nil { 445 | return nil, err 446 | } 447 | if interceptor == nil { 448 | return srv.(UserServer).GetUserInfo(ctx, in) 449 | } 450 | info := &grpc.UnaryServerInfo{ 451 | Server: srv, 452 | FullMethod: "/pb.User/GetUserInfo", 453 | } 454 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 455 | return srv.(UserServer).GetUserInfo(ctx, req.(*UserId)) 456 | } 457 | return interceptor(ctx, in, info, handler) 458 | } 459 | 460 | var _User_serviceDesc = grpc.ServiceDesc{ 461 | ServiceName: "pb.User", 462 | HandlerType: (*UserServer)(nil), 463 | Methods: []grpc.MethodDesc{ 464 | { 465 | MethodName: "GetUserInfo", 466 | Handler: _User_GetUserInfo_Handler, 467 | }, 468 | }, 469 | Streams: []grpc.StreamDesc{}, 470 | Metadata: "user.proto", 471 | } 472 | 473 | func init() { proto.RegisterFile("user.proto", fileDescriptor0) } 474 | 475 | var fileDescriptor0 = []byte{ 476 | // 684 bytes of a gzipped FileDescriptorProto 477 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x6f, 0x1b, 0x37, 478 | 0x10, 0xad, 0x2c, 0x4b, 0x96, 0x28, 0xb7, 0x75, 0x59, 0xd7, 0x1e, 0xab, 0x6e, 0xbb, 0x55, 0xbe, 479 | 0xe4, 0x43, 0x84, 0xc4, 0xbe, 0xd8, 0xbe, 0x07, 0x46, 0x0e, 0x41, 0x82, 0x05, 0xf2, 0x03, 0x56, 480 | 0xcb, 0x91, 0x4c, 0x48, 0x4b, 0x2e, 0x48, 0xae, 0x1c, 0xfd, 0x80, 0xfc, 0xef, 0x60, 0x38, 0xbb, 481 | 0xb2, 0x8c, 0xf8, 0xa2, 0xdb, 0xbc, 0x37, 0x6f, 0x38, 0xc3, 0x37, 0x04, 0x85, 0xa8, 0x3c, 0xba, 482 | 0x49, 0xe9, 0x6c, 0xb0, 0x72, 0xaf, 0x9c, 0x8e, 0xbe, 0x0f, 0x44, 0xef, 0xab, 0x47, 0xf7, 0xd1, 483 | 0xcc, 0xac, 0x3c, 0x12, 0xed, 0x4a, 0x2b, 0x68, 0x25, 0xad, 0x71, 0x3b, 0xa5, 0x50, 0x82, 0x38, 484 | 0xa8, 0x8c, 0xb6, 0x46, 0x2b, 0xd8, 0x4b, 0x5a, 0xe3, 0x7e, 0xda, 0x40, 0x29, 0xc5, 0x7e, 0x45, 485 | 0xe2, 0x76, 0xa4, 0x63, 0x2c, 0x87, 0xa2, 0x47, 0xc7, 0x9b, 0xac, 0x40, 0xd8, 0x8f, 0xfc, 0x06, 486 | 0x53, 0xae, 0xcc, 0xbc, 0x7f, 0xb0, 0x4e, 0x41, 0x87, 0x73, 0x0d, 0xa6, 0xb3, 0x8c, 0xce, 0x17, 487 | 0xd0, 0xe5, 0xb3, 0x28, 0x96, 0x27, 0xa2, 0x3b, 0x47, 0xa3, 0xd0, 0xc1, 0x41, 0xd2, 0x1a, 0xf7, 488 | 0xd2, 0x1a, 0x91, 0x36, 0x53, 0xca, 0x41, 0x8f, 0xb5, 0x14, 0x93, 0x36, 0x5b, 0x65, 0x21, 0x73, 489 | 0xd0, 0x8f, 0x6c, 0x8d, 0x68, 0x7a, 0xed, 0xe7, 0x15, 0xfa, 0x00, 0x22, 0x1e, 0xd2, 0x40, 0xca, 490 | 0xe4, 0xd6, 0xa8, 0x6c, 0xed, 0x61, 0x90, 0xb4, 0xc6, 0x9d, 0xb4, 0x81, 0x34, 0xa7, 0xd7, 0x73, 491 | 0xa3, 0xb2, 0x80, 0x70, 0x18, 0x8d, 0xd8, 0x60, 0x99, 0x88, 0xc1, 0x4a, 0x97, 0x9b, 0xf4, 0xaf, 492 | 0x31, 0xbd, 0x4d, 0xd1, 0x24, 0x3e, 0x64, 0xa1, 0xf2, 0xf0, 0x1b, 0x4f, 0xcd, 0x48, 0x1e, 0x8b, 493 | 0x4e, 0x11, 0x74, 0x81, 0xf0, 0x7b, 0xac, 0x61, 0x40, 0x6c, 0x1e, 0xd9, 0x23, 0x66, 0xf3, 0x86, 494 | 0x0d, 0x76, 0x81, 0x06, 0xfe, 0x88, 0x97, 0x61, 0x40, 0x13, 0x4f, 0x33, 0xb3, 0x28, 0x1f, 0x14, 495 | 0x48, 0xde, 0x44, 0x0d, 0xa9, 0xe7, 0xcc, 0xba, 0xa9, 0x56, 0xf0, 0x27, 0xdf, 0x9e, 0x11, 0x39, 496 | 0xa5, 0x0b, 0xaf, 0xe1, 0x98, 0x9d, 0xa2, 0x98, 0x39, 0xd4, 0xf0, 0x57, 0xc3, 0xa1, 0xa6, 0xad, 497 | 0x17, 0x59, 0x0e, 0x27, 0x91, 0xa2, 0x90, 0x18, 0xa5, 0x15, 0x9c, 0x32, 0xa3, 0xf8, 0x1d, 0x94, 498 | 0x7e, 0xed, 0x03, 0x16, 0x00, 0xdc, 0xbd, 0x86, 0xd4, 0xbd, 0x2c, 0xac, 0xc2, 0x25, 0x9c, 0x71, 499 | 0x77, 0x46, 0xf2, 0x9d, 0xe8, 0xda, 0x70, 0x8f, 0xce, 0xc3, 0x30, 0x69, 0x8f, 0x07, 0x97, 0x30, 500 | 0x29, 0xa7, 0x93, 0xe6, 0xa5, 0x4d, 0x3e, 0xc7, 0xd4, 0x07, 0x13, 0xdc, 0x3a, 0xad, 0x75, 0x34, 501 | 0x5b, 0x6e, 0xb5, 0x81, 0xbf, 0xa3, 0x19, 0x31, 0xa6, 0x49, 0xe6, 0x58, 0xc0, 0x79, 0xdc, 0x11, 502 | 0x85, 0xa4, 0xa2, 0x8b, 0xc3, 0x3f, 0xac, 0xa2, 0x38, 0xbe, 0x15, 0x67, 0x1f, 0xc2, 0x3d, 0xfc, 503 | 0x1b, 0x85, 0x35, 0x22, 0x27, 0x97, 0xb8, 0xc2, 0x25, 0xfc, 0x17, 0x69, 0x06, 0x74, 0x97, 0x95, 504 | 0x2e, 0xc3, 0xba, 0x44, 0x48, 0x78, 0xf7, 0x35, 0x94, 0xe7, 0xa2, 0xbf, 0xd2, 0x25, 0x7e, 0x2b, 505 | 0xb5, 0x5b, 0xc3, 0xff, 0xb1, 0xc1, 0x23, 0x11, 0xeb, 0x6c, 0x95, 0xdf, 0xa3, 0x83, 0x51, 0x5d, 506 | 0xc7, 0x90, 0xfa, 0x5b, 0xb3, 0xd4, 0x06, 0xe1, 0x05, 0xf7, 0x67, 0x24, 0xdf, 0x8a, 0x4e, 0xe9, 507 | 0x6c, 0xe9, 0xe1, 0x65, 0xb4, 0xe0, 0xf4, 0x89, 0x05, 0x5f, 0x28, 0xc3, 0x0e, 0xb0, 0x8a, 0xe4, 508 | 0x73, 0x3d, 0x0b, 0x1e, 0x5e, 0x3d, 0x23, 0xbf, 0xa3, 0x4c, 0x2d, 0x8f, 0x2a, 0x72, 0xb8, 0x40, 509 | 0x95, 0x2d, 0x3d, 0xbc, 0x7e, 0xc6, 0xe1, 0x4f, 0x31, 0x55, 0x3b, 0xcc, 0x3a, 0xba, 0xc1, 0xcc, 510 | 0x69, 0x34, 0xca, 0xc3, 0x9b, 0xa4, 0x3d, 0x6e, 0xa7, 0x0d, 0x24, 0x57, 0x43, 0x36, 0xf7, 0x30, 511 | 0x4e, 0xda, 0xf4, 0x2e, 0x28, 0x96, 0x57, 0xe2, 0xc0, 0x61, 0x6e, 0x9d, 0xf2, 0x70, 0x11, 0x1b, 512 | 0x9c, 0x3d, 0x69, 0x90, 0x72, 0x8e, 0x3b, 0x34, 0x4a, 0x5a, 0x18, 0x3a, 0x07, 0x39, 0x3f, 0x1d, 513 | 0x74, 0x6e, 0x78, 0x23, 0x06, 0x5b, 0xdb, 0x26, 0xc1, 0x02, 0xd7, 0xf1, 0x8f, 0xe9, 0xa7, 0x14, 514 | 0xd2, 0x96, 0x56, 0xd9, 0xb2, 0xc2, 0xf8, 0xc3, 0x74, 0x52, 0x06, 0xb7, 0x7b, 0xd7, 0xad, 0xe1, 515 | 0xb5, 0x10, 0x8f, 0x2e, 0xed, 0x5a, 0xf9, 0x68, 0xd8, 0x4e, 0x95, 0x37, 0x62, 0xb0, 0x65, 0xdd, 516 | 0x4e, 0xa5, 0xb7, 0xe2, 0x70, 0xdb, 0x94, 0x5d, 0x6a, 0x47, 0x43, 0xd1, 0x8d, 0xce, 0xaa, 0x9f, 517 | 0x3f, 0xe1, 0xcb, 0xf7, 0x62, 0x9f, 0x72, 0xf2, 0x42, 0x0c, 0xee, 0x30, 0x6c, 0x7e, 0x6b, 0xb1, 518 | 0x59, 0x87, 0x1a, 0x1e, 0x6e, 0xaf, 0x66, 0xf4, 0xcb, 0xb4, 0x1b, 0x7f, 0xf8, 0xab, 0x1f, 0x01, 519 | 0x00, 0x00, 0xff, 0xff, 0x96, 0x99, 0xaf, 0x06, 0xef, 0x05, 0x00, 0x00, 520 | } 521 | -------------------------------------------------------------------------------- /usercenter/pb/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | service User { 6 | rpc GetUserInfo (UserId) returns (UserInfo) { 7 | } 8 | } 9 | message UserInfo { 10 | int64 uid = 1; 11 | string unionid = 2; 12 | string uuid = 3; 13 | string username = 4; 14 | string password = 5; 15 | string nick = 6; 16 | bool gender = 7; 17 | string addr = 8; 18 | string avatar = 9; 19 | bool isguest = 10; 20 | int32 condays = 11; 21 | int64 signdate = 12; 22 | int64 vipsigndate = 13; 23 | bool status = 14; 24 | int64 mtime = 15; 25 | int64 ctime = 16; 26 | string token = 17; 27 | string bankpwd = 18; 28 | string forbid = 19; 29 | string imsi = 20; 30 | string imei = 21; 31 | string mac = 22; 32 | string did = 23; 33 | string psystem = 24; 34 | string pmodel = 25; 35 | map others = 26; 36 | int64 coin = 27; 37 | int32 gem = 28; 38 | int64 bank = 29; 39 | int32 growth = 30; 40 | int32 level = 31; 41 | int32 viptype = 32; 42 | int64 vipexpiry = 33; 43 | int32 voucher = 34; 44 | int32 online = 35; 45 | map props = 36; 46 | map gifts = 37; 47 | map medals = 38; 48 | repeated int64 friends = 39; 49 | repeated string tags = 40; 50 | map records = 41; 51 | string err = 99; 52 | } 53 | message UserId { 54 | int64 uid = 1; 55 | } 56 | -------------------------------------------------------------------------------- /usercenter/service/endpoints.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-kit/kit/endpoint" 9 | "github.com/go-kit/kit/log" 10 | "github.com/go-kit/kit/metrics" 11 | 12 | "github.com/yiv/yivgame/usercenter/center" 13 | ) 14 | 15 | type Endpoints struct { 16 | GetUserInfoEndpoint endpoint.Endpoint 17 | } 18 | 19 | type getUserInfoRes struct { 20 | Uid int64 21 | Unionid string 22 | Uuid string 23 | Username string 24 | Password string 25 | Nick string 26 | Gender bool 27 | Addr string 28 | Avatar string 29 | Isguest bool 30 | Condays int32 31 | Signdate int64 32 | Vipsigndate int64 33 | Status bool 34 | Mtime int64 35 | Ctime int64 36 | Token string 37 | Bankpwd string 38 | Forbid string 39 | Imsi string 40 | Imei string 41 | Mac string 42 | Did string 43 | Psystem string 44 | Pmodel string 45 | Others map[string]int32 46 | Coin int64 47 | Gem int32 48 | Bank int64 49 | Growth int32 50 | Level int32 51 | Viptype int32 52 | Vipexpiry int64 53 | Voucher int32 54 | Online int32 55 | Props map[string]int32 56 | Gifts map[string]int32 57 | Medals map[string]int32 58 | Friends []int64 59 | Tags []string 60 | Records map[string]int32 61 | Err error `json:"err"` 62 | } 63 | 64 | func MakeGetUserInfoEndpoint(s Service) endpoint.Endpoint { 65 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 66 | id := request.(int64) 67 | u, err := s.GetUserInfo(ctx, id) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return getUserInfoRes{ 72 | Uid: u.Uid, 73 | Unionid: u.Unionid, 74 | Uuid: u.Uuid, 75 | Username: u.Username, 76 | Password: u.Password, 77 | Nick: u.Nick, 78 | Gender: u.Gender, 79 | Addr: u.Addr, 80 | Avatar: u.Avatar, 81 | Isguest: u.Isguest, 82 | Condays: u.Condays, 83 | Signdate: u.Signdate, 84 | Vipsigndate: u.Vipsigndate, 85 | Status: u.Status, 86 | Mtime: u.Mtime, 87 | Ctime: u.Ctime, 88 | Token: u.Token, 89 | Bankpwd: u.Bankpwd, 90 | Forbid: u.Forbid, 91 | Imsi: u.Imsi, 92 | Imei: u.Imei, 93 | Mac: u.Mac, 94 | Did: u.Did, 95 | Psystem: u.Psystem, 96 | Pmodel: u.Pmodel, 97 | Others: u.Others, 98 | Coin: u.Coin, 99 | Gem: u.Gem, 100 | Bank: u.Bank, 101 | Growth: u.Growth, 102 | Level: u.Level, 103 | Viptype: u.Viptype, 104 | Vipexpiry: u.Vipexpiry, 105 | Voucher: u.Voucher, 106 | Online: u.Online, 107 | Props: u.Props, 108 | Gifts: u.Gifts, 109 | Medals: u.Medals, 110 | Friends: u.Friends, 111 | Tags: u.Tags, 112 | Records: u.Records, 113 | }, err 114 | } 115 | } 116 | func (e Endpoints) GetUserInfo(ctx context.Context, uid int64) (user *center.User, err error) { 117 | response, err := e.GetUserInfoEndpoint(ctx, uid) 118 | if err != nil { 119 | return nil, err 120 | } 121 | r := response.(getUserInfoRes) 122 | return ¢er.User{ 123 | Account: center.Account{ 124 | Uid: r.Uid, 125 | Unionid: r.Unionid, 126 | Uuid: r.Uuid, 127 | Username: r.Username, 128 | Password: r.Password, 129 | Nick: r.Nick, 130 | Gender: r.Gender, 131 | Addr: r.Addr, 132 | Avatar: r.Avatar, 133 | Isguest: r.Isguest, 134 | Condays: r.Condays, 135 | Signdate: r.Signdate, 136 | Vipsigndate: r.Vipsigndate, 137 | Status: r.Status, 138 | Mtime: r.Mtime, 139 | Ctime: r.Ctime, 140 | Bankpwd: r.Bankpwd, 141 | Forbid: r.Forbid, 142 | Imsi: r.Imsi, 143 | Imei: r.Imei, 144 | Mac: r.Mac, 145 | Did: r.Did, 146 | Psystem: r.Psystem, 147 | Pmodel: r.Pmodel, 148 | Others: r.Others, 149 | }, 150 | AccountInfo: center.AccountInfo{ 151 | Token: r.Token, 152 | Coin: r.Coin, 153 | Gem: r.Gem, 154 | Bank: r.Bank, 155 | Growth: r.Growth, 156 | Level: r.Level, 157 | Viptype: r.Viptype, 158 | Vipexpiry: r.Vipexpiry, 159 | Voucher: r.Voucher, 160 | Online: r.Online, 161 | Props: r.Props, 162 | Gifts: r.Gifts, 163 | Medals: r.Medals, 164 | Friends: r.Friends, 165 | Tags: r.Tags, 166 | Records: r.Records, 167 | }, 168 | }, nil 169 | } 170 | 171 | func EndpointInstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware { 172 | return func(next endpoint.Endpoint) endpoint.Endpoint { 173 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 174 | defer func(begin time.Time) { 175 | duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds()) 176 | }(time.Now()) 177 | return next(ctx, request) 178 | 179 | } 180 | } 181 | } 182 | 183 | // EndpointLoggingMiddleware returns an endpoint middleware that logs the 184 | // duration of each invocation, and the resulting error, if any. 185 | func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware { 186 | return func(next endpoint.Endpoint) endpoint.Endpoint { 187 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { 188 | 189 | defer func(begin time.Time) { 190 | logger.Log("error", err, "took", time.Since(begin)) 191 | }(time.Now()) 192 | return next(ctx, request) 193 | 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /usercenter/service/error.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrLoadToMem = errors.New("err occur on loading data from db to memory") 7 | ) 8 | -------------------------------------------------------------------------------- /usercenter/service/instrumenting.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-kit/kit/metrics" 9 | "github.com/yiv/yivgame/usercenter/center" 10 | ) 11 | 12 | type serviceInstrumentingMiddleware struct { 13 | requestCount metrics.Counter 14 | requestLatency metrics.Histogram 15 | next Service 16 | } 17 | 18 | func ServiceInstrumentingMiddleware(requestCount metrics.Counter, requestLatency metrics.Histogram) Middleware { 19 | return func(next Service) Service { 20 | return serviceInstrumentingMiddleware{ 21 | requestCount: requestCount, 22 | requestLatency: requestLatency, 23 | next: next, 24 | } 25 | } 26 | } 27 | 28 | //GetUserInfo 获取帐号详细信息 29 | func (mw serviceInstrumentingMiddleware) GetUserInfo(ctx context.Context, id int64) (user *center.User, err error) { 30 | defer func(begin time.Time) { 31 | lvs := []string{"method", "GetUserInfo", "error", fmt.Sprint(err != nil)} 32 | mw.requestCount.With(lvs...).Add(1) 33 | mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) 34 | }(time.Now()) 35 | return mw.next.GetUserInfo(ctx, id) 36 | } 37 | -------------------------------------------------------------------------------- /usercenter/service/logging.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-kit/kit/log" 9 | "github.com/yiv/yivgame/usercenter/center" 10 | ) 11 | 12 | func ServiceLoggingMiddleware(logger log.Logger) Middleware { 13 | return func(next Service) Service { 14 | return serviceLoggingMiddleware{ 15 | logger: logger, 16 | next: next, 17 | } 18 | } 19 | } 20 | 21 | type serviceLoggingMiddleware struct { 22 | logger log.Logger 23 | next Service 24 | } 25 | 26 | //GetUserInfo 获取帐号详细信息 27 | func (mw serviceLoggingMiddleware) GetUserInfo(ctx context.Context, id int64) (user *center.User, err error) { 28 | defer func(begin time.Time) { 29 | mw.logger.Log( 30 | "method", "GetUserInfo", 31 | "id", id, 32 | "err", err, 33 | "took", time.Since(begin), 34 | "user", fmt.Sprintf("%v", user), 35 | ) 36 | }(time.Now()) 37 | return mw.next.GetUserInfo(ctx, id) 38 | } 39 | -------------------------------------------------------------------------------- /usercenter/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/go-kit/kit/log" 8 | 9 | "github.com/yiv/yivgame/usercenter/center" 10 | ) 11 | 12 | type Middleware func(Service) Service 13 | 14 | type Service interface { 15 | GetUserInfo(ctx context.Context, id int64) (user *center.User, err error) 16 | } 17 | 18 | var ( 19 | ErrBadRequest = errors.New("bad request") 20 | ) 21 | 22 | func str2err(s string) error { 23 | if s == "" { 24 | return nil 25 | } 26 | return errors.New(s) 27 | } 28 | 29 | func err2str(err error) string { 30 | if err == nil { 31 | return "" 32 | } 33 | return err.Error() 34 | } 35 | 36 | type basicService struct { 37 | DBRepository center.DBRepository 38 | MQRepository center.MQRepository 39 | UserManager *center.UserManager 40 | logger log.Logger 41 | } 42 | 43 | func NewBasicService(dbRep center.DBRepository, mqRep center.MQRepository, logger log.Logger) Service { 44 | service := basicService{} 45 | service.DBRepository = dbRep 46 | service.MQRepository = mqRep 47 | service.UserManager = center.NewUserManager(dbRep, mqRep) 48 | service.logger = logger 49 | return service 50 | } 51 | 52 | //loadUserFromDBtoMem 从数据库中将帐号信息加载到内存中 53 | func (s basicService) loadUserFromDBtoMem(_ context.Context, id int64) (err error) { 54 | if isExist := s.UserManager.IsUserExist(id); isExist { 55 | return center.ErrUserExist 56 | } 57 | user, err := s.DBRepository.FindUserById(id) 58 | if err != nil { 59 | return err 60 | } 61 | err = s.UserManager.AddUser(id, user, s.MQRepository) 62 | if err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | //GetUserInfo 获取帐号详细信息 69 | func (s basicService) GetUserInfo(_ context.Context, id int64) (user *center.User, err error) { 70 | if isExist := s.UserManager.IsUserExist(id); !isExist { 71 | if err = s.loadUserFromDBtoMem(nil, id); err != nil { 72 | return nil, err 73 | } 74 | } 75 | return s.UserManager.GetUser(id) 76 | } 77 | -------------------------------------------------------------------------------- /usercenter/service/transportgrpc.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | 6 | stdopentracing "github.com/opentracing/opentracing-go" 7 | oldcontext "golang.org/x/net/context" 8 | 9 | "github.com/go-kit/kit/log" 10 | "github.com/go-kit/kit/tracing/opentracing" 11 | kitgrpc "github.com/go-kit/kit/transport/grpc" 12 | 13 | "github.com/yiv/yivgame/usercenter/pb" 14 | ) 15 | 16 | type grpcHandler struct { 17 | getUserInfo kitgrpc.Handler 18 | } 19 | 20 | func MakeGRPCHandler(endpoints Endpoints, tracer stdopentracing.Tracer, logger log.Logger) pb.UserServer { 21 | options := []kitgrpc.ServerOption{ 22 | kitgrpc.ServerErrorLogger(logger), 23 | } 24 | return &grpcHandler{ 25 | getUserInfo: kitgrpc.NewServer( 26 | endpoints.GetUserInfoEndpoint, 27 | DecodeGRPCGetUserInfoReq, 28 | EncodeGRPCGetUserInfoRes, 29 | append(options, kitgrpc.ServerBefore(opentracing.GRPCToContext(tracer, "getUserInfo", logger)))..., 30 | ), 31 | } 32 | 33 | } 34 | 35 | //for server 36 | func DecodeGRPCGetUserInfoReq(_ context.Context, request interface{}) (interface{}, error) { 37 | r := request.(*pb.UserId) 38 | return r.Uid, nil 39 | } 40 | func EncodeGRPCGetUserInfoRes(_ context.Context, response interface{}) (interface{}, error) { 41 | r := response.(getUserInfoRes) 42 | return &pb.UserInfo{ 43 | Uid: r.Uid, 44 | Unionid: r.Unionid, 45 | Uuid: r.Uuid, 46 | Username: r.Username, 47 | Password: r.Password, 48 | Nick: r.Nick, 49 | Gender: r.Gender, 50 | Addr: r.Addr, 51 | Avatar: r.Avatar, 52 | Isguest: r.Isguest, 53 | Condays: r.Condays, 54 | Signdate: r.Signdate, 55 | Vipsigndate: r.Vipsigndate, 56 | Status: r.Status, 57 | Mtime: r.Mtime, 58 | Ctime: r.Ctime, 59 | Token: r.Token, 60 | Bankpwd: r.Bankpwd, 61 | Forbid: r.Forbid, 62 | Imsi: r.Imsi, 63 | Imei: r.Imei, 64 | Mac: r.Mac, 65 | Did: r.Did, 66 | Psystem: r.Psystem, 67 | Pmodel: r.Pmodel, 68 | Others: r.Others, 69 | Coin: r.Coin, 70 | Gem: r.Gem, 71 | Bank: r.Bank, 72 | Growth: r.Growth, 73 | Level: r.Level, 74 | Viptype: r.Viptype, 75 | Vipexpiry: r.Vipexpiry, 76 | Voucher: r.Voucher, 77 | Online: r.Online, 78 | Props: r.Props, 79 | Gifts: r.Gifts, 80 | Medals: r.Medals, 81 | Friends: r.Friends, 82 | Tags: r.Tags, 83 | Records: r.Records, 84 | Err: err2str(r.Err), 85 | }, nil 86 | } 87 | 88 | //for client 89 | func EncodeGRPCGetUserInfoReq(_ context.Context, request interface{}) (interface{}, error) { 90 | req := request.(int64) 91 | return &pb.UserId{Uid: req}, nil 92 | } 93 | func DecodeGRPCGetUserInfoRes(_ context.Context, response interface{}) (interface{}, error) { 94 | r := response.(*pb.UserInfo) 95 | return getUserInfoRes{ 96 | Uid: r.Uid, 97 | Unionid: r.Unionid, 98 | Uuid: r.Uuid, 99 | Username: r.Username, 100 | Password: r.Password, 101 | Nick: r.Nick, 102 | Gender: r.Gender, 103 | Addr: r.Addr, 104 | Avatar: r.Avatar, 105 | Isguest: r.Isguest, 106 | Condays: r.Condays, 107 | Signdate: r.Signdate, 108 | Vipsigndate: r.Vipsigndate, 109 | Status: r.Status, 110 | Mtime: r.Mtime, 111 | Ctime: r.Ctime, 112 | Token: r.Token, 113 | Bankpwd: r.Bankpwd, 114 | Forbid: r.Forbid, 115 | Imsi: r.Imsi, 116 | Imei: r.Imei, 117 | Mac: r.Mac, 118 | Did: r.Did, 119 | Psystem: r.Psystem, 120 | Pmodel: r.Pmodel, 121 | Others: r.Others, 122 | Coin: r.Coin, 123 | Gem: r.Gem, 124 | Bank: r.Bank, 125 | Growth: r.Growth, 126 | Level: r.Level, 127 | Viptype: r.Viptype, 128 | Vipexpiry: r.Vipexpiry, 129 | Voucher: r.Voucher, 130 | Online: r.Online, 131 | Props: r.Props, 132 | Gifts: r.Gifts, 133 | Medals: r.Medals, 134 | Friends: r.Friends, 135 | Tags: r.Tags, 136 | Records: r.Records, 137 | Err: str2err(r.Err), 138 | }, nil 139 | } 140 | func (g *grpcHandler) GetUserInfo(ctx oldcontext.Context, req *pb.UserId) (*pb.UserInfo, error) { 141 | _, rep, err := g.getUserInfo.ServeGRPC(ctx, req) 142 | if err != nil { 143 | return nil, err 144 | } 145 | return rep.(*pb.UserInfo), nil 146 | } 147 | -------------------------------------------------------------------------------- /usercenter/service/transporthttp.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | // This file provides server-side bindings for the HTTP transport. 4 | // It utilizes the transport/http.Server. 5 | 6 | import ( 7 | "context" 8 | "crypto/rc4" 9 | "encoding/json" 10 | "errors" 11 | "net/http" 12 | ) 13 | 14 | func errorEncoder(_ context.Context, err error, w http.ResponseWriter) { 15 | code := http.StatusInternalServerError 16 | msg := err.Error() 17 | 18 | switch err { 19 | case ErrBadRequest: 20 | code = http.StatusBadRequest 21 | default: 22 | code = http.StatusInternalServerError 23 | } 24 | 25 | w.WriteHeader(code) 26 | s, err := json.Marshal(errorWrapper{Error: msg}) 27 | d := RC4Crypt(s) 28 | w.Write(d) 29 | } 30 | 31 | func errorDecoder(r *http.Response) error { 32 | var w errorWrapper 33 | if err := json.NewDecoder(r.Body).Decode(&w); err != nil { 34 | return err 35 | } 36 | return errors.New(w.Error) 37 | } 38 | 39 | type errorWrapper struct { 40 | Error string `json:"Err"` 41 | } 42 | 43 | func EncodeHTTPGenericResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { 44 | s, err := json.Marshal(response) 45 | d := RC4Crypt(s) 46 | w.Write(d) 47 | return err 48 | } 49 | 50 | func RC4Crypt(s []byte) []byte { 51 | key := []byte("e39e7594feaaf4af26cdaea078a316cb") 52 | c, _ := rc4.NewCipher(key) 53 | d := make([]byte, len(s)) 54 | c.XORKeyStream(d, s) 55 | return d 56 | } 57 | --------------------------------------------------------------------------------