大厅服如何对模块功能进行拆分隔离?

简介

通常一个游戏大厅会有很多功能模块,比如用户信息管理,活动大厅,任务,邮件,充值,商店,这些功能通过不会一个人开发,会很多人一起开发,如果都写在一个文件中,耦合度太高,合作编码太容易修改冲突。所以在编码逻辑上要做到尽量隔离。

skynet_fly_demo是如何做的?

一个独立的功能模块无法是需要处理5种事件

  1. 客户端消息
  2. 服务器之间的消息
  3. 模块之间的接口调用
  4. 用户的登录,登出事情
  5. 自己注册的定时器任务

关于第五条,非常简单,只是使用定时器即可

1,2,4 通过抽象出通用的hall_plug就可以做到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

local log = require "skynet-fly.log"
local ws_pbnet_util = require "skynet-fly.utils.net.ws_pbnet_util"
local pb_netpack = require "skynet-fly.netpack.pb_netpack"
local timer = require "skynet-fly.timer"
local errors_msg = require "gamecommon.msg.errors_msg"
local skynet = require "skynet"
local g_modules_list = require "hall.hall"

local assert = assert
local ipairs = ipairs
local pairs = pairs

local g_interface_mgr = nil

local M = {}

--指定解包函数
M.unpack = ws_pbnet_util.unpack
M.send = ws_pbnet_util.send
M.broadcast = ws_pbnet_util.broadcast
M.disconn_time_out = timer.minute --掉线一分钟就清理

--初始化
function M.init(interface_mgr)
--加载协议
pb_netpack.load('../../commonlualib/gamecommon/proto')
pb_netpack.load('./proto')
g_interface_mgr = interface_mgr
errors_msg = errors_msg:new(interface_mgr)

--注册handle
for _, m in ipairs(g_modules_list) do
local handle = m.handle
for packname,func in pairs(handle) do
g_interface_mgr:handle(packname, func)
end
end

--初始化
for _, m in ipairs(g_modules_list) do
m.init(interface_mgr)
end
end

local function on_login(player_id)
for _, m in ipairs(g_modules_list) do
if m.on_login then
m.on_login(player_id)
end
end
end

--连接成功
function M.connect(player_id)
skynet.fork(on_login, player_id)
return {
isreconnect = 0,
}
end

--掉线
function M.disconnect(player_id)
for _, m in ipairs(g_modules_list) do
if m.on_disconnect then
m.on_disconnect(player_id)
end
end
end

local function on_reconnect(player_id)
for _, m in ipairs(g_modules_list) do
if m.on_reconnect then
m.on_reconnect(player_id)
end
end
end
--重连
function M.reconnect(player_id)
skynet.fork(on_reconnect, player_id)
return {
isreconnect = 1,
}
end

--登出
function M.goout(player_id)
for _, m in ipairs(g_modules_list) do
if m.on_loginout then
m.on_loginout(player_id)
end
end
end

M.register_cmd = {}

--设置CMD命令
for _, m in ipairs(g_modules_list) do
local register_cmd = m.register_cmd
for cmdname,func in pairs(register_cmd) do
assert(not M.register_cmd[cmdname], "exists cmdname: " .. cmdname)
M.register_cmd[cmdname] = func
end
end

-- 客户端消息处理结束
function M.handle_end(player_id, packname, pack_body, ret, errcode, errmsg)
if not ret then
log.info("handle_end err >>> ", packname, ret, errcode, errmsg)
errors_msg:errors(player_id, errcode, errmsg, packname)
end
end

-- 离开房间回调
function M.leave_table(player_id, table_name, table_id)
--离开房间说明对局结束了,就直接踢掉吧
skynet.fork(g_interface_mgr.goout, g_interface_mgr, player_id)
end

return M

最关键的一行代码就是 local g_modules_list = require "hall.hall"
引入hall_plug的服务只需要在hall文件夹下创建hall.lua,文件hall.lua再去require对应的模块入口即可。

hall.lua

1
2
3
4
5
6
return {
require "hall.player.player",
require "hall.match.match",
require "hall.item.item",
require "hall.game_record.game_record"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

local match_logic = require "hall.match.match_logic"


local M = {}

function M.init(interface_mgr)
match_logic.init(interface_mgr)
end

function M.on_login(player_id)
match_logic.on_login(player_id)
end

function M.on_reconnect(player_id)
match_logic.on_reconnect(player_id)
end

M.handle = {
--匹配
['.hallserver_match.MatchGameReq'] = function(player_id, packname, pack_body)
return match_logic.do_match_game(player_id, pack_body)
end,
--取消匹配
['.hallserver_match.CancelMatchGameReq'] = function(player_id, packname, pack_body)
return match_logic.do_cancel_match_game(player_id, pack_body)
end,
--接受对局
['.hallserver_match.AcceptMatchReq'] = function(player_id, packname, pack_body)
return match_logic.do_accept_match(player_id, pack_body)
end,
}

local CMD = {}

--匹配成功
function CMD.match_succ(...)
return match_logic.cmd_match_succ(...)
end

--加入对局
function CMD.match_join_game(...)
return match_logic.cmd_join_game(...)
end

M.register_cmd = CMD

return M

服务启动之后,会根据require的顺序调用init函数。

客户端消息处理

通过在M.handle中实现对应消息名处理函数。

服务器之间的消息

通过register_cmd注册服务之间的消息处理函数。

用户的登录,登出事情

通过实现 on_login接口来实现,调用顺序按照require顺序执行。

模块之间的接口调用

通过各模块提供interface.lua文件来实现接口调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local error = error
local M = {}

--这是player对外的接口 这里写定义,player_logic写实现

--获取玩家列表
function M.get_online_list()
error("调用了未实现的接口 get_online_list")
end

--获取玩家信息
function M.get_info(player_id)
error("调用了未实现的接口 get_info")
end

return M

比如这是player模块定义的接口,player逻辑模块需要重写这些接口,否则其他模块调用就会报错。


大厅服如何对模块功能进行拆分隔离?
https://huahua132.github.io/2023/05/03/skynet_fly_word/word_4/D_q/
作者
huahua132
发布于
2023年5月3日
许可协议