diff --git a/client/Client.cpp b/client/Client.cpp index 15f7c47a0..174acdb39 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -556,6 +556,13 @@ void CClient::invalidatePaths() pathCache.clear(); } +vstd::RNG & CClient::getRandomGenerator() +{ + // Client should use CRandomGenerator::getDefault() for UI logic + // Gamestate should never call this method on client! + throw std::runtime_error("Illegal access to random number generator from client code!"); +} + std::shared_ptr CClient::getPathsInfo(const CGHeroInstance * h) { assert(h); diff --git a/client/Client.h b/client/Client.h index a775ad732..68e116603 100644 --- a/client/Client.h +++ b/client/Client.h @@ -218,6 +218,8 @@ public: void showInfoDialog(InfoWindow * iw) override {}; void removeGUI() const; + vstd::RNG & getRandomGenerator() override; + #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; #endif diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index a42530adc..493091422 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -135,6 +135,9 @@ public: virtual void changeFogOfWar(std::unordered_set &tiles, PlayerColor player, ETileVisibility mode) = 0; virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0; + + virtual vstd::RNG & getRandomGenerator() = 0; + }; class DLL_LINKAGE CNonConstInfoCallback : public CPrivilegedInfoCallback diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 225eb4566..1be6e9241 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -185,8 +185,6 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog { assert(services); assert(callback); - logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed); - rand = std::make_unique(si->seedToBeUsed); scenarioOps = CMemorySerializer::deepCopy(*si).release(); initialOpts = CMemorySerializer::deepCopy(*si).release(); si = nullptr; @@ -1966,7 +1964,7 @@ TeamState::TeamState() vstd::RNG & CGameState::getRandomGenerator() { - return *rand; + return callback->getRandomGenerator(); } ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, int flags, std::function accepts) diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index d31f41f16..f5818d59b 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -9,10 +9,10 @@ */ #pragma once -#include "bonuses/CBonusSystemNode.h" -#include "IGameCallback.h" -#include "LoadProgress.h" -#include "ConstTransitivePtr.h" +#include "../bonuses/CBonusSystemNode.h" +#include "../IGameCallback.h" +#include "../LoadProgress.h" +#include "../ConstTransitivePtr.h" namespace boost { @@ -186,7 +186,11 @@ public: h & teams; h & heroesPool; h & globalEffects; - h & rand; + if (h.version < Handler::Version::REMOVE_LIB_RNG) + { + std::string oldStateOfRNG; + h & oldStateOfRNG; + } h & rumor; h & campaign; h & allocatedArtifacts; @@ -234,7 +238,6 @@ private: // ---- data ----- std::shared_ptr> applier; - std::unique_ptr rand; Services * services; /// Pointer to campaign state manager. Nullptr for single scenarios diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index d7d830afc..68e3e1f5a 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -56,6 +56,7 @@ enum class ESerializationVersion : int32_t REMOVE_FOG_OF_WAR_POINTER, // 846 - fog of war is serialized as reference instead of pointer SIMPLE_TEXT_CONTAINER_SERIALIZATION, // 847 - text container is serialized using common routine instead of custom approach MAP_FORMAT_ADDITIONAL_INFOS, // 848 - serialize new infos in map format + REMOVE_LIB_RNG, // 849 - removed random number generators from library classes - CURRENT = MAP_FORMAT_ADDITIONAL_INFOS + CURRENT = REMOVE_LIB_RNG }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ecd945e2b..a7f303981 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -549,9 +549,11 @@ void CGameHandler::reinitScripting() void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking) { if (si->seedToBeUsed == 0) - { si->seedToBeUsed = CRandomGenerator::getDefault().nextInt(); - } + + logGlobal->info("Using random seed: %d", si->seedToBeUsed); + randomNumberGenerator = std::make_unique(si->seedToBeUsed); + CMapService mapService; gs = new CGameState(); gs->preInit(VLC, this); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 4cd5fc573..d6db0a878 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -277,7 +277,7 @@ public: void checkVictoryLossConditions(const std::set & playerColors); void checkVictoryLossConditionsForAll(); - vstd::RNG & getRandomGenerator(); + vstd::RNG & getRandomGenerator() override; #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; diff --git a/test/mock/mock_IGameCallback.cpp b/test/mock/mock_IGameCallback.cpp index 13a6ea0ff..43428b698 100644 --- a/test/mock/mock_IGameCallback.cpp +++ b/test/mock/mock_IGameCallback.cpp @@ -31,3 +31,8 @@ void GameCallbackMock::sendAndApply(CPackForClient * pack) { upperCallback->apply(pack); } + +vstd::RNG & GameCallbackMock::getRandomGenerator() +{ + throw std::runtime_error("Not implemented!"); +} diff --git a/test/mock/mock_IGameCallback.h b/test/mock/mock_IGameCallback.h index 9ab8675f1..78e11699c 100644 --- a/test/mock/mock_IGameCallback.h +++ b/test/mock/mock_IGameCallback.h @@ -93,6 +93,8 @@ public: ///useful callback methods void sendAndApply(CPackForClient * pack) override; + vstd::RNG & getRandomGenerator() override; + #if SCRIPTING_ENABLED MOCK_CONST_METHOD0(getGlobalContextPool, scripting::Pool *()); #endif