diff --git a/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt index fb4fd3155..8389fcf10 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt +++ b/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt @@ -13,6 +13,7 @@ set(HEADERS game/game_config.h game/server_deck_validation_strategy.h game/server_game.h + game/server_game_lifecycle_strategy.h game/server_player.h game/server_spectator.h server.h diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index f537c3cd5..425fefcb3 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -63,7 +63,7 @@ Server_Game::Server_Game(const GameConfig &config, Server_Room *_room) startingLifeTotal(config.startingLifeTotal), shareDecklistsOnLoad(config.shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr), - deckValidationStrategy(new Server_DefaultDeckValidationStrategy), gameMutex() + lifecycleStrategy(new Server_DefaultLifecycleStrategy), gameMutex() { currentReplay = new GameReplay; currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId()); @@ -329,6 +329,11 @@ void Server_Game::doStartGameIfReady(bool forceStartGame) } players = getPlayers(); // players could have been kicked, get new list of players + if (lifecycleStrategy->onGameStarting(this) == Server_GameLifecycleStrategy::StartAction::Handled) { + locker.unlock(); + return; + } + for (Server_AbstractPlayer *player : players.values()) { player->setupZones(); } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h index da316975d..8ed0769a6 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h @@ -23,6 +23,7 @@ #include "../server_response_containers.h" #include "game_config.h" #include "server_deck_validation_strategy.h" +#include "server_game_lifecycle_strategy.h" #include #include @@ -83,6 +84,8 @@ private: QScopedPointer deckValidationStrategy; + QScopedPointer lifecycleStrategy; + void createGameStateChangedEvent(Event_GameStateChanged *event, Server_AbstractParticipant *recipient, bool omniscient, @@ -220,6 +223,12 @@ public: } /** @brief Replace the deck validation strategy; takes ownership of @p strategy. */ void setDeckValidationStrategy(Server_DeckValidationStrategy *strategy); + + /** @brief Get the current game lifecycle strategy (non-owning). */ + Server_GameLifecycleStrategy *getLifecycleStrategy() const + { + return lifecycleStrategy.data(); + } }; #endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game_lifecycle_strategy.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game_lifecycle_strategy.h new file mode 100644 index 000000000..b71ce486d --- /dev/null +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game_lifecycle_strategy.h @@ -0,0 +1,43 @@ +#ifndef SERVER_GAME_LIFECYCLE_STRATEGY_H +#define SERVER_GAME_LIFECYCLE_STRATEGY_H + +class Server_Game; + +/** + * @brief Strategy hook invoked around a game's lifecycle transitions. + * + * Subclasses can intercept the game start to perform custom setup (e.g. draft or + * tournament initialization); the default implementation never intercepts. + */ +class Server_GameLifecycleStrategy +{ +public: + virtual ~Server_GameLifecycleStrategy() = default; + + /** @brief How the game start should proceed after this hook returns. */ + enum class StartAction + { + ProceedNormal, ///< Continue with the normal game start flow. + Handled, ///< The strategy handled the start; abort the normal flow. + }; + + /** + * @brief Called when a game is about to start. + * @return How the start flow should proceed. + */ + virtual StartAction onGameStarting(Server_Game *game) = 0; +}; + +/** + * @brief Default lifecycle strategy that never intercepts the game start. + */ +class Server_DefaultLifecycleStrategy : public Server_GameLifecycleStrategy +{ +public: + StartAction onGameStarting(Server_Game *) override + { + return StartAction::ProceedNormal; + } +}; + +#endif