├── .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
--------------------------------------------------------------------------------