package battleroyale import ( "context" "database/sql" "encoding/json" "github.com/heroiclabs/nakama-common/runtime" ) func CheckMatchTerminate(lobbyState *MatchState, logger *runtime.Logger) bool { // If we have no presences in the match according to the match state, increment the empty ticks count if len(lobbyState.presences) == 0 { lobbyState.emptyTicks++ } // If the match has been empty for more than 100 ticks, end the match by returning nil if lobbyState.emptyTicks > 100 { (*logger).Info("Match terminated due to empty lobby.") return true } return false } func StorePlayerInputs(lobbyState *MatchState, messages []runtime.MatchData, logger *runtime.Logger, tick int64) { for _, msg := range messages { _, exists := lobbyState.presences[msg.GetSessionId()] if !exists { (*logger).Warn("Received input for non-existent player session ID: %v", msg.GetSessionId()) continue } var update ClientUpdate if err := json.Unmarshal(msg.GetData(), &update); err != nil { (*logger).Warn("Failed to parse input: %v", err) continue } // Store the input in the player's stage state lobbyState.presences[msg.GetSessionId()].stageState.playerInputs = append(lobbyState.presences[msg.GetSessionId()].stageState.playerInputs, &update) } } func BroadcastToPresences(tick int64, lobbyState *MatchState, logger *runtime.Logger, dispatcher *runtime.MatchDispatcher) { for _, v := range lobbyState.presences { var tickData = v.stageState.MakeServerTick(tick) data, err := json.Marshal(tickData) if err != nil { (*logger).Error("Error marshalling bullet data", err) } else { reliable := false (*dispatcher).BroadcastMessage(STATE_UPDATE, data, nil, nil, reliable) } } } // Main game loop, executed at tickRate per second specified in MatchInit func (m *Match) MatchLoop(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, dispatcher runtime.MatchDispatcher, tick int64, state any, messages []runtime.MatchData) any { lobbyState, ok := state.(*MatchState) if !ok { logger.Error("State is not a valid lobby state object for MatchLoop.") return nil } if CheckMatchTerminate(lobbyState, &logger) { return nil } StorePlayerInputs(lobbyState, messages, &logger, tick) for _, playerState := range lobbyState.presences { playerState.stageState.MarkBulletsBeyondKillBoundary(tick) playerState.stageState.ProcessPlayerInputs(tick) playerState.stageState.HandleDeath(tick) playerState.stageState.CleanupOldBullets(tick) } BroadcastToPresences(tick, lobbyState, &logger, &dispatcher) return lobbyState }