All checks were successful
continuous-integration/drone/tag Build is passing
102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package modules
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
|
|
"github.com/heroiclabs/nakama-common/runtime"
|
|
)
|
|
|
|
type MatchmakingTicket struct {
|
|
Game string `json:"game"` // e.g. "tictactoe", "battleship"
|
|
Mode string `json:"mode"` // e.g. "classic", "ranked", "blitz"
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// GENERIC MATCHMAKER — Supports ALL Games & Modes
|
|
// --------------------------------------------------
|
|
func MatchmakerMatched(
|
|
ctx context.Context,
|
|
logger runtime.Logger,
|
|
db *sql.DB,
|
|
nk runtime.NakamaModule,
|
|
entries []runtime.MatchmakerEntry,
|
|
) (string, error) {
|
|
|
|
if len(entries) == 0 {
|
|
return "", nil
|
|
}
|
|
|
|
// Extract the first player's desired properties
|
|
props0 := entries[0].GetProperties()
|
|
|
|
game, okGame := props0["game"].(string)
|
|
mode, okMode := props0["mode"].(string)
|
|
|
|
if !okGame || !okMode {
|
|
logger.Error("MatchmakerMatched: Missing 'game' or 'mode' properties.")
|
|
return "", runtime.NewError("missing matchmaking properties", 3)
|
|
}
|
|
|
|
// Ensure ALL players match game + mode
|
|
for _, e := range entries {
|
|
p := e.GetProperties()
|
|
|
|
g, okG := p["game"].(string)
|
|
m, okM := p["mode"].(string)
|
|
|
|
if !okG || !okM || g != game || m != mode {
|
|
logger.Warn("MatchmakerMatched: Player properties do not match — retrying matchmaking.")
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
// Create the correct authoritative match handler.
|
|
// This depends on how "game" was registered in main.go.
|
|
// Example: initializer.RegisterMatch("tictactoe", NewGenericMatch(TicTacToeRules))
|
|
matchParams := map[string]interface{}{
|
|
"game": game,
|
|
"mode": mode,
|
|
}
|
|
|
|
matchID, err := nk.MatchCreate(ctx, "generic", matchParams)
|
|
if err != nil {
|
|
logger.Error("MatchmakerMatched: MatchCreate failed: %v", err)
|
|
return "", runtime.NewError("failed to create match", 13)
|
|
}
|
|
|
|
logger.Info("✔ Match created game=%s mode=%s id=%s", game, mode, matchID)
|
|
return matchID, nil
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// RPC: Leave matchmaking (generic cancel API)
|
|
// --------------------------------------------------
|
|
|
|
func RpcLeaveMatchmaking(
|
|
ctx context.Context,
|
|
logger runtime.Logger,
|
|
db *sql.DB,
|
|
nk runtime.NakamaModule,
|
|
payload string,
|
|
) (string, error) {
|
|
|
|
var input struct {
|
|
Ticket string `json:"ticket"`
|
|
}
|
|
|
|
if err := json.Unmarshal([]byte(payload), &input); err != nil {
|
|
return "", runtime.NewError("invalid JSON", 3)
|
|
}
|
|
|
|
if input.Ticket == "" {
|
|
return "", runtime.NewError("missing ticket", 3)
|
|
}
|
|
|
|
// Client removes ticket locally — server doesn't need to do anything
|
|
logger.Info("✔ Player left matchmaking: ticket=%s", input.Ticket)
|
|
|
|
return "{}", nil
|
|
}
|