用户是如何匹配进入到游戏的?

简介

目前domo中实现了一个完整的游戏,中国象棋,实现了对应用cocos creator实现的客户端。之后做了匹配逻辑,匹配逻辑的表现是仿照LOL英雄联盟做的。

匹配进入游戏流程

  1. c2s 玩家请求hallserver匹配
  2. s2s hallserver请求matchserver匹配
  3. matchserver 定时执行撮合逻辑
  4. s2s matchserver找到撮合对象后,请求对应gameserver游戏服分配房间,生成token并记录到redis
  5. s2s matchserver 通知 到对应玩家的hallserver匹配成功
  6. s2c hallserver 通知 客户端匹配成功
  7. cs2 客户端请求hallserver接受对局
  8. s2s hallserver 通知 matchserver接受对局
  9. s2s matchserver 统计到所有玩家接受对局后,请求对应游戏服记录游戏房间信息,通知玩家对应hallserver对局开始
  10. s2c hallserver 通知玩家对局开始

流程1 c2s 玩家请求hallserver匹配

涉及skynet_fly技术

* cluster rpc

涉及服务

* hallserver
* matchserver
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
--请求匹配
function M.do_match_game(player_id, pack_body)
--log.info("do_match_game >>> ",player_id, pack_body)
local game_id = pack_body.game_id
local game_server = GAME_ID_ENUM[game_id]
if not game_server then
log.error("do_match_game not exists gameid ", game_id)
return nil, errorcode.GAME_NOT_EXISTS, "GAME_NOT_EXISTS"
end

local game_room_info = game_redis.get_game_room_info(player_id)
if game_room_info and next(game_room_info) then
log.error("exists game_room_info ",player_id, game_room_info)
return
end

--log.info("do_match_game2 >>> ",player_id, pack_body)
if not rpc_matchserver_match.match(game_server, player_id) then
return nil
end
--log.info("do_match_game3 >>> ",player_id, pack_body)
--回复匹配
match_msg:match_game_res(player_id, {game_id = game_id})

return true
end

玩家请求匹配游戏,指定游戏ID,通过游戏ID索引找到对应游戏服名称,然后通过rpc_matchserver_match.match(game_server, player_id)rpc传递到匹配服。

流程2 s2s hallserver请求matchserver匹配

涉及skynet_fly技术

* cluster rpc

涉及服务

* hallserver
* matchserver
1
2
3
4
5
6
7
--匹配
function M.match(game_server, player_id)
local ret = frpc_client:instance("matchserver", "match_m", game_server):one_balance_call_by_name("match", player_id)
if not ret then return end

return table.unpack(ret.result)
end

frpc_client:instance("matchserver", "match_m", game_server):one_balance_call_by_name("match", player_id)这个语句的意思是发给matchserver中的match_m服务one_balance_call_by_name意思是在所有match_minstance_namegame_server的服务中进行简单轮询负载均衡。

match_m的启动配置

1
2
3
4
5
6
7
8
9
--匹配逻辑
match_m = {
launch_seq = 5,
launch_num = 2,
mod_args = {
{instance_name = "chinese_chess"},
{instance_name = "digitalbomb"},
}
},

可以看到,match_m启动了2个,一个instance_name = chinese_chess, 一个为 digitalbomb
在我们想为chinese_chess匹配时就是这样调用 frpc_client:instance("matchserver", "match_m", 'chinese_chess'):one_balance_call_by_name("match", player_id)

流程3 s2s matchserver 定时执行撮合逻辑

match_m启动了2个间隔5秒的定时器,一个用于同步指定instance_name游戏服的游戏房间信息用于负载均衡。一个用于撮合匹配。

流程4 s2s matchserver找到撮合对象后,请求对应gameserver游戏服分配房间,生成token并记录到redis

涉及skynet_fly技术

* cluster rpc
* redis script

涉及服务

* hallserver
* matchserver

匹配成功后,会先把玩家剔除出集合,这里需要用到redis的脚本调用,由于这个过程有异步,可能玩家在这个过程中已经取消匹配了,所以需要用脚本检查所有玩家是否存在,然后再剔除出去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--清除出redis匹配集合
local script_str = [[
local key = KEYS[1]
for i = 1,#ARGV do
local player_id = ARGV[i]
local rank = redis.call('zrank', key, player_id)
if not rank then
return 0
end
end

for i = 1,#ARGV do
redis.call('zrem', key, ARGV[i])
end

return 1
]]

流程5 s2s matchserver 通知 到对应玩家的hallserver匹配成功

剔除之后记录关键的房间信息,然后通过player_id找到玩家对应的大厅服的服务ID,通过byid_mod_send通知到对应的游戏服。

1
2
3
4
5
6
7
8
9
--通知大厅服,匹配成功了
for j,player_id in ipairs(match_list) do
local svr_id = player_util.get_svr_id_by_player_id(player_id)
--log.info("get_svr_id_by_player_id >>> ",player_id, svr_id)
local hallcli = frpc_client:instance("hallserver", "room_game_hall_m")
hallcli:set_svr_id(svr_id) --指定服
hallcli:set_mod_num(player_id) --指定mod_num
hallcli:byid_mod_send("match_succ", player_id, session_id, GAME_ID_ENUM[module_info.get_cfg().instance_name], ENUM.MATCH_ACCEPT_TIME_OUT)
end

流程6 s2c hallserver 通知 客户端匹配成功

hallserver 再通知玩家匹配成功了

流程7 cs2 客户端请求hallserver接受对局

玩家需要在指定时间内接受对局,超时或者拒绝后,客户端等待定时器结束,进行重试匹配。

流程8 s2s hallserver 通知 matchserver接受对局

match server 收到接受对局,记录接受数量

流程9 s2s matchserver 统计到所有玩家接受对局后,请求对应游戏服记录游戏房间信息,通知玩家对应hallserver对局开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
--通知游戏服记录游戏房间信息
g_game_cli:set_svr_id(svr_id)
local ret = g_game_cli:byid_mod_call("set_game_room_info", game_info_map) --记录游戏房间信息
--log.info("set_game_room_info ret >>>", ret)
if not ret then
log.warn("set_game_room_info err ", svr_name, svr_id, table_id)
return
end

for l_player_id,info in pairs(game_info_map) do
--通知加入对局
local svr_id = player_util.get_svr_id_by_player_id(l_player_id)
--log.info("get_svr_id_by_player_id >>> ",l_player_id, svr_id)
local hallcli = frpc_client:instance("hallserver", "room_game_hall_m")
hallcli:set_svr_id(svr_id) --指定服
hallcli:set_mod_num(l_player_id) --指定mod_num
hallcli:byid_mod_send("match_join_game", l_player_id, info.token, host, table_id)
end

set_game_room_info 会把房间信息记录到redis,在退出房间的时候清除,这样方便做重连。

流程10 s2c hallserver 通知玩家对局开始

hallserver 在通知玩家进入游戏,此时客户端可以拿着游戏服的host和token去进入游戏了。

重连处理

当玩家重新登录时,hallserver大厅服,会去尝试拿玩家的游戏服房间信息,如果房间还存在会发给玩家进去重连,如果房间已经不存在,删除信息避免玩家不能再进去匹配。


用户是如何匹配进入到游戏的?
https://huahua132.github.io/2023/05/03/skynet_fly_word/word_4/E_q/
作者
huahua132
发布于
2023年5月3日
许可协议