package battleroyale import ( "context" "database/sql" "github.com/heroiclabs/nakama-common/runtime" ) // Run on match start, initializes game state and sets tick rate func (m *Match) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]any) (any, int, string) { state := &MatchState{ tickRate: TICK_RATE, presences: map[string]*PresenceState{}, emptyTicks: 0, currentMatchPhase: MATCH_LOADING, } return state, TICK_RATE, "" } // Run when a user attempts to join or rejoin a match. Responsible for deciding whether or not to let them in. func (m *Match) MatchJoinAttempt(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, presence runtime.Presence, metadata map[string]string) (any, bool, string) { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchJoin.") return nil, false, "Failed to join match: match does not exist." } accepted := true rejectedMessage := "" return lobbyState, accepted, rejectedMessage } // Run when a user successfully joins a match, registers their presence in the game state func (m *Match) MatchJoin(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, presences []runtime.Presence) any { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchJoin.") return nil } // Register every new presence associated with the new player for i := range len(presences) { lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{ presence: &presences[i], stageState: NewPlayerStage(), } } return lobbyState } // Run when a user successfully leaves a match, de-registers their presence in the game state func (m *Match) MatchLeave(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, presences []runtime.Presence) any { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchLeave.") return nil } // De-register every presence associated with the leaving player for i := range len(presences) { sessionID := presences[i].GetSessionId() playerState, exists := lobbyState.presences[sessionID] if exists { playerState.stageState.Delete() delete(lobbyState.presences, sessionID) } } return lobbyState } // Run when a match gets an arbitrary signal from the Nakama runtime (probably from the matchmaker/match lister APIs) func (m *Match) MatchSignal(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, data string) (any, string) { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchSignal.") return nil, "Failed to get valid state for return signal" } returnMessage := "" return lobbyState, returnMessage } // Run when the server enters the graceful shutdown flow. Gives the match a chance to shutdown cleanly within graceSeconds. func (m *Match) MatchTerminate(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, graceSeconds int) any { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchTerminate.") return nil } return lobbyState }