diff --git a/client/CMT.cpp b/client/CMT.cpp index 3d9382016..c471cb8c6 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -1367,7 +1367,7 @@ static void handleEvent(SDL_Event & ev) break; case EUserEvent::RESTART_GAME: { - CSH->sendStartGame(); + CSH->sendRestartGame(); } break; case EUserEvent::CAMPAIGN_START_SCENARIO: diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 91f8c6472..8a7a4eac6 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -512,6 +512,14 @@ void CServerHandler::sendGuiAction(ui8 action) const sendLobbyPack(lga); } +void CServerHandler::sendRestartGame() const +{ + LobbyEndGame endGame; + endGame.closeConnection = false; + endGame.restart = true; + sendLobbyPack(endGame); +} + void CServerHandler::sendStartGame(bool allowOnlyAI) const { verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool()); @@ -524,9 +532,11 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const * si = * lsg.initializedStartInfo; } sendLobbyPack(lsg); + c->enterLobbyConnectionMode(); + c->disableStackSendingByID(); } -void CServerHandler::startGameplay() +void CServerHandler::startGameplay(CGameState * gameState) { if(CMM) CMM->disable(); @@ -535,13 +545,13 @@ void CServerHandler::startGameplay() switch(si->mode) { case StartInfo::NEW_GAME: - client->newGame(); + client->newGame(gameState); break; case StartInfo::CAMPAIGN: - client->newGame(); + client->newGame(gameState); break; case StartInfo::LOAD_GAME: - client->loadGame(); + client->loadGame(gameState); break; default: throw std::runtime_error("Invalid mode"); @@ -576,6 +586,9 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart) GH.curInt = CMainMenu::create().get(); } } + + c->enterLobbyConnectionMode(); + c->disableStackSendingByID(); } void CServerHandler::startCampaignScenario(std::shared_ptr cs) diff --git a/client/CServerHandler.h b/client/CServerHandler.h index 58b122693..4265cf124 100644 --- a/client/CServerHandler.h +++ b/client/CServerHandler.h @@ -21,6 +21,7 @@ class PlayerColor; struct StartInfo; class CMapInfo; +class CGameState; struct ClientPlayer; struct CPack; struct CPackForLobby; @@ -69,6 +70,7 @@ public: virtual void sendMessage(const std::string & txt) const = 0; virtual void sendGuiAction(ui8 action) const = 0; // TODO: possibly get rid of it? virtual void sendStartGame(bool allowOnlyAI = false) const = 0; + virtual void sendRestartGame() const = 0; }; /// structure to handle running server and connecting to it @@ -142,9 +144,10 @@ public: void setTurnLength(int npos) const override; void sendMessage(const std::string & txt) const override; void sendGuiAction(ui8 action) const override; + void sendRestartGame() const override; void sendStartGame(bool allowOnlyAI = false) const override; - void startGameplay(); + void startGameplay(CGameState * gameState = nullptr); void endGameplay(bool closeConnection = true, bool restart = false); void startCampaignScenario(std::shared_ptr cs = {}); void showServerError(std::string txt); diff --git a/client/Client.cpp b/client/Client.cpp index e145c1b12..5ee3c6cb0 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -180,14 +180,15 @@ events::EventBus * CClient::eventBus() const return clientEventBus.get(); } -void CClient::newGame() +void CClient::newGame(CGameState * initializedGameState) { CSH->th->update(); CMapService mapService; - gs = new CGameState(); + gs = initializedGameState ? initializedGameState : new CGameState(); gs->preInit(VLC); logNetwork->trace("\tCreating gamestate: %i", CSH->th->getDiff()); - gs->init(&mapService, CSH->si.get(), settings["general"]["saveRandomMaps"].Bool()); + if(!initializedGameState) + gs->init(&mapService, CSH->si.get(), settings["general"]["saveRandomMaps"].Bool()); logNetwork->trace("Initializing GameState (together): %d ms", CSH->th->getDiff()); initMapHandler(); @@ -196,53 +197,64 @@ void CClient::newGame() initPlayerInterfaces(); } -void CClient::loadGame() +void CClient::loadGame(CGameState * initializedGameState) { logNetwork->info("Loading procedure started!"); - + std::unique_ptr loader; - try + + if(initializedGameState) { - boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME)); - boost::filesystem::path controlServerSaveName; - - if(CResourceHandler::get("local")->existsResource(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME))) - { - controlServerSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME)); - } - else // create entry for server savegame. Triggered if save was made after launch and not yet present in res handler - { - controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1"); - CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true); - } - - if(clientSaveName.empty()) - throw std::runtime_error("Cannot open client part of " + CSH->si->mapname); - if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName)) - throw std::runtime_error("Cannot open server part of " + CSH->si->mapname); - - { - CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION); - loadCommonState(checkingLoader); - loader = checkingLoader.decay(); - } - + logNetwork->info("Game state was transferred over network, loading."); + gs = initializedGameState; } - catch(std::exception & e) + else { - logGlobal->error("Cannot load game %s. Error: %s", CSH->si->mapname, e.what()); - throw; //obviously we cannot continue here + try + { + boost::filesystem::path clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::CLIENT_SAVEGAME)); + boost::filesystem::path controlServerSaveName; + + if(CResourceHandler::get("local")->existsResource(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME))) + { + controlServerSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(CSH->si->mapname, EResType::SERVER_SAVEGAME)); + } + else // create entry for server savegame. Triggered if save was made after launch and not yet present in res handler + { + controlServerSaveName = boost::filesystem::path(clientSaveName).replace_extension(".vsgm1"); + CResourceHandler::get("local")->createResource(controlServerSaveName.string(), true); + } + + if(clientSaveName.empty()) + throw std::runtime_error("Cannot open client part of " + CSH->si->mapname); + if(controlServerSaveName.empty() || !boost::filesystem::exists(controlServerSaveName)) + throw std::runtime_error("Cannot open server part of " + CSH->si->mapname); + + { + CLoadIntegrityValidator checkingLoader(clientSaveName, controlServerSaveName, MINIMAL_SERIALIZATION_VERSION); + loadCommonState(checkingLoader); + loader = checkingLoader.decay(); + } + } + catch(std::exception & e) + { + logGlobal->error("Cannot load game %s. Error: %s", CSH->si->mapname, e.what()); + throw; //obviously we cannot continue here + } + logNetwork->trace("Loaded common part of save %d ms", CSH->th->getDiff()); } - logNetwork->trace("Loaded common part of save %d ms", CSH->th->getDiff()); gs->preInit(VLC); gs->updateOnLoad(CSH->si.get()); + logNetwork->info("Game loaded, initialize interfaces."); + initMapHandler(); reinitScripting(); initPlayerEnvironments(); - - serialize(loader->serializer, loader->serializer.fileVersion); + + if(loader) + serialize(loader->serializer, loader->serializer.fileVersion); initPlayerInterfaces(); } diff --git a/client/Client.h b/client/Client.h index 569277e93..71fbd2eb2 100644 --- a/client/Client.h +++ b/client/Client.h @@ -150,8 +150,8 @@ public: vstd::CLoggerBase * logger() const override; events::EventBus * eventBus() const override; - void newGame(); - void loadGame(); + void newGame(CGameState * gameState); + void loadGame(CGameState * gameState); void serialize(BinarySerializer & h, const int version); void serialize(BinaryDeserializer & h, const int version); diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index 88df3c774..a5966a40e 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/client/NetPacksLobbyClient.cpp @@ -57,7 +57,8 @@ bool LobbyClientDisconnected::applyOnLobbyHandler(CServerHandler * handler) void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) { - GH.popInts(1); + if(GH.listInt.size()) + GH.popInts(1); } void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) @@ -93,25 +94,34 @@ void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * h } } -bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler) +bool LobbyEndGame::applyOnLobbyHandler(CServerHandler * handler) { if(handler->state == EClientState::GAMEPLAY) { - handler->endGameplay(false, true); + handler->endGameplay(closeConnection, restart); } + + if(restart) + handler->sendStartGame(); + + return true; +} + +bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler) +{ handler->state = EClientState::STARTING; if(handler->si->mode != StartInfo::LOAD_GAME) { handler->si = initializedStartInfo; } if(settings["session"]["headless"].Bool()) - handler->startGameplay(); + handler->startGameplay(initializedGameState); return true; } void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) { - GH.pushIntT(std::bind(&CServerHandler::startGameplay, handler)); + GH.pushIntT(std::bind(&CServerHandler::startGameplay, handler, initializedGameState)); } bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler) diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 6753c7174..412316449 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -859,9 +859,6 @@ PlayerColor CStackInstance::getOwner() const void CStackInstance::deserializationFix() { - const CCreature *backup = type; - type = nullptr; - setType(backup); const CArmedInstance *armyBackup = _armyObj; _armyObj = nullptr; setArmyObj(armyBackup); diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 484133c2b..258f3c7b2 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -12,6 +12,7 @@ #include "HeroBonus.h" #include "GameConstants.h" #include "CArtHandler.h" +#include "CCreatureHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -40,7 +41,20 @@ public: template void serialize(Handler &h, const int version) { - h & type; + if(h.saving) + { + CreatureID idNumber = type ? type->idNumber : CreatureID(CreatureID::NONE); + h & idNumber; + } + else + { + CreatureID idNumber; + h & idNumber; + if(idNumber != CreatureID::NONE) + setType(VLC->creh->objects[idNumber]); + else + type = nullptr; + } h & count; } diff --git a/lib/NetPacksLobby.h b/lib/NetPacksLobby.h index 0f70fb2e8..b4d8c95b5 100644 --- a/lib/NetPacksLobby.h +++ b/lib/NetPacksLobby.h @@ -138,12 +138,29 @@ struct LobbyGuiAction : public CLobbyPackToPropagate } }; +struct LobbyEndGame : public CLobbyPackToPropagate +{ + bool closeConnection = false, restart = false; + + bool checkClientPermissions(CVCMIServer * srv) const; + bool applyOnServer(CVCMIServer * srv); + void applyOnServerAfterAnnounce(CVCMIServer * srv); + bool applyOnLobbyHandler(CServerHandler * handler); + + template void serialize(Handler &h, const int version) + { + h & closeConnection; + h & restart; + } +}; + struct LobbyStartGame : public CLobbyPackToPropagate { // Set by server std::shared_ptr initializedStartInfo; + CGameState * initializedGameState; - LobbyStartGame() : initializedStartInfo(nullptr) {} + LobbyStartGame() : initializedStartInfo(nullptr), initializedGameState(nullptr) {} bool checkClientPermissions(CVCMIServer * srv) const; bool applyOnServer(CVCMIServer * srv); void applyOnServerAfterAnnounce(CVCMIServer * srv); @@ -153,6 +170,10 @@ struct LobbyStartGame : public CLobbyPackToPropagate template void serialize(Handler &h, const int version) { h & initializedStartInfo; + bool sps = h.smartPointerSerialization; + h.smartPointerSerialization = true; + h & initializedGameState; + h.smartPointerSerialization = sps; } }; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index e32d47ab6..b8047624b 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -330,7 +330,7 @@ public: h & players; h & howManyTeams; h & allowedHeroes; - h & triggeredEvents; + //Do not serialize triggeredEvents in header as they can contain information about heroes and armies h & victoryMessage; h & victoryIconIndex; h & defeatMessage; @@ -424,6 +424,7 @@ public: void serialize(Handler &h, const int formatVersion) { h & static_cast(*this); + h & triggeredEvents; //from CMapHeader h & rumors; h & allowedSpell; h & allowedAbilities; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 1e068c8ca..e73740b80 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -372,6 +372,7 @@ void registerTypesLobbyPacks(Serializer &s) s.template registerType(); // Only host client send s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); // Only server send diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 12f972cd0..dc0e587df 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -220,7 +220,7 @@ void CVCMIServer::threadAnnounceLobby() } } -bool CVCMIServer::prepareToStartGame() +void CVCMIServer::prepareToRestart() { if(state == EServerState::GAMEPLAY) { @@ -231,9 +231,19 @@ bool CVCMIServer::prepareToStartGame() // FIXME: dirry hack to make sure old CGameHandler::run is finished boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); } + + for(auto c : connections) + { + c->enterLobbyConnectionMode(); + c->disableStackSendingByID(); + } + boost::unique_lock queueLock(mx); + gh = nullptr; +} - if(!gh) - gh = std::make_shared(this); +bool CVCMIServer::prepareToStartGame() +{ + gh = std::make_shared(this); switch(si->mode) { case StartInfo::CAMPAIGN: diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index e2f9d029c..0cffc2835 100644 --- a/server/CVCMIServer.h +++ b/server/CVCMIServer.h @@ -69,6 +69,7 @@ public: ~CVCMIServer(); void run(); bool prepareToStartGame(); + void prepareToRestart(); void startGameImmidiately(); void startAsyncAccept(); diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index 5885630fd..5cea42e1d 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/server/NetPacksLobbyServer.cpp @@ -161,6 +161,27 @@ bool LobbyGuiAction::checkClientPermissions(CVCMIServer * srv) const return srv->isClientHost(c->connectionID); } +bool LobbyEndGame::checkClientPermissions(CVCMIServer * srv) const +{ + return srv->isClientHost(c->connectionID); +} + +bool LobbyEndGame::applyOnServer(CVCMIServer * srv) +{ + srv->prepareToRestart(); + return true; +} + +void LobbyEndGame::applyOnServerAfterAnnounce(CVCMIServer * srv) +{ + boost::unique_lock stateLock(srv->stateMutex); + for(auto & c : srv->connections) + { + c->enterLobbyConnectionMode(); + c->disableStackSendingByID(); + } +} + bool LobbyStartGame::checkClientPermissions(CVCMIServer * srv) const { return srv->isClientHost(c->connectionID); @@ -181,6 +202,8 @@ bool LobbyStartGame::applyOnServer(CVCMIServer * srv) return false; initializedStartInfo = std::make_shared(*srv->gh->getStartInfo(true)); + initializedGameState = srv->gh->gameState(); + return true; }