147 lines
3.6 KiB
Go

package handlers
import (
"context"
"database/sql"
"encoding/json"
"time"
"github.com/heroiclabs/nakama-common/runtime"
)
func RpcListMatches(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
limit := 100
authoritative := true
label := "" // 我们想列出所有 animal_minesweeper
minSize := 0
maxSize := 8
query := "*" // 默认查询
matches, err := nk.MatchList(ctx, limit, authoritative, label, &minSize, &maxSize, query)
if err != nil {
logger.Error("Failed to list matches: %v", err)
return "", err
}
result := make([]map[string]interface{}, 0)
for _, m := range matches {
var labelObj MatchLabel
if err := json.Unmarshal([]byte(m.GetLabel().Value), &labelObj); err != nil {
// 如果不是 minesweeper 房间,跳过
continue
}
result = append(result, map[string]interface{}{
"match_id": m.GetMatchId(),
"player_count": labelObj.PlayerCount,
"max_players": labelObj.MaxPlayers,
"started": labelObj.Started,
"open": labelObj.Open,
})
}
response, err := json.Marshal(result)
if err != nil {
return "", err
}
return string(response), nil
}
func RpcFindMyMatch(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
userID, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
if !ok {
return "", runtime.NewError("user not authenticated", 16)
}
readObjects, err := nk.StorageRead(ctx, []*runtime.StorageRead{
{
Collection: "game_data",
Key: "active_match",
UserID: userID,
},
})
if err != nil {
logger.Error("Failed to read storage: %v", err)
return "", err
}
if len(readObjects) == 0 {
return "{}", nil
}
return readObjects[0].Value, nil
}
// RpcGetOnlineCount 返回当前在线玩家数量(基于心跳)
func RpcGetOnlineCount(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
userID, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
if !ok || userID == "" {
return "", runtime.NewError("user not authenticated", 16)
}
// 1. 更新当前用户的心跳时间戳
now := time.Now().Unix()
heartbeatData, _ := json.Marshal(map[string]int64{"ts": now})
_, err := nk.StorageWrite(ctx, []*runtime.StorageWrite{
{
Collection: "game_lobby",
Key: "heartbeat",
UserID: userID,
Value: string(heartbeatData),
PermissionRead: 0, // 不可读
PermissionWrite: 0, // 仅服务器可写
},
})
if err != nil {
logger.Warn("Failed to write heartbeat: %v", err)
}
// 2. 读取所有用户的心跳记录
cursor := ""
onlineCount := 0
expireThreshold := now - 60 // 60秒内有心跳的算在线
for {
// StorageList(ctx, callerID, collection, userID, limit, cursor)
objects, nextCursor, err := nk.StorageList(ctx, "", "game_lobby", "", 100, cursor)
if err != nil {
logger.Error("Failed to list heartbeats: %v", err)
break
}
for _, obj := range objects {
var data map[string]int64
if err := json.Unmarshal([]byte(obj.Value), &data); err != nil {
continue
}
if ts, ok := data["ts"]; ok && ts >= expireThreshold {
onlineCount++
}
}
if nextCursor == "" {
break
}
cursor = nextCursor
}
// 3. 获取比赛中的玩家数
matches, _ := nk.MatchList(ctx, 100, true, "", nil, nil, "*")
inGameCount := 0
for _, m := range matches {
inGameCount += int(m.GetSize())
}
result := map[string]interface{}{
"online_count": onlineCount,
"match_count": len(matches),
"in_game_count": inGameCount,
}
response, _ := json.Marshal(result)
return string(response), nil
}