├── .gitignore ├── README.md ├── bin ├── conf │ ├── gamesvr.json │ ├── lobbysvr.json │ └── loginsvr.json ├── gamedata │ └── .gitignore └── log │ └── .gitignore ├── build.sh └── src ├── client └── test_client.go ├── framework ├── LICENSE ├── README.md ├── TUTORIAL_EN.md ├── TUTORIAL_ZH.md ├── chanrpc │ ├── chanrpc.go │ └── example_test.go ├── cluster │ ├── agent.go │ └── cluster.go ├── conf │ └── conf.go ├── console │ ├── command.go │ └── console.go ├── db │ └── mongodb │ │ ├── example_test.go │ │ └── mongodb.go ├── gate │ ├── agent.go │ └── gate.go ├── go │ ├── example_test.go │ └── go.go ├── leaf.go ├── log │ ├── example_test.go │ └── log.go ├── module │ ├── module.go │ └── skeleton.go ├── network │ ├── agent.go │ ├── conn.go │ ├── json │ │ └── json.go │ ├── processor.go │ ├── protobuf │ │ └── protobuf.go │ ├── tcp_client.go │ ├── tcp_conn.go │ ├── tcp_msg.go │ ├── tcp_server.go │ ├── ws_client.go │ ├── ws_conn.go │ └── ws_server.go ├── recordfile │ ├── example_test.go │ ├── recordfile.go │ └── test.txt ├── timer │ ├── cronexpr.go │ ├── example_test.go │ └── timer.go ├── util │ ├── deepcopy.go │ ├── example_test.go │ ├── map.go │ ├── rand.go │ └── semaphore.go └── version.go ├── gamesvr ├── base │ └── skeleton.go ├── cluster │ ├── external.go │ └── internal │ │ └── module.go ├── game │ ├── external.go │ └── internal │ │ ├── chanrpc.go │ │ ├── handler.go │ │ └── module.go ├── gamedata │ └── reader.go ├── gate │ ├── external.go │ └── internal │ │ └── module.go ├── main.go └── svrconf │ ├── json.go │ └── svrconf.go ├── lobbysvr ├── base │ └── skeleton.go ├── cluster │ ├── external.go │ └── internal │ │ └── module.go ├── game │ ├── external.go │ └── internal │ │ ├── chanrpc.go │ │ ├── handler.go │ │ └── module.go ├── gamedata │ └── reader.go ├── main.go └── svrconf │ ├── json.go │ └── svrconf.go ├── loginsvr ├── base │ └── skeleton.go ├── cluster │ ├── external.go │ └── internal │ │ └── module.go ├── game │ ├── external.go │ └── internal │ │ ├── chanrpc.go │ │ ├── handler.go │ │ └── module.go ├── gamedata │ └── reader.go ├── gate │ ├── external.go │ └── internal │ │ └── module.go ├── main.go └── svrconf │ ├── json.go │ └── svrconf.go └── proto ├── build.sh ├── conf ├── gamemsg.proto └── login.proto ├── gameproto ├── gamemsg.pb.go └── login.pb.go ├── protoc ├── protoc-gen-go └── protoc.exe /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/lobbysvr.exe 2 | /bin/loginsvr.exe 3 | /bin/client 4 | /bin/lobbysvr 5 | /pkg/windows_386 6 | /pkg/linux_amd64 7 | /bin/loginsvr 8 | /bin/gamesvr 9 | /pkg/darwin_amd64 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 捕鱼侠游戏服务器 2 | 3 | 1.捕鱼侠游戏服务器包含: 登录服务器,游戏服务器和中心服务器; 4 | 2.基于捕鱼游戏数据特性,采用mongodb代替mysql; 5 | 3.中心服务器上的数据可能需要采用缓存,待定; 6 | 7 | 集群通信组件 8 | 1.集群服务器之间通过socket连接进行通信; 9 | 2.每个服务器都有自己唯一的通信组件ID,生成规则为: ServerType*1000+ServerID; 10 | ServerType为服务器类型,区间0-99; 11 | ServerID为服务器在同类服务中的index,区间为1-999; 12 | 3.目前初步使用的ServerType分配为: 13 | LOGIN_SERVER 10 //登录服务器组 14 | GAME_SERVER 11 //游戏服务器组 15 | LOBBY_SERVER 12 //中心服务器组 16 | -------------------------------------------------------------------------------- /bin/conf/gamesvr.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCPAddr": "127.0.0.1:3566", 3 | "MaxConnNum": 20000, 4 | 5 | "SvrBaseConfig":{ 6 | "LogLevel": "debug", 7 | "LogPath": "./log/", 8 | "ConsolePort":0, 9 | "ConsolePrompt":"#fishdebug#", 10 | "ProfilePath":"", 11 | "ServerID":11002, 12 | "ListenAddr":"", 13 | "ConnAddrs":[ 14 | {"Id":10001,"ConnAddr":"127.0.0.1:3001"} 15 | ], 16 | "PendingWriteNum":2000 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bin/conf/lobbysvr.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCPAddr": "", 3 | "MaxConnNum": 20000, 4 | 5 | "SvrBaseConfig":{ 6 | "LogLevel": "debug", 7 | "LogPath": "", 8 | "ConsolePort":0, 9 | "ConsolePrompt":"#fishdebug#", 10 | "ProfilePath":"", 11 | "ServerID":12001, 12 | "ListenAddr":"127.0.0.1:3001", 13 | "ConnAddrs":[], 14 | "PendingWriteNum":2000 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bin/conf/loginsvr.json: -------------------------------------------------------------------------------- 1 | { 2 | "TCPAddr": "127.0.0.1:3564", 3 | "MaxConnNum": 20000, 4 | 5 | "SvrBaseConfig":{ 6 | "LogLevel": "debug", 7 | "LogPath": "", 8 | "ConsolePort":0, 9 | "ConsolePrompt":"#fishdebug#", 10 | "ProfilePath":"", 11 | "ServerID":10001, 12 | "ListenAddr":"", 13 | "ConnAddrs":[ 14 | {"Id":12001,"ConnAddr":"127.0.0.1:3001"} 15 | ], 16 | "PendingWriteNum":2000 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bin/gamedata/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bin/log/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #编译loginsvr 4 | go install loginsvr 5 | 6 | #编译lobbysvr 7 | go install lobbysvr 8 | 9 | #编译gamesvr 10 | go install gamesvr 11 | -------------------------------------------------------------------------------- /src/client/test_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | //"framework/cluster" 7 | 8 | "net" 9 | "proto/gameproto" 10 | 11 | "github.com/golang/protobuf/proto" 12 | ) 13 | 14 | func main() { 15 | conn, err := net.Dial("tcp", "127.0.0.1:3563") 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | fmt.Println("Success to connect to server.") 21 | 22 | //发送loginreq消息 23 | reqLogin, err := proto.Marshal(&gameproto.LoginSvr_LoginReq{ 24 | Uin: proto.Uint32(10000), 25 | StrSessionKey: proto.String("aaaaabbbbbb"), 26 | }) 27 | 28 | reqData, err := proto.Marshal(&gameproto.ProtoMsg{ 29 | Msgid: gameproto.MsgID_LOGINSVR_LOGIN_REQ.Enum(), 30 | Uin: proto.Uint32(10000), 31 | Msgdata: reqLogin, 32 | }) 33 | 34 | //2bytes len + data 35 | m := make([]byte, 2+len(reqData)) 36 | 37 | //写入2bytes长度 38 | binary.BigEndian.PutUint16(m, uint16(len(reqData))) 39 | 40 | //写入实际数据 41 | copy(m[2:], reqData) 42 | 43 | //发送消息 44 | conn.Write(m) 45 | 46 | fmt.Println("Success to send data, len ", len(m)) 47 | 48 | //接收消息 49 | recvMsg := make([]byte, 1024) 50 | len, err := conn.Read(recvMsg) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | fmt.Println("Success to read data from server ", len) 56 | 57 | //接收到消息格式也为2bytes len + data 58 | msgLen := binary.BigEndian.Uint16(recvMsg[:2]) 59 | 60 | fmt.Printf("recv msg %v\n", recvMsg[2:]) 61 | 62 | respMsg := &gameproto.ProtoMsg{} 63 | proto.Unmarshal(recvMsg[2:msgLen], respMsg) 64 | 65 | respLogin := &gameproto.LoginSvr_LoginResp{} 66 | proto.Unmarshal(respMsg.GetMsgdata(), respLogin) 67 | 68 | //cluster.Agent.Run() 69 | fmt.Printf("recv msg len: %d, msg uin: %u, result %d\n", msgLen, respMsg.GetUin(), respLogin.GetIResult()) 70 | 71 | conn.Close() 72 | } 73 | -------------------------------------------------------------------------------- /src/framework/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2014-2016 jasonxiong. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /src/framework/README.md: -------------------------------------------------------------------------------- 1 | Leaf 2 | ==== 3 | A pragmatic game server framework in Go (golang). 4 | 5 | Features 6 | --------- 7 | 8 | * Extremely easy to use 9 | * Reliable 10 | * Multicore support 11 | * Modularity 12 | 13 | Community 14 | --------- 15 | 16 | * QQ 群:376389675 17 | 18 | Documentation 19 | --------- 20 | 21 | * [中文文档](https://framework/blob/master/TUTORIAL_ZH.md) 22 | * [English](https://framework/blob/master/TUTORIAL_EN.md) 23 | 24 | Licensing 25 | --------- 26 | 27 | Leaf is licensed under the Apache License, Version 2.0. See [LICENSE](https://framework/blob/master/LICENSE) for the full license text. 28 | -------------------------------------------------------------------------------- /src/framework/TUTORIAL_EN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonxiong/GoFramework/770fc784beaa9eba4fc7b73411633148eb341b9f/src/framework/TUTORIAL_EN.md -------------------------------------------------------------------------------- /src/framework/TUTORIAL_ZH.md: -------------------------------------------------------------------------------- 1 | Leaf 游戏服务器框架简介 2 | ================== 3 | 4 | Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏服务器框架。Leaf 适用于各类游戏服务器的开发,包括 H5(HTML5)游戏服务器。 5 | 6 | Leaf 的关注点: 7 | 8 | * 良好的使用体验。Leaf 总是尽可能的提供简洁和易用的接口,尽可能的提升开发的效率 9 | * 稳定性。Leaf 总是尽可能的恢复运行过程中的错误,避免崩溃 10 | * 多核支持。Leaf 通过模块机制和 [leaf/go](https://framework/tree/master/go) 尽可能的利用多核资源,同时又尽量避免各种副作用 11 | * 模块机制。 12 | 13 | Leaf 的模块机制 14 | --------------- 15 | 16 | 一个 Leaf 开发的游戏服务器由多个模块组成(例如 [LeafServer](https://frameworkserver)),模块有以下特点: 17 | 18 | * 每个模块运行在一个单独的 goroutine 中 19 | * 模块间通过一套轻量的 RPC 机制通讯([leaf/chanrpc](https://framework/tree/master/chanrpc)) 20 | 21 | Leaf 不建议在游戏服务器中设计过多的模块。 22 | 23 | 游戏服务器在启动时进行模块的注册,例如: 24 | 25 | ```go 26 | leaf.Run( 27 | game.Module, 28 | gate.Module, 29 | login.Module, 30 | ) 31 | ``` 32 | 33 | 这里按顺序注册了 game、gate、login 三个模块。每个模块都需要实现接口: 34 | 35 | ```go 36 | type Module interface { 37 | OnInit() 38 | OnDestroy() 39 | Run(closeSig chan bool) 40 | } 41 | ``` 42 | 43 | Leaf 首先会在同一个 goroutine 中按模块注册顺序执行模块的 OnInit 方法,等到所有模块 OnInit 方法执行完成后则为每一个模块启动一个 goroutine 并执行模块的 Run 方法。最后,游戏服务器关闭时(Ctrl + C 关闭游戏服务器)将按模块注册相反顺序在同一个 goroutine 中执行模块的 OnDestroy 方法。 44 | 45 | Leaf 源码概览 46 | --------------- 47 | 48 | * leaf/chanrpc 提供了一套基于 channel 的 RPC 机制,用于游戏服务器模块间通讯 49 | * leaf/db 数据库相关,目前支持 [MongoDB](https://www.mongodb.org/) 50 | * leaf/gate 网关模块,负责游戏客户端的接入 51 | * leaf/go 用于创建能够被 Leaf 管理的 goroutine 52 | * leaf/log 日志相关 53 | * leaf/network 网络相关,使用 TCP 和 WebSocket 协议,可自定义消息格式,默认 Leaf 提供了基于 [protobuf](https://developers.google.com/protocol-buffers) 和 JSON 的消息格式 54 | * leaf/recordfile 用于管理游戏数据 55 | * leaf/timer 定时器相关 56 | * leaf/util 辅助库 57 | 58 | 使用 Leaf 开发游戏服务器 59 | --------------- 60 | 61 | [LeafServer](https://frameworkserver) 是一个基于 Leaf 开发的游戏服务器,我们以 LeafServer 作为起点。 62 | 63 | 获取 LeafServer: 64 | 65 | ``` 66 | git clone https://frameworkserver 67 | ``` 68 | 69 | 设置 leafserver 目录到 GOPATH 环境变量后获取 Leaf: 70 | 71 | ``` 72 | go get framework 73 | ``` 74 | 75 | 编译 LeafServer: 76 | 77 | ``` 78 | go install server 79 | ``` 80 | 81 | 如果一切顺利,运行 server 你可以获得以下输出: 82 | 83 | ``` 84 | 2015/08/26 22:11:27 [release] Leaf 1.1.2 starting up 85 | ``` 86 | 87 | 敲击 Ctrl + C 关闭游戏服务器,服务器正常关闭输出: 88 | 89 | ``` 90 | 2015/08/26 22:12:30 [release] Leaf closing down (signal: interrupt) 91 | ``` 92 | 93 | ### Hello Leaf 94 | 95 | 现在,在 LeafServer 的基础上,我们来看看游戏服务器如何接收和处理网络消息。 96 | 97 | 首先定义一个 JSON 格式的消息(protobuf 类似)。打开 LeafServer msg/msg.go 文件可以看到如下代码: 98 | 99 | ```go 100 | package msg 101 | 102 | import ( 103 | "framework/network" 104 | ) 105 | 106 | var Processor network.Processor 107 | 108 | func init() { 109 | 110 | } 111 | ``` 112 | 113 | Processor 为消息的处理器(可由用户自定义),这里我们使用 Leaf 默认提供的 JSON 消息处理器并尝试添加一个名字为 Hello 的消息: 114 | 115 | ```go 116 | package msg 117 | 118 | import ( 119 | "framework/network/json" 120 | ) 121 | 122 | // 使用默认的 JSON 消息处理器(默认还提供了 protobuf 消息处理器) 123 | var Processor = json.NewProcessor() 124 | 125 | func init() { 126 | // 这里我们注册了一个 JSON 消息 Hello 127 | Processor.Register(&Hello{}) 128 | } 129 | 130 | // 一个结构体定义了一个 JSON 消息的格式 131 | // 消息名为 Hello 132 | type Hello struct { 133 | Name string 134 | } 135 | ``` 136 | 137 | 客户端发送到游戏服务器的消息需要通过 gate 模块路由,简而言之,gate 模块决定了某个消息具体交给内部的哪个模块来处理。这里,我们将 Hello 消息路由到 game 模块中。打开 LeafServer gate/router.go,敲入如下代码: 138 | 139 | ```go 140 | package gate 141 | 142 | import ( 143 | "server/game" 144 | "server/msg" 145 | ) 146 | 147 | func init() { 148 | // 这里指定消息 Hello 路由到 game 模块 149 | // 模块间使用 ChanRPC 通讯,消息路由也不例外 150 | msg.Processor.SetRouter(&msg.Hello{}, game.ChanRPC) 151 | } 152 | ``` 153 | 154 | 一切就绪,我们现在可以在 game 模块中处理 Hello 消息了。打开 LeafServer game/internal/handler.go,敲入如下代码: 155 | 156 | ```go 157 | package internal 158 | 159 | import ( 160 | "framework/log" 161 | "framework/gate" 162 | "reflect" 163 | "server/msg" 164 | ) 165 | 166 | func init() { 167 | // 向当前模块(game 模块)注册 Hello 消息的消息处理函数 handleHello 168 | handler(&msg.Hello{}, handleHello) 169 | } 170 | 171 | func handler(m interface{}, h interface{}) { 172 | skeleton.RegisterChanRPC(reflect.TypeOf(m), h) 173 | } 174 | 175 | func handleHello(args []interface{}) { 176 | // 收到的 Hello 消息 177 | m := args[0].(*msg.Hello) 178 | // 消息的发送者 179 | a := args[1].(gate.Agent) 180 | 181 | // 输出收到的消息的内容 182 | log.Debug("hello %v", m.Name) 183 | 184 | // 给发送者回应一个 Hello 消息 185 | a.WriteMsg(&msg.Hello{ 186 | Name: "client", 187 | }) 188 | } 189 | ``` 190 | 191 | 到这里,一个简单的范例就完成了。为了更加清楚的了解消息的格式,我们从 0 编写一个最简单的测试客户端。 192 | 193 | Leaf 中,当选择使用 TCP 协议时,在网络中传输的消息都会使用以下格式: 194 | 195 | ``` 196 | -------------- 197 | | len | data | 198 | -------------- 199 | ``` 200 | 201 | 其中: 202 | 203 | 1. len 表示了 data 部分的长度(字节数)。len 本身也有长度,默认为 2 字节(可配置),len 本身的长度决定了单个消息的最大大小 204 | 2. data 部分使用 JSON 或者 protobuf 编码(也可自定义其他编码方式) 205 | 206 | 测试客户端同样使用 Go 语言编写: 207 | ```go 208 | package main 209 | 210 | import ( 211 | "encoding/binary" 212 | "net" 213 | ) 214 | 215 | func main() { 216 | conn, err := net.Dial("tcp", "127.0.0.1:3563") 217 | if err != nil { 218 | panic(err) 219 | } 220 | 221 | // Hello 消息(JSON 格式) 222 | // 对应游戏服务器 Hello 消息结构体 223 | data := []byte(`{ 224 | "Hello": { 225 | "Name": "leaf" 226 | } 227 | }`) 228 | 229 | // len + data 230 | m := make([]byte, 2+len(data)) 231 | 232 | // 默认使用大端序 233 | binary.BigEndian.PutUint16(m, uint16(len(data))) 234 | 235 | copy(m[2:], data) 236 | 237 | // 发送消息 238 | conn.Write(m) 239 | } 240 | ``` 241 | 242 | 执行此测试客户端,游戏服务器输出: 243 | 244 | ``` 245 | 2015/09/25 07:41:03 [debug ] hello leaf 246 | 2015/09/25 07:41:03 [debug ] read message: read tcp 127.0.0.1:3563->127.0.0.1:54599: wsarecv: An existing connection was forcibly closed by the remote host. 247 | ``` 248 | 249 | 测试客户端发送完消息以后就退出了,此时和游戏服务器的连接断开,相应的,游戏服务器输出连接断开的提示日志(第二条日志,日志的具体内容和 Go 语言版本有关)。 250 | 251 | 除了使用 TCP 协议外,还可以选择使用 WebSocket 协议(例如开发 H5 游戏)。Leaf 可以单独使用 TCP 协议或 WebSocket 协议,也可以同时使用两者,换而言之,服务器可以同时接受 TCP 连接和 WebSocket 连接,对开发者而言消息来自 TCP 还是 WebSocket 是完全透明的。现在,我们来编写一个对应上例的使用 WebSocket 协议的客户端: 252 | ```html 253 | 263 | ``` 264 | 265 | 保存上述代码到某 HTML 文件中并使用(任意支持 WebSocket 协议的)浏览器打开。在打开此 HTML 文件前,首先需要配置一下 LeafServer 的 bin/conf/server.json 文件,增加 WebSocket 监听地址(WSAddr): 266 | ```json 267 | { 268 | "LogLevel": "debug", 269 | "LogPath": "", 270 | "TCPAddr": "127.0.0.1:3563", 271 | "WSAddr": "127.0.0.1:3653", 272 | "MaxConnNum": 20000 273 | } 274 | ``` 275 | 276 | 重启游戏服务器后,方可接受 WebSocket 消息: 277 | 278 | ``` 279 | 2015/09/25 07:50:03 [debug ] hello leaf 280 | ``` 281 | 282 | 在 Leaf 中使用 WebSocket 需要注意的一点是:Leaf 总是发送二进制消息而非文本消息。 283 | 284 | ### Leaf 模块详解 285 | 286 | LeafServer 中包含了 3 个模块,它们分别是: 287 | 288 | * gate 模块,负责游戏客户端的接入 289 | * login 模块,负责登录流程 290 | * game 模块,负责游戏主逻辑 291 | 292 | 一般来说(而非强制规定),从代码结构上,一个 Leaf 模块: 293 | 294 | 1. 放置于一个目录中(例如 game 模块放置于 game 目录中) 295 | 2. 模块的具体实现放置于 internal 包中(例如 game 模块的具体实现放置于 game/internal 包中) 296 | 297 | 每个模块下一般有一个 external.go 的文件,顾名思义表示模块对外暴露的接口,这里以 game 模块的 external.go 文件为例: 298 | 299 | ```go 300 | package game 301 | 302 | import ( 303 | "server/game/internal" 304 | ) 305 | 306 | var ( 307 | // 实例化 game 模块 308 | Module = new(internal.Module) 309 | // 暴露 ChanRPC 310 | ChanRPC = internal.ChanRPC 311 | ) 312 | ``` 313 | 314 | 首先,模块会被实例化,这样才能注册到 Leaf 框架中(详见 LeafServer main.go),另外,模块暴露的 ChanRPC 被用于模块间通讯。 315 | 316 | 进入 game 模块的内部(LeafServer game/internal/module.go): 317 | 318 | ```go 319 | package internal 320 | 321 | import ( 322 | "framework/module" 323 | "server/base" 324 | ) 325 | 326 | var ( 327 | skeleton = base.NewSkeleton() 328 | ChanRPC = skeleton.ChanRPCServer 329 | ) 330 | 331 | type Module struct { 332 | *module.Skeleton 333 | } 334 | 335 | func (m *Module) OnInit() { 336 | m.Skeleton = skeleton 337 | } 338 | 339 | func (m *Module) OnDestroy() { 340 | 341 | } 342 | ``` 343 | 344 | 模块中最关键的就是 skeleton(骨架),skeleton 实现了 Module 接口的 Run 方法并提供了: 345 | 346 | * ChanRPC 347 | * goroutine 348 | * 定时器 349 | 350 | ### Leaf ChanRPC 351 | 352 | 由于 Leaf 中,每个模块跑在独立的 goroutine 上,为了模块间方便的相互调用就有了基于 channel 的 RPC 机制。一个 ChanRPC 需要在游戏服务器初始化的时候进行注册(注册过程不是 goroutine 安全的),例如 LeafServer 中 game 模块注册了 NewAgent 和 CloseAgent 两个 ChanRPC: 353 | 354 | ```go 355 | package internal 356 | 357 | import ( 358 | "framework/gate" 359 | ) 360 | 361 | func init() { 362 | skeleton.RegisterChanRPC("NewAgent", rpcNewAgent) 363 | skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent) 364 | } 365 | 366 | func rpcNewAgent(args []interface{}) { 367 | 368 | } 369 | 370 | func rpcCloseAgent(args []interface{}) { 371 | 372 | } 373 | ``` 374 | 375 | 使用 skeleton 来注册 ChanRPC。RegisterChanRPC 的第一个参数是 ChanRPC 的名字,第二个参数是 ChanRPC 的实现。这里的 NewAgent 和 CloseAgent 会被 LeafServer 的 gate 模块在连接建立和连接中断时调用。ChanRPC 的调用方有 3 种调用模式: 376 | 377 | 1. 同步模式,调用并等待 ChanRPC 返回 378 | 2. 异步模式,调用并提供回调函数,回调函数会在 ChanRPC 返回后被调用 379 | 3. Go 模式,调用并立即返回,忽略任何返回值和错误 380 | 381 | gate 模块这样调用 game 模块的 NewAgent ChanRPC(这仅仅是一个示例,实际的代码细节复杂的多): 382 | 383 | ```go 384 | game.ChanRPC.Go("NewAgent", a) 385 | ``` 386 | 387 | 这里调用 NewAgent 并传递参数 a,我们在 rpcNewAgent 的参数 args[0] 中可以取到 a(args[1] 表示第二个参数,以此类推)。 388 | 389 | 更加详细的用法可以参考 [leaf/chanrpc](https://framework/blob/master/chanrpc)。需要注意的是,无论封装多么精巧,跨 goroutine 的调用总不能像直接的函数调用那样简单直接,因此除非必要我们不要构建太多的模块,模块间不要太频繁的交互。模块在 Leaf 中被设计出来最主要是用于划分功能而非利用多核,Leaf 认为在模块内按需使用 goroutine 才是多核利用率问题的解决之道。 390 | 391 | ### Leaf Go 392 | 393 | 善用 goroutine 能够充分利用多核资源,Leaf 提供的 Go 机制解决了原生 goroutine 存在的一些问题: 394 | 395 | * 能够恢复 goroutine 运行过程中的错误 396 | * 游戏服务器会等待所有 goroutine 执行结束后才关闭 397 | * 非常方便的获取 goroutine 执行的结果数据 398 | * 在一些特殊场合保证 goroutine 按创建顺序执行 399 | 400 | 我们来看一个例子(可以在 LeafServer 的模块的 OnInit 方法中测试): 401 | 402 | ```go 403 | log.Debug("1") 404 | 405 | // 定义变量 res 接收结果 406 | var res string 407 | 408 | skeleton.Go(func() { 409 | // 这里使用 Sleep 来模拟一个很慢的操作 410 | time.Sleep(1 * time.Second) 411 | 412 | // 假定得到结果 413 | res = "3" 414 | }, func() { 415 | log.Debug(res) 416 | }) 417 | 418 | log.Debug("2") 419 | ``` 420 | 421 | 上面代码执行结果如下: 422 | 423 | ```go 424 | 2015/08/27 20:37:17 [debug ] 1 425 | 2015/08/27 20:37:17 [debug ] 2 426 | 2015/08/27 20:37:18 [debug ] 3 427 | ``` 428 | 429 | 这里的 Go 方法接收 2 个函数作为参数,第一个函数会被放置在一个新创建的 goroutine 中执行,在其执行完成之后,第二个函数会在当前 goroutine 中被执行。由此,我们可以看到变量 res 同一时刻总是只被一个 goroutine 访问,这就避免了同步机制的使用。Go 的设计使得 CPU 得到充分利用,避免操作阻塞当前 goroutine,同时又无需为共享资源同步而忧心。 430 | 431 | 更加详细的用法可以参考 [leaf/go](https://framework/blob/master/go)。 432 | 433 | ### Leaf timer 434 | 435 | Go 语言标准库提供了定时器的支持: 436 | 437 | ```go 438 | func AfterFunc(d Duration, f func()) *Timer 439 | ``` 440 | 441 | AfterFunc 会等待 d 时长后调用 f 函数,这里的 f 函数将在另外一个 goroutine 中执行。Leaf 提供了一个相同的 AfterFunc 函数,相比之下,f 函数在 AfterFunc 的调用 goroutine 中执行,这样就避免了同步机制的使用: 442 | 443 | ```go 444 | skeleton.AfterFunc(5 * time.Second, func() { 445 | // ... 446 | }) 447 | ``` 448 | 449 | 另外,Leaf timer 还支持 [cron 表达式](https://en.wikipedia.org/wiki/Cron),用于实现诸如“每天 9 点执行”、“每周末 6 点执行”的逻辑。 450 | 451 | 更加详细的用法可以参考 [leaf/timer](https://framework/blob/master/timer)。 452 | 453 | ### Leaf log 454 | 455 | Leaf 的 log 系统支持多种日志级别: 456 | 457 | 1. Debug 日志,非关键日志 458 | 2. Release 日志,关键日志 459 | 3. Error 日志,错误日志 460 | 4. Fatal 日志,致命错误日志 461 | 462 | Debug < Release < Error < Fatal(日志级别高低) 463 | 464 | 在 LeafServer 中,bin/conf/server.json 可以配置日志级别,低于配置的日志级别的日志将不会输出。Fatal 日志比较特殊,每次输出 Fatal 日志之后游戏服务器进程就会结束,通常来说,只在游戏服务器初始化失败时使用 Fatal 日志。 465 | 466 | 更加详细的用法可以参考 [leaf/log](https://framework/blob/master/log)。 467 | 468 | ### Leaf recordfile 469 | 470 | Leaf 的 recordfile 是基于 CSV 格式(范例见[这里](https://framework/blob/master/recordfile/test.txt))。recordfile 用于管理游戏配置数据。在 LeafServer 中使用 recordfile 非常简单: 471 | 472 | 1. 将 CSV 文件放置于 bin/gamedata 目录中 473 | 2. 在 gamedata 模块中调用函数 readRf 读取 CSV 文件 474 | 475 | 范例: 476 | 477 | ```go 478 | // 确保 bin/gamedata 目录中存在 Test.txt 文件 479 | // 文件名必须和此结构体名称相同(大小写敏感) 480 | // 结构体的一个实例映射 recordfile 中的一行 481 | type Test struct { 482 | // 将第一列按 int 类型解析 483 | // "index" 表明在此列上建立唯一索引 484 | Id int "index" 485 | // 将第二列解析为长度为 4 的整型数组 486 | Arr [4]int 487 | // 将第三列解析为字符串 488 | Str string 489 | } 490 | 491 | // 读取 recordfile Test.txt 到内存中 492 | // RfTest 即为 Test.txt 的内存镜像 493 | var RfTest = readRf(Test{}) 494 | 495 | func init() { 496 | // 按索引查找 497 | // 获取 Test.txt 中 Id 为 1 的那一行 498 | r := RfTest.Index(1) 499 | 500 | if r != nil { 501 | row := r.(*Test) 502 | 503 | // 输出此行的所有列的数据 504 | log.Debug("%v %v %v", row.Id, row.Arr, row.Str) 505 | } 506 | } 507 | ``` 508 | 509 | 更加详细的用法可以参考 [leaf/recordfile](https://framework/blob/master/recordfile)。 510 | 511 | 写在最后的话 512 | --------------- 513 | 514 | 本文虽然未能全面描述 Leaf 服务器框架的方方面面,但整体轮廓已经显现。Leaf 尚小,但已经足够构建一个完整游戏服务器。在对开发效率的执着追求上,Leaf 还有很长的路需要走。 -------------------------------------------------------------------------------- /src/framework/chanrpc/chanrpc.go: -------------------------------------------------------------------------------- 1 | package chanrpc 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "framework/conf" 7 | "framework/log" 8 | "runtime" 9 | ) 10 | 11 | // one server per goroutine (goroutine not safe) 12 | // one client per goroutine (goroutine not safe) 13 | type Server struct { 14 | // id -> function 15 | // 16 | // function: 17 | // func(args []interface{}) 18 | // func(args []interface{}) interface{} 19 | // func(args []interface{}) []interface{} 20 | functions map[interface{}]interface{} 21 | ChanCall chan *CallInfo 22 | } 23 | 24 | type CallInfo struct { 25 | f interface{} 26 | args []interface{} 27 | chanRet chan *RetInfo 28 | cb interface{} 29 | } 30 | 31 | type RetInfo struct { 32 | // nil 33 | // interface{} 34 | // []interface{} 35 | ret interface{} 36 | err error 37 | // callback: 38 | // func(err error) 39 | // func(ret interface{}, err error) 40 | // func(ret []interface{}, err error) 41 | cb interface{} 42 | } 43 | 44 | type Client struct { 45 | s *Server 46 | chanSyncRet chan *RetInfo 47 | ChanAsynRet chan *RetInfo 48 | pendingAsynCall int 49 | } 50 | 51 | func NewServer(l int) *Server { 52 | s := new(Server) 53 | s.functions = make(map[interface{}]interface{}) 54 | s.ChanCall = make(chan *CallInfo, l) 55 | return s 56 | } 57 | 58 | func assert(i interface{}) []interface{} { 59 | if i == nil { 60 | return nil 61 | } else { 62 | return i.([]interface{}) 63 | } 64 | } 65 | 66 | // you must call the function before calling Open and Go 67 | func (s *Server) Register(id interface{}, f interface{}) { 68 | switch f.(type) { 69 | case func([]interface{}): 70 | case func([]interface{}) interface{}: 71 | case func([]interface{}) []interface{}: 72 | default: 73 | panic(fmt.Sprintf("function id %v: definition of function is invalid", id)) 74 | } 75 | 76 | if _, ok := s.functions[id]; ok { 77 | panic(fmt.Sprintf("function id %v: already registered", id)) 78 | } 79 | 80 | s.functions[id] = f 81 | } 82 | 83 | func (s *Server) ret(ci *CallInfo, ri *RetInfo) (err error) { 84 | if ci.chanRet == nil { 85 | return 86 | } 87 | 88 | defer func() { 89 | if r := recover(); r != nil { 90 | err = r.(error) 91 | } 92 | }() 93 | 94 | ri.cb = ci.cb 95 | ci.chanRet <- ri 96 | return 97 | } 98 | 99 | func (s *Server) exec(ci *CallInfo) (err error) { 100 | defer func() { 101 | if r := recover(); r != nil { 102 | if conf.LenStackBuf > 0 { 103 | buf := make([]byte, conf.LenStackBuf) 104 | l := runtime.Stack(buf, false) 105 | err = fmt.Errorf("%v: %s", r, buf[:l]) 106 | } else { 107 | err = fmt.Errorf("%v", r) 108 | } 109 | 110 | s.ret(ci, &RetInfo{err: fmt.Errorf("%v", r)}) 111 | } 112 | }() 113 | 114 | // execute 115 | switch ci.f.(type) { 116 | case func([]interface{}): 117 | ci.f.(func([]interface{}))(ci.args) 118 | return s.ret(ci, &RetInfo{}) 119 | case func([]interface{}) interface{}: 120 | ret := ci.f.(func([]interface{}) interface{})(ci.args) 121 | return s.ret(ci, &RetInfo{ret: ret}) 122 | case func([]interface{}) []interface{}: 123 | ret := ci.f.(func([]interface{}) []interface{})(ci.args) 124 | return s.ret(ci, &RetInfo{ret: ret}) 125 | } 126 | 127 | panic("bug") 128 | } 129 | 130 | func (s *Server) Exec(ci *CallInfo) { 131 | err := s.exec(ci) 132 | if err != nil { 133 | log.Error("%v", err) 134 | } 135 | } 136 | 137 | // goroutine safe 138 | func (s *Server) Go(id interface{}, args ...interface{}) { 139 | f := s.functions[id] 140 | if f == nil { 141 | return 142 | } 143 | 144 | defer recover() 145 | 146 | s.ChanCall <- &CallInfo{ 147 | f: f, 148 | args: args, 149 | } 150 | } 151 | 152 | // goroutine safe 153 | func (s *Server) Call0(id interface{}, args ...interface{}) error { 154 | return s.Open(0).Call0(id, args...) 155 | } 156 | 157 | // goroutine safe 158 | func (s *Server) Call1(id interface{}, args ...interface{}) (interface{}, error) { 159 | return s.Open(0).Call1(id, args...) 160 | } 161 | 162 | // goroutine safe 163 | func (s *Server) CallN(id interface{}, args ...interface{}) ([]interface{}, error) { 164 | return s.Open(0).CallN(id, args...) 165 | } 166 | 167 | func (s *Server) Close() { 168 | close(s.ChanCall) 169 | 170 | for ci := range s.ChanCall { 171 | s.ret(ci, &RetInfo{ 172 | err: errors.New("chanrpc server closed"), 173 | }) 174 | } 175 | } 176 | 177 | // goroutine safe 178 | func (s *Server) Open(l int) *Client { 179 | c := NewClient(l) 180 | c.Attach(s) 181 | return c 182 | } 183 | 184 | func NewClient(l int) *Client { 185 | c := new(Client) 186 | c.chanSyncRet = make(chan *RetInfo, 1) 187 | c.ChanAsynRet = make(chan *RetInfo, l) 188 | return c 189 | } 190 | 191 | func (c *Client) Attach(s *Server) { 192 | c.s = s 193 | } 194 | 195 | func (c *Client) call(ci *CallInfo, block bool) (err error) { 196 | defer func() { 197 | if r := recover(); r != nil { 198 | err = r.(error) 199 | } 200 | }() 201 | 202 | if block { 203 | c.s.ChanCall <- ci 204 | } else { 205 | select { 206 | case c.s.ChanCall <- ci: 207 | default: 208 | err = errors.New("chanrpc channel full") 209 | } 210 | } 211 | return 212 | } 213 | 214 | func (c *Client) f(id interface{}, n int) (f interface{}, err error) { 215 | if c.s == nil { 216 | err = errors.New("server not attached") 217 | return 218 | } 219 | 220 | f = c.s.functions[id] 221 | if f == nil { 222 | err = fmt.Errorf("function id %v: function not registered", id) 223 | return 224 | } 225 | 226 | var ok bool 227 | switch n { 228 | case 0: 229 | _, ok = f.(func([]interface{})) 230 | case 1: 231 | _, ok = f.(func([]interface{}) interface{}) 232 | case 2: 233 | _, ok = f.(func([]interface{}) []interface{}) 234 | default: 235 | panic("bug") 236 | } 237 | 238 | if !ok { 239 | err = fmt.Errorf("function id %v: return type mismatch", id) 240 | } 241 | return 242 | } 243 | 244 | func (c *Client) Call0(id interface{}, args ...interface{}) error { 245 | f, err := c.f(id, 0) 246 | if err != nil { 247 | return err 248 | } 249 | 250 | err = c.call(&CallInfo{ 251 | f: f, 252 | args: args, 253 | chanRet: c.chanSyncRet, 254 | }, true) 255 | if err != nil { 256 | return err 257 | } 258 | 259 | ri := <-c.chanSyncRet 260 | return ri.err 261 | } 262 | 263 | func (c *Client) Call1(id interface{}, args ...interface{}) (interface{}, error) { 264 | f, err := c.f(id, 1) 265 | if err != nil { 266 | return nil, err 267 | } 268 | 269 | err = c.call(&CallInfo{ 270 | f: f, 271 | args: args, 272 | chanRet: c.chanSyncRet, 273 | }, true) 274 | if err != nil { 275 | return nil, err 276 | } 277 | 278 | ri := <-c.chanSyncRet 279 | return ri.ret, ri.err 280 | } 281 | 282 | func (c *Client) CallN(id interface{}, args ...interface{}) ([]interface{}, error) { 283 | f, err := c.f(id, 2) 284 | if err != nil { 285 | return nil, err 286 | } 287 | 288 | err = c.call(&CallInfo{ 289 | f: f, 290 | args: args, 291 | chanRet: c.chanSyncRet, 292 | }, true) 293 | if err != nil { 294 | return nil, err 295 | } 296 | 297 | ri := <-c.chanSyncRet 298 | return assert(ri.ret), ri.err 299 | } 300 | 301 | func (c *Client) asynCall(id interface{}, args []interface{}, cb interface{}, n int) { 302 | f, err := c.f(id, n) 303 | if err != nil { 304 | c.ChanAsynRet <- &RetInfo{err: err, cb: cb} 305 | return 306 | } 307 | 308 | err = c.call(&CallInfo{ 309 | f: f, 310 | args: args, 311 | chanRet: c.ChanAsynRet, 312 | cb: cb, 313 | }, false) 314 | if err != nil { 315 | c.ChanAsynRet <- &RetInfo{err: err, cb: cb} 316 | return 317 | } 318 | } 319 | 320 | func (c *Client) AsynCall(id interface{}, _args ...interface{}) { 321 | if len(_args) < 1 { 322 | panic("callback function not found") 323 | } 324 | 325 | args := _args[:len(_args)-1] 326 | cb := _args[len(_args)-1] 327 | 328 | var n int 329 | switch cb.(type) { 330 | case func(error): 331 | n = 0 332 | case func(interface{}, error): 333 | n = 1 334 | case func([]interface{}, error): 335 | n = 2 336 | default: 337 | panic("definition of callback function is invalid") 338 | } 339 | 340 | // too many calls 341 | if c.pendingAsynCall >= cap(c.ChanAsynRet) { 342 | execCb(&RetInfo{err: errors.New("too many calls"), cb: cb}) 343 | return 344 | } 345 | 346 | c.asynCall(id, args, cb, n) 347 | c.pendingAsynCall++ 348 | } 349 | 350 | func execCb(ri *RetInfo) { 351 | defer func() { 352 | if r := recover(); r != nil { 353 | if conf.LenStackBuf > 0 { 354 | buf := make([]byte, conf.LenStackBuf) 355 | l := runtime.Stack(buf, false) 356 | log.Error("%v: %s", r, buf[:l]) 357 | } else { 358 | log.Error("%v", r) 359 | } 360 | } 361 | }() 362 | 363 | // execute 364 | switch ri.cb.(type) { 365 | case func(error): 366 | ri.cb.(func(error))(ri.err) 367 | case func(interface{}, error): 368 | ri.cb.(func(interface{}, error))(ri.ret, ri.err) 369 | case func([]interface{}, error): 370 | ri.cb.(func([]interface{}, error))(assert(ri.ret), ri.err) 371 | default: 372 | panic("bug") 373 | } 374 | return 375 | } 376 | 377 | func (c *Client) Cb(ri *RetInfo) { 378 | c.pendingAsynCall-- 379 | execCb(ri) 380 | } 381 | 382 | func (c *Client) Close() { 383 | for c.pendingAsynCall > 0 { 384 | c.Cb(<-c.ChanAsynRet) 385 | } 386 | } 387 | 388 | func (c *Client) Idle() bool { 389 | return c.pendingAsynCall == 0 390 | } 391 | -------------------------------------------------------------------------------- /src/framework/chanrpc/example_test.go: -------------------------------------------------------------------------------- 1 | package chanrpc_test 2 | 3 | import ( 4 | "fmt" 5 | "framework/chanrpc" 6 | "sync" 7 | ) 8 | 9 | func Example() { 10 | s := chanrpc.NewServer(10) 11 | 12 | var wg sync.WaitGroup 13 | wg.Add(1) 14 | 15 | // goroutine 1 16 | go func() { 17 | s.Register("f0", func(args []interface{}) { 18 | 19 | }) 20 | 21 | s.Register("f1", func(args []interface{}) interface{} { 22 | return 1 23 | }) 24 | 25 | s.Register("fn", func(args []interface{}) []interface{} { 26 | return []interface{}{1, 2, 3} 27 | }) 28 | 29 | s.Register("add", func(args []interface{}) interface{} { 30 | n1 := args[0].(int) 31 | n2 := args[1].(int) 32 | return n1 + n2 33 | }) 34 | 35 | wg.Done() 36 | 37 | for { 38 | err := s.Exec(<-s.ChanCall) 39 | if err != nil { 40 | fmt.Println(err) 41 | } 42 | } 43 | }() 44 | 45 | wg.Wait() 46 | wg.Add(1) 47 | 48 | // goroutine 2 49 | go func() { 50 | c := s.Open(10) 51 | 52 | // sync 53 | err := c.Call0("f0") 54 | if err != nil { 55 | fmt.Println(err) 56 | } 57 | 58 | r1, err := c.Call1("f1") 59 | if err != nil { 60 | fmt.Println(err) 61 | } else { 62 | fmt.Println(r1) 63 | } 64 | 65 | rn, err := c.CallN("fn") 66 | if err != nil { 67 | fmt.Println(err) 68 | } else { 69 | fmt.Println(rn[0], rn[1], rn[2]) 70 | } 71 | 72 | ra, err := c.Call1("add", 1, 2) 73 | if err != nil { 74 | fmt.Println(err) 75 | } else { 76 | fmt.Println(ra) 77 | } 78 | 79 | // asyn 80 | c.AsynCall("f0", func(err error) { 81 | if err != nil { 82 | fmt.Println(err) 83 | } 84 | }) 85 | 86 | c.AsynCall("f1", func(ret interface{}, err error) { 87 | if err != nil { 88 | fmt.Println(err) 89 | } else { 90 | fmt.Println(ret) 91 | } 92 | }) 93 | 94 | c.AsynCall("fn", func(ret []interface{}, err error) { 95 | if err != nil { 96 | fmt.Println(err) 97 | } else { 98 | fmt.Println(ret[0], ret[1], ret[2]) 99 | } 100 | }) 101 | 102 | c.AsynCall("add", 1, 2, func(ret interface{}, err error) { 103 | if err != nil { 104 | fmt.Println(err) 105 | } else { 106 | fmt.Println(ret) 107 | } 108 | }) 109 | 110 | c.Cb(<-c.ChanAsynRet) 111 | c.Cb(<-c.ChanAsynRet) 112 | c.Cb(<-c.ChanAsynRet) 113 | c.Cb(<-c.ChanAsynRet) 114 | 115 | // go 116 | s.Go("f0") 117 | 118 | wg.Done() 119 | }() 120 | 121 | wg.Wait() 122 | 123 | // Output: 124 | // 1 125 | // 1 2 3 126 | // 3 127 | // 1 128 | // 1 2 3 129 | // 3 130 | } 131 | -------------------------------------------------------------------------------- /src/framework/cluster/agent.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | type Agent interface { 4 | WriteMsg(data interface{}) 5 | Close() 6 | Destroy() 7 | UserData() interface{} 8 | SetUserData(data interface{}) 9 | } 10 | -------------------------------------------------------------------------------- /src/framework/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "framework/chanrpc" 5 | "framework/conf" 6 | "framework/log" 7 | "framework/network" 8 | "math" 9 | "time" 10 | ) 11 | 12 | //Client 集群通信客户端 13 | type Client struct { 14 | id int32 //通信对端的唯一标识 15 | tcpclient network.TCPClient //TCP Client结构 16 | } 17 | 18 | //Cluster 集群通信管理类 19 | type Cluster struct { 20 | MaxMsgLen uint32 21 | AgentChanRPC *chanrpc.Server 22 | 23 | server *network.TCPServer 24 | clients []*Client 25 | } 26 | 27 | //Run 运行函数 28 | func (cluster *Cluster) Run(closeSig chan bool) { 29 | if conf.SvrBase.ListenAddr != "" { 30 | cluster.server = new(network.TCPServer) 31 | cluster.server.Addr = conf.SvrBase.ListenAddr 32 | cluster.server.MaxConnNum = int(math.MaxInt32) 33 | cluster.server.PendingWriteNum = conf.SvrBase.PendingWriteNum 34 | cluster.server.LenMsgLen = 2 35 | cluster.server.MaxMsgLen = cluster.MaxMsgLen 36 | cluster.server.NewAgent = func(conn *network.TCPConn) network.Agent { 37 | a := new(agent) 38 | a.conn = conn 39 | a.cluster = cluster 40 | 41 | if a.cluster.AgentChanRPC != nil { 42 | //对端ID不确定,传0 43 | var id int32 44 | a.cluster.AgentChanRPC.Go("NewClusterAgent", a, id) 45 | } 46 | return a 47 | } 48 | 49 | cluster.server.Start() 50 | } 51 | 52 | for _, addr := range conf.SvrBase.ConnAddrs { 53 | client := new(Client) 54 | client.id = addr.ID 55 | client.tcpclient.Addr = addr.ConnAddr 56 | client.tcpclient.ConnNum = 1 57 | client.tcpclient.ConnectInterval = 3 * time.Second 58 | client.tcpclient.PendingWriteNum = conf.SvrBase.PendingWriteNum 59 | client.tcpclient.LenMsgLen = 2 60 | client.tcpclient.MaxMsgLen = cluster.MaxMsgLen 61 | client.tcpclient.NewAgent = func(conn *network.TCPConn) network.Agent { 62 | a := new(agent) 63 | a.conn = conn 64 | a.cluster = cluster 65 | 66 | if a.cluster.AgentChanRPC != nil { 67 | //对端ID确定 68 | a.cluster.AgentChanRPC.Go("NewClusterAgent", a, client.id) 69 | } 70 | return a 71 | } 72 | 73 | client.tcpclient.Start() 74 | cluster.clients = append(cluster.clients, client) 75 | } 76 | 77 | <-closeSig 78 | 79 | //关闭Cluster服务 80 | if cluster.server != nil { 81 | cluster.server.Close() 82 | } 83 | 84 | //关闭Cluster客户端 85 | for _, client := range cluster.clients { 86 | client.tcpclient.Close() 87 | } 88 | } 89 | 90 | //OnDestroy 销毁 91 | func (cluster *Cluster) OnDestroy() {} 92 | 93 | type agent struct { 94 | cluster *Cluster 95 | conn *network.TCPConn 96 | userData interface{} 97 | } 98 | 99 | func (a *agent) Run() { 100 | for { 101 | data, err := a.conn.ReadMsg() 102 | if err != nil { 103 | log.Debug("read message failed, err %v\n", err) 104 | break 105 | } 106 | 107 | //路由给其他模块处理 108 | if a.cluster.AgentChanRPC != nil { 109 | a.cluster.AgentChanRPC.Go("OnClusterMsg", data, a) 110 | } 111 | } 112 | } 113 | 114 | func (a *agent) OnClose() { 115 | if a.cluster.AgentChanRPC != nil { 116 | err := a.cluster.AgentChanRPC.Call0("CloseClusterAgent", a) 117 | if err != nil { 118 | log.Error("chanrpc error: %v", err) 119 | } 120 | } 121 | } 122 | 123 | func (a *agent) WriteMsg(data interface{}) { 124 | err := a.conn.WriteMsg([][]byte{data.([]byte)}...) 125 | if err != nil { 126 | log.Error("write message to gate error: %v", err) 127 | } 128 | } 129 | 130 | func (a *agent) Close() { 131 | a.conn.Close() 132 | } 133 | 134 | func (a *agent) Destroy() { 135 | a.conn.Destroy() 136 | } 137 | 138 | func (a *agent) UserData() interface{} { 139 | return a.userData 140 | } 141 | 142 | func (a *agent) SetUserData(data interface{}) { 143 | a.userData = data 144 | } 145 | -------------------------------------------------------------------------------- /src/framework/conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | //AddrInfo 集群通信连接对端信息 4 | type AddrInfo struct { 5 | ID int32 //通信对端的唯一标识 6 | ConnAddr string //通信对端的地址信息 7 | } 8 | 9 | //BaseConf 基础配置 10 | type BaseConf struct { 11 | // 日志 12 | LogLevel string 13 | LogPath string 14 | 15 | //服务ID 16 | ServerID int32 17 | 18 | //控制台 19 | ConsolePort int 20 | ConsolePrompt string 21 | ProfilePath string 22 | 23 | //Cluster 24 | ListenAddr string 25 | ConnAddrs []AddrInfo 26 | PendingWriteNum int 27 | } 28 | 29 | var ( 30 | LenStackBuf = 4096 31 | SvrBase BaseConf 32 | ) 33 | -------------------------------------------------------------------------------- /src/framework/console/command.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | import ( 4 | "fmt" 5 | "framework/chanrpc" 6 | "framework/conf" 7 | "framework/log" 8 | "os" 9 | "path" 10 | "runtime/pprof" 11 | "time" 12 | ) 13 | 14 | var commands = []Command{ 15 | new(CommandHelp), 16 | new(CommandCPUProf), 17 | new(CommandProf), 18 | } 19 | 20 | type Command interface { 21 | // must goroutine safe 22 | name() string 23 | // must goroutine safe 24 | help() string 25 | // must goroutine safe 26 | run(args []string) string 27 | } 28 | 29 | type ExternalCommand struct { 30 | _name string 31 | _help string 32 | server *chanrpc.Server 33 | } 34 | 35 | func (c *ExternalCommand) name() string { 36 | return c._name 37 | } 38 | 39 | func (c *ExternalCommand) help() string { 40 | return c._help 41 | } 42 | 43 | func (c *ExternalCommand) run(_args []string) string { 44 | args := make([]interface{}, len(_args)) 45 | for i, v := range _args { 46 | args[i] = v 47 | } 48 | 49 | ret, err := c.server.Call1(c._name, args...) 50 | if err != nil { 51 | return err.Error() 52 | } 53 | output, ok := ret.(string) 54 | if !ok { 55 | return "invalid output type" 56 | } 57 | 58 | return output 59 | } 60 | 61 | // you must call the function before calling console.Init 62 | // goroutine not safe 63 | func Register(name string, help string, f interface{}, server *chanrpc.Server) { 64 | for _, c := range commands { 65 | if c.name() == name { 66 | log.Fatal("command %v is already registered", name) 67 | } 68 | } 69 | 70 | server.Register(name, f) 71 | 72 | c := new(ExternalCommand) 73 | c._name = name 74 | c._help = help 75 | c.server = server 76 | commands = append(commands, c) 77 | } 78 | 79 | // help 80 | type CommandHelp struct{} 81 | 82 | func (c *CommandHelp) name() string { 83 | return "help" 84 | } 85 | 86 | func (c *CommandHelp) help() string { 87 | return "this help text" 88 | } 89 | 90 | func (c *CommandHelp) run([]string) string { 91 | output := "Commands:\r\n" 92 | for _, c := range commands { 93 | output += c.name() + " - " + c.help() + "\r\n" 94 | } 95 | output += "quit - exit console" 96 | 97 | return output 98 | } 99 | 100 | // cpuprof 101 | type CommandCPUProf struct{} 102 | 103 | func (c *CommandCPUProf) name() string { 104 | return "cpuprof" 105 | } 106 | 107 | func (c *CommandCPUProf) help() string { 108 | return "CPU profiling for the current process" 109 | } 110 | 111 | func (c *CommandCPUProf) usage() string { 112 | return "cpuprof writes runtime profiling data in the format expected by \r\n" + 113 | "the pprof visualization tool\r\n\r\n" + 114 | "Usage: cpuprof start|stop\r\n" + 115 | " start - enables CPU profiling\r\n" + 116 | " stop - stops the current CPU profile" 117 | } 118 | 119 | func (c *CommandCPUProf) run(args []string) string { 120 | if len(args) == 0 { 121 | return c.usage() 122 | } 123 | 124 | switch args[0] { 125 | case "start": 126 | fn := profileName() + ".cpuprof" 127 | f, err := os.Create(fn) 128 | if err != nil { 129 | return err.Error() 130 | } 131 | err = pprof.StartCPUProfile(f) 132 | if err != nil { 133 | f.Close() 134 | return err.Error() 135 | } 136 | return fn 137 | case "stop": 138 | pprof.StopCPUProfile() 139 | return "" 140 | default: 141 | return c.usage() 142 | } 143 | } 144 | 145 | func profileName() string { 146 | now := time.Now() 147 | return path.Join(conf.SvrBase.ProfilePath, 148 | fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d", 149 | now.Year(), 150 | now.Month(), 151 | now.Day(), 152 | now.Hour(), 153 | now.Minute(), 154 | now.Second())) 155 | } 156 | 157 | // prof 158 | type CommandProf struct{} 159 | 160 | func (c *CommandProf) name() string { 161 | return "prof" 162 | } 163 | 164 | func (c *CommandProf) help() string { 165 | return "writes a pprof-formatted snapshot" 166 | } 167 | 168 | func (c *CommandProf) usage() string { 169 | return "prof writes runtime profiling data in the format expected by \r\n" + 170 | "the pprof visualization tool\r\n\r\n" + 171 | "Usage: prof goroutine|heap|thread|block\r\n" + 172 | " goroutine - stack traces of all current goroutines\r\n" + 173 | " heap - a sampling of all heap allocations\r\n" + 174 | " thread - stack traces that led to the creation of new OS threads\r\n" + 175 | " block - stack traces that led to blocking on synchronization primitives" 176 | } 177 | 178 | func (c *CommandProf) run(args []string) string { 179 | if len(args) == 0 { 180 | return c.usage() 181 | } 182 | 183 | var ( 184 | p *pprof.Profile 185 | fn string 186 | ) 187 | switch args[0] { 188 | case "goroutine": 189 | p = pprof.Lookup("goroutine") 190 | fn = profileName() + ".gprof" 191 | case "heap": 192 | p = pprof.Lookup("heap") 193 | fn = profileName() + ".hprof" 194 | case "thread": 195 | p = pprof.Lookup("threadcreate") 196 | fn = profileName() + ".tprof" 197 | case "block": 198 | p = pprof.Lookup("block") 199 | fn = profileName() + ".bprof" 200 | default: 201 | return c.usage() 202 | } 203 | 204 | f, err := os.Create(fn) 205 | if err != nil { 206 | return err.Error() 207 | } 208 | defer f.Close() 209 | err = p.WriteTo(f, 0) 210 | if err != nil { 211 | return err.Error() 212 | } 213 | 214 | return fn 215 | } 216 | -------------------------------------------------------------------------------- /src/framework/console/console.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | import ( 4 | "bufio" 5 | "framework/conf" 6 | "framework/network" 7 | "math" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var server *network.TCPServer 13 | 14 | func Init() { 15 | if conf.SvrBase.ConsolePort == 0 { 16 | return 17 | } 18 | 19 | server = new(network.TCPServer) 20 | server.Addr = "localhost:" + strconv.Itoa(conf.SvrBase.ConsolePort) 21 | server.MaxConnNum = int(math.MaxInt32) 22 | server.PendingWriteNum = 100 23 | server.NewAgent = newAgent 24 | 25 | server.Start() 26 | } 27 | 28 | func Destroy() { 29 | if server != nil { 30 | server.Close() 31 | } 32 | } 33 | 34 | type Agent struct { 35 | conn *network.TCPConn 36 | reader *bufio.Reader 37 | } 38 | 39 | func newAgent(conn *network.TCPConn) network.Agent { 40 | a := new(Agent) 41 | a.conn = conn 42 | a.reader = bufio.NewReader(conn) 43 | return a 44 | } 45 | 46 | func (a *Agent) Run() { 47 | for { 48 | if conf.SvrBase.ConsolePrompt != "" { 49 | a.conn.Write([]byte(conf.SvrBase.ConsolePrompt)) 50 | } 51 | 52 | line, err := a.reader.ReadString('\n') 53 | if err != nil { 54 | break 55 | } 56 | line = strings.TrimSuffix(line[:len(line)-1], "\r") 57 | 58 | args := strings.Fields(line) 59 | if len(args) == 0 { 60 | continue 61 | } 62 | if args[0] == "quit" { 63 | break 64 | } 65 | var c Command 66 | for _, _c := range commands { 67 | if _c.name() == args[0] { 68 | c = _c 69 | break 70 | } 71 | } 72 | if c == nil { 73 | a.conn.Write([]byte("command not found, try `help` for help\r\n")) 74 | continue 75 | } 76 | output := c.run(args[1:]) 77 | if output != "" { 78 | a.conn.Write([]byte(output + "\r\n")) 79 | } 80 | } 81 | } 82 | 83 | func (a *Agent) OnClose() {} 84 | -------------------------------------------------------------------------------- /src/framework/db/mongodb/example_test.go: -------------------------------------------------------------------------------- 1 | package mongodb_test 2 | 3 | import ( 4 | "fmt" 5 | "framework/db/mongodb" 6 | "gopkg.in/mgo.v2" 7 | ) 8 | 9 | func Example() { 10 | c, err := mongodb.Dial("localhost", 10) 11 | if err != nil { 12 | fmt.Println(err) 13 | return 14 | } 15 | defer c.Close() 16 | 17 | // session 18 | s := c.Ref() 19 | defer c.UnRef(s) 20 | err = s.DB("test").C("counters").RemoveId("test") 21 | if err != nil && err != mgo.ErrNotFound { 22 | fmt.Println(err) 23 | return 24 | } 25 | 26 | // auto increment 27 | err = c.EnsureCounter("test", "counters", "test") 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | for i := 0; i < 3; i++ { 33 | id, err := c.NextSeq("test", "counters", "test") 34 | if err != nil { 35 | fmt.Println(err) 36 | return 37 | } 38 | fmt.Println(id) 39 | } 40 | 41 | // index 42 | c.EnsureUniqueIndex("test", "counters", []string{"key1"}) 43 | 44 | // Output: 45 | // 1 46 | // 2 47 | // 3 48 | } 49 | -------------------------------------------------------------------------------- /src/framework/db/mongodb/mongodb.go: -------------------------------------------------------------------------------- 1 | package mongodb 2 | 3 | import ( 4 | "container/heap" 5 | "framework/log" 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // session 13 | type Session struct { 14 | *mgo.Session 15 | ref int 16 | index int 17 | } 18 | 19 | // session heap 20 | type SessionHeap []*Session 21 | 22 | func (h SessionHeap) Len() int { 23 | return len(h) 24 | } 25 | 26 | func (h SessionHeap) Less(i, j int) bool { 27 | return h[i].ref < h[j].ref 28 | } 29 | 30 | func (h SessionHeap) Swap(i, j int) { 31 | h[i], h[j] = h[j], h[i] 32 | h[i].index = i 33 | h[j].index = j 34 | } 35 | 36 | func (h *SessionHeap) Push(s interface{}) { 37 | s.(*Session).index = len(*h) 38 | *h = append(*h, s.(*Session)) 39 | } 40 | 41 | func (h *SessionHeap) Pop() interface{} { 42 | l := len(*h) 43 | s := (*h)[l-1] 44 | s.index = -1 45 | *h = (*h)[:l-1] 46 | return s 47 | } 48 | 49 | type DialContext struct { 50 | sync.Mutex 51 | sessions SessionHeap 52 | } 53 | 54 | // goroutine safe 55 | func Dial(url string, sessionNum int) (*DialContext, error) { 56 | c, err := DialWithTimeout(url, sessionNum, 10*time.Second, 5*time.Minute) 57 | return c, err 58 | } 59 | 60 | // goroutine safe 61 | func DialWithTimeout(url string, sessionNum int, dialTimeout time.Duration, timeout time.Duration) (*DialContext, error) { 62 | if sessionNum <= 0 { 63 | sessionNum = 100 64 | log.Release("invalid sessionNum, reset to %v", sessionNum) 65 | } 66 | 67 | s, err := mgo.DialWithTimeout(url, dialTimeout) 68 | if err != nil { 69 | return nil, err 70 | } 71 | s.SetSyncTimeout(timeout) 72 | s.SetSocketTimeout(timeout) 73 | 74 | c := new(DialContext) 75 | 76 | // sessions 77 | c.sessions = make(SessionHeap, sessionNum) 78 | c.sessions[0] = &Session{s, 0, 0} 79 | for i := 1; i < sessionNum; i++ { 80 | c.sessions[i] = &Session{s.New(), 0, i} 81 | } 82 | heap.Init(&c.sessions) 83 | 84 | return c, nil 85 | } 86 | 87 | // goroutine safe 88 | func (c *DialContext) Close() { 89 | c.Lock() 90 | for _, s := range c.sessions { 91 | s.Close() 92 | if s.ref != 0 { 93 | log.Error("session ref = %v", s.ref) 94 | } 95 | } 96 | c.Unlock() 97 | } 98 | 99 | // goroutine safe 100 | func (c *DialContext) Ref() *Session { 101 | c.Lock() 102 | s := c.sessions[0] 103 | if s.ref == 0 { 104 | s.Refresh() 105 | } 106 | s.ref++ 107 | heap.Fix(&c.sessions, 0) 108 | c.Unlock() 109 | 110 | return s 111 | } 112 | 113 | // goroutine safe 114 | func (c *DialContext) UnRef(s *Session) { 115 | c.Lock() 116 | s.ref-- 117 | heap.Fix(&c.sessions, s.index) 118 | c.Unlock() 119 | } 120 | 121 | // goroutine safe 122 | func (c *DialContext) EnsureCounter(db string, collection string, id string) error { 123 | s := c.Ref() 124 | defer c.UnRef(s) 125 | 126 | err := s.DB(db).C(collection).Insert(bson.M{ 127 | "_id": id, 128 | "seq": 0, 129 | }) 130 | if mgo.IsDup(err) { 131 | return nil 132 | } else { 133 | return err 134 | } 135 | } 136 | 137 | // goroutine safe 138 | func (c *DialContext) NextSeq(db string, collection string, id string) (int, error) { 139 | s := c.Ref() 140 | defer c.UnRef(s) 141 | 142 | var res struct { 143 | Seq int 144 | } 145 | _, err := s.DB(db).C(collection).FindId(id).Apply(mgo.Change{ 146 | Update: bson.M{"$inc": bson.M{"seq": 1}}, 147 | ReturnNew: true, 148 | }, &res) 149 | 150 | return res.Seq, err 151 | } 152 | 153 | // goroutine safe 154 | func (c *DialContext) EnsureIndex(db string, collection string, key []string) error { 155 | s := c.Ref() 156 | defer c.UnRef(s) 157 | 158 | return s.DB(db).C(collection).EnsureIndex(mgo.Index{ 159 | Key: key, 160 | Unique: false, 161 | Sparse: true, 162 | }) 163 | } 164 | 165 | // goroutine safe 166 | func (c *DialContext) EnsureUniqueIndex(db string, collection string, key []string) error { 167 | s := c.Ref() 168 | defer c.UnRef(s) 169 | 170 | return s.DB(db).C(collection).EnsureIndex(mgo.Index{ 171 | Key: key, 172 | Unique: true, 173 | Sparse: true, 174 | }) 175 | } 176 | -------------------------------------------------------------------------------- /src/framework/gate/agent.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | type Agent interface { 4 | WriteMsg(data interface{}) 5 | Close() 6 | Destroy() 7 | UserData() interface{} 8 | SetUserData(data interface{}) 9 | } 10 | -------------------------------------------------------------------------------- /src/framework/gate/gate.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "time" 5 | 6 | "framework/chanrpc" 7 | "framework/log" 8 | "framework/network" 9 | ) 10 | 11 | //Gate中不再解析消息,所有网络消息都发送到gate中进行处理 12 | 13 | type Gate struct { 14 | MaxConnNum int 15 | PendingWriteNum int 16 | MaxMsgLen uint32 17 | AgentChanRPC *chanrpc.Server 18 | 19 | // websocket 20 | WSAddr string 21 | HTTPTimeout time.Duration 22 | 23 | // tcp 24 | TCPAddr string 25 | LenMsgLen int 26 | LittleEndian bool 27 | } 28 | 29 | func (gate *Gate) Run(closeSig chan bool) { 30 | var wsServer *network.WSServer 31 | if gate.WSAddr != "" { 32 | wsServer = new(network.WSServer) 33 | wsServer.Addr = gate.WSAddr 34 | wsServer.MaxConnNum = gate.MaxConnNum 35 | wsServer.PendingWriteNum = gate.PendingWriteNum 36 | wsServer.MaxMsgLen = gate.MaxMsgLen 37 | wsServer.HTTPTimeout = gate.HTTPTimeout 38 | wsServer.NewAgent = func(conn *network.WSConn) network.Agent { 39 | a := &agent{conn: conn, gate: gate} 40 | if gate.AgentChanRPC != nil { 41 | gate.AgentChanRPC.Go("NewGateAgent", a) 42 | } 43 | return a 44 | } 45 | } 46 | 47 | var tcpServer *network.TCPServer 48 | if gate.TCPAddr != "" { 49 | tcpServer = new(network.TCPServer) 50 | tcpServer.Addr = gate.TCPAddr 51 | tcpServer.MaxConnNum = gate.MaxConnNum 52 | tcpServer.PendingWriteNum = gate.PendingWriteNum 53 | tcpServer.LenMsgLen = gate.LenMsgLen 54 | tcpServer.MaxMsgLen = gate.MaxMsgLen 55 | tcpServer.LittleEndian = gate.LittleEndian 56 | tcpServer.NewAgent = func(conn *network.TCPConn) network.Agent { 57 | a := &agent{conn: conn, gate: gate} 58 | if gate.AgentChanRPC != nil { 59 | gate.AgentChanRPC.Go("NewGateAgent", a) 60 | } 61 | return a 62 | } 63 | } 64 | 65 | if wsServer != nil { 66 | wsServer.Start() 67 | } 68 | if tcpServer != nil { 69 | tcpServer.Start() 70 | } 71 | <-closeSig 72 | if wsServer != nil { 73 | wsServer.Close() 74 | } 75 | if tcpServer != nil { 76 | tcpServer.Close() 77 | } 78 | } 79 | 80 | func (gate *Gate) OnDestroy() {} 81 | 82 | type agent struct { 83 | conn network.Conn 84 | gate *Gate 85 | userData interface{} 86 | } 87 | 88 | func (a *agent) Run() { 89 | for { 90 | data, err := a.conn.ReadMsg() 91 | if err != nil { 92 | log.Debug("read message: %v", err) 93 | break 94 | } 95 | 96 | //直接路由给game模块处理 97 | if a.gate.AgentChanRPC != nil { 98 | a.gate.AgentChanRPC.Go("OnGateMsg", data, a) 99 | } 100 | 101 | //Gate模块只路由,不解析消息 102 | /* 103 | if a.gate.Processor != nil { 104 | msg, err := a.gate.Processor.Unmarshal(data) 105 | if err != nil { 106 | log.Debug("unmarshal message error: %v", err) 107 | break 108 | } 109 | err = a.gate.Processor.Route(msg, a) 110 | if err != nil { 111 | log.Debug("route message error: %v", err) 112 | break 113 | } 114 | } 115 | */ 116 | } 117 | } 118 | 119 | func (a *agent) OnClose() { 120 | if a.gate.AgentChanRPC != nil { 121 | err := a.gate.AgentChanRPC.Call0("NewGateAgent", a) 122 | if err != nil { 123 | log.Error("chanrpc error: %v", err) 124 | } 125 | } 126 | } 127 | 128 | func (a *agent) WriteMsg(data interface{}) { 129 | err := a.conn.WriteMsg([][]byte{data.([]byte)}...) 130 | if err != nil { 131 | log.Error("write message to gate error: %v", err) 132 | } 133 | 134 | //Gate现在只负责发送消息,而不负责消息的打解包 135 | /* 136 | if a.gate.Processor != nil { 137 | data, err := a.gate.Processor.Marshal(msg) 138 | if err != nil { 139 | log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err) 140 | return 141 | } 142 | err = a.conn.WriteMsg(data...) 143 | if err != nil { 144 | log.Error("write message %v error: %v", reflect.TypeOf(msg), err) 145 | } 146 | } 147 | */ 148 | } 149 | 150 | func (a *agent) Close() { 151 | a.conn.Close() 152 | } 153 | 154 | func (a *agent) Destroy() { 155 | a.conn.Destroy() 156 | } 157 | 158 | func (a *agent) UserData() interface{} { 159 | return a.userData 160 | } 161 | 162 | func (a *agent) SetUserData(data interface{}) { 163 | a.userData = data 164 | } 165 | -------------------------------------------------------------------------------- /src/framework/go/example_test.go: -------------------------------------------------------------------------------- 1 | package g_test 2 | 3 | import ( 4 | "fmt" 5 | "framework/go" 6 | "time" 7 | ) 8 | 9 | func Example() { 10 | d := g.New(10) 11 | 12 | // go 1 13 | var res int 14 | d.Go(func() { 15 | fmt.Println("1 + 1 = ?") 16 | res = 1 + 1 17 | }, func() { 18 | fmt.Println(res) 19 | }) 20 | 21 | d.Cb(<-d.ChanCb) 22 | 23 | // go 2 24 | d.Go(func() { 25 | fmt.Print("My name is ") 26 | }, func() { 27 | fmt.Println("Leaf") 28 | }) 29 | 30 | d.Close() 31 | 32 | // Output: 33 | // 1 + 1 = ? 34 | // 2 35 | // My name is Leaf 36 | } 37 | 38 | func ExampleLinearContext() { 39 | d := g.New(10) 40 | 41 | // parallel 42 | d.Go(func() { 43 | time.Sleep(time.Second / 2) 44 | fmt.Println("1") 45 | }, nil) 46 | d.Go(func() { 47 | fmt.Println("2") 48 | }, nil) 49 | 50 | d.Cb(<-d.ChanCb) 51 | d.Cb(<-d.ChanCb) 52 | 53 | // linear 54 | c := d.NewLinearContext() 55 | c.Go(func() { 56 | time.Sleep(time.Second / 2) 57 | fmt.Println("1") 58 | }, nil) 59 | c.Go(func() { 60 | fmt.Println("2") 61 | }, nil) 62 | 63 | d.Close() 64 | 65 | // Output: 66 | // 2 67 | // 1 68 | // 1 69 | // 2 70 | } 71 | -------------------------------------------------------------------------------- /src/framework/go/go.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "container/list" 5 | "framework/conf" 6 | "framework/log" 7 | "runtime" 8 | "sync" 9 | ) 10 | 11 | // one Go per goroutine (goroutine not safe) 12 | type Go struct { 13 | ChanCb chan func() 14 | pendingGo int 15 | } 16 | 17 | type LinearGo struct { 18 | f func() 19 | cb func() 20 | } 21 | 22 | type LinearContext struct { 23 | g *Go 24 | linearGo *list.List 25 | mutexLinearGo sync.Mutex 26 | mutexExecution sync.Mutex 27 | } 28 | 29 | func New(l int) *Go { 30 | g := new(Go) 31 | g.ChanCb = make(chan func(), l) 32 | return g 33 | } 34 | 35 | func (g *Go) Go(f func(), cb func()) { 36 | g.pendingGo++ 37 | 38 | go func() { 39 | defer func() { 40 | g.ChanCb <- cb 41 | if r := recover(); r != nil { 42 | if conf.LenStackBuf > 0 { 43 | buf := make([]byte, conf.LenStackBuf) 44 | l := runtime.Stack(buf, false) 45 | log.Error("%v: %s", r, buf[:l]) 46 | } else { 47 | log.Error("%v", r) 48 | } 49 | } 50 | }() 51 | 52 | f() 53 | }() 54 | } 55 | 56 | func (g *Go) Cb(cb func()) { 57 | defer func() { 58 | g.pendingGo-- 59 | if r := recover(); r != nil { 60 | if conf.LenStackBuf > 0 { 61 | buf := make([]byte, conf.LenStackBuf) 62 | l := runtime.Stack(buf, false) 63 | log.Error("%v: %s", r, buf[:l]) 64 | } else { 65 | log.Error("%v", r) 66 | } 67 | } 68 | }() 69 | 70 | if cb != nil { 71 | cb() 72 | } 73 | } 74 | 75 | func (g *Go) Close() { 76 | for g.pendingGo > 0 { 77 | g.Cb(<-g.ChanCb) 78 | } 79 | } 80 | 81 | func (g *Go) Idle() bool { 82 | return g.pendingGo == 0 83 | } 84 | 85 | func (g *Go) NewLinearContext() *LinearContext { 86 | c := new(LinearContext) 87 | c.g = g 88 | c.linearGo = list.New() 89 | return c 90 | } 91 | 92 | func (c *LinearContext) Go(f func(), cb func()) { 93 | c.g.pendingGo++ 94 | 95 | c.mutexLinearGo.Lock() 96 | c.linearGo.PushBack(&LinearGo{f: f, cb: cb}) 97 | c.mutexLinearGo.Unlock() 98 | 99 | go func() { 100 | c.mutexExecution.Lock() 101 | defer c.mutexExecution.Unlock() 102 | 103 | c.mutexLinearGo.Lock() 104 | e := c.linearGo.Remove(c.linearGo.Front()).(*LinearGo) 105 | c.mutexLinearGo.Unlock() 106 | 107 | defer func() { 108 | c.g.ChanCb <- e.cb 109 | if r := recover(); r != nil { 110 | if conf.LenStackBuf > 0 { 111 | buf := make([]byte, conf.LenStackBuf) 112 | l := runtime.Stack(buf, false) 113 | log.Error("%v: %s", r, buf[:l]) 114 | } else { 115 | log.Error("%v", r) 116 | } 117 | } 118 | }() 119 | 120 | e.f() 121 | }() 122 | } 123 | -------------------------------------------------------------------------------- /src/framework/leaf.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import ( 4 | "framework/conf" 5 | "framework/console" 6 | "framework/log" 7 | "framework/module" 8 | "os" 9 | "os/signal" 10 | ) 11 | 12 | func Run(mods ...module.Module) { 13 | // logger 14 | if conf.SvrBase.LogLevel != "" { 15 | logger, err := log.New(conf.SvrBase.LogLevel, conf.SvrBase.LogPath) 16 | if err != nil { 17 | panic(err) 18 | } 19 | log.Export(logger) 20 | defer logger.Close() 21 | } 22 | 23 | log.Release("Leaf %v starting up", version) 24 | 25 | // module 26 | for i := 0; i < len(mods); i++ { 27 | module.Register(mods[i]) 28 | } 29 | module.Init() 30 | 31 | // console 32 | console.Init() 33 | 34 | // close 35 | c := make(chan os.Signal, 1) 36 | signal.Notify(c, os.Interrupt, os.Kill) 37 | sig := <-c 38 | log.Release("Leaf closing down (signal: %v)", sig) 39 | console.Destroy() 40 | module.Destroy() 41 | } 42 | -------------------------------------------------------------------------------- /src/framework/log/example_test.go: -------------------------------------------------------------------------------- 1 | package log_test 2 | 3 | import ( 4 | "framework/log" 5 | ) 6 | 7 | func Example() { 8 | name := "Leaf" 9 | 10 | log.Debug("My name is %v", name) 11 | log.Release("My name is %v", name) 12 | log.Error("My name is %v", name) 13 | // log.Fatal("My name is %v", name) 14 | 15 | logger, err := log.New("release", "") 16 | if err != nil { 17 | return 18 | } 19 | defer logger.Close() 20 | 21 | logger.Debug("will not print") 22 | logger.Release("My name is %v", name) 23 | 24 | log.Export(logger) 25 | 26 | log.Debug("will not print") 27 | log.Release("My name is %v", name) 28 | } 29 | -------------------------------------------------------------------------------- /src/framework/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // levels 14 | const ( 15 | debugLevel = 0 16 | releaseLevel = 1 17 | errorLevel = 2 18 | fatalLevel = 3 19 | ) 20 | 21 | const ( 22 | printDebugLevel = "[debug ] " 23 | printReleaseLevel = "[release] " 24 | printErrorLevel = "[error ] " 25 | printFatalLevel = "[fatal ] " 26 | ) 27 | 28 | type Logger struct { 29 | level int 30 | baseLogger *log.Logger 31 | baseFile *os.File 32 | } 33 | 34 | func New(strLevel string, pathname string) (*Logger, error) { 35 | // level 36 | var level int 37 | switch strings.ToLower(strLevel) { 38 | case "debug": 39 | level = debugLevel 40 | case "release": 41 | level = releaseLevel 42 | case "error": 43 | level = errorLevel 44 | case "fatal": 45 | level = fatalLevel 46 | default: 47 | return nil, errors.New("unknown level: " + strLevel) 48 | } 49 | 50 | // logger 51 | var baseLogger *log.Logger 52 | var baseFile *os.File 53 | if pathname != "" { 54 | now := time.Now() 55 | 56 | filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", 57 | now.Year(), 58 | now.Month(), 59 | now.Day(), 60 | now.Hour(), 61 | now.Minute(), 62 | now.Second()) 63 | 64 | file, err := os.Create(path.Join(pathname, filename)) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | baseLogger = log.New(file, "", log.LstdFlags) 70 | baseFile = file 71 | } else { 72 | baseLogger = log.New(os.Stdout, "", log.LstdFlags) 73 | } 74 | 75 | // new 76 | logger := new(Logger) 77 | logger.level = level 78 | logger.baseLogger = baseLogger 79 | logger.baseFile = baseFile 80 | 81 | return logger, nil 82 | } 83 | 84 | // It's dangerous to call the method on logging 85 | func (logger *Logger) Close() { 86 | if logger.baseFile != nil { 87 | logger.baseFile.Close() 88 | } 89 | 90 | logger.baseLogger = nil 91 | logger.baseFile = nil 92 | } 93 | 94 | func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) { 95 | if level < logger.level { 96 | return 97 | } 98 | if logger.baseLogger == nil { 99 | panic("logger closed") 100 | } 101 | 102 | format = printLevel + format 103 | logger.baseLogger.Printf(format, a...) 104 | 105 | if level == fatalLevel { 106 | os.Exit(1) 107 | } 108 | } 109 | 110 | func (logger *Logger) Debug(format string, a ...interface{}) { 111 | logger.doPrintf(debugLevel, printDebugLevel, format, a...) 112 | } 113 | 114 | func (logger *Logger) Release(format string, a ...interface{}) { 115 | logger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 116 | } 117 | 118 | func (logger *Logger) Error(format string, a ...interface{}) { 119 | logger.doPrintf(errorLevel, printErrorLevel, format, a...) 120 | } 121 | 122 | func (logger *Logger) Fatal(format string, a ...interface{}) { 123 | logger.doPrintf(fatalLevel, printFatalLevel, format, a...) 124 | } 125 | 126 | var gLogger, _ = New("debug", "") 127 | 128 | // It's dangerous to call the method on logging 129 | func Export(logger *Logger) { 130 | if logger != nil { 131 | gLogger = logger 132 | } 133 | } 134 | 135 | func Debug(format string, a ...interface{}) { 136 | gLogger.Debug(format, a...) 137 | } 138 | 139 | func Release(format string, a ...interface{}) { 140 | gLogger.Release(format, a...) 141 | } 142 | 143 | func Error(format string, a ...interface{}) { 144 | gLogger.Error(format, a...) 145 | } 146 | 147 | func Fatal(format string, a ...interface{}) { 148 | gLogger.Fatal(format, a...) 149 | } 150 | 151 | func Close() { 152 | gLogger.Close() 153 | } 154 | -------------------------------------------------------------------------------- /src/framework/module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "framework/conf" 5 | "framework/log" 6 | "runtime" 7 | "sync" 8 | ) 9 | 10 | type Module interface { 11 | OnInit() 12 | OnDestroy() 13 | Run(closeSig chan bool) 14 | } 15 | 16 | type module struct { 17 | mi Module 18 | closeSig chan bool 19 | wg sync.WaitGroup 20 | } 21 | 22 | var mods []*module 23 | 24 | func Register(mi Module) { 25 | m := new(module) 26 | m.mi = mi 27 | m.closeSig = make(chan bool, 1) 28 | 29 | mods = append(mods, m) 30 | } 31 | 32 | func Init() { 33 | for i := 0; i < len(mods); i++ { 34 | mods[i].mi.OnInit() 35 | } 36 | 37 | for i := 0; i < len(mods); i++ { 38 | m := mods[i] 39 | m.wg.Add(1) 40 | go run(m) 41 | } 42 | } 43 | 44 | func Destroy() { 45 | for i := len(mods) - 1; i >= 0; i-- { 46 | m := mods[i] 47 | m.closeSig <- true 48 | m.wg.Wait() 49 | destroy(m) 50 | } 51 | } 52 | 53 | func run(m *module) { 54 | m.mi.Run(m.closeSig) 55 | m.wg.Done() 56 | } 57 | 58 | func destroy(m *module) { 59 | defer func() { 60 | if r := recover(); r != nil { 61 | if conf.LenStackBuf > 0 { 62 | buf := make([]byte, conf.LenStackBuf) 63 | l := runtime.Stack(buf, false) 64 | log.Error("%v: %s", r, buf[:l]) 65 | } else { 66 | log.Error("%v", r) 67 | } 68 | } 69 | }() 70 | 71 | m.mi.OnDestroy() 72 | } 73 | -------------------------------------------------------------------------------- /src/framework/module/skeleton.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "framework/chanrpc" 5 | "framework/console" 6 | "framework/go" 7 | "framework/timer" 8 | "time" 9 | ) 10 | 11 | type Skeleton struct { 12 | GoLen int 13 | TimerDispatcherLen int 14 | AsynCallLen int 15 | ChanRPCServer *chanrpc.Server 16 | g *g.Go 17 | dispatcher *timer.Dispatcher 18 | client *chanrpc.Client 19 | server *chanrpc.Server 20 | commandServer *chanrpc.Server 21 | } 22 | 23 | func (s *Skeleton) Init() { 24 | if s.GoLen <= 0 { 25 | s.GoLen = 0 26 | } 27 | if s.TimerDispatcherLen <= 0 { 28 | s.TimerDispatcherLen = 0 29 | } 30 | if s.AsynCallLen <= 0 { 31 | s.AsynCallLen = 0 32 | } 33 | 34 | s.g = g.New(s.GoLen) 35 | s.dispatcher = timer.NewDispatcher(s.TimerDispatcherLen) 36 | s.client = chanrpc.NewClient(s.AsynCallLen) 37 | s.server = s.ChanRPCServer 38 | 39 | if s.server == nil { 40 | s.server = chanrpc.NewServer(0) 41 | } 42 | s.commandServer = chanrpc.NewServer(0) 43 | } 44 | 45 | func (s *Skeleton) Run(closeSig chan bool) { 46 | for { 47 | select { 48 | case <-closeSig: 49 | s.commandServer.Close() 50 | s.server.Close() 51 | for !s.g.Idle() || !s.client.Idle() { 52 | s.g.Close() 53 | s.client.Close() 54 | } 55 | return 56 | case ri := <-s.client.ChanAsynRet: 57 | s.client.Cb(ri) 58 | case ci := <-s.server.ChanCall: 59 | s.server.Exec(ci) 60 | case ci := <-s.commandServer.ChanCall: 61 | s.commandServer.Exec(ci) 62 | case cb := <-s.g.ChanCb: 63 | s.g.Cb(cb) 64 | case t := <-s.dispatcher.ChanTimer: 65 | t.Cb() 66 | } 67 | } 68 | } 69 | 70 | func (s *Skeleton) AfterFunc(d time.Duration, cb func()) *timer.Timer { 71 | if s.TimerDispatcherLen == 0 { 72 | panic("invalid TimerDispatcherLen") 73 | } 74 | 75 | return s.dispatcher.AfterFunc(d, cb) 76 | } 77 | 78 | func (s *Skeleton) CronFunc(cronExpr *timer.CronExpr, cb func()) *timer.Cron { 79 | if s.TimerDispatcherLen == 0 { 80 | panic("invalid TimerDispatcherLen") 81 | } 82 | 83 | return s.dispatcher.CronFunc(cronExpr, cb) 84 | } 85 | 86 | func (s *Skeleton) Go(f func(), cb func()) { 87 | if s.GoLen == 0 { 88 | panic("invalid GoLen") 89 | } 90 | 91 | s.g.Go(f, cb) 92 | } 93 | 94 | func (s *Skeleton) NewLinearContext() *g.LinearContext { 95 | if s.GoLen == 0 { 96 | panic("invalid GoLen") 97 | } 98 | 99 | return s.g.NewLinearContext() 100 | } 101 | 102 | func (s *Skeleton) AsynCall(server *chanrpc.Server, id interface{}, args ...interface{}) { 103 | if s.AsynCallLen == 0 { 104 | panic("invalid AsynCallLen") 105 | } 106 | 107 | s.client.Attach(server) 108 | s.client.AsynCall(id, args...) 109 | } 110 | 111 | func (s *Skeleton) RegisterChanRPC(id interface{}, f interface{}) { 112 | if s.ChanRPCServer == nil { 113 | panic("invalid ChanRPCServer") 114 | } 115 | 116 | s.server.Register(id, f) 117 | } 118 | 119 | func (s *Skeleton) RegisterCommand(name string, help string, f interface{}) { 120 | console.Register(name, help, f, s.commandServer) 121 | } 122 | -------------------------------------------------------------------------------- /src/framework/network/agent.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Agent interface { 4 | Run() 5 | OnClose() 6 | } 7 | -------------------------------------------------------------------------------- /src/framework/network/conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type Conn interface { 8 | ReadMsg() ([]byte, error) 9 | WriteMsg(args ...[]byte) error 10 | LocalAddr() net.Addr 11 | RemoteAddr() net.Addr 12 | Close() 13 | Destroy() 14 | } 15 | -------------------------------------------------------------------------------- /src/framework/network/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "framework/chanrpc" 8 | "framework/log" 9 | "reflect" 10 | ) 11 | 12 | type Processor struct { 13 | msgInfo map[string]*MsgInfo 14 | } 15 | 16 | type MsgInfo struct { 17 | msgType reflect.Type 18 | msgRouter *chanrpc.Server 19 | msgHandler MsgHandler 20 | } 21 | 22 | type MsgHandler func([]interface{}) 23 | 24 | func NewProcessor() *Processor { 25 | p := new(Processor) 26 | p.msgInfo = make(map[string]*MsgInfo) 27 | return p 28 | } 29 | 30 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 31 | func (p *Processor) Register(msg interface{}) { 32 | msgType := reflect.TypeOf(msg) 33 | if msgType == nil || msgType.Kind() != reflect.Ptr { 34 | log.Fatal("json message pointer required") 35 | } 36 | msgID := msgType.Elem().Name() 37 | if msgID == "" { 38 | log.Fatal("unnamed json message") 39 | } 40 | if _, ok := p.msgInfo[msgID]; ok { 41 | log.Fatal("message %v is already registered", msgID) 42 | } 43 | 44 | i := new(MsgInfo) 45 | i.msgType = msgType 46 | p.msgInfo[msgID] = i 47 | } 48 | 49 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 50 | func (p *Processor) SetRouter(msg interface{}, msgRouter *chanrpc.Server) { 51 | msgType := reflect.TypeOf(msg) 52 | if msgType == nil || msgType.Kind() != reflect.Ptr { 53 | log.Fatal("json message pointer required") 54 | } 55 | msgID := msgType.Elem().Name() 56 | i, ok := p.msgInfo[msgID] 57 | if !ok { 58 | log.Fatal("message %v not registered", msgID) 59 | } 60 | 61 | i.msgRouter = msgRouter 62 | } 63 | 64 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 65 | func (p *Processor) SetHandler(msg interface{}, msgHandler MsgHandler) { 66 | msgType := reflect.TypeOf(msg) 67 | if msgType == nil || msgType.Kind() != reflect.Ptr { 68 | log.Fatal("json message pointer required") 69 | } 70 | msgID := msgType.Elem().Name() 71 | i, ok := p.msgInfo[msgID] 72 | if !ok { 73 | log.Fatal("message %v not registered", msgID) 74 | } 75 | 76 | i.msgHandler = msgHandler 77 | } 78 | 79 | // goroutine safe 80 | func (p *Processor) Route(msg interface{}, userData interface{}) error { 81 | msgType := reflect.TypeOf(msg) 82 | if msgType == nil || msgType.Kind() != reflect.Ptr { 83 | return errors.New("json message pointer required") 84 | } 85 | msgID := msgType.Elem().Name() 86 | i, ok := p.msgInfo[msgID] 87 | if !ok { 88 | return fmt.Errorf("message %v not registered", msgID) 89 | } 90 | 91 | if i.msgHandler != nil { 92 | i.msgHandler([]interface{}{msg, userData}) 93 | } 94 | if i.msgRouter != nil { 95 | i.msgRouter.Go(msgType, msg, userData) 96 | } 97 | return nil 98 | } 99 | 100 | // goroutine safe 101 | func (p *Processor) Unmarshal(data []byte) (interface{}, error) { 102 | var m map[string]json.RawMessage 103 | err := json.Unmarshal(data, &m) 104 | if err != nil { 105 | return nil, err 106 | } 107 | if len(m) != 1 { 108 | return nil, errors.New("invalid json data") 109 | } 110 | 111 | for msgID, data := range m { 112 | i, ok := p.msgInfo[msgID] 113 | if !ok { 114 | return nil, fmt.Errorf("message %v not registered", msgID) 115 | } 116 | 117 | // msg 118 | msg := reflect.New(i.msgType.Elem()).Interface() 119 | return msg, json.Unmarshal(data, msg) 120 | } 121 | 122 | panic("bug") 123 | } 124 | 125 | // goroutine safe 126 | func (p *Processor) Marshal(msg interface{}) ([][]byte, error) { 127 | msgType := reflect.TypeOf(msg) 128 | if msgType == nil || msgType.Kind() != reflect.Ptr { 129 | return nil, errors.New("json message pointer required") 130 | } 131 | msgID := msgType.Elem().Name() 132 | if _, ok := p.msgInfo[msgID]; !ok { 133 | return nil, fmt.Errorf("message %v not registered", msgID) 134 | } 135 | 136 | // data 137 | m := map[string]interface{}{msgID: msg} 138 | data, err := json.Marshal(m) 139 | return [][]byte{data}, err 140 | } 141 | -------------------------------------------------------------------------------- /src/framework/network/processor.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Processor interface { 4 | // must goroutine safe 5 | Route(msg interface{}, userData interface{}) error 6 | // must goroutine safe 7 | Unmarshal(data []byte) (interface{}, error) 8 | // must goroutine safe 9 | Marshal(msg interface{}) ([][]byte, error) 10 | } 11 | -------------------------------------------------------------------------------- /src/framework/network/protobuf/protobuf.go: -------------------------------------------------------------------------------- 1 | package protobuf 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "github.com/golang/protobuf/proto" 8 | "framework/chanrpc" 9 | "framework/log" 10 | "math" 11 | "reflect" 12 | ) 13 | 14 | // ------------------------- 15 | // | id | protobuf message | 16 | // ------------------------- 17 | type Processor struct { 18 | littleEndian bool 19 | msgInfo []*MsgInfo 20 | msgID map[reflect.Type]uint16 21 | } 22 | 23 | type MsgInfo struct { 24 | msgType reflect.Type 25 | msgRouter *chanrpc.Server 26 | msgHandler MsgHandler 27 | } 28 | 29 | type MsgHandler func([]interface{}) 30 | 31 | func NewProcessor() *Processor { 32 | p := new(Processor) 33 | p.littleEndian = false 34 | p.msgID = make(map[reflect.Type]uint16) 35 | return p 36 | } 37 | 38 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 39 | func (p *Processor) SetByteOrder(littleEndian bool) { 40 | p.littleEndian = littleEndian 41 | } 42 | 43 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 44 | func (p *Processor) Register(msg proto.Message) { 45 | msgType := reflect.TypeOf(msg) 46 | if msgType == nil || msgType.Kind() != reflect.Ptr { 47 | log.Fatal("protobuf message pointer required") 48 | } 49 | if _, ok := p.msgID[msgType]; ok { 50 | log.Fatal("message %s is already registered", msgType) 51 | } 52 | if len(p.msgInfo) >= math.MaxUint16 { 53 | log.Fatal("too many protobuf messages (max = %v)", math.MaxUint16) 54 | } 55 | 56 | i := new(MsgInfo) 57 | i.msgType = msgType 58 | p.msgInfo = append(p.msgInfo, i) 59 | p.msgID[msgType] = uint16(len(p.msgInfo) - 1) 60 | } 61 | 62 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 63 | func (p *Processor) SetRouter(msg proto.Message, msgRouter *chanrpc.Server) { 64 | msgType := reflect.TypeOf(msg) 65 | id, ok := p.msgID[msgType] 66 | if !ok { 67 | log.Fatal("message %s not registered", msgType) 68 | } 69 | 70 | p.msgInfo[id].msgRouter = msgRouter 71 | } 72 | 73 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 74 | func (p *Processor) SetHandler(msg proto.Message, msgHandler MsgHandler) { 75 | msgType := reflect.TypeOf(msg) 76 | id, ok := p.msgID[msgType] 77 | if !ok { 78 | log.Fatal("message %s not registered", msgType) 79 | } 80 | 81 | p.msgInfo[id].msgHandler = msgHandler 82 | } 83 | 84 | // goroutine safe 85 | func (p *Processor) Route(msg interface{}, userData interface{}) error { 86 | msgType := reflect.TypeOf(msg) 87 | id, ok := p.msgID[msgType] 88 | if !ok { 89 | return fmt.Errorf("message %s not registered", msgType) 90 | } 91 | 92 | i := p.msgInfo[id] 93 | if i.msgHandler != nil { 94 | i.msgHandler([]interface{}{msg, userData}) 95 | } 96 | if i.msgRouter != nil { 97 | i.msgRouter.Go(msgType, msg, userData) 98 | } 99 | return nil 100 | } 101 | 102 | // goroutine safe 103 | func (p *Processor) Unmarshal(data []byte) (interface{}, error) { 104 | if len(data) < 2 { 105 | return nil, errors.New("protobuf data too short") 106 | } 107 | 108 | // id 109 | var id uint16 110 | if p.littleEndian { 111 | id = binary.LittleEndian.Uint16(data) 112 | } else { 113 | id = binary.BigEndian.Uint16(data) 114 | } 115 | 116 | // msg 117 | if id >= uint16(len(p.msgInfo)) { 118 | return nil, fmt.Errorf("message id %v not registered", id) 119 | } 120 | msg := reflect.New(p.msgInfo[id].msgType.Elem()).Interface() 121 | return msg, proto.UnmarshalMerge(data[2:], msg.(proto.Message)) 122 | } 123 | 124 | // goroutine safe 125 | func (p *Processor) Marshal(msg interface{}) ([][]byte, error) { 126 | msgType := reflect.TypeOf(msg) 127 | 128 | // id 129 | _id, ok := p.msgID[msgType] 130 | if !ok { 131 | err := fmt.Errorf("message %s not registered", msgType) 132 | return nil, err 133 | } 134 | 135 | id := make([]byte, 2) 136 | if p.littleEndian { 137 | binary.LittleEndian.PutUint16(id, _id) 138 | } else { 139 | binary.BigEndian.PutUint16(id, _id) 140 | } 141 | 142 | // data 143 | data, err := proto.Marshal(msg.(proto.Message)) 144 | return [][]byte{id, data}, err 145 | } 146 | 147 | // goroutine safe 148 | func (p *Processor) Range(f func(id uint16, t reflect.Type)) { 149 | for id, i := range p.msgInfo { 150 | f(uint16(id), i.msgType) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/framework/network/tcp_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "framework/log" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type TCPClient struct { 11 | sync.Mutex 12 | Addr string 13 | ConnNum int 14 | ConnectInterval time.Duration 15 | PendingWriteNum int 16 | AutoReconnect bool 17 | NewAgent func(*TCPConn) Agent 18 | conns ConnSet 19 | wg sync.WaitGroup 20 | closeFlag bool 21 | 22 | // msg parser 23 | LenMsgLen int 24 | MinMsgLen uint32 25 | MaxMsgLen uint32 26 | LittleEndian bool 27 | msgParser *MsgParser 28 | } 29 | 30 | func (client *TCPClient) Start() { 31 | client.init() 32 | 33 | for i := 0; i < client.ConnNum; i++ { 34 | client.wg.Add(1) 35 | go client.connect() 36 | } 37 | } 38 | 39 | func (client *TCPClient) init() { 40 | client.Lock() 41 | defer client.Unlock() 42 | 43 | if client.ConnNum <= 0 { 44 | client.ConnNum = 1 45 | log.Release("invalid ConnNum, reset to %v", client.ConnNum) 46 | } 47 | if client.ConnectInterval <= 0 { 48 | client.ConnectInterval = 3 * time.Second 49 | log.Release("invalid ConnectInterval, reset to %v", client.ConnectInterval) 50 | } 51 | if client.PendingWriteNum <= 0 { 52 | client.PendingWriteNum = 100 53 | log.Release("invalid PendingWriteNum, reset to %v", client.PendingWriteNum) 54 | } 55 | if client.NewAgent == nil { 56 | log.Fatal("NewAgent must not be nil") 57 | } 58 | if client.conns != nil { 59 | log.Fatal("client is running") 60 | } 61 | 62 | client.conns = make(ConnSet) 63 | client.closeFlag = false 64 | 65 | // msg parser 66 | msgParser := NewMsgParser() 67 | msgParser.SetMsgLen(client.LenMsgLen, client.MinMsgLen, client.MaxMsgLen) 68 | msgParser.SetByteOrder(client.LittleEndian) 69 | client.msgParser = msgParser 70 | } 71 | 72 | func (client *TCPClient) dial() net.Conn { 73 | for { 74 | conn, err := net.Dial("tcp", client.Addr) 75 | if err == nil || client.closeFlag { 76 | return conn 77 | } 78 | 79 | log.Release("connect to %v error: %v", client.Addr, err) 80 | time.Sleep(client.ConnectInterval) 81 | continue 82 | } 83 | } 84 | 85 | func (client *TCPClient) connect() { 86 | defer client.wg.Done() 87 | 88 | reconnect: 89 | conn := client.dial() 90 | if conn == nil { 91 | return 92 | } 93 | 94 | client.Lock() 95 | if client.closeFlag { 96 | client.Unlock() 97 | conn.Close() 98 | return 99 | } 100 | client.conns[conn] = struct{}{} 101 | client.Unlock() 102 | 103 | tcpConn := newTCPConn(conn, client.PendingWriteNum, client.msgParser) 104 | agent := client.NewAgent(tcpConn) 105 | agent.Run() 106 | 107 | // cleanup 108 | tcpConn.Close() 109 | client.Lock() 110 | delete(client.conns, conn) 111 | client.Unlock() 112 | agent.OnClose() 113 | 114 | if client.AutoReconnect { 115 | time.Sleep(client.ConnectInterval) 116 | goto reconnect 117 | } 118 | } 119 | 120 | func (client *TCPClient) Close() { 121 | client.Lock() 122 | client.closeFlag = true 123 | for conn := range client.conns { 124 | conn.Close() 125 | } 126 | client.conns = nil 127 | client.Unlock() 128 | 129 | client.wg.Wait() 130 | } 131 | -------------------------------------------------------------------------------- /src/framework/network/tcp_conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "framework/log" 5 | "net" 6 | "sync" 7 | ) 8 | 9 | type ConnSet map[net.Conn]struct{} 10 | 11 | type TCPConn struct { 12 | sync.Mutex 13 | conn net.Conn 14 | writeChan chan []byte 15 | closeFlag bool 16 | msgParser *MsgParser 17 | } 18 | 19 | func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser) *TCPConn { 20 | tcpConn := new(TCPConn) 21 | tcpConn.conn = conn 22 | tcpConn.writeChan = make(chan []byte, pendingWriteNum) 23 | tcpConn.msgParser = msgParser 24 | 25 | go func() { 26 | for b := range tcpConn.writeChan { 27 | if b == nil { 28 | break 29 | } 30 | 31 | _, err := conn.Write(b) 32 | if err != nil { 33 | break 34 | } 35 | } 36 | 37 | conn.Close() 38 | tcpConn.Lock() 39 | tcpConn.closeFlag = true 40 | tcpConn.Unlock() 41 | }() 42 | 43 | return tcpConn 44 | } 45 | 46 | func (tcpConn *TCPConn) doDestroy() { 47 | tcpConn.conn.(*net.TCPConn).SetLinger(0) 48 | tcpConn.conn.Close() 49 | 50 | if !tcpConn.closeFlag { 51 | close(tcpConn.writeChan) 52 | tcpConn.closeFlag = true 53 | } 54 | } 55 | 56 | func (tcpConn *TCPConn) Destroy() { 57 | tcpConn.Lock() 58 | defer tcpConn.Unlock() 59 | 60 | tcpConn.doDestroy() 61 | } 62 | 63 | func (tcpConn *TCPConn) Close() { 64 | tcpConn.Lock() 65 | defer tcpConn.Unlock() 66 | if tcpConn.closeFlag { 67 | return 68 | } 69 | 70 | tcpConn.doWrite(nil) 71 | tcpConn.closeFlag = true 72 | } 73 | 74 | func (tcpConn *TCPConn) doWrite(b []byte) { 75 | if len(tcpConn.writeChan) == cap(tcpConn.writeChan) { 76 | log.Debug("close conn: channel full") 77 | tcpConn.doDestroy() 78 | return 79 | } 80 | 81 | tcpConn.writeChan <- b 82 | } 83 | 84 | // b must not be modified by the others goroutines 85 | func (tcpConn *TCPConn) Write(b []byte) { 86 | tcpConn.Lock() 87 | defer tcpConn.Unlock() 88 | if tcpConn.closeFlag || b == nil { 89 | return 90 | } 91 | 92 | tcpConn.doWrite(b) 93 | } 94 | 95 | func (tcpConn *TCPConn) Read(b []byte) (int, error) { 96 | return tcpConn.conn.Read(b) 97 | } 98 | 99 | func (tcpConn *TCPConn) LocalAddr() net.Addr { 100 | return tcpConn.conn.LocalAddr() 101 | } 102 | 103 | func (tcpConn *TCPConn) RemoteAddr() net.Addr { 104 | return tcpConn.conn.RemoteAddr() 105 | } 106 | 107 | func (tcpConn *TCPConn) ReadMsg() ([]byte, error) { 108 | return tcpConn.msgParser.Read(tcpConn) 109 | } 110 | 111 | func (tcpConn *TCPConn) WriteMsg(args ...[]byte) error { 112 | return tcpConn.msgParser.Write(tcpConn, args...) 113 | } 114 | -------------------------------------------------------------------------------- /src/framework/network/tcp_msg.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | "math" 8 | ) 9 | 10 | // -------------- 11 | // | len | data | 12 | // -------------- 13 | type MsgParser struct { 14 | lenMsgLen int 15 | minMsgLen uint32 16 | maxMsgLen uint32 17 | littleEndian bool 18 | } 19 | 20 | func NewMsgParser() *MsgParser { 21 | p := new(MsgParser) 22 | p.lenMsgLen = 2 23 | p.minMsgLen = 1 24 | p.maxMsgLen = 4096 25 | p.littleEndian = false 26 | 27 | return p 28 | } 29 | 30 | // It's dangerous to call the method on reading or writing 31 | func (p *MsgParser) SetMsgLen(lenMsgLen int, minMsgLen uint32, maxMsgLen uint32) { 32 | if lenMsgLen == 1 || lenMsgLen == 2 || lenMsgLen == 4 { 33 | p.lenMsgLen = lenMsgLen 34 | } 35 | if minMsgLen != 0 { 36 | p.minMsgLen = minMsgLen 37 | } 38 | if maxMsgLen != 0 { 39 | p.maxMsgLen = maxMsgLen 40 | } 41 | 42 | var max uint32 43 | switch p.lenMsgLen { 44 | case 1: 45 | max = math.MaxUint8 46 | case 2: 47 | max = math.MaxUint16 48 | case 4: 49 | max = math.MaxUint32 50 | } 51 | if p.minMsgLen > max { 52 | p.minMsgLen = max 53 | } 54 | if p.maxMsgLen > max { 55 | p.maxMsgLen = max 56 | } 57 | } 58 | 59 | // It's dangerous to call the method on reading or writing 60 | func (p *MsgParser) SetByteOrder(littleEndian bool) { 61 | p.littleEndian = littleEndian 62 | } 63 | 64 | // goroutine safe 65 | func (p *MsgParser) Read(conn *TCPConn) ([]byte, error) { 66 | var b [4]byte 67 | bufMsgLen := b[:p.lenMsgLen] 68 | 69 | // read len 70 | if _, err := io.ReadFull(conn, bufMsgLen); err != nil { 71 | return nil, err 72 | } 73 | 74 | // parse len 75 | var msgLen uint32 76 | switch p.lenMsgLen { 77 | case 1: 78 | msgLen = uint32(bufMsgLen[0]) 79 | case 2: 80 | if p.littleEndian { 81 | msgLen = uint32(binary.LittleEndian.Uint16(bufMsgLen)) 82 | } else { 83 | msgLen = uint32(binary.BigEndian.Uint16(bufMsgLen)) 84 | } 85 | case 4: 86 | if p.littleEndian { 87 | msgLen = binary.LittleEndian.Uint32(bufMsgLen) 88 | } else { 89 | msgLen = binary.BigEndian.Uint32(bufMsgLen) 90 | } 91 | } 92 | 93 | // check len 94 | if msgLen > p.maxMsgLen { 95 | return nil, errors.New("message too long") 96 | } else if msgLen < p.minMsgLen { 97 | return nil, errors.New("message too short") 98 | } 99 | 100 | // data 101 | msgData := make([]byte, msgLen) 102 | if _, err := io.ReadFull(conn, msgData); err != nil { 103 | return nil, err 104 | } 105 | 106 | return msgData, nil 107 | } 108 | 109 | // goroutine safe 110 | func (p *MsgParser) Write(conn *TCPConn, args ...[]byte) error { 111 | // get len 112 | var msgLen uint32 113 | for i := 0; i < len(args); i++ { 114 | msgLen += uint32(len(args[i])) 115 | } 116 | 117 | // check len 118 | if msgLen > p.maxMsgLen { 119 | return errors.New("message too long") 120 | } else if msgLen < p.minMsgLen { 121 | return errors.New("message too short") 122 | } 123 | 124 | msg := make([]byte, uint32(p.lenMsgLen)+msgLen) 125 | 126 | // write len 127 | switch p.lenMsgLen { 128 | case 1: 129 | msg[0] = byte(msgLen) 130 | case 2: 131 | if p.littleEndian { 132 | binary.LittleEndian.PutUint16(msg, uint16(msgLen)) 133 | } else { 134 | binary.BigEndian.PutUint16(msg, uint16(msgLen)) 135 | } 136 | case 4: 137 | if p.littleEndian { 138 | binary.LittleEndian.PutUint32(msg, msgLen) 139 | } else { 140 | binary.BigEndian.PutUint32(msg, msgLen) 141 | } 142 | } 143 | 144 | // write data 145 | l := p.lenMsgLen 146 | for i := 0; i < len(args); i++ { 147 | copy(msg[l:], args[i]) 148 | l += len(args[i]) 149 | } 150 | 151 | conn.Write(msg) 152 | 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /src/framework/network/tcp_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "framework/log" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type TCPServer struct { 11 | Addr string 12 | MaxConnNum int 13 | PendingWriteNum int 14 | NewAgent func(*TCPConn) Agent 15 | ln net.Listener 16 | conns ConnSet 17 | mutexConns sync.Mutex 18 | wgLn sync.WaitGroup 19 | wgConns sync.WaitGroup 20 | 21 | // msg parser 22 | LenMsgLen int 23 | MinMsgLen uint32 24 | MaxMsgLen uint32 25 | LittleEndian bool 26 | msgParser *MsgParser 27 | } 28 | 29 | func (server *TCPServer) Start() { 30 | server.init() 31 | go server.run() 32 | } 33 | 34 | func (server *TCPServer) init() { 35 | ln, err := net.Listen("tcp", server.Addr) 36 | if err != nil { 37 | log.Fatal("%v", err) 38 | } 39 | 40 | if server.MaxConnNum <= 0 { 41 | server.MaxConnNum = 100 42 | log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum) 43 | } 44 | if server.PendingWriteNum <= 0 { 45 | server.PendingWriteNum = 100 46 | log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum) 47 | } 48 | if server.NewAgent == nil { 49 | log.Fatal("NewAgent must not be nil") 50 | } 51 | 52 | server.ln = ln 53 | server.conns = make(ConnSet) 54 | 55 | // msg parser 56 | msgParser := NewMsgParser() 57 | msgParser.SetMsgLen(server.LenMsgLen, server.MinMsgLen, server.MaxMsgLen) 58 | msgParser.SetByteOrder(server.LittleEndian) 59 | server.msgParser = msgParser 60 | } 61 | 62 | func (server *TCPServer) run() { 63 | server.wgLn.Add(1) 64 | defer server.wgLn.Done() 65 | 66 | var tempDelay time.Duration 67 | for { 68 | conn, err := server.ln.Accept() 69 | if err != nil { 70 | if ne, ok := err.(net.Error); ok && ne.Temporary() { 71 | if tempDelay == 0 { 72 | tempDelay = 5 * time.Millisecond 73 | } else { 74 | tempDelay *= 2 75 | } 76 | if max := 1 * time.Second; tempDelay > max { 77 | tempDelay = max 78 | } 79 | log.Release("accept error: %v; retrying in %v", err, tempDelay) 80 | time.Sleep(tempDelay) 81 | continue 82 | } 83 | return 84 | } 85 | tempDelay = 0 86 | 87 | server.mutexConns.Lock() 88 | if len(server.conns) >= server.MaxConnNum { 89 | server.mutexConns.Unlock() 90 | conn.Close() 91 | log.Debug("too many connections") 92 | continue 93 | } 94 | server.conns[conn] = struct{}{} 95 | server.mutexConns.Unlock() 96 | 97 | server.wgConns.Add(1) 98 | 99 | tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser) 100 | agent := server.NewAgent(tcpConn) 101 | go func() { 102 | agent.Run() 103 | 104 | // cleanup 105 | tcpConn.Close() 106 | server.mutexConns.Lock() 107 | delete(server.conns, conn) 108 | server.mutexConns.Unlock() 109 | agent.OnClose() 110 | 111 | server.wgConns.Done() 112 | }() 113 | } 114 | } 115 | 116 | func (server *TCPServer) Close() { 117 | server.ln.Close() 118 | server.wgLn.Wait() 119 | 120 | server.mutexConns.Lock() 121 | for conn := range server.conns { 122 | conn.Close() 123 | } 124 | server.conns = nil 125 | server.mutexConns.Unlock() 126 | server.wgConns.Wait() 127 | } 128 | -------------------------------------------------------------------------------- /src/framework/network/ws_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | "framework/log" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type WSClient struct { 11 | sync.Mutex 12 | Addr string 13 | ConnNum int 14 | ConnectInterval time.Duration 15 | PendingWriteNum int 16 | MaxMsgLen uint32 17 | HandshakeTimeout time.Duration 18 | AutoReconnect bool 19 | NewAgent func(*WSConn) Agent 20 | dialer websocket.Dialer 21 | conns WebsocketConnSet 22 | wg sync.WaitGroup 23 | closeFlag bool 24 | } 25 | 26 | func (client *WSClient) Start() { 27 | client.init() 28 | 29 | for i := 0; i < client.ConnNum; i++ { 30 | client.wg.Add(1) 31 | go client.connect() 32 | } 33 | } 34 | 35 | func (client *WSClient) init() { 36 | client.Lock() 37 | defer client.Unlock() 38 | 39 | if client.ConnNum <= 0 { 40 | client.ConnNum = 1 41 | log.Release("invalid ConnNum, reset to %v", client.ConnNum) 42 | } 43 | if client.ConnectInterval <= 0 { 44 | client.ConnectInterval = 3 * time.Second 45 | log.Release("invalid ConnectInterval, reset to %v", client.ConnectInterval) 46 | } 47 | if client.PendingWriteNum <= 0 { 48 | client.PendingWriteNum = 100 49 | log.Release("invalid PendingWriteNum, reset to %v", client.PendingWriteNum) 50 | } 51 | if client.MaxMsgLen <= 0 { 52 | client.MaxMsgLen = 4096 53 | log.Release("invalid MaxMsgLen, reset to %v", client.MaxMsgLen) 54 | } 55 | if client.HandshakeTimeout <= 0 { 56 | client.HandshakeTimeout = 10 * time.Second 57 | log.Release("invalid HandshakeTimeout, reset to %v", client.HandshakeTimeout) 58 | } 59 | if client.NewAgent == nil { 60 | log.Fatal("NewAgent must not be nil") 61 | } 62 | if client.conns != nil { 63 | log.Fatal("client is running") 64 | } 65 | 66 | client.conns = make(WebsocketConnSet) 67 | client.closeFlag = false 68 | client.dialer = websocket.Dialer{ 69 | HandshakeTimeout: client.HandshakeTimeout, 70 | } 71 | } 72 | 73 | func (client *WSClient) dial() *websocket.Conn { 74 | for { 75 | conn, _, err := client.dialer.Dial(client.Addr, nil) 76 | if err == nil || client.closeFlag { 77 | return conn 78 | } 79 | 80 | log.Release("connect to %v error: %v", client.Addr, err) 81 | time.Sleep(client.ConnectInterval) 82 | continue 83 | } 84 | } 85 | 86 | func (client *WSClient) connect() { 87 | defer client.wg.Done() 88 | 89 | reconnect: 90 | conn := client.dial() 91 | if conn == nil { 92 | return 93 | } 94 | conn.SetReadLimit(int64(client.MaxMsgLen)) 95 | 96 | client.Lock() 97 | if client.closeFlag { 98 | client.Unlock() 99 | conn.Close() 100 | return 101 | } 102 | client.conns[conn] = struct{}{} 103 | client.Unlock() 104 | 105 | wsConn := newWSConn(conn, client.PendingWriteNum, client.MaxMsgLen) 106 | agent := client.NewAgent(wsConn) 107 | agent.Run() 108 | 109 | // cleanup 110 | wsConn.Close() 111 | client.Lock() 112 | delete(client.conns, conn) 113 | client.Unlock() 114 | agent.OnClose() 115 | 116 | if client.AutoReconnect { 117 | time.Sleep(client.ConnectInterval) 118 | goto reconnect 119 | } 120 | } 121 | 122 | func (client *WSClient) Close() { 123 | client.Lock() 124 | client.closeFlag = true 125 | for conn := range client.conns { 126 | conn.Close() 127 | } 128 | client.conns = nil 129 | client.Unlock() 130 | 131 | client.wg.Wait() 132 | } 133 | -------------------------------------------------------------------------------- /src/framework/network/ws_conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "errors" 5 | "github.com/gorilla/websocket" 6 | "framework/log" 7 | "net" 8 | "sync" 9 | ) 10 | 11 | type WebsocketConnSet map[*websocket.Conn]struct{} 12 | 13 | type WSConn struct { 14 | sync.Mutex 15 | conn *websocket.Conn 16 | writeChan chan []byte 17 | maxMsgLen uint32 18 | closeFlag bool 19 | } 20 | 21 | func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32) *WSConn { 22 | wsConn := new(WSConn) 23 | wsConn.conn = conn 24 | wsConn.writeChan = make(chan []byte, pendingWriteNum) 25 | wsConn.maxMsgLen = maxMsgLen 26 | 27 | go func() { 28 | for b := range wsConn.writeChan { 29 | if b == nil { 30 | break 31 | } 32 | 33 | err := conn.WriteMessage(websocket.BinaryMessage, b) 34 | if err != nil { 35 | break 36 | } 37 | } 38 | 39 | conn.Close() 40 | wsConn.Lock() 41 | wsConn.closeFlag = true 42 | wsConn.Unlock() 43 | }() 44 | 45 | return wsConn 46 | } 47 | 48 | func (wsConn *WSConn) doDestroy() { 49 | wsConn.conn.UnderlyingConn().(*net.TCPConn).SetLinger(0) 50 | wsConn.conn.Close() 51 | 52 | if !wsConn.closeFlag { 53 | close(wsConn.writeChan) 54 | wsConn.closeFlag = true 55 | } 56 | } 57 | 58 | func (wsConn *WSConn) Destroy() { 59 | wsConn.Lock() 60 | defer wsConn.Unlock() 61 | 62 | wsConn.doDestroy() 63 | } 64 | 65 | func (wsConn *WSConn) Close() { 66 | wsConn.Lock() 67 | defer wsConn.Unlock() 68 | if wsConn.closeFlag { 69 | return 70 | } 71 | 72 | wsConn.doWrite(nil) 73 | wsConn.closeFlag = true 74 | } 75 | 76 | func (wsConn *WSConn) doWrite(b []byte) { 77 | if len(wsConn.writeChan) == cap(wsConn.writeChan) { 78 | log.Debug("close conn: channel full") 79 | wsConn.doDestroy() 80 | return 81 | } 82 | 83 | wsConn.writeChan <- b 84 | } 85 | 86 | func (wsConn *WSConn) LocalAddr() net.Addr { 87 | return wsConn.conn.LocalAddr() 88 | } 89 | 90 | func (wsConn *WSConn) RemoteAddr() net.Addr { 91 | return wsConn.conn.RemoteAddr() 92 | } 93 | 94 | // goroutine not safe 95 | func (wsConn *WSConn) ReadMsg() ([]byte, error) { 96 | _, b, err := wsConn.conn.ReadMessage() 97 | return b, err 98 | } 99 | 100 | // args must not be modified by the others goroutines 101 | func (wsConn *WSConn) WriteMsg(args ...[]byte) error { 102 | wsConn.Lock() 103 | defer wsConn.Unlock() 104 | if wsConn.closeFlag { 105 | return nil 106 | } 107 | 108 | // get len 109 | var msgLen uint32 110 | for i := 0; i < len(args); i++ { 111 | msgLen += uint32(len(args[i])) 112 | } 113 | 114 | // check len 115 | if msgLen > wsConn.maxMsgLen { 116 | return errors.New("message too long") 117 | } else if msgLen < 1 { 118 | return errors.New("message too short") 119 | } 120 | 121 | // don't copy 122 | if len(args) == 1 { 123 | wsConn.doWrite(args[0]) 124 | return nil 125 | } 126 | 127 | // merge the args 128 | msg := make([]byte, msgLen) 129 | l := 0 130 | for i := 0; i < len(args); i++ { 131 | copy(msg[l:], args[i]) 132 | l += len(args[i]) 133 | } 134 | 135 | wsConn.doWrite(msg) 136 | 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /src/framework/network/ws_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | "framework/log" 6 | "net" 7 | "net/http" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type WSServer struct { 13 | Addr string 14 | MaxConnNum int 15 | PendingWriteNum int 16 | MaxMsgLen uint32 17 | HTTPTimeout time.Duration 18 | NewAgent func(*WSConn) Agent 19 | ln net.Listener 20 | handler *WSHandler 21 | } 22 | 23 | type WSHandler struct { 24 | maxConnNum int 25 | pendingWriteNum int 26 | maxMsgLen uint32 27 | newAgent func(*WSConn) Agent 28 | upgrader websocket.Upgrader 29 | conns WebsocketConnSet 30 | mutexConns sync.Mutex 31 | wg sync.WaitGroup 32 | } 33 | 34 | func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 35 | if r.Method != "GET" { 36 | http.Error(w, "Method not allowed", 405) 37 | return 38 | } 39 | conn, err := handler.upgrader.Upgrade(w, r, nil) 40 | if err != nil { 41 | log.Debug("upgrade error: %v", err) 42 | return 43 | } 44 | conn.SetReadLimit(int64(handler.maxMsgLen)) 45 | 46 | handler.wg.Add(1) 47 | defer handler.wg.Done() 48 | 49 | handler.mutexConns.Lock() 50 | if handler.conns == nil { 51 | handler.mutexConns.Unlock() 52 | conn.Close() 53 | return 54 | } 55 | if len(handler.conns) >= handler.maxConnNum { 56 | handler.mutexConns.Unlock() 57 | conn.Close() 58 | log.Debug("too many connections") 59 | return 60 | } 61 | handler.conns[conn] = struct{}{} 62 | handler.mutexConns.Unlock() 63 | 64 | wsConn := newWSConn(conn, handler.pendingWriteNum, handler.maxMsgLen) 65 | agent := handler.newAgent(wsConn) 66 | agent.Run() 67 | 68 | // cleanup 69 | wsConn.Close() 70 | handler.mutexConns.Lock() 71 | delete(handler.conns, conn) 72 | handler.mutexConns.Unlock() 73 | agent.OnClose() 74 | } 75 | 76 | func (server *WSServer) Start() { 77 | ln, err := net.Listen("tcp", server.Addr) 78 | if err != nil { 79 | log.Fatal("%v", err) 80 | } 81 | 82 | if server.MaxConnNum <= 0 { 83 | server.MaxConnNum = 100 84 | log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum) 85 | } 86 | if server.PendingWriteNum <= 0 { 87 | server.PendingWriteNum = 100 88 | log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum) 89 | } 90 | if server.MaxMsgLen <= 0 { 91 | server.MaxMsgLen = 4096 92 | log.Release("invalid MaxMsgLen, reset to %v", server.MaxMsgLen) 93 | } 94 | if server.HTTPTimeout <= 0 { 95 | server.HTTPTimeout = 10 * time.Second 96 | log.Release("invalid HTTPTimeout, reset to %v", server.HTTPTimeout) 97 | } 98 | if server.NewAgent == nil { 99 | log.Fatal("NewAgent must not be nil") 100 | } 101 | 102 | server.ln = ln 103 | server.handler = &WSHandler{ 104 | maxConnNum: server.MaxConnNum, 105 | pendingWriteNum: server.PendingWriteNum, 106 | maxMsgLen: server.MaxMsgLen, 107 | newAgent: server.NewAgent, 108 | conns: make(WebsocketConnSet), 109 | upgrader: websocket.Upgrader{ 110 | HandshakeTimeout: server.HTTPTimeout, 111 | CheckOrigin: func(_ *http.Request) bool { return true }, 112 | }, 113 | } 114 | 115 | httpServer := &http.Server{ 116 | Addr: server.Addr, 117 | Handler: server.handler, 118 | ReadTimeout: server.HTTPTimeout, 119 | WriteTimeout: server.HTTPTimeout, 120 | MaxHeaderBytes: 1024, 121 | } 122 | 123 | go httpServer.Serve(ln) 124 | } 125 | 126 | func (server *WSServer) Close() { 127 | server.ln.Close() 128 | 129 | server.handler.mutexConns.Lock() 130 | for conn := range server.handler.conns { 131 | conn.Close() 132 | } 133 | server.handler.conns = nil 134 | server.handler.mutexConns.Unlock() 135 | 136 | server.handler.wg.Wait() 137 | } 138 | -------------------------------------------------------------------------------- /src/framework/recordfile/example_test.go: -------------------------------------------------------------------------------- 1 | package recordfile_test 2 | 3 | import ( 4 | "fmt" 5 | "framework/recordfile" 6 | ) 7 | 8 | func Example() { 9 | type Record struct { 10 | // index 0 11 | IndexInt int "index" 12 | // index 1 13 | IndexStr string "index" 14 | _Number int32 15 | Str string 16 | Arr1 [2]int 17 | Arr2 [3][2]int 18 | Arr3 []int 19 | St struct { 20 | Name string "name" 21 | Num int "num" 22 | } 23 | M map[string]int 24 | } 25 | 26 | rf, err := recordfile.New(Record{}) 27 | if err != nil { 28 | return 29 | } 30 | 31 | err = rf.Read("test.txt") 32 | if err != nil { 33 | return 34 | } 35 | 36 | for i := 0; i < rf.NumRecord(); i++ { 37 | r := rf.Record(i).(*Record) 38 | fmt.Println(r.IndexInt) 39 | } 40 | 41 | r := rf.Index(2).(*Record) 42 | fmt.Println(r.Str) 43 | 44 | r = rf.Indexes(0)[2].(*Record) 45 | fmt.Println(r.Str) 46 | 47 | r = rf.Indexes(1)["three"].(*Record) 48 | fmt.Println(r.Str) 49 | fmt.Println(r.Arr1[1]) 50 | fmt.Println(r.Arr2[2][0]) 51 | fmt.Println(r.Arr3[0]) 52 | fmt.Println(r.St.Name) 53 | fmt.Println(r.M["key6"]) 54 | 55 | // Output: 56 | // 1 57 | // 2 58 | // 3 59 | // cat 60 | // cat 61 | // book 62 | // 6 63 | // 4 64 | // 6 65 | // jasonxiong 66 | // 6 67 | } 68 | -------------------------------------------------------------------------------- /src/framework/recordfile/recordfile.go: -------------------------------------------------------------------------------- 1 | package recordfile 2 | 3 | import ( 4 | "encoding/csv" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "reflect" 10 | "strconv" 11 | ) 12 | 13 | var Comma = '\t' 14 | var Comment = '#' 15 | 16 | type Index map[interface{}]interface{} 17 | 18 | type RecordFile struct { 19 | Comma rune 20 | Comment rune 21 | typeRecord reflect.Type 22 | records []interface{} 23 | indexes []Index 24 | } 25 | 26 | func New(st interface{}) (*RecordFile, error) { 27 | typeRecord := reflect.TypeOf(st) 28 | if typeRecord == nil || typeRecord.Kind() != reflect.Struct { 29 | return nil, errors.New("st must be a struct") 30 | } 31 | 32 | for i := 0; i < typeRecord.NumField(); i++ { 33 | f := typeRecord.Field(i) 34 | 35 | kind := f.Type.Kind() 36 | switch kind { 37 | case reflect.Bool: 38 | case reflect.Int: 39 | case reflect.Int8: 40 | case reflect.Int16: 41 | case reflect.Int32: 42 | case reflect.Int64: 43 | case reflect.Uint: 44 | case reflect.Uint8: 45 | case reflect.Uint16: 46 | case reflect.Uint32: 47 | case reflect.Uint64: 48 | case reflect.Float32: 49 | case reflect.Float64: 50 | case reflect.String: 51 | case reflect.Struct: 52 | case reflect.Array: 53 | case reflect.Slice: 54 | case reflect.Map: 55 | default: 56 | return nil, fmt.Errorf("invalid type: %v %s", 57 | f.Name, kind) 58 | } 59 | 60 | tag := f.Tag 61 | if tag == "index" { 62 | switch kind { 63 | case reflect.Struct, reflect.Slice, reflect.Map: 64 | return nil, fmt.Errorf("could not index %s field %v %v", 65 | kind, i, f.Name) 66 | } 67 | } 68 | } 69 | 70 | rf := new(RecordFile) 71 | rf.typeRecord = typeRecord 72 | 73 | return rf, nil 74 | } 75 | 76 | func (rf *RecordFile) Read(name string) error { 77 | file, err := os.Open(name) 78 | if err != nil { 79 | return err 80 | } 81 | defer file.Close() 82 | 83 | if rf.Comma == 0 { 84 | rf.Comma = Comma 85 | } 86 | if rf.Comment == 0 { 87 | rf.Comment = Comment 88 | } 89 | reader := csv.NewReader(file) 90 | reader.Comma = rf.Comma 91 | reader.Comment = rf.Comment 92 | lines, err := reader.ReadAll() 93 | if err != nil { 94 | return err 95 | } 96 | 97 | typeRecord := rf.typeRecord 98 | 99 | // make records 100 | records := make([]interface{}, len(lines)-1) 101 | 102 | // make indexes 103 | indexes := []Index{} 104 | for i := 0; i < typeRecord.NumField(); i++ { 105 | tag := typeRecord.Field(i).Tag 106 | if tag == "index" { 107 | indexes = append(indexes, make(Index)) 108 | } 109 | } 110 | 111 | for n := 1; n < len(lines); n++ { 112 | value := reflect.New(typeRecord) 113 | records[n-1] = value.Interface() 114 | record := value.Elem() 115 | 116 | line := lines[n] 117 | if len(line) != typeRecord.NumField() { 118 | return fmt.Errorf("line %v, field count mismatch: %v (file) %v (st)", 119 | n, len(line), typeRecord.NumField()) 120 | } 121 | 122 | iIndex := 0 123 | 124 | for i := 0; i < typeRecord.NumField(); i++ { 125 | f := typeRecord.Field(i) 126 | 127 | // records 128 | strField := line[i] 129 | field := record.Field(i) 130 | if !field.CanSet() { 131 | continue 132 | } 133 | 134 | var err error 135 | 136 | kind := f.Type.Kind() 137 | if kind == reflect.Bool { 138 | var v bool 139 | v, err = strconv.ParseBool(strField) 140 | if err == nil { 141 | field.SetBool(v) 142 | } 143 | } else if kind == reflect.Int || 144 | kind == reflect.Int8 || 145 | kind == reflect.Int16 || 146 | kind == reflect.Int32 || 147 | kind == reflect.Int64 { 148 | var v int64 149 | v, err = strconv.ParseInt(strField, 0, f.Type.Bits()) 150 | if err == nil { 151 | field.SetInt(v) 152 | } 153 | } else if kind == reflect.Uint || 154 | kind == reflect.Uint8 || 155 | kind == reflect.Uint16 || 156 | kind == reflect.Uint32 || 157 | kind == reflect.Uint64 { 158 | var v uint64 159 | v, err = strconv.ParseUint(strField, 0, f.Type.Bits()) 160 | if err == nil { 161 | field.SetUint(v) 162 | } 163 | } else if kind == reflect.Float32 || 164 | kind == reflect.Float64 { 165 | var v float64 166 | v, err = strconv.ParseFloat(strField, f.Type.Bits()) 167 | if err == nil { 168 | field.SetFloat(v) 169 | } 170 | } else if kind == reflect.String { 171 | field.SetString(strField) 172 | } else if kind == reflect.Struct || 173 | kind == reflect.Array || 174 | kind == reflect.Slice || 175 | kind == reflect.Map { 176 | err = json.Unmarshal([]byte(strField), field.Addr().Interface()) 177 | } 178 | 179 | if err != nil { 180 | return fmt.Errorf("parse field (row=%v, col=%v) error: %v", 181 | n, i, err) 182 | } 183 | 184 | // indexes 185 | if f.Tag == "index" { 186 | index := indexes[iIndex] 187 | iIndex++ 188 | if _, ok := index[field.Interface()]; ok { 189 | return fmt.Errorf("index error: duplicate at (row=%v, col=%v)", 190 | n, i) 191 | } 192 | index[field.Interface()] = records[n-1] 193 | } 194 | } 195 | } 196 | 197 | rf.records = records 198 | rf.indexes = indexes 199 | 200 | return nil 201 | } 202 | 203 | func (rf *RecordFile) Record(i int) interface{} { 204 | return rf.records[i] 205 | } 206 | 207 | func (rf *RecordFile) NumRecord() int { 208 | return len(rf.records) 209 | } 210 | 211 | func (rf *RecordFile) Indexes(i int) Index { 212 | if i >= len(rf.indexes) { 213 | return nil 214 | } 215 | return rf.indexes[i] 216 | } 217 | 218 | func (rf *RecordFile) Index(i interface{}) interface{} { 219 | index := rf.Indexes(0) 220 | if index == nil { 221 | return nil 222 | } 223 | return index[i] 224 | } 225 | -------------------------------------------------------------------------------- /src/framework/recordfile/test.txt: -------------------------------------------------------------------------------- 1 | 数字索引 字符串索引 数字类型 字符串类型 数组类型 嵌套数组 变长数组 结构体类型 map类型 2 | 1 one 0 knife "[1, 2]" "[[0,1], [1,2], [2,3]]" "[1, 2, 3]" "{""name"": ""jasonxiong"", ""num"": 1}" "{""key1"": 1, ""key2"": 2}" 3 | 2 two 0 cat "[3, 4]" "[[1,2], [2,3], [3,4]]" "[4, 5]" "{""name"": ""jasonxiong"", ""num"": 2}" "{""key3"": 3, ""key4"": 4}" 4 | 3 three 0 book "[5, 6]" "[[2,3], [3,4], [4,5]]" [6] "{""name"": ""jasonxiong"", ""num"": 3}" "{""key5"": 5, ""key6"": 6}" 5 | -------------------------------------------------------------------------------- /src/framework/timer/cronexpr.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | // reference: https://github.com/robfig/cron 4 | import ( 5 | "fmt" 6 | "math" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // Field name | Mandatory? | Allowed values | Allowed special characters 13 | // ---------- | ---------- | -------------- | -------------------------- 14 | // Seconds | No | 0-59 | * / , - 15 | // Minutes | Yes | 0-59 | * / , - 16 | // Hours | Yes | 0-23 | * / , - 17 | // Day of month | Yes | 1-31 | * / , - 18 | // Month | Yes | 1-12 | * / , - 19 | // Day of week | Yes | 0-6 | * / , - 20 | type CronExpr struct { 21 | sec uint64 22 | min uint64 23 | hour uint64 24 | dom uint64 25 | month uint64 26 | dow uint64 27 | } 28 | 29 | // goroutine safe 30 | func NewCronExpr(expr string) (cronExpr *CronExpr, err error) { 31 | fields := strings.Fields(expr) 32 | if len(fields) != 5 && len(fields) != 6 { 33 | err = fmt.Errorf("invalid expr %v: expected 5 or 6 fields, got %v", expr, len(fields)) 34 | return 35 | } 36 | 37 | if len(fields) == 5 { 38 | fields = append([]string{"0"}, fields...) 39 | } 40 | 41 | cronExpr = new(CronExpr) 42 | // Seconds 43 | cronExpr.sec, err = parseCronField(fields[0], 0, 59) 44 | if err != nil { 45 | goto onError 46 | } 47 | // Minutes 48 | cronExpr.min, err = parseCronField(fields[1], 0, 59) 49 | if err != nil { 50 | goto onError 51 | } 52 | // Hours 53 | cronExpr.hour, err = parseCronField(fields[2], 0, 23) 54 | if err != nil { 55 | goto onError 56 | } 57 | // Day of month 58 | cronExpr.dom, err = parseCronField(fields[3], 1, 31) 59 | if err != nil { 60 | goto onError 61 | } 62 | // Month 63 | cronExpr.month, err = parseCronField(fields[4], 1, 12) 64 | if err != nil { 65 | goto onError 66 | } 67 | // Day of week 68 | cronExpr.dow, err = parseCronField(fields[5], 0, 6) 69 | if err != nil { 70 | goto onError 71 | } 72 | return 73 | 74 | onError: 75 | err = fmt.Errorf("invalid expr %v: %v", expr, err) 76 | return 77 | } 78 | 79 | // 1. * 80 | // 2. num 81 | // 3. num-num 82 | // 4. */num 83 | // 5. num/num (means num-max/num) 84 | // 6. num-num/num 85 | func parseCronField(field string, min int, max int) (cronField uint64, err error) { 86 | fields := strings.Split(field, ",") 87 | for _, field := range fields { 88 | rangeAndIncr := strings.Split(field, "/") 89 | if len(rangeAndIncr) > 2 { 90 | err = fmt.Errorf("too many slashes: %v", field) 91 | return 92 | } 93 | 94 | // range 95 | startAndEnd := strings.Split(rangeAndIncr[0], "-") 96 | if len(startAndEnd) > 2 { 97 | err = fmt.Errorf("too many hyphens: %v", rangeAndIncr[0]) 98 | return 99 | } 100 | 101 | var start, end int 102 | if startAndEnd[0] == "*" { 103 | if len(startAndEnd) != 1 { 104 | err = fmt.Errorf("invalid range: %v", rangeAndIncr[0]) 105 | return 106 | } 107 | start = min 108 | end = max 109 | } else { 110 | // start 111 | start, err = strconv.Atoi(startAndEnd[0]) 112 | if err != nil { 113 | err = fmt.Errorf("invalid range: %v", rangeAndIncr[0]) 114 | return 115 | } 116 | // end 117 | if len(startAndEnd) == 1 { 118 | if len(rangeAndIncr) == 2 { 119 | end = max 120 | } else { 121 | end = start 122 | } 123 | } else { 124 | end, err = strconv.Atoi(startAndEnd[1]) 125 | if err != nil { 126 | err = fmt.Errorf("invalid range: %v", rangeAndIncr[0]) 127 | return 128 | } 129 | } 130 | } 131 | 132 | if start > end { 133 | err = fmt.Errorf("invalid range: %v", rangeAndIncr[0]) 134 | return 135 | } 136 | if start < min { 137 | err = fmt.Errorf("out of range [%v, %v]: %v", min, max, rangeAndIncr[0]) 138 | return 139 | } 140 | if end > max { 141 | err = fmt.Errorf("out of range [%v, %v]: %v", min, max, rangeAndIncr[0]) 142 | return 143 | } 144 | 145 | // increment 146 | var incr int 147 | if len(rangeAndIncr) == 1 { 148 | incr = 1 149 | } else { 150 | incr, err = strconv.Atoi(rangeAndIncr[1]) 151 | if err != nil { 152 | err = fmt.Errorf("invalid increment: %v", rangeAndIncr[1]) 153 | return 154 | } 155 | if incr <= 0 { 156 | err = fmt.Errorf("invalid increment: %v", rangeAndIncr[1]) 157 | return 158 | } 159 | } 160 | 161 | // cronField 162 | if incr == 1 { 163 | cronField |= ^(math.MaxUint64 << uint(end+1)) & (math.MaxUint64 << uint(start)) 164 | } else { 165 | for i := start; i <= end; i += incr { 166 | cronField |= 1 << uint(i) 167 | } 168 | } 169 | } 170 | 171 | return 172 | } 173 | 174 | func (e *CronExpr) matchDay(t time.Time) bool { 175 | // day-of-month blank 176 | if e.dom == 0xfffffffe { 177 | return 1< year+1 { 200 | return time.Time{} 201 | } 202 | 203 | // Month 204 | for 1< 0 { 37 | buf := make([]byte, conf.LenStackBuf) 38 | l := runtime.Stack(buf, false) 39 | log.Error("%v: %s", r, buf[:l]) 40 | } else { 41 | log.Error("%v", r) 42 | } 43 | } 44 | }() 45 | 46 | if t.cb != nil { 47 | t.cb() 48 | } 49 | } 50 | 51 | func (disp *Dispatcher) AfterFunc(d time.Duration, cb func()) *Timer { 52 | t := new(Timer) 53 | t.cb = cb 54 | t.t = time.AfterFunc(d, func() { 55 | disp.ChanTimer <- t 56 | }) 57 | return t 58 | } 59 | 60 | // Cron 61 | type Cron struct { 62 | t *Timer 63 | } 64 | 65 | func (c *Cron) Stop() { 66 | if c.t != nil { 67 | c.t.Stop() 68 | } 69 | } 70 | 71 | func (disp *Dispatcher) CronFunc(cronExpr *CronExpr, _cb func()) *Cron { 72 | c := new(Cron) 73 | 74 | now := time.Now() 75 | nextTime := cronExpr.Next(now) 76 | if nextTime.IsZero() { 77 | return c 78 | } 79 | 80 | // callback 81 | var cb func() 82 | cb = func() { 83 | defer _cb() 84 | 85 | now := time.Now() 86 | nextTime := cronExpr.Next(now) 87 | if nextTime.IsZero() { 88 | return 89 | } 90 | c.t = disp.AfterFunc(nextTime.Sub(now), cb) 91 | } 92 | 93 | c.t = disp.AfterFunc(nextTime.Sub(now), cb) 94 | return c 95 | } 96 | -------------------------------------------------------------------------------- /src/framework/util/deepcopy.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // reference: https://github.com/mohae/deepcopy 4 | import ( 5 | "reflect" 6 | ) 7 | 8 | func deepCopy(dst, src reflect.Value) { 9 | switch src.Kind() { 10 | case reflect.Interface: 11 | value := src.Elem() 12 | if !value.IsValid() { 13 | return 14 | } 15 | newValue := reflect.New(value.Type()).Elem() 16 | deepCopy(newValue, value) 17 | dst.Set(newValue) 18 | case reflect.Ptr: 19 | value := src.Elem() 20 | if !value.IsValid() { 21 | return 22 | } 23 | dst.Set(reflect.New(value.Type())) 24 | deepCopy(dst.Elem(), value) 25 | case reflect.Map: 26 | dst.Set(reflect.MakeMap(src.Type())) 27 | keys := src.MapKeys() 28 | for _, key := range keys { 29 | value := src.MapIndex(key) 30 | newValue := reflect.New(value.Type()).Elem() 31 | deepCopy(newValue, value) 32 | dst.SetMapIndex(key, newValue) 33 | } 34 | case reflect.Slice: 35 | dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap())) 36 | for i := 0; i < src.Len(); i++ { 37 | deepCopy(dst.Index(i), src.Index(i)) 38 | } 39 | case reflect.Struct: 40 | typeSrc := src.Type() 41 | for i := 0; i < src.NumField(); i++ { 42 | value := src.Field(i) 43 | tag := typeSrc.Field(i).Tag 44 | if value.CanSet() && tag.Get("deepcopy") != "-" { 45 | deepCopy(dst.Field(i), value) 46 | } 47 | } 48 | default: 49 | dst.Set(src) 50 | } 51 | } 52 | 53 | func DeepCopy(dst, src interface{}) { 54 | typeDst := reflect.TypeOf(dst) 55 | typeSrc := reflect.TypeOf(src) 56 | if typeDst != typeSrc { 57 | panic("DeepCopy: " + typeDst.String() + " != " + typeSrc.String()) 58 | } 59 | if typeSrc.Kind() != reflect.Ptr { 60 | panic("DeepCopy: pass arguments by address") 61 | } 62 | 63 | valueDst := reflect.ValueOf(dst).Elem() 64 | valueSrc := reflect.ValueOf(src).Elem() 65 | if !valueDst.IsValid() || !valueSrc.IsValid() { 66 | panic("DeepCopy: invalid arguments") 67 | } 68 | 69 | deepCopy(valueDst, valueSrc) 70 | } 71 | 72 | func DeepClone(v interface{}) interface{} { 73 | dst := reflect.New(reflect.TypeOf(v)).Elem() 74 | deepCopy(dst, reflect.ValueOf(v)) 75 | return dst.Interface() 76 | } 77 | -------------------------------------------------------------------------------- /src/framework/util/example_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "fmt" 5 | "framework/util" 6 | ) 7 | 8 | func ExampleMap() { 9 | m := new(util.Map) 10 | 11 | fmt.Println(m.Get("key")) 12 | m.Set("key", "value") 13 | fmt.Println(m.Get("key")) 14 | m.Del("key") 15 | fmt.Println(m.Get("key")) 16 | 17 | m.Set(1, "1") 18 | m.Set(2, 2) 19 | m.Set("3", 3) 20 | 21 | fmt.Println(m.Len()) 22 | 23 | // Output: 24 | // 25 | // value 26 | // 27 | // 3 28 | } 29 | 30 | func ExampleRandGroup() { 31 | i := util.RandGroup(0, 0, 50, 50) 32 | switch i { 33 | case 2, 3: 34 | fmt.Println("ok") 35 | } 36 | 37 | // Output: 38 | // ok 39 | } 40 | 41 | func ExampleRandInterval() { 42 | v := util.RandInterval(-1, 1) 43 | switch v { 44 | case -1, 0, 1: 45 | fmt.Println("ok") 46 | } 47 | 48 | // Output: 49 | // ok 50 | } 51 | 52 | func ExampleRandIntervalN() { 53 | r := util.RandIntervalN(-1, 0, 2) 54 | if r[0] == -1 && r[1] == 0 || 55 | r[0] == 0 && r[1] == -1 { 56 | fmt.Println("ok") 57 | } 58 | 59 | // Output: 60 | // ok 61 | } 62 | 63 | func ExampleDeepCopy() { 64 | src := []int{1, 2, 3} 65 | 66 | var dst []int 67 | util.DeepCopy(&dst, &src) 68 | 69 | for _, v := range dst { 70 | fmt.Println(v) 71 | } 72 | 73 | // Output: 74 | // 1 75 | // 2 76 | // 3 77 | } 78 | 79 | func ExampleDeepClone() { 80 | src := []int{1, 2, 3} 81 | 82 | dst := util.DeepClone(src).([]int) 83 | 84 | for _, v := range dst { 85 | fmt.Println(v) 86 | } 87 | 88 | // Output: 89 | // 1 90 | // 2 91 | // 3 92 | } 93 | -------------------------------------------------------------------------------- /src/framework/util/map.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Map struct { 8 | sync.RWMutex 9 | m map[interface{}]interface{} 10 | } 11 | 12 | func (m *Map) init() { 13 | if m.m == nil { 14 | m.m = make(map[interface{}]interface{}) 15 | } 16 | } 17 | 18 | func (m *Map) UnsafeGet(key interface{}) interface{} { 19 | if m.m == nil { 20 | return nil 21 | } else { 22 | return m.m[key] 23 | } 24 | } 25 | 26 | func (m *Map) Get(key interface{}) interface{} { 27 | m.RLock() 28 | defer m.RUnlock() 29 | return m.UnsafeGet(key) 30 | } 31 | 32 | func (m *Map) UnsafeSet(key interface{}, value interface{}) { 33 | m.init() 34 | m.m[key] = value 35 | } 36 | 37 | func (m *Map) Set(key interface{}, value interface{}) { 38 | m.Lock() 39 | defer m.Unlock() 40 | m.UnsafeSet(key, value) 41 | } 42 | 43 | func (m *Map) TestAndSet(key interface{}, value interface{}) interface{} { 44 | m.Lock() 45 | defer m.Unlock() 46 | 47 | m.init() 48 | 49 | if v, ok := m.m[key]; ok { 50 | return v 51 | } else { 52 | m.m[key] = value 53 | return nil 54 | } 55 | } 56 | 57 | func (m *Map) UnsafeDel(key interface{}) { 58 | m.init() 59 | delete(m.m, key) 60 | } 61 | 62 | func (m *Map) Del(key interface{}) { 63 | m.Lock() 64 | defer m.Unlock() 65 | m.UnsafeDel(key) 66 | } 67 | 68 | func (m *Map) UnsafeLen() int { 69 | if m.m == nil { 70 | return 0 71 | } else { 72 | return len(m.m) 73 | } 74 | } 75 | 76 | func (m *Map) Len() int { 77 | m.RLock() 78 | defer m.RUnlock() 79 | return m.UnsafeLen() 80 | } 81 | 82 | func (m *Map) UnsafeRange(f func(interface{}, interface{})) { 83 | if m.m == nil { 84 | return 85 | } 86 | for k, v := range m.m { 87 | f(k, v) 88 | } 89 | } 90 | 91 | func (m *Map) RLockRange(f func(interface{}, interface{})) { 92 | m.RLock() 93 | defer m.RUnlock() 94 | m.UnsafeRange(f) 95 | } 96 | 97 | func (m *Map) LockRange(f func(interface{}, interface{})) { 98 | m.Lock() 99 | defer m.Unlock() 100 | m.UnsafeRange(f) 101 | } 102 | -------------------------------------------------------------------------------- /src/framework/util/rand.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | func RandGroup(p ...uint32) int { 13 | if p == nil { 14 | panic("args not found") 15 | } 16 | 17 | r := make([]uint32, len(p)) 18 | for i := 0; i < len(p); i++ { 19 | if i == 0 { 20 | r[0] = p[0] 21 | } else { 22 | r[i] = r[i-1] + p[i] 23 | } 24 | } 25 | 26 | rl := r[len(r)-1] 27 | if rl == 0 { 28 | return 0 29 | } 30 | 31 | rn := uint32(rand.Int63n(int64(rl))) 32 | for i := 0; i < len(r); i++ { 33 | if rn < r[i] { 34 | return i 35 | } 36 | } 37 | 38 | panic("bug") 39 | } 40 | 41 | func RandInterval(b1, b2 int32) int32 { 42 | if b1 == b2 { 43 | return b1 44 | } 45 | 46 | min, max := int64(b1), int64(b2) 47 | if min > max { 48 | min, max = max, min 49 | } 50 | return int32(rand.Int63n(max-min+1) + min) 51 | } 52 | 53 | func RandIntervalN(b1, b2 int32, n uint32) []int32 { 54 | if b1 == b2 { 55 | return []int32{b1} 56 | } 57 | 58 | min, max := int64(b1), int64(b2) 59 | if min > max { 60 | min, max = max, min 61 | } 62 | l := max - min + 1 63 | if int64(n) > l { 64 | n = uint32(l) 65 | } 66 | 67 | r := make([]int32, n) 68 | m := make(map[int32]int32) 69 | for i := uint32(0); i < n; i++ { 70 | v := int32(rand.Int63n(l) + min) 71 | 72 | if mv, ok := m[v]; ok { 73 | r[i] = mv 74 | } else { 75 | r[i] = v 76 | } 77 | 78 | lv := int32(l - 1 + min) 79 | if v != lv { 80 | if mv, ok := m[lv]; ok { 81 | m[v] = mv 82 | } else { 83 | m[v] = lv 84 | } 85 | } 86 | 87 | l-- 88 | } 89 | 90 | return r 91 | } 92 | -------------------------------------------------------------------------------- /src/framework/util/semaphore.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | type Semaphore chan struct{} 4 | 5 | func MakeSemaphore(n int) Semaphore { 6 | return make(Semaphore, n) 7 | } 8 | 9 | func (s Semaphore) Acquire() { 10 | s <- struct{}{} 11 | } 12 | 13 | func (s Semaphore) Release() { 14 | <-s 15 | } 16 | -------------------------------------------------------------------------------- /src/framework/version.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | const version = "1.1.2" 4 | -------------------------------------------------------------------------------- /src/gamesvr/base/skeleton.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "framework/chanrpc" 5 | "framework/module" 6 | "gamesvr/svrconf" 7 | ) 8 | 9 | func NewSkeleton() *module.Skeleton { 10 | skeleton := &module.Skeleton{ 11 | GoLen: svrconf.GoLen, 12 | TimerDispatcherLen: svrconf.TimerDispatcherLen, 13 | AsynCallLen: svrconf.AsynCallLen, 14 | ChanRPCServer: chanrpc.NewServer(svrconf.ChanRPCLen), 15 | } 16 | skeleton.Init() 17 | return skeleton 18 | } 19 | -------------------------------------------------------------------------------- /src/gamesvr/cluster/external.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "gamesvr/cluster/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/gamesvr/cluster/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "gamesvr/game" 6 | "gamesvr/svrconf" 7 | ) 8 | 9 | type Module struct { 10 | *cluster.Cluster 11 | } 12 | 13 | func (m *Module) OnInit() { 14 | m.Cluster = &cluster.Cluster{ 15 | MaxMsgLen: svrconf.MaxMsgLen, 16 | AgentChanRPC: game.ChanRPC, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/gamesvr/game/external.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import ( 4 | "gamesvr/game/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /src/gamesvr/game/internal/chanrpc.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/conf" 6 | "framework/gate" 7 | "framework/log" 8 | "proto/gameproto" 9 | 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | //ClusterAgentInfo 集群Agent信息 14 | type ClusterAgentInfo struct { 15 | endpointID int32 //对端ID 16 | } 17 | 18 | var ( 19 | clusterAgent = make(map[int32]cluster.Agent) 20 | ) 21 | 22 | func init() { 23 | 24 | //创建和关闭Gate Agent 25 | skeleton.RegisterChanRPC("NewGateAgent", newGateAgent) 26 | skeleton.RegisterChanRPC("CloseGateAgent", closeGateAgent) 27 | 28 | //Gate消息的处理 29 | skeleton.RegisterChanRPC("OnGateMsg", handleGateMsg) 30 | 31 | //创建和关闭Cluster Agent 32 | skeleton.RegisterChanRPC("NewClusterAgent", newClusterAgent) 33 | skeleton.RegisterChanRPC("CloseClusterAgent", closeClusterAgent) 34 | 35 | //Cluster消息处理 36 | skeleton.RegisterChanRPC("OnClusterMsg", handleClusterMsg) 37 | } 38 | 39 | func newGateAgent(args []interface{}) { 40 | //todo jasonxiong 41 | a := args[0].(gate.Agent) 42 | _ = a 43 | } 44 | 45 | func closeGateAgent(args []interface{}) { 46 | //todo jasonxiong 47 | a := args[0].(gate.Agent) 48 | _ = a 49 | } 50 | 51 | //处理Gate的消息 52 | func handleGateMsg(args []interface{}) { 53 | data := args[0].([]byte) 54 | a := args[1].(gate.Agent) 55 | 56 | //data解包 57 | msg := &gameproto.ProtoMsg{} 58 | err := proto.Unmarshal(data, msg) 59 | if err != nil { 60 | log.Error("Failed to parse ProtoMsg from gate, err %v\n", err) 61 | return 62 | } 63 | 64 | //处理实际的消息 65 | if handler, ok := gameMsgHandlers[msg.GetMsgid()]; !ok { 66 | //不存在 67 | log.Error("Failed to handler game msg %u, handler not registered.\n", msg.GetMsgid()) 68 | } else { 69 | handler([]interface{}{a, msg.GetMsgdata()}) 70 | } 71 | 72 | return 73 | } 74 | 75 | func newClusterAgent(args []interface{}) { 76 | a := args[0].(cluster.Agent) 77 | endpointID := args[1].(int32) 78 | 79 | agentInfo := new(ClusterAgentInfo) 80 | a.SetUserData(agentInfo) 81 | 82 | if endpointID != 0 { 83 | if _, ok := clusterAgent[endpointID]; ok { 84 | //对端agent已经注册过 85 | log.Error("failed to update endpointID %d, already registered.\n", endpointID) 86 | return 87 | } 88 | 89 | //对端为Cluster服务端 90 | agentInfo.endpointID = endpointID 91 | clusterAgent[endpointID] = a 92 | 93 | log.Debug("new cluster agent, endpointID %d\n", endpointID) 94 | 95 | //通知对端本端的ID 96 | notifyClusterInfo, err := proto.Marshal(&gameproto.Cluster_UpdateInfoNotify{LocalendID: proto.Int32(conf.SvrBase.ServerID)}) 97 | if err != nil { 98 | log.Error("Failed to encode notify cluster info msg, err %v\n", err) 99 | return 100 | } 101 | 102 | notifyMsg := &gameproto.ProtoMsg{ 103 | Msgid: gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY.Enum(), 104 | Msgdata: notifyClusterInfo, 105 | } 106 | 107 | //序列化 108 | notifyData, err := proto.Marshal(notifyMsg) 109 | if err != nil { 110 | log.Error("Failed to marshal notify msg, err %v\n", err) 111 | return 112 | } 113 | 114 | a.WriteMsg(notifyData) 115 | } 116 | } 117 | 118 | func closeClusterAgent(args []interface{}) { 119 | a := args[0].(gate.Agent) 120 | endpointID := a.UserData().(*ClusterAgentInfo).endpointID 121 | 122 | //先从map中删除 123 | delete(clusterAgent, endpointID) 124 | 125 | //关闭agent 126 | a.SetUserData(nil) 127 | a.Close() 128 | 129 | log.Debug("Success to close cluster agent, endpointID %d\n", endpointID) 130 | } 131 | 132 | //处理Gate的消息 133 | func handleClusterMsg(args []interface{}) { 134 | data := args[0].([]byte) 135 | a := args[1].(gate.Agent) 136 | 137 | //data解包 138 | msg := &gameproto.ProtoMsg{} 139 | err := proto.Unmarshal(data, msg) 140 | if err != nil { 141 | log.Error("Failed to parse ProtoMsg from gate, err %v\n", err) 142 | return 143 | } 144 | 145 | //处理实际的消息 146 | if handler, ok := gameMsgHandlers[msg.GetMsgid()]; !ok { 147 | //不存在 148 | log.Error("Failed to handler game msg %u, handler not registered.\n", msg.GetMsgid()) 149 | } else { 150 | handler([]interface{}{a, msg.GetMsgdata()}) 151 | } 152 | 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /src/gamesvr/game/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/gate" 6 | "framework/log" 7 | "proto/gameproto" 8 | 9 | "github.com/golang/protobuf/proto" 10 | ) 11 | 12 | //GameMsgHandler 消息handler 13 | type GameMsgHandler func([]interface{}) 14 | 15 | var ( 16 | //Game模块消息处理 17 | gameMsgHandlers = make(map[gameproto.MsgID]GameMsgHandler) 18 | ) 19 | 20 | func init() { 21 | //集群相关消息 22 | registerHandler(gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY, handleClusterInfo) 23 | 24 | //Gate登录消息 25 | registerHandler(gameproto.MsgID_LOGINSVR_LOGIN_REQ, handleLogin) 26 | } 27 | 28 | //注册消息handler 29 | func registerHandler(id gameproto.MsgID, gameMsgHandler GameMsgHandler) { 30 | if _, ok := gameMsgHandlers[id]; ok { 31 | //已经注册过 32 | log.Error("Failed to register msg handler %u\n", id) 33 | return 34 | } 35 | 36 | gameMsgHandlers[id] = gameMsgHandler 37 | 38 | return 39 | } 40 | 41 | //处理集群信息更新 42 | func handleClusterInfo(args []interface{}) { 43 | a := args[0].(cluster.Agent) 44 | data := args[1].([]byte) 45 | 46 | //解析消息 47 | reqMsg := &gameproto.Cluster_UpdateInfoNotify{} 48 | err := proto.Unmarshal(data, reqMsg) 49 | if err != nil { 50 | log.Error("Failed to parse clustinfo msg, err %v\n", err) 51 | return 52 | } 53 | 54 | if _, ok := clusterAgent[reqMsg.GetLocalendID()]; ok { 55 | //agent已经注册 56 | log.Error("failed to update endpointid %d, already registered.\n", reqMsg.GetLocalendID()) 57 | return 58 | } 59 | 60 | //更新Cluster对端ID 61 | clusterAgent[reqMsg.GetLocalendID()] = a 62 | a.UserData().(*ClusterAgentInfo).endpointID = reqMsg.GetLocalendID() 63 | 64 | log.Debug("update cluster info, endpointid %d\n", reqMsg.GetLocalendID()) 65 | 66 | return 67 | } 68 | 69 | func handleLogin(args []interface{}) { 70 | a := args[0].(gate.Agent) 71 | data := args[1].([]byte) 72 | 73 | //解析消息 74 | reqMsg := &gameproto.LoginSvr_LoginReq{} 75 | err := proto.Unmarshal(data, reqMsg) 76 | if err != nil { 77 | log.Error("Failed to parse login msg, err %v\n", err) 78 | return 79 | } 80 | 81 | //todo jasonxiong 确认后编写登录的详细逻辑 82 | log.Debug("success to process login request, uin %u\n", reqMsg.GetUin()) 83 | 84 | //发送返回 85 | respLogin, err := proto.Marshal(&gameproto.LoginSvr_LoginResp{IResult: proto.Int32(0)}) 86 | if err != nil { 87 | log.Error("Failed to encode resp login msg, err %v\n", err) 88 | return 89 | } 90 | 91 | respMsg := &gameproto.ProtoMsg{ 92 | Msgid: gameproto.MsgID_LOGINSVR_LOGIN_RESP.Enum(), 93 | Uin: proto.Uint32(reqMsg.GetUin()), 94 | Msgdata: respLogin, 95 | } 96 | 97 | //序列化 98 | respData, err := proto.Marshal(respMsg) 99 | if err != nil { 100 | log.Error("Failed to marshal resp msg, err %v\n", err) 101 | return 102 | } 103 | 104 | a.WriteMsg(respData) 105 | 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /src/gamesvr/game/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/module" 5 | "lobbysvr/base" 6 | ) 7 | 8 | var ( 9 | skeleton = base.NewSkeleton() 10 | ChanRPC = skeleton.ChanRPCServer 11 | ) 12 | 13 | type Module struct { 14 | *module.Skeleton 15 | } 16 | 17 | func (m *Module) OnInit() { 18 | m.Skeleton = skeleton 19 | } 20 | 21 | func (m *Module) OnDestroy() { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/gamesvr/gamedata/reader.go: -------------------------------------------------------------------------------- 1 | package gamedata 2 | 3 | import ( 4 | "framework/log" 5 | "framework/recordfile" 6 | "reflect" 7 | ) 8 | 9 | func readRf(st interface{}) *recordfile.RecordFile { 10 | rf, err := recordfile.New(st) 11 | if err != nil { 12 | log.Fatal("%v", err) 13 | } 14 | fn := reflect.TypeOf(st).Name() + ".txt" 15 | err = rf.Read("gamedata/" + fn) 16 | if err != nil { 17 | log.Fatal("%v: %v", fn, err) 18 | } 19 | 20 | return rf 21 | } 22 | -------------------------------------------------------------------------------- /src/gamesvr/gate/external.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "gamesvr/gate/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/gamesvr/gate/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/conf" 5 | "framework/gate" 6 | "gamesvr/game" 7 | "gamesvr/svrconf" 8 | ) 9 | 10 | type Module struct { 11 | *gate.Gate 12 | } 13 | 14 | func (m *Module) OnInit() { 15 | m.Gate = &gate.Gate{ 16 | MaxConnNum: svrconf.Server.MaxConnNum, 17 | PendingWriteNum: conf.SvrBase.PendingWriteNum, 18 | MaxMsgLen: svrconf.MaxMsgLen, 19 | WSAddr: svrconf.Server.WSAddr, 20 | HTTPTimeout: svrconf.HTTPTimeout, 21 | TCPAddr: svrconf.Server.TCPAddr, 22 | LenMsgLen: svrconf.LenMsgLen, 23 | LittleEndian: svrconf.LittleEndian, 24 | AgentChanRPC: game.ChanRPC, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/gamesvr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gamesvr/cluster" 5 | "gamesvr/game" 6 | "gamesvr/gate" 7 | 8 | "framework" 9 | ) 10 | 11 | func main() { 12 | framework.Run( 13 | game.Module, 14 | gate.Module, 15 | cluster.Module, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/gamesvr/svrconf/json.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "encoding/json" 5 | "framework/conf" 6 | "framework/log" 7 | "io/ioutil" 8 | ) 9 | 10 | //Server 服务器配置 11 | var Server struct { 12 | WSAddr string 13 | TCPAddr string 14 | MaxConnNum int 15 | SvrBaseConfig *conf.BaseConf 16 | } 17 | 18 | func init() { 19 | data, err := ioutil.ReadFile("conf/gamesvr.json") 20 | if err != nil { 21 | log.Fatal("%v", err) 22 | } 23 | 24 | Server.SvrBaseConfig = &conf.SvrBase 25 | err = json.Unmarshal(data, &Server) 26 | if err != nil { 27 | log.Fatal("%v", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/gamesvr/svrconf/svrconf.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var ( 8 | // gate conf 9 | PendingWriteNum = 2000 10 | MaxMsgLen uint32 = 4096 11 | HTTPTimeout = 10 * time.Second 12 | LenMsgLen = 2 13 | LittleEndian = false 14 | 15 | // skeleton conf 16 | GoLen = 10000 17 | TimerDispatcherLen = 10000 18 | AsynCallLen = 10000 19 | ChanRPCLen = 10000 20 | ) 21 | -------------------------------------------------------------------------------- /src/lobbysvr/base/skeleton.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "framework/chanrpc" 5 | "framework/module" 6 | "lobbysvr/svrconf" 7 | ) 8 | 9 | func NewSkeleton() *module.Skeleton { 10 | skeleton := &module.Skeleton{ 11 | GoLen: svrconf.GoLen, 12 | TimerDispatcherLen: svrconf.TimerDispatcherLen, 13 | AsynCallLen: svrconf.AsynCallLen, 14 | ChanRPCServer: chanrpc.NewServer(svrconf.ChanRPCLen), 15 | } 16 | skeleton.Init() 17 | return skeleton 18 | } 19 | -------------------------------------------------------------------------------- /src/lobbysvr/cluster/external.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "lobbysvr/cluster/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/lobbysvr/cluster/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "lobbysvr/game" 6 | "lobbysvr/svrconf" 7 | ) 8 | 9 | type Module struct { 10 | *cluster.Cluster 11 | } 12 | 13 | func (m *Module) OnInit() { 14 | m.Cluster = &cluster.Cluster{ 15 | MaxMsgLen: svrconf.MaxMsgLen, 16 | AgentChanRPC: game.ChanRPC, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lobbysvr/game/external.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import ( 4 | "lobbysvr/game/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /src/lobbysvr/game/internal/chanrpc.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/conf" 6 | "framework/gate" 7 | "framework/log" 8 | "proto/gameproto" 9 | 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | //ClusterAgentInfo 集群Agent信息 14 | type ClusterAgentInfo struct { 15 | endpointID int32 //对端ID 16 | } 17 | 18 | var ( 19 | clusterAgent = make(map[int32]cluster.Agent) 20 | ) 21 | 22 | func init() { 23 | //创建和关闭Cluster Agent 24 | skeleton.RegisterChanRPC("NewClusterAgent", newClusterAgent) 25 | skeleton.RegisterChanRPC("CloseClusterAgent", closeClusterAgent) 26 | 27 | //Cluster消息处理 28 | skeleton.RegisterChanRPC("OnClusterMsg", handleClusterMsg) 29 | } 30 | 31 | func newClusterAgent(args []interface{}) { 32 | a := args[0].(cluster.Agent) 33 | endpointID := args[1].(int32) 34 | 35 | agentInfo := new(ClusterAgentInfo) 36 | a.SetUserData(agentInfo) 37 | 38 | if endpointID != 0 { 39 | if _, ok := clusterAgent[endpointID]; ok { 40 | //对端agent已经注册过 41 | log.Error("failed to update endpointID %d, already registered.\n", endpointID) 42 | return 43 | } 44 | 45 | //对端为Cluster服务端 46 | agentInfo.endpointID = endpointID 47 | clusterAgent[endpointID] = a 48 | 49 | log.Debug("new cluster agent, endpointID %d\n", endpointID) 50 | 51 | //通知对端本端的ID 52 | notifyClusterInfo, err := proto.Marshal(&gameproto.Cluster_UpdateInfoNotify{LocalendID: proto.Int32(conf.SvrBase.ServerID)}) 53 | if err != nil { 54 | log.Error("Failed to encode notify cluster info msg, err %v\n", err) 55 | return 56 | } 57 | 58 | notifyMsg := &gameproto.ProtoMsg{ 59 | Msgid: gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY.Enum(), 60 | Msgdata: notifyClusterInfo, 61 | } 62 | 63 | //序列化 64 | notifyData, err := proto.Marshal(notifyMsg) 65 | if err != nil { 66 | log.Error("Failed to marshal notify msg, err %v\n", err) 67 | return 68 | } 69 | 70 | a.WriteMsg(notifyData) 71 | } 72 | } 73 | 74 | func closeClusterAgent(args []interface{}) { 75 | a := args[0].(gate.Agent) 76 | endpointID := a.UserData().(*ClusterAgentInfo).endpointID 77 | 78 | //先从map中删除 79 | delete(clusterAgent, endpointID) 80 | 81 | //关闭agent 82 | a.SetUserData(nil) 83 | a.Close() 84 | 85 | log.Debug("Success to close cluster agent, endpointID %d\n", endpointID) 86 | } 87 | 88 | //处理Gate的消息 89 | func handleClusterMsg(args []interface{}) { 90 | data := args[0].([]byte) 91 | a := args[1].(gate.Agent) 92 | 93 | //data解包 94 | msg := &gameproto.ProtoMsg{} 95 | err := proto.Unmarshal(data, msg) 96 | if err != nil { 97 | log.Error("Failed to parse ProtoMsg from gate, err %v\n", err) 98 | return 99 | } 100 | 101 | //处理实际的消息 102 | if handler, ok := gameMsgHandlers[msg.GetMsgid()]; !ok { 103 | //不存在 104 | log.Error("Failed to handler game msg %u, handler not registered.\n", msg.GetMsgid()) 105 | } else { 106 | handler([]interface{}{a, msg.GetMsgdata()}) 107 | } 108 | 109 | return 110 | } 111 | -------------------------------------------------------------------------------- /src/lobbysvr/game/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/log" 6 | "proto/gameproto" 7 | 8 | "github.com/golang/protobuf/proto" 9 | ) 10 | 11 | //GameMsgHandler 消息handler 12 | type GameMsgHandler func([]interface{}) 13 | 14 | var ( 15 | //Game模块消息处理 16 | gameMsgHandlers = make(map[gameproto.MsgID]GameMsgHandler) 17 | ) 18 | 19 | func init() { 20 | //集群相关消息 21 | registerHandler(gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY, handleClusterInfo) 22 | } 23 | 24 | //注册消息handler 25 | func registerHandler(id gameproto.MsgID, gameMsgHandler GameMsgHandler) { 26 | if _, ok := gameMsgHandlers[id]; ok { 27 | //已经注册过 28 | log.Error("Failed to register msg handler %u\n", id) 29 | return 30 | } 31 | 32 | gameMsgHandlers[id] = gameMsgHandler 33 | 34 | return 35 | } 36 | 37 | //处理集群信息更新 38 | func handleClusterInfo(args []interface{}) { 39 | a := args[0].(cluster.Agent) 40 | data := args[1].([]byte) 41 | 42 | //解析消息 43 | reqMsg := &gameproto.Cluster_UpdateInfoNotify{} 44 | err := proto.Unmarshal(data, reqMsg) 45 | if err != nil { 46 | log.Error("Failed to parse clustinfo msg, err %v\n", err) 47 | return 48 | } 49 | 50 | if _, ok := clusterAgent[reqMsg.GetLocalendID()]; ok { 51 | //agent已经注册 52 | log.Error("failed to update endpointid %d, already registered.\n", reqMsg.GetLocalendID()) 53 | return 54 | } 55 | 56 | //更新Cluster对端ID 57 | clusterAgent[reqMsg.GetLocalendID()] = a 58 | a.UserData().(*ClusterAgentInfo).endpointID = reqMsg.GetLocalendID() 59 | 60 | log.Debug("update cluster info, endpointid %d\n", reqMsg.GetLocalendID()) 61 | 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /src/lobbysvr/game/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/module" 5 | "lobbysvr/base" 6 | ) 7 | 8 | var ( 9 | skeleton = base.NewSkeleton() 10 | ChanRPC = skeleton.ChanRPCServer 11 | ) 12 | 13 | type Module struct { 14 | *module.Skeleton 15 | } 16 | 17 | func (m *Module) OnInit() { 18 | m.Skeleton = skeleton 19 | } 20 | 21 | func (m *Module) OnDestroy() { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/lobbysvr/gamedata/reader.go: -------------------------------------------------------------------------------- 1 | package gamedata 2 | 3 | import ( 4 | "framework/log" 5 | "framework/recordfile" 6 | "reflect" 7 | ) 8 | 9 | func readRf(st interface{}) *recordfile.RecordFile { 10 | rf, err := recordfile.New(st) 11 | if err != nil { 12 | log.Fatal("%v", err) 13 | } 14 | fn := reflect.TypeOf(st).Name() + ".txt" 15 | err = rf.Read("gamedata/" + fn) 16 | if err != nil { 17 | log.Fatal("%v: %v", fn, err) 18 | } 19 | 20 | return rf 21 | } 22 | -------------------------------------------------------------------------------- /src/lobbysvr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //lobbysvr 为内网服务器,所以只有Cluster,没有Gate 4 | 5 | import ( 6 | "lobbysvr/cluster" 7 | "lobbysvr/game" 8 | 9 | "framework" 10 | ) 11 | 12 | func main() { 13 | framework.Run( 14 | game.Module, 15 | cluster.Module, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/lobbysvr/svrconf/json.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "encoding/json" 5 | "framework/conf" 6 | "framework/log" 7 | "io/ioutil" 8 | ) 9 | 10 | //Server 服务器配置 11 | var Server struct { 12 | WSAddr string 13 | TCPAddr string 14 | MaxConnNum int 15 | SvrBaseConfig *conf.BaseConf 16 | } 17 | 18 | func init() { 19 | data, err := ioutil.ReadFile("conf/lobbysvr.json") 20 | if err != nil { 21 | log.Fatal("%v", err) 22 | } 23 | 24 | Server.SvrBaseConfig = &conf.SvrBase 25 | err = json.Unmarshal(data, &Server) 26 | if err != nil { 27 | log.Fatal("%v", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lobbysvr/svrconf/svrconf.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var ( 8 | // gate conf 9 | PendingWriteNum = 2000 10 | MaxMsgLen uint32 = 4096 11 | HTTPTimeout = 10 * time.Second 12 | LenMsgLen = 2 13 | LittleEndian = false 14 | 15 | // skeleton conf 16 | GoLen = 10000 17 | TimerDispatcherLen = 10000 18 | AsynCallLen = 10000 19 | ChanRPCLen = 10000 20 | ) 21 | -------------------------------------------------------------------------------- /src/loginsvr/base/skeleton.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "framework/chanrpc" 5 | "framework/module" 6 | "loginsvr/svrconf" 7 | ) 8 | 9 | func NewSkeleton() *module.Skeleton { 10 | skeleton := &module.Skeleton{ 11 | GoLen: svrconf.GoLen, 12 | TimerDispatcherLen: svrconf.TimerDispatcherLen, 13 | AsynCallLen: svrconf.AsynCallLen, 14 | ChanRPCServer: chanrpc.NewServer(svrconf.ChanRPCLen), 15 | } 16 | skeleton.Init() 17 | return skeleton 18 | } 19 | -------------------------------------------------------------------------------- /src/loginsvr/cluster/external.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "loginsvr/cluster/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/loginsvr/cluster/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "loginsvr/game" 6 | "loginsvr/svrconf" 7 | ) 8 | 9 | type Module struct { 10 | *cluster.Cluster 11 | } 12 | 13 | func (m *Module) OnInit() { 14 | m.Cluster = &cluster.Cluster{ 15 | MaxMsgLen: svrconf.MaxMsgLen, 16 | AgentChanRPC: game.ChanRPC, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/loginsvr/game/external.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import ( 4 | "loginsvr/game/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ChanRPC = internal.ChanRPC 10 | ) 11 | -------------------------------------------------------------------------------- /src/loginsvr/game/internal/chanrpc.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/conf" 6 | "framework/gate" 7 | "framework/log" 8 | "proto/gameproto" 9 | 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | //ClusterAgentInfo 集群Agent信息 14 | type ClusterAgentInfo struct { 15 | endpointID int32 //对端ID 16 | } 17 | 18 | var ( 19 | clusterAgent = make(map[int32]cluster.Agent) 20 | ) 21 | 22 | func init() { 23 | 24 | //创建和关闭Gate Agent 25 | skeleton.RegisterChanRPC("NewGateAgent", newGateAgent) 26 | skeleton.RegisterChanRPC("CloseGateAgent", closeGateAgent) 27 | 28 | //Gate消息的处理 29 | skeleton.RegisterChanRPC("OnGateMsg", handleGateMsg) 30 | 31 | //创建和关闭Cluster Agent 32 | skeleton.RegisterChanRPC("NewClusterAgent", newClusterAgent) 33 | skeleton.RegisterChanRPC("CloseClusterAgent", closeClusterAgent) 34 | 35 | //Cluster消息处理 36 | skeleton.RegisterChanRPC("OnClusterMsg", handleClusterMsg) 37 | } 38 | 39 | func newGateAgent(args []interface{}) { 40 | //todo jasonxiong 41 | a := args[0].(gate.Agent) 42 | _ = a 43 | } 44 | 45 | func closeGateAgent(args []interface{}) { 46 | //todo jasonxiong 47 | a := args[0].(gate.Agent) 48 | _ = a 49 | } 50 | 51 | //处理Gate的消息 52 | func handleGateMsg(args []interface{}) { 53 | data := args[0].([]byte) 54 | a := args[1].(gate.Agent) 55 | 56 | //data解包 57 | msg := &gameproto.ProtoMsg{} 58 | err := proto.Unmarshal(data, msg) 59 | if err != nil { 60 | log.Error("Failed to parse ProtoMsg from gate, err %v\n", err) 61 | return 62 | } 63 | 64 | //处理实际的消息 65 | if handler, ok := gameMsgHandlers[msg.GetMsgid()]; !ok { 66 | //不存在 67 | log.Error("Failed to handler game msg %u, handler not registered.\n", msg.GetMsgid()) 68 | } else { 69 | handler([]interface{}{a, msg.GetMsgdata()}) 70 | } 71 | 72 | return 73 | } 74 | 75 | func newClusterAgent(args []interface{}) { 76 | a := args[0].(cluster.Agent) 77 | endpointID := args[1].(int32) 78 | 79 | log.Debug("new cluster agent, endpointID %d\n", endpointID) 80 | 81 | agentInfo := new(ClusterAgentInfo) 82 | a.SetUserData(agentInfo) 83 | 84 | if endpointID != 0 { 85 | if _, ok := clusterAgent[endpointID]; ok { 86 | //对端agent已经注册过 87 | log.Error("failed to update endpointID %d, already registered.\n", endpointID) 88 | return 89 | } 90 | 91 | //对端为Cluster服务端 92 | agentInfo.endpointID = endpointID 93 | clusterAgent[endpointID] = a 94 | 95 | //通知对端本端的ID 96 | notifyClusterInfo, err := proto.Marshal(&gameproto.Cluster_UpdateInfoNotify{LocalendID: proto.Int32(conf.SvrBase.ServerID)}) 97 | if err != nil { 98 | log.Error("Failed to encode notify cluster info msg, err %v\n", err) 99 | return 100 | } 101 | 102 | notifyMsg := &gameproto.ProtoMsg{ 103 | Msgid: gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY.Enum(), 104 | Msgdata: notifyClusterInfo, 105 | } 106 | 107 | //序列化 108 | notifyData, err := proto.Marshal(notifyMsg) 109 | if err != nil { 110 | log.Error("Failed to marshal notify msg, err %v\n", err) 111 | return 112 | } 113 | 114 | a.WriteMsg(notifyData) 115 | } 116 | } 117 | 118 | func closeClusterAgent(args []interface{}) { 119 | a := args[0].(gate.Agent) 120 | endpointID := a.UserData().(*ClusterAgentInfo).endpointID 121 | 122 | //先从map中删除 123 | delete(clusterAgent, endpointID) 124 | 125 | //关闭agent 126 | a.SetUserData(nil) 127 | a.Close() 128 | 129 | log.Debug("Success to close cluster agent, endpointID %d\n", endpointID) 130 | } 131 | 132 | //处理Gate的消息 133 | func handleClusterMsg(args []interface{}) { 134 | data := args[0].([]byte) 135 | a := args[1].(gate.Agent) 136 | 137 | //data解包 138 | msg := &gameproto.ProtoMsg{} 139 | err := proto.Unmarshal(data, msg) 140 | if err != nil { 141 | log.Error("Failed to parse ProtoMsg from gate, err %v\n", err) 142 | return 143 | } 144 | 145 | //处理实际的消息 146 | if handler, ok := gameMsgHandlers[msg.GetMsgid()]; !ok { 147 | //不存在 148 | log.Error("Failed to handler game msg %u, handler not registered.\n", msg.GetMsgid()) 149 | } else { 150 | handler([]interface{}{a, msg.GetMsgdata()}) 151 | } 152 | 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /src/loginsvr/game/internal/handler.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/cluster" 5 | "framework/gate" 6 | "framework/log" 7 | "proto/gameproto" 8 | 9 | "github.com/golang/protobuf/proto" 10 | ) 11 | 12 | //GameMsgHandler 消息handler 13 | type GameMsgHandler func([]interface{}) 14 | 15 | var ( 16 | //Game模块消息处理 17 | gameMsgHandlers = make(map[gameproto.MsgID]GameMsgHandler) 18 | ) 19 | 20 | func init() { 21 | //集群相关消息 22 | registerHandler(gameproto.MsgID_CLUSTER_UPDATEINFO_NOTIFY, handleClusterInfo) 23 | 24 | //Gate登录消息 25 | registerHandler(gameproto.MsgID_LOGINSVR_LOGIN_REQ, handleLogin) 26 | } 27 | 28 | //注册消息handler 29 | func registerHandler(id gameproto.MsgID, gameMsgHandler GameMsgHandler) { 30 | if _, ok := gameMsgHandlers[id]; ok { 31 | //已经注册过 32 | log.Error("Failed to register msg handler %u\n", id) 33 | return 34 | } 35 | 36 | gameMsgHandlers[id] = gameMsgHandler 37 | 38 | return 39 | } 40 | 41 | //处理集群信息更新 42 | func handleClusterInfo(args []interface{}) { 43 | a := args[0].(cluster.Agent) 44 | data := args[1].([]byte) 45 | 46 | //解析消息 47 | reqMsg := &gameproto.Cluster_UpdateInfoNotify{} 48 | err := proto.Unmarshal(data, reqMsg) 49 | if err != nil { 50 | log.Error("Failed to parse clustinfo msg, err %v\n", err) 51 | return 52 | } 53 | 54 | if _, ok := clusterAgent[reqMsg.GetLocalendID()]; ok { 55 | //agent已经注册 56 | log.Error("failed to update endpointid %d, already registered.\n", reqMsg.GetLocalendID()) 57 | return 58 | } 59 | 60 | //更新Cluster对端ID 61 | clusterAgent[reqMsg.GetLocalendID()] = a 62 | a.UserData().(*ClusterAgentInfo).endpointID = reqMsg.GetLocalendID() 63 | 64 | log.Debug("update cluster info, endpointid %d\n", reqMsg.GetLocalendID()) 65 | 66 | return 67 | } 68 | 69 | func handleLogin(args []interface{}) { 70 | a := args[0].(gate.Agent) 71 | data := args[1].([]byte) 72 | 73 | //解析消息 74 | reqMsg := &gameproto.LoginSvr_LoginReq{} 75 | err := proto.Unmarshal(data, reqMsg) 76 | if err != nil { 77 | log.Error("Failed to parse login msg, err %v\n", err) 78 | return 79 | } 80 | 81 | //todo jasonxiong 确认后编写登录的详细逻辑 82 | log.Debug("success to process login request, uin %u\n", reqMsg.GetUin()) 83 | 84 | //发送返回 85 | respLogin, err := proto.Marshal(&gameproto.LoginSvr_LoginResp{IResult: proto.Int32(0)}) 86 | if err != nil { 87 | log.Error("Failed to encode resp login msg, err %v\n", err) 88 | return 89 | } 90 | 91 | respMsg := &gameproto.ProtoMsg{ 92 | Msgid: gameproto.MsgID_LOGINSVR_LOGIN_RESP.Enum(), 93 | Uin: proto.Uint32(reqMsg.GetUin()), 94 | Msgdata: respLogin, 95 | } 96 | 97 | //序列化 98 | respData, err := proto.Marshal(respMsg) 99 | if err != nil { 100 | log.Error("Failed to marshal resp msg, err %v\n", err) 101 | return 102 | } 103 | 104 | a.WriteMsg(respData) 105 | 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /src/loginsvr/game/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/module" 5 | "lobbysvr/base" 6 | ) 7 | 8 | var ( 9 | skeleton = base.NewSkeleton() 10 | ChanRPC = skeleton.ChanRPCServer 11 | ) 12 | 13 | type Module struct { 14 | *module.Skeleton 15 | } 16 | 17 | func (m *Module) OnInit() { 18 | m.Skeleton = skeleton 19 | } 20 | 21 | func (m *Module) OnDestroy() { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/loginsvr/gamedata/reader.go: -------------------------------------------------------------------------------- 1 | package gamedata 2 | 3 | import ( 4 | "framework/log" 5 | "framework/recordfile" 6 | "reflect" 7 | ) 8 | 9 | func readRf(st interface{}) *recordfile.RecordFile { 10 | rf, err := recordfile.New(st) 11 | if err != nil { 12 | log.Fatal("%v", err) 13 | } 14 | fn := reflect.TypeOf(st).Name() + ".txt" 15 | err = rf.Read("gamedata/" + fn) 16 | if err != nil { 17 | log.Fatal("%v: %v", fn, err) 18 | } 19 | 20 | return rf 21 | } 22 | -------------------------------------------------------------------------------- /src/loginsvr/gate/external.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "loginsvr/gate/internal" 5 | ) 6 | 7 | var ( 8 | Module = new(internal.Module) 9 | ) 10 | -------------------------------------------------------------------------------- /src/loginsvr/gate/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "framework/conf" 5 | "framework/gate" 6 | "loginsvr/game" 7 | "loginsvr/svrconf" 8 | ) 9 | 10 | type Module struct { 11 | *gate.Gate 12 | } 13 | 14 | func (m *Module) OnInit() { 15 | m.Gate = &gate.Gate{ 16 | MaxConnNum: svrconf.Server.MaxConnNum, 17 | PendingWriteNum: conf.SvrBase.PendingWriteNum, 18 | MaxMsgLen: svrconf.MaxMsgLen, 19 | WSAddr: svrconf.Server.WSAddr, 20 | HTTPTimeout: svrconf.HTTPTimeout, 21 | TCPAddr: svrconf.Server.TCPAddr, 22 | LenMsgLen: svrconf.LenMsgLen, 23 | LittleEndian: svrconf.LittleEndian, 24 | AgentChanRPC: game.ChanRPC, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/loginsvr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "loginsvr/cluster" 5 | "loginsvr/game" 6 | "loginsvr/gate" 7 | 8 | "framework" 9 | ) 10 | 11 | func main() { 12 | framework.Run( 13 | game.Module, 14 | gate.Module, 15 | cluster.Module, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/loginsvr/svrconf/json.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "encoding/json" 5 | "framework/conf" 6 | "framework/log" 7 | "io/ioutil" 8 | ) 9 | 10 | //Server 服务器配置 11 | var Server struct { 12 | WSAddr string 13 | TCPAddr string 14 | MaxConnNum int 15 | SvrBaseConfig *conf.BaseConf 16 | } 17 | 18 | func init() { 19 | data, err := ioutil.ReadFile("conf/loginsvr.json") 20 | if err != nil { 21 | log.Fatal("%v", err) 22 | } 23 | 24 | Server.SvrBaseConfig = &conf.SvrBase 25 | err = json.Unmarshal(data, &Server) 26 | if err != nil { 27 | log.Fatal("%v", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/loginsvr/svrconf/svrconf.go: -------------------------------------------------------------------------------- 1 | package svrconf 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var ( 8 | // gate conf 9 | PendingWriteNum = 2000 10 | MaxMsgLen uint32 = 4096 11 | HTTPTimeout = 10 * time.Second 12 | LenMsgLen = 2 13 | LittleEndian = false 14 | 15 | // skeleton conf 16 | GoLen = 10000 17 | TimerDispatcherLen = 10000 18 | AsynCallLen = 10000 19 | ChanRPCLen = 10000 20 | ) 21 | -------------------------------------------------------------------------------- /src/proto/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm gameproto/* 4 | 5 | cd conf/ 6 | 7 | ../protoc --plugin=../protoc-gen-go --go_out=../gameproto/ *proto 8 | 9 | -------------------------------------------------------------------------------- /src/proto/conf/gamemsg.proto: -------------------------------------------------------------------------------- 1 | package gameproto; 2 | 3 | //客户端服务器通信的消息 4 | message ProtoMsg 5 | { 6 | optional MsgID msgid = 1; //消息ID 7 | optional uint32 uin = 2; //玩家uin 8 | optional bytes msgdata = 3; //实际消息体序列化后的二进制 9 | } 10 | 11 | //定义消息ID 12 | enum MsgID 13 | { 14 | MSGID_INVALID = 0; //非法的消息 15 | 16 | //系统消息ID 17 | CLUSTER_UPDATEINFO_NOTIFY = 1; //通知集群对端本地端点信息 18 | 19 | //登录服务消息ID 20 | LOGINSVR_LOGIN_REQ = 30; //登录请求 21 | LOGINSVR_LOGIN_RESP = 31; //登录返回 22 | } 23 | 24 | //以下为服务器消息,业务无关 25 | 26 | //集群信息通知,对应CLUSTER_UPDATEINFO_NOTIFY 27 | message Cluster_UpdateInfoNotify 28 | { 29 | optional int32 localendID = 1; //本端服务器ID 30 | } -------------------------------------------------------------------------------- /src/proto/conf/login.proto: -------------------------------------------------------------------------------- 1 | package gameproto; 2 | 3 | //登录请求,对应LOGINSVR_LOGIN_REQ 4 | message LoginSvr_LoginReq 5 | { 6 | optional uint32 uin = 1; //玩家的uin 7 | optional string strSessionKey = 2; //玩家登录的Key 8 | } 9 | 10 | //登录返回 11 | message LoginSvr_LoginResp 12 | { 13 | optional int32 iResult = 1; //登录的结果 14 | //todo jasonxiong 后续增加玩家详细数据 15 | } -------------------------------------------------------------------------------- /src/proto/gameproto/gamemsg.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: gamemsg.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package gameproto is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | gamemsg.proto 10 | login.proto 11 | 12 | It has these top-level messages: 13 | ProtoMsg 14 | Cluster_UpdateInfoNotify 15 | LoginSvr_LoginReq 16 | LoginSvr_LoginResp 17 | */ 18 | package gameproto 19 | 20 | import proto "github.com/golang/protobuf/proto" 21 | import fmt "fmt" 22 | import math "math" 23 | 24 | // Reference imports to suppress errors if they are not otherwise used. 25 | var _ = proto.Marshal 26 | var _ = fmt.Errorf 27 | var _ = math.Inf 28 | 29 | // This is a compile-time assertion to ensure that this generated file 30 | // is compatible with the proto package it is being compiled against. 31 | // A compilation error at this line likely means your copy of the 32 | // proto package needs to be updated. 33 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 34 | 35 | // 定义消息ID 36 | type MsgID int32 37 | 38 | const ( 39 | MsgID_MSGID_INVALID MsgID = 0 40 | // 系统消息ID 41 | MsgID_CLUSTER_UPDATEINFO_NOTIFY MsgID = 1 42 | // 登录服务消息ID 43 | MsgID_LOGINSVR_LOGIN_REQ MsgID = 30 44 | MsgID_LOGINSVR_LOGIN_RESP MsgID = 31 45 | ) 46 | 47 | var MsgID_name = map[int32]string{ 48 | 0: "MSGID_INVALID", 49 | 1: "CLUSTER_UPDATEINFO_NOTIFY", 50 | 30: "LOGINSVR_LOGIN_REQ", 51 | 31: "LOGINSVR_LOGIN_RESP", 52 | } 53 | var MsgID_value = map[string]int32{ 54 | "MSGID_INVALID": 0, 55 | "CLUSTER_UPDATEINFO_NOTIFY": 1, 56 | "LOGINSVR_LOGIN_REQ": 30, 57 | "LOGINSVR_LOGIN_RESP": 31, 58 | } 59 | 60 | func (x MsgID) Enum() *MsgID { 61 | p := new(MsgID) 62 | *p = x 63 | return p 64 | } 65 | func (x MsgID) String() string { 66 | return proto.EnumName(MsgID_name, int32(x)) 67 | } 68 | func (x *MsgID) UnmarshalJSON(data []byte) error { 69 | value, err := proto.UnmarshalJSONEnum(MsgID_value, data, "MsgID") 70 | if err != nil { 71 | return err 72 | } 73 | *x = MsgID(value) 74 | return nil 75 | } 76 | func (MsgID) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 77 | 78 | // 客户端服务器通信的消息 79 | type ProtoMsg struct { 80 | Msgid *MsgID `protobuf:"varint,1,opt,name=msgid,enum=gameproto.MsgID" json:"msgid,omitempty"` 81 | Uin *uint32 `protobuf:"varint,2,opt,name=uin" json:"uin,omitempty"` 82 | Msgdata []byte `protobuf:"bytes,3,opt,name=msgdata" json:"msgdata,omitempty"` 83 | XXX_unrecognized []byte `json:"-"` 84 | } 85 | 86 | func (m *ProtoMsg) Reset() { *m = ProtoMsg{} } 87 | func (m *ProtoMsg) String() string { return proto.CompactTextString(m) } 88 | func (*ProtoMsg) ProtoMessage() {} 89 | func (*ProtoMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 90 | 91 | func (m *ProtoMsg) GetMsgid() MsgID { 92 | if m != nil && m.Msgid != nil { 93 | return *m.Msgid 94 | } 95 | return MsgID_MSGID_INVALID 96 | } 97 | 98 | func (m *ProtoMsg) GetUin() uint32 { 99 | if m != nil && m.Uin != nil { 100 | return *m.Uin 101 | } 102 | return 0 103 | } 104 | 105 | func (m *ProtoMsg) GetMsgdata() []byte { 106 | if m != nil { 107 | return m.Msgdata 108 | } 109 | return nil 110 | } 111 | 112 | // 集群信息通知,对应CLUSTER_UPDATEINFO_NOTIFY 113 | type Cluster_UpdateInfoNotify struct { 114 | LocalendID *int32 `protobuf:"varint,1,opt,name=localendID" json:"localendID,omitempty"` 115 | XXX_unrecognized []byte `json:"-"` 116 | } 117 | 118 | func (m *Cluster_UpdateInfoNotify) Reset() { *m = Cluster_UpdateInfoNotify{} } 119 | func (m *Cluster_UpdateInfoNotify) String() string { return proto.CompactTextString(m) } 120 | func (*Cluster_UpdateInfoNotify) ProtoMessage() {} 121 | func (*Cluster_UpdateInfoNotify) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 122 | 123 | func (m *Cluster_UpdateInfoNotify) GetLocalendID() int32 { 124 | if m != nil && m.LocalendID != nil { 125 | return *m.LocalendID 126 | } 127 | return 0 128 | } 129 | 130 | func init() { 131 | proto.RegisterType((*ProtoMsg)(nil), "gameproto.ProtoMsg") 132 | proto.RegisterType((*Cluster_UpdateInfoNotify)(nil), "gameproto.Cluster_UpdateInfoNotify") 133 | proto.RegisterEnum("gameproto.MsgID", MsgID_name, MsgID_value) 134 | } 135 | 136 | func init() { proto.RegisterFile("gamemsg.proto", fileDescriptor0) } 137 | 138 | var fileDescriptor0 = []byte{ 139 | // 239 bytes of a gzipped FileDescriptorProto 140 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0xcc, 0x4f, 0x4f, 0xb3, 0x40, 141 | 0x10, 0xc7, 0xf1, 0x87, 0xa7, 0x21, 0xea, 0x28, 0x8a, 0x63, 0xa2, 0x78, 0xd0, 0x92, 0x9e, 0x88, 142 | 0x07, 0x0e, 0xbe, 0x83, 0xa6, 0xd0, 0x66, 0x12, 0x58, 0x90, 0x3f, 0x4d, 0x3c, 0x6d, 0x36, 0x42, 143 | 0x37, 0x18, 0x60, 0x9b, 0xee, 0xf6, 0xe0, 0xbb, 0x37, 0xc5, 0xc4, 0x8b, 0xb7, 0xcf, 0x4c, 0x7e, 144 | 0xf9, 0x82, 0x23, 0xc5, 0xd0, 0x0e, 0x5a, 0x86, 0xfb, 0x83, 0x32, 0x0a, 0x2f, 0x4e, 0xe7, 0xc4, 145 | 0x05, 0xc1, 0x79, 0x7e, 0x42, 0xaa, 0x25, 0xce, 0xc1, 0x1e, 0xb4, 0xec, 0x1a, 0xcf, 0xf2, 0xad, 146 | 0xe0, 0xfa, 0xd5, 0x0d, 0x7f, 0x67, 0x61, 0xaa, 0x25, 0x45, 0x78, 0x09, 0xb3, 0x63, 0x37, 0x7a, 147 | 0xff, 0x7d, 0x2b, 0x70, 0xf0, 0x06, 0xce, 0x06, 0x2d, 0x1b, 0x61, 0x84, 0x37, 0xf3, 0xad, 0xe0, 148 | 0x6a, 0x11, 0x82, 0xb7, 0xea, 0x8f, 0xda, 0xb4, 0x07, 0x5e, 0xef, 0x1b, 0x61, 0x5a, 0x1a, 0x77, 149 | 0x8a, 0x29, 0xd3, 0xed, 0xbe, 0x10, 0x01, 0x7a, 0xf5, 0x21, 0xfa, 0x76, 0x6c, 0x28, 0x9a, 0xfa, 150 | 0xf6, 0xcb, 0x27, 0xd8, 0x3f, 0xd9, 0x5b, 0x70, 0xd2, 0x72, 0x43, 0x11, 0x27, 0xb6, 0x5d, 0x26, 151 | 0x14, 0xb9, 0xff, 0xf0, 0x09, 0x1e, 0x57, 0x49, 0x5d, 0x56, 0x71, 0xc1, 0xeb, 0x3c, 0x5a, 0x56, 152 | 0x31, 0xb1, 0x75, 0xc6, 0x59, 0x56, 0xd1, 0xfa, 0xdd, 0xb5, 0xf0, 0x1e, 0x30, 0xc9, 0x36, 0xc4, 153 | 0xca, 0x6d, 0xc1, 0x27, 0xf0, 0x22, 0x7e, 0x73, 0x9f, 0xf1, 0x01, 0xee, 0xfe, 0xfc, 0xcb, 0xdc, 154 | 0x9d, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x4a, 0x0b, 0xa5, 0x6d, 0x01, 0x01, 0x00, 0x00, 155 | } 156 | -------------------------------------------------------------------------------- /src/proto/gameproto/login.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: login.proto 3 | // DO NOT EDIT! 4 | 5 | package gameproto 6 | 7 | import proto "github.com/golang/protobuf/proto" 8 | import fmt "fmt" 9 | import math "math" 10 | 11 | // Reference imports to suppress errors if they are not otherwise used. 12 | var _ = proto.Marshal 13 | var _ = fmt.Errorf 14 | var _ = math.Inf 15 | 16 | // 登录请求,对应LOGINSVR_LOGIN_REQ 17 | type LoginSvr_LoginReq struct { 18 | Uin *uint32 `protobuf:"varint,1,opt,name=uin" json:"uin,omitempty"` 19 | StrSessionKey *string `protobuf:"bytes,2,opt,name=strSessionKey" json:"strSessionKey,omitempty"` 20 | XXX_unrecognized []byte `json:"-"` 21 | } 22 | 23 | func (m *LoginSvr_LoginReq) Reset() { *m = LoginSvr_LoginReq{} } 24 | func (m *LoginSvr_LoginReq) String() string { return proto.CompactTextString(m) } 25 | func (*LoginSvr_LoginReq) ProtoMessage() {} 26 | func (*LoginSvr_LoginReq) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } 27 | 28 | func (m *LoginSvr_LoginReq) GetUin() uint32 { 29 | if m != nil && m.Uin != nil { 30 | return *m.Uin 31 | } 32 | return 0 33 | } 34 | 35 | func (m *LoginSvr_LoginReq) GetStrSessionKey() string { 36 | if m != nil && m.StrSessionKey != nil { 37 | return *m.StrSessionKey 38 | } 39 | return "" 40 | } 41 | 42 | // 登录返回 43 | type LoginSvr_LoginResp struct { 44 | IResult *int32 `protobuf:"varint,1,opt,name=iResult" json:"iResult,omitempty"` 45 | XXX_unrecognized []byte `json:"-"` 46 | } 47 | 48 | func (m *LoginSvr_LoginResp) Reset() { *m = LoginSvr_LoginResp{} } 49 | func (m *LoginSvr_LoginResp) String() string { return proto.CompactTextString(m) } 50 | func (*LoginSvr_LoginResp) ProtoMessage() {} 51 | func (*LoginSvr_LoginResp) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } 52 | 53 | func (m *LoginSvr_LoginResp) GetIResult() int32 { 54 | if m != nil && m.IResult != nil { 55 | return *m.IResult 56 | } 57 | return 0 58 | } 59 | 60 | func init() { 61 | proto.RegisterType((*LoginSvr_LoginReq)(nil), "gameproto.LoginSvr_LoginReq") 62 | proto.RegisterType((*LoginSvr_LoginResp)(nil), "gameproto.LoginSvr_LoginResp") 63 | } 64 | 65 | func init() { proto.RegisterFile("login.proto", fileDescriptor1) } 66 | 67 | var fileDescriptor1 = []byte{ 68 | // 119 bytes of a gzipped FileDescriptorProto 69 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0xc9, 0x4f, 0xcf, 70 | 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4c, 0x4f, 0xcc, 0x4d, 0x05, 0x33, 0x95, 71 | 0xcc, 0xb9, 0x04, 0x7d, 0x40, 0x32, 0xc1, 0x65, 0x45, 0xf1, 0x60, 0x46, 0x50, 0x6a, 0xa1, 0x10, 72 | 0x37, 0x17, 0x73, 0x69, 0x66, 0x9e, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xaf, 0x90, 0x28, 0x17, 0x6f, 73 | 0x71, 0x49, 0x51, 0x70, 0x6a, 0x71, 0x71, 0x66, 0x7e, 0x9e, 0x77, 0x6a, 0xa5, 0x04, 0x93, 0x02, 74 | 0xa3, 0x06, 0xa7, 0x92, 0x2a, 0x97, 0x10, 0xba, 0xc6, 0xe2, 0x02, 0x21, 0x7e, 0x2e, 0xf6, 0xcc, 75 | 0xa0, 0xd4, 0xe2, 0xd2, 0x9c, 0x12, 0xb0, 0x6e, 0x56, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xba, 76 | 0x40, 0xb7, 0x0d, 0x78, 0x00, 0x00, 0x00, 77 | } 78 | -------------------------------------------------------------------------------- /src/proto/protoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonxiong/GoFramework/770fc784beaa9eba4fc7b73411633148eb341b9f/src/proto/protoc -------------------------------------------------------------------------------- /src/proto/protoc-gen-go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonxiong/GoFramework/770fc784beaa9eba4fc7b73411633148eb341b9f/src/proto/protoc-gen-go -------------------------------------------------------------------------------- /src/proto/protoc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonxiong/GoFramework/770fc784beaa9eba4fc7b73411633148eb341b9f/src/proto/protoc.exe --------------------------------------------------------------------------------