目录

Unity网络同步框架-Nakama研究四

【Unity网络同步框架 - Nakama研究(四)】

【Unity网络同步框架 - Nakama研究(四)】

关于Nakama的源码问题,Nakama的源码官方是不建议修改的,不建议重构以添加新的功能,推荐使用嵌入式运行库,也就是使用lua,gotypescript进行扩展。

分析

  1. 扩展使用的语言中Go作为源码语言,能进行断点调试,而且性能高效,适合原生的服务端用户书写。相对应的,tslua就更加适合比如cocos creator或者使用lua进行Unity开发的人员书写,各有对应,遗憾的是目前官方对这两种语言都不支持断点调试,https://i-blog.csdnimg.cn/direct/722c6c7e02ad416c81a4b35764e021cc.jpeg#pic_center
  2. 官方的案例中使用了ts进行扩展(在PiratePanic中能看到),在此之前,我们需要做一些工作,就是让Nakama能够识别你的扩展目录,在服务器的Docker配置中,https://i-blog.csdnimg.cn/direct/15ce828b8e6e45d4805721415eceecb4.png#pic_center这一行就是你指定你的扩展的文件所存放的位置,比如我放两个lua文件ltest.luamain.lua进去,Nakama会自动读取对应位置的文件信息,然后打印出来https://i-blog.csdnimg.cn/direct/e97b98ce5c6043e185f74c5c69c8946c.png#pic_center,载入的顺序是按照你的文件名来排序的,日志里面也显示了你在里面注册了哪些rpc方法,你也能在客户端直接调用。

创建权威比赛

  • 权威比赛的一个关键点就是房间内没人也依旧会存在,而且可以自定义房间内的比赛逻辑,包括如何开始如何结束等等。逻辑也很简单,就是那几个关键的函数方法:match_join,match_leave,match_loop(主要是这三个),下面是我写着测试的代码:main.lualtest.lua local nk = require(“nakama”) nk.logger_info(“Lua模块进入——”) – 创建比赛 local state = { label = “比赛一” } local match_id = nk.match_create(“ltest”, state) local state_2 = { label = “比赛二” } local match_id_2 = nk.match_create(“ltest”, state_2) local state_3 = { label = “比赛三” } local match_id_3 = nk.match_create(“ltest”, state_3)

function healthcheck_rpc(content, payload) nk.logger_info(“Healthcheck RPC called”) return nk.json_encode({[“success”] = true}) end function getmatchlist_rpc(content, payload) – 列出当前所有比赛 local limit = 10 local authoritative = false local label = nil local min_size = 1 local max_size = 10 local matches = nk.match_list(limit, authoritative, label, min_size, max_size) – 构建返回的 JSON 结构 local matchList = {} for _, m in ipairs(matches) do table.insert(matchList, { match_id = m.match_id, size = m.size, max_size = m.max_size, authoritative = m.is_authoritative }) end – 返回 JSON 结构 return nk.json_encode({ matches = matchList }) end nk.register_rpc(healthcheck_rpc, “healthcheck_lua”) nk.register_rpc(getmatchlist_rpc, “getmatchlist_lua”) local M = {} local nk = require(“nakama”) function M.match_init(context, setupstate) local gamestate = { presences = {}, name = “”, min_size = 1, max_size = 10, is_authoritative = false, label = setupstate.label } – 设置 tick 率 local tickrate = 30 – 设置比赛标签 local label = gamestate.label or "" nk.logger_warn(“match_init”) return gamestate, tickrate, label end function M.match_join_attempt(context, dispatcher, tick, state, presence, metadata) local acceptuser = true nk.logger_warn(“match_join_attempt”) return state, acceptuser end function M.match_join(context, dispatcher, tick, state, presences) nk.logger_warn(“match_join”) – 先添加新玩家到状态 for _, presence in ipairs(presences) do state.presences[presence.session_id] = presence end – 发送玩家加入通知 for _, presence in ipairs(presences) do – 构造加入消息 local join_message = { op = 10, – 新操作码表示玩家加入 type = “PLAYER_JOINED”, timestamp = os.time(), data = { user = { id = presence.user_id, name = presence.username }, session = presence.session_id } } – 构建收件人列表(排除自己) local recipients = {} for _, p in pairs(state.presences) do if p.session_id ~= presence.session_id then table.insert(recipients, p) end end – 发送加入广播 if #recipients > 0 then dispatcher.broadcast_message(10, nk.json_encode(join_message), recipients) nk.logger_info(string.format(“Player %s(%s) joined the match”, presence.user_id, presence.username)) end – 给新玩家发送现有玩家列表 local existing_players = {} for _, p in pairs(state.presences) do if p.session_id ~= presence.session_id then table.insert(existing_players, { user_id = p.user_id, username = p.username, session_id = p.session_id }) end end if #existing_players > 0 then local existing_msg = { op = 11, type = “EXISTING_PLAYERS”, data = existing_players } dispatcher.broadcast_message(11, nk.json_encode(existing_msg), { presence }) end end – 原有存储数据逻辑(保持兼容) for _, presence in pairs(state.presences) do local storageObjectId = { { collection = “test”, key = “key1”, user_id = presence.user_id } } local storageObject = nk.storage_read(storageObjectId) if storageObject then local message = { op = 99, data = storageObject } – 使用新通知系统 dispatcher.broadcast_message(99, nk.json_encode(message)) else nk.logger_warn(“storageObject为空,查询的user id为” .. presence.user_id) end end return state end – presences表示离开的人数 function M.match_leave(context, dispatcher, tick, state, presences) – 遍历所有离开的玩家 for _, presence in ipairs(presences) do – 构造离开消息 local leave_message = { UserId = presence.user_id, UserName = presence.username, SessionId = presence.session_id, Reason = “player_left” } – 构建收件人列表(排除离开的玩家) local recipients = {} for _, p in pairs(state.presences) do if p.session_id ~= presence.session_id then table.insert(recipients, p) end end – 发送离开通知(使用op_code 6表示玩家离开) if #recipients > 0 then dispatcher.broadcast_message(6, nk.json_encode(leave_message), recipients) nk.logger_info(string.format(“Player %s(%s) left the match”, presence.user_id, presence.username)) end – 从状态中移除玩家 state.presences[presence.session_id] = nil end return state end – 处理比赛人员交互操作 function M.match_loop(context, dispatcher, tick, state, messages) if(messages ~= nil) then for _, m in ipairs(messages) do local recipients = {} for _, p in pairs(state.presences) do if p.user_id ~= m.sender.user_id then table.insert(recipients, p) end end if #recipients > 0 then if(m ~= nil and m.op_code ~= nil) then dispatcher.broadcast_message(m.op_code, m.data, recipients) end end end end return state end – 用于在服务器关闭时,优雅地处理比赛状态,确保比赛能够正确结束并通知客户端 function M.match_terminate(context, dispatcher, tick, state, grace_seconds) local message = “Server shutting down in " .. grace_seconds .. " seconds” dispatcher.broadcast_message(2, message) return nil end – 用于在用户正式加入比赛之前,提前预留位置或进行一些预处理 function M.match_signal(context, dispatcher, tick, state, data) return state, “signal received: " .. data end return M 对了,还有一点麻烦的是,vscode里面没有代码提示,可以搞个前人写好的库,我找了下,在github上能找到[地址]( Intellisense-Module-for-Lua),能补充一下缺失的提示框(但是错误提示依旧没有)

控制台使用

  • 控制台如果你想要其他的图形化的比如Grafana 啥的,也能自己搭建,不过目前我看这个控制大多数都能满足,包括查看用户,查看聊天信息,查看比赛(房间)信息,然后主动调用接口,还是挺方便的,这个没什么好讲的https://i-blog.csdnimg.cn/direct/0fe2b1a25ab84b92a33a95c479ff7c4c.png#pic_center

结语,Nakama相关的一些使用就到这,再深的讲就是一些服务器的布置,负载均衡,集群啥的了(集群需要Nakama的企业版),Nakama本身自带的一些功能比如排行榜,好友群组,连接facebook,x啥的我都没试。再深入研究要等下次有机会了