### Highlights
- Introduced generic match engine (`generic_match.go`) implementing dynamic GameRules-based runtime.
- Added modular structure under `/plugins`:
- /plugins/game → GameRules interface + TicTacToe + Battleship rule sets
- /plugins/structs → Board, Player, MatchState generic structs
- /plugins/modules → matchmaking + RPC handlers + match engine
- Migrated TicTacToe logic into reusable rule implementation.
- Added Battleship game support using same engine.
- Updated matchmaking to accept { game, mode } for multi-game routing.
- Updated UI contract: clients must send `game` (and optional `mode`) when joining matchmaking.
- Removed hardcoded TicTacToe match registration.
- Registered a single “generic” authoritative match with ruleset registry.
- Normalized imports under local dev module path.
- Ensured MatchState and Board are now generic and reusable across games.
- Added strict requirement for `game` metadata in match flow (error if missing).
- Cleaned initial state creation into MatchInit with flexible board dimensions.
- Improved MatchLeave for proper forfeit handling through GameRules.
### Result
The server now supports an unlimited number of turn-based board games
via swappable rulesets while keeping a single authoritative Nakama match loop.
92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"github.com/heroiclabs/nakama-common/runtime"
|
|
|
|
// Adjust these imports to match your project structure
|
|
"localrepo/plugins/modules"
|
|
"localrepo/plugins/games"
|
|
)
|
|
|
|
func InitModule(
|
|
ctx context.Context,
|
|
logger runtime.Logger,
|
|
db *sql.DB,
|
|
nk runtime.NakamaModule,
|
|
initializer runtime.Initializer,
|
|
) error {
|
|
|
|
//--------------------------------------------------------
|
|
// 1. Register RPCs
|
|
//--------------------------------------------------------
|
|
if err := initializer.RegisterRpc("hello_world", HelloWorld); err != nil {
|
|
logger.Error("Failed to register RPC hello_world: %v", err)
|
|
return err
|
|
}
|
|
|
|
if err := initializer.RegisterRpc("leave_matchmaking", modules.RpcLeaveMatchmaking); err != nil {
|
|
logger.Error("Failed to register RPC leave_matchmaking: %v", err)
|
|
return err
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 2. Register Matchmaker Handler
|
|
//--------------------------------------------------------
|
|
if err := initializer.RegisterMatchmakerMatched(modules.MatchmakerMatched); err != nil {
|
|
logger.Error("Failed to register MatchmakerMatched: %v", err)
|
|
return err
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 3. Register MATCHES for ALL games
|
|
//--------------------------------------------------------
|
|
|
|
// Build registry: game name → GameRules implementation
|
|
registry := map[string]game.GameRules{
|
|
"tictactoe": &game.TicTacToeRules{},
|
|
"battleship": &game.BattleshipRules{},
|
|
}
|
|
|
|
// Register a Generic Match Handler that can run ANY game from registry
|
|
if err := initializer.RegisterMatch("generic", modules.NewGenericMatch(registry)); err != nil {
|
|
logger.Error("Failed to register generic match: %v", err)
|
|
return err
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 4. Register Leaderboards dynamically (optional)
|
|
//--------------------------------------------------------
|
|
|
|
leaderboards := []string{
|
|
"tictactoe_classic",
|
|
"tictactoe_ranked",
|
|
"battleship_classic",
|
|
"battleship_ranked",
|
|
}
|
|
|
|
for _, lb := range leaderboards {
|
|
err := nk.LeaderboardCreate(
|
|
ctx,
|
|
lb, // leaderboard ID
|
|
true, // authoritative
|
|
"desc", // sort order
|
|
"incr", // operator
|
|
"", // reset schedule (none)
|
|
map[string]interface{}{}, // metadata
|
|
)
|
|
|
|
if err != nil && err.Error() != "Leaderboard ID already exists" {
|
|
logger.Error("Failed to create leaderboard %s: %v", lb, err)
|
|
return err
|
|
}
|
|
|
|
logger.Info("Leaderboard ready: %s", lb)
|
|
}
|
|
|
|
logger.Info("Go module loaded successfully!")
|
|
return nil
|
|
}
|