game/server/main.go
2026-04-20 16:07:22 +08:00

151 lines
4.8 KiB
Go
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"database/sql"
"fmt"
"math/rand"
"os"
"strings"
"time"
"wuziqi-server/config"
"wuziqi-server/handlers"
"github.com/heroiclabs/nakama-common/runtime"
)
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
logger.Info("Nakama Go Module Loaded - Refactored Version")
// 随机数种子
rand.Seed(time.Now().UnixNano())
// 如果存在,从环境变量初始化后端 URL
envURL := os.Getenv("MINESWEEPER_BACKEND_URL")
if envURL != "" {
newURL := strings.TrimSuffix(envURL, "/")
config.SetBackendBaseURL(newURL)
logger.Info("Setting BackendBaseURL from environment: %s", newURL)
}
// 注册比赛处理器
if err := initializer.RegisterMatch("animal_minesweeper", func(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule) (runtime.Match, error) {
logger.Debug("Creating new MatchHandler instance")
return &handlers.MatchHandler{}, nil
}); err != nil {
logger.Error("Unable to register match: %v", err)
return err
}
// 注册匹配器匹配钩子
if err := initializer.RegisterMatchmakerMatched(func(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, entries []runtime.MatchmakerEntry) (string, error) {
logger.Info("Matchmaker matched! Creating authoritative match for %d players", len(entries))
// 1. 遍历所有玩家,提取有效的 game_type
// 我们不能只看第一个玩家,因为有时某些玩家的属性可能为空或丢失
var gameType string
// 调试日志:打印所有玩家的 properties
for i, entry := range entries {
props := entry.GetProperties()
var pType string
if props != nil {
if gt, ok := props["game_type"]; ok {
if gtStr, ok := gt.(string); ok {
pType = gtStr
}
}
}
logger.Debug("Matchmaker Entry [%d] UserID: %s, Properties: %v, GameType: %s", i, entry.GetPresence().GetUserId(), props, pType)
// 如果还没找到主 gameType 且当前玩家有,则采用
if gameType == "" && pType != "" {
gameType = pType
}
}
// 如果遍历完还是空的,说明没人带 game_type直接拒绝匹配
// 严禁使用默认值,防止未携带属性的玩家被错误分配
// 如果遍历完还是空的,说明没人带 game_type直接拒绝匹配
if gameType == "" {
errMsg := "CRITICAL: No game_type found in any matchmaker entry. Creating REJECT match."
logger.Error(errMsg)
// Don't return error, create a reject match
gameType = "REJECT_mismatch_no_type"
}
// 2. 安全校验:再次遍历,确保所有玩家都明确携带了 game_type 且一致
for i, entry := range entries {
var entryGameType string
props := entry.GetProperties()
if props != nil {
if gt, ok := props["game_type"]; ok {
if gtStr, ok := gt.(string); ok {
entryGameType = gtStr
}
}
}
// 强制要求每位玩家必须携带 game_type
if entryGameType == "" {
errMsg := fmt.Sprintf("CRITICAL: Player %s matched without game_type property! Creating REJECT match.", entry.GetPresence().GetUserId())
logger.Error(errMsg)
gameType = "REJECT_mismatch_missing_type"
break
}
// 一致性检查
// Note: If gameType is already REJECT, we don't need to check.
if !strings.HasPrefix(gameType, "REJECT_") && entryGameType != gameType {
errMsg := fmt.Sprintf("CRITICAL: Matchmaker matched players with DIFFERENT game_types! Target=%s, Entry[%d]=%s. Creating REJECT match.", gameType, i, entryGameType)
logger.Error(errMsg)
gameType = "REJECT_mismatch_mixed_types"
break
}
}
// 将 game_type 作为参数传递给 MatchCreate
params := map[string]interface{}{
"game_type": gameType,
}
matchId, err := nk.MatchCreate(ctx, "animal_minesweeper", params)
if err != nil {
logger.Error("Failed to create match: %v", err)
return "", err
}
logger.Info("Created authoritative match: %s (GameType: %s)", matchId, gameType)
return matchId, nil
}); err != nil {
logger.Error("Unable to register matchmaker matched hook: %v", err)
return err
}
logger.Info("Match registration completed successfully")
// 注册 RPC
if err := initializer.RegisterRpc("list_matches", handlers.RpcListMatches); err != nil {
logger.Error("Unable to register rpc: %v", err)
return err
}
if err := initializer.RegisterRpc("find_my_match", handlers.RpcFindMyMatch); err != nil {
logger.Error("Unable to register rpc: %v", err)
return err
}
if err := initializer.RegisterRpc("get_online_count", handlers.RpcGetOnlineCount); err != nil {
logger.Error("Unable to register rpc: %v", err)
return err
}
if err := initializer.RegisterRpc("get_leaderboard", handlers.RpcGetLeaderboard); err != nil {
logger.Error("Unable to register rpc: %v", err)
return err
}
return nil
}