nakama modules 配制
1.如何使用nakama实现MMO
首先我们看到了nakama支持
Authoritative Multiplayer (权威多人游戏)
而传统的mmo的模式都是Asynchronous real-time authoritative multiplayer(异步实时(权威多人游戏))
nakama权威多人游戏概念
而nakama官方提供了一系列的概念去实现多人游戏
1.Match handler
用于管理所有服务器端函数。
2.Tick rate
代表服务器处理/更新状态的频率, 数值越大,更新的越快,客户端更流畅,但是服务器压力也会越大
3.Match state
这个是nakama给游戏中系统分配的内存,可以视作为游戏信息缓存
4.Match Label
匹配标签是一个字符串值,可通过匹配列表 API 用于过滤匹配项。
5.Host node
主机节点负责维护内存中的匹配状态并分配 CPU 资源以按滴答率执行循环,集群中的所有节点都可以立即访问匹配列表和匹配参与者的详细信息
nakama权威多人游戏功能方法
1.手动创建比赛
func CreateMatchRPC(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
params := make(map[string]interface{})
if err := json.Unmarshal([]byte(payload), ¶ms); err != nil {
return "", err
}
modulename := "pingpong" // Name with which match handler was registered in InitModule, see example above.
if matchId, err := nk.MatchCreate(ctx, modulename, params); err != nil {
return "", err
} else {
return matchId, nil // 创建成功后可以获得一个比赛id。通过该id用户可以进行加入
}
}
// Register as RPC function
if err := initializer.RegisterRpc("create_match_rpc", CreateMatchRPC); err != nil {
logger.Error("Unable to register: %v", err)
return err
}
```ß
2.Matchmaker比赛匹配器
```go
func MakeMatch(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, entries []runtime.MatchmakerEntry) (string, error) {
for _, e := range entries {
logger.Info("Matched user '%s' named '%s'", e.GetPresence().GetUserId(), e.GetPresence().GetUsername())
for k, v := range e.GetProperties() {
logger.Info("Matched on '%s' value '%v'", k, v)
}
}
matchId, err := nk.MatchCreate(ctx, "pingpong", map[string]interface{}{"invited": entries})
if err != nil {
return "", err
}
return matchId, nil // 创建成功后可以获得一个比赛id。通过该id用户可以进行加入
}
// Register as RPC function 0
if err := initializer.RegisterMatchmakerMatched(MakeMatch); err != nil {
logger.Error("Unable to register: %v", err)
return err
}
3.Match listings 匹配列表
limit := 10
isAuthoritative := true
label := "skill=100-150"
min_size := 0
max_size := 4
// 搜索满足条件的游戏列表
if matches, err := nk.MatchList(ctx, limit, isAuthoritative, label, min_size, max_size, ""); err != nil {
logger.WithField("err", err).Error("Match list error.")
} else {
for _, match := range matches {
logger.Info("Match id %s", match.GetMatchId())
}
}
nakama 匹配处理API(Match handler)
1.MatchInit(ctx, logger, db, nk, params) -> (state, tickrate, label)
// 这将在比赛开始时调用一次
func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
state := &MatchState{Debug: true} // Define custom MatchState in the code as per your game's requirements
tickRate := 1 // Call MatchLoop() every 1s.
label := "skill=100-150" // Custom label that will be used to filter match listings.
return state, tickRate, label
}
2.MatchJoinAttempt(ctx, logger, db, nk, dispatcher, tick, state, presence, metadata) -> (state, result, reason)
// 用户尝试加入游戏
func (m *Match) MatchJoinAttempt(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presence runtime.Presence, metadata map[string]string) (interface{}, bool, string) {
result := true // 是否允许加入
// 这里可以加入一些权限判断 (比如说是否被封号啊,或是是否实命,是否防沉迷)
// Custom code to process match join attempt.
return state, result, ""
}
3.MatchJoin(ctx、logger、db、nk、dispatcher、tick、state、presences)-> state
// 用户加入游戏后
func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presences []runtime.Presence) interface{} {
// Custom code to process match join and send updated state to a joining or re-joining user.
// 可以判断是否是第一次进入游戏 第一次则进行新手初始化
// 非第一次如何还原用户状态
return state
}
4.MatchLeave(ctx、logger、db、nk、dispatcher、tick、state、presences)-> state
// 用户离开游戏后
func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, presences []runtime.Presence) interface{} {
// Custom code to handle a disconnected/leaving user.
// 用户离开/丢失链接游戏
// 这里应该做一次状态保存
return state
}
5.MatchLoop(ctx, logger, db, nk, dispatcher, tick, state, messages) -> state
// 状态更新循环
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
// Custom code to:
// - Process the messages received.
// - Update the match state based on the messages and time elapsed.
// - Broadcast new data messages to match participants.
return state
}
6.MatchTerminate(ctx, logger, db, nk, dispatcher, tick, state, graceSeconds) -> state
// 正常关闭游戏
func (m *Match) MatchTerminate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, graceSeconds int) interface{} {
// Custom code to process the termination of match.
return state
}
nakama运行时API(Match runtime)
1.BroadcastMessage(opCode, data, presences, sender) -> error
// 发送广播
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
var opCode int64 = 123 // 操作码
var data []byte = []byte("test") // 数据字节切片
// 用作消息目标或nil发送到整个比赛的出席信息列表(比赛参与者的子集)
// 将消息标记为“发件人”的存在,或nil。
dispatcher.BroadcastMessage(opcodeHello, b, nil, nil) // Broadcast to all match participants.
return state
}
2.MatchKick(presences) -> error
// 踢出用户
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
if tick >= 10 {
// For example we'll kick everyone that sends a message on or after tick 10.
for _, message := range messages {
// Each message implements the runtime.Presence interface to identify its sender.
dispatcher.MatchKick(message)
}
}
return state
}
3.MatchLabelUpdate(label) -> error
// 更新匹配处理相关配制
func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state interface{}, messages []runtime.MatchData) interface{} {
// As an example update the match label in the 10th match tick.
if tick == 10 {
dispatcher.MatchLabelUpdate("Crossed 10 ticks!")
}
return state
}