Browse Source

CGameHandler no longer inherits CGameInfoCallback, reduce direct access
to gamestate

Ivan Savenko 5 months ago
parent
commit
8255a4b9ae

+ 1 - 1
client/CServerHandler.cpp

@@ -633,7 +633,7 @@ void CServerHandler::startGameplay(std::shared_ptr<CGameState> gameState)
 		throw std::runtime_error("Invalid mode");
 	}
 	// After everything initialized we can accept CPackToClient netpacks
-	logicConnection->enterGameplayConnectionMode(client->gameState());
+	logicConnection->setCallback(&client->gameState());
 	setState(EClientState::GAMEPLAY);
 }
 

+ 4 - 4
lib/battle/BattleLayout.cpp

@@ -18,12 +18,12 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-BattleLayout BattleLayout::createDefaultLayout(IGameInfoCallback * cb, const CArmedInstance * attacker, const CArmedInstance * defender)
+BattleLayout BattleLayout::createDefaultLayout(const IGameInfoCallback & gameInfo, const CArmedInstance * attacker, const CArmedInstance * defender)
 {
-	return createLayout(cb, "default", attacker, defender);
+	return createLayout(gameInfo, "default", attacker, defender);
 }
 
-BattleLayout BattleLayout::createLayout(IGameInfoCallback * cb, const std::string & layoutName, const CArmedInstance * attacker, const CArmedInstance * defender)
+BattleLayout BattleLayout::createLayout(const IGameInfoCallback & gameInfo, const std::string & layoutName, const CArmedInstance * attacker, const CArmedInstance * defender)
 {
 	const auto & loadHex = [](const JsonNode & node)
 	{
@@ -44,7 +44,7 @@ BattleLayout BattleLayout::createLayout(IGameInfoCallback * cb, const std::strin
 		return result;
 	};
 
-	const JsonNode & configRoot = cb->getSettings().getValue(EGameSettings::COMBAT_LAYOUTS);
+	const JsonNode & configRoot = gameInfo.getSettings().getValue(EGameSettings::COMBAT_LAYOUTS);
 	const JsonNode & config = configRoot[layoutName];
 
 	BattleLayout result;

+ 2 - 2
lib/battle/BattleLayout.h

@@ -32,8 +32,8 @@ struct DLL_EXPORT BattleLayout
 	bool tacticsAllowed = false;
 	bool obstaclesAllowed = false;
 
-	static BattleLayout createDefaultLayout(IGameInfoCallback * cb, const CArmedInstance * attacker, const CArmedInstance * defender);
-	static BattleLayout createLayout(IGameInfoCallback * cb, const std::string & layoutName, const CArmedInstance * attacker, const CArmedInstance * defender);
+	static BattleLayout createDefaultLayout(const IGameInfoCallback & gameInfo, const CArmedInstance * attacker, const CArmedInstance * defender);
+	static BattleLayout createLayout(const IGameInfoCallback & gameInfo, const std::string & layoutName, const CArmedInstance * attacker, const CArmedInstance * defender);
 };
 
 VCMI_LIB_NAMESPACE_END

+ 8 - 11
lib/gameState/CGameState.cpp

@@ -151,8 +151,7 @@ int CGameState::getDate(Date mode) const
 	return getDate(day, mode);
 }
 
-CGameState::CGameState(IGameInfoCallback * callback)
-	: GameCallbackHolder(callback)
+CGameState::CGameState()
 {
 	heroesPool = std::make_unique<TavernHeroesPool>(this);
 	globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
@@ -177,7 +176,6 @@ void CGameState::preInit(Services * newServices)
 void CGameState::init(const IMapService * mapService, StartInfo * si, IGameRandomizer & gameRandomizer, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap)
 {
 	assert(services);
-	assert(cb);
 	scenarioOps = CMemorySerializer::deepCopy(*si);
 	initialOpts = CMemorySerializer::deepCopy(*si);
 	si = nullptr;
@@ -284,7 +282,6 @@ void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode &
 void CGameState::updateOnLoad(StartInfo * si)
 {
 	assert(services);
-	assert(cb);
 	scenarioOps->playerInfos = si->playerInfos;
 	for(auto & i : si->playerInfos)
 		players.at(i.first).human = i.second.isControlledByHuman();
@@ -301,7 +298,7 @@ void CGameState::initNewGame(const IMapService * mapService, vstd::RNG & randomG
 		CStopWatch sw;
 
 		// Gen map
-		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, cb, randomGenerator.nextInt());
+		CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, this, randomGenerator.nextInt());
 		progressTracking.include(mapGenerator);
 
 		map = mapGenerator.generate();
@@ -363,7 +360,7 @@ void CGameState::initNewGame(const IMapService * mapService, vstd::RNG & randomG
 	{
 		logGlobal->info("Open map file: %s", scenarioOps->mapname);
 		const ResourcePath mapURI(scenarioOps->mapname, EResType::MAP);
-		map = mapService->loadMap(mapURI, cb);
+		map = mapService->loadMap(mapURI, this);
 	}
 }
 
@@ -526,7 +523,7 @@ void CGameState::initPlayerStates()
 	logGlobal->debug("\tCreating player entries in gs");
 	for(auto & elem : scenarioOps->playerInfos)
 	{
-		players.try_emplace(elem.first, cb);
+		players.try_emplace(elem.first, this);
 		PlayerState & p = players.at(elem.first);
 		p.color=elem.first;
 		p.human = elem.second.isControlledByHuman();
@@ -553,7 +550,7 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
 	if (!hero)
 	{
 		auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::HERO, heroTypeId.toHeroType()->heroClass->getIndex());
-		auto object = handler->create(cb, handler->getTemplates().front());
+		auto object = handler->create(this, handler->getTemplates().front());
 		hero = std::dynamic_pointer_cast<CGHeroInstance>(object);
 		hero->ID = Obj::HERO;
 		hero->setHeroType(heroTypeId);
@@ -623,7 +620,7 @@ void CGameState::initHeroes(IGameRandomizer & gameRandomizer)
 		if (tile.isWater())
 		{
 			auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum());
-			auto boat = std::dynamic_pointer_cast<CGBoat>(handler->create(cb, nullptr));
+			auto boat = std::dynamic_pointer_cast<CGBoat>(handler->create(this, nullptr));
 			handler->configureObject(boat.get(), gameRandomizer);
 
 			boat->setAnchorPos(hero->anchorPos());
@@ -644,7 +641,7 @@ void CGameState::initHeroes(IGameRandomizer & gameRandomizer)
 		// instances for h3 heroes from roe/ab h3m maps and heroes from mods at this point don't exist -> create them
 		if (!heroInPool)
 		{
-			auto newHeroPtr = std::make_shared<CGHeroInstance>(cb);
+			auto newHeroPtr = std::make_shared<CGHeroInstance>(this);
 			newHeroPtr->subID = htype.getNum();
 			map->addToHeroPool(newHeroPtr);
 			heroInPool = newHeroPtr.get();
@@ -938,7 +935,7 @@ void CGameState::initMapObjects(IGameRandomizer & gameRandomizer)
 		if (q->ID ==Obj::QUEST_GUARD || q->ID ==Obj::SEER_HUT)
 			q->setObjToKill();
 	}
-	CGSubterraneanGate::postInit(cb); //pairing subterranean gates
+	CGSubterraneanGate::postInit(this); //pairing subterranean gates
 
 	map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
 }

+ 2 - 2
lib/gameState/CGameState.h

@@ -42,7 +42,7 @@ class UpgradeInfo;
 
 DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
 
-class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeable, public GameCallbackHolder
+class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeable
 {
 	friend class CGameStateCampaign;
 
@@ -66,7 +66,7 @@ public:
 	/// list of players currently making turn. Usually - just one, except for simturns
 	std::set<PlayerColor> actingPlayers;
 
-	CGameState(IGameInfoCallback * callback);
+	CGameState();
 	virtual ~CGameState();
 
 	CGameState & gameState() final { return *this; }

+ 1 - 1
lib/gameState/CGameStateCampaign.cpp

@@ -694,7 +694,7 @@ bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
 
 std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap()
 {
-	return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE, gameState->cb);
+	return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE, gameState);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/gameState/GameStatePackVisitor.cpp

@@ -716,7 +716,7 @@ void GameStatePackVisitor::visitSwapStacks(SwapStacks & pack)
 void GameStatePackVisitor::visitInsertNewStack(InsertNewStack & pack)
 {
 	if(auto * obj = gs.getArmyInstance(pack.army))
-		obj->putStack(pack.slot, std::make_unique<CStackInstance>(gs.cb, pack.type, pack.count));
+		obj->putStack(pack.slot, std::make_unique<CStackInstance>(&gs, pack.type, pack.count));
 	else
 		throw std::runtime_error("InsertNewStack: invalid army object " + std::to_string(pack.army.getNum()) + ", possible game state corruption.");
 }

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -325,7 +325,7 @@ void CGTownInstance::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroIn
 
 				const_cast<CGHeroInstance *>(defendingHero)->setVisitedTown(this, false); //hack to return visitor from garrison after battle
 			}
-			gameEvents.startBattle(h, defendingArmy, getSightCenter(), h, defendingHero, BattleLayout::createDefaultLayout(cb, h, defendingArmy), (isBattleOutside ? nullptr : this));
+			gameEvents.startBattle(h, defendingArmy, getSightCenter(), h, defendingHero, BattleLayout::createDefaultLayout(*cb, h, defendingArmy), (isBattleOutside ? nullptr : this));
 		}
 		else
 		{

+ 1 - 1
lib/mapObjects/CRewardableObject.cpp

@@ -99,7 +99,7 @@ void CRewardableObject::garrisonDialogClosed(IGameEventCallback & gameEvents, co
 
 void CRewardableObject::doStartBattle(IGameEventCallback & gameEvents, const CGHeroInstance * hero) const
 {
-	auto layout = BattleLayout::createLayout(cb, configuration.guardsLayout, hero, this);
+	auto layout = BattleLayout::createLayout(*cb, configuration.guardsLayout, hero, this);
 	gameEvents.startBattle(hero, this, visitablePos(), hero, nullptr, layout, nullptr);
 }
 

+ 0 - 5
lib/serializer/Connection.cpp

@@ -129,11 +129,6 @@ void CConnection::setCallback(IGameInfoCallback * cb)
 	deserializer->cb = cb;
 }
 
-void CConnection::enterGameplayConnectionMode(CGameState & gs)
-{
-	setCallback(gs.cb);
-}
-
 void CConnection::setSerializationVersion(ESerializationVersion version)
 {
 	deserializer->version = version;

+ 0 - 1
lib/serializer/Connection.h

@@ -51,7 +51,6 @@ public:
 
 	void enterLobbyConnectionMode();
 	void setCallback(IGameInfoCallback * cb);
-	void enterGameplayConnectionMode(CGameState & gs);
 	void setSerializationVersion(ESerializationVersion version);
 };
 

+ 138 - 133
server/CGameHandler.cpp

@@ -115,6 +115,11 @@ const Services * CGameHandler::services() const
 	return LIBRARY;
 }
 
+IGameInfoCallback & CGameHandler::gameInfo()
+{
+	return *gs;
+}
+
 const CGameHandler::BattleCb * CGameHandler::battle(const BattleID & battleID) const
 {
 	return gameState().getBattle(battleID);
@@ -122,7 +127,7 @@ const CGameHandler::BattleCb * CGameHandler::battle(const BattleID & battleID) c
 
 const CGameHandler::GameCb * CGameHandler::game() const
 {
-	return this;
+	return gs.get();
 }
 
 vstd::CLoggerBase * CGameHandler::logger() const
@@ -449,7 +454,7 @@ void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c)
 			if (i->first == playerId)
 				continue;
 
-			if (getPlayerState(i->first)->status != EPlayerStatus::INGAME)
+			if (gameInfo().getPlayerState(i->first)->status != EPlayerStatus::INGAME)
 				continue;
 
 			logGlobal->trace("Notifying player %s", i->first);
@@ -512,7 +517,6 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
 	, turnOrder(std::make_unique<TurnOrderProcessor>(this))
 	, queries(std::make_unique<QueriesProcessor>())
 	, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
-	, randomizer(std::make_unique<GameRandomizer>(*this))
 	, complainNoCreatures("No creatures to split")
 	, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
 	, complainInvalidSlot("Invalid slot accessed!")
@@ -545,7 +549,8 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 	logGlobal->info("Using random seed: %d", randomizer->getDefault().nextInt());
 
 	CMapService mapService;
-	gs = std::make_shared<CGameState>(this);
+	gs = std::make_shared<CGameState>();
+	randomizer = std::make_unique<GameRandomizer>(*gs);
 	gs->preInit(LIBRARY);
 	logGlobal->info("Gamestate created!");
 	gs->init(&mapService, si, *randomizer, progressTracking);
@@ -560,7 +565,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=false, bool clear = false)
 {// bool forced = true - if creature should be replaced, if false - only if no creature was set
 
-	const PlayerState * p = getPlayerState(town->tempOwner);
+	const PlayerState * p = gameInfo().getPlayerState(town->tempOwner);
 	if (!p)
 	{
 		assert(town->tempOwner == PlayerColor::NEUTRAL);
@@ -628,8 +633,8 @@ void CGameHandler::onNewTurn()
 {
 	logGlobal->trace("Turn %d", gameState().day+1);
 
-	bool firstTurn = !getDate(Date::DAY);
-	bool newMonth = getDate(Date::DAY_OF_MONTH) == 28;
+	bool firstTurn = !gameInfo().getDate(Date::DAY);
+	bool newMonth = gameInfo().getDate(Date::DAY_OF_MONTH) == 28;
 
 	if (firstTurn)
 	{
@@ -672,8 +677,8 @@ void CGameHandler::onNewTurn()
 		{
 			for(const auto & player : gameState().players)
 			{
-				if (getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
-					getPlayerRelations(player.first, t->tempOwner) == PlayerRelations::ENEMIES)
+				if (gameInfo().getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
+					gameInfo().getPlayerRelations(player.first, t->tempOwner) == PlayerRelations::ENEMIES)
 					changeFogOfWar(t->getSightCenter(), t->valOfBonuses(BonusType::DARKNESS), player.first, ETileVisibility::HIDDEN);
 			}
 		}
@@ -752,7 +757,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
 		for (int i = 0; i < h->maxSpellLevel(); i++)
 		{
 			std::vector<SpellID> spells;
-			getAllowedSpells(spells, i+1);
+			gameState().getAllowedSpells(spells, i+1);
 			for (auto & spell : spells)
 				cs.spells.insert(spell);
 		}
@@ -774,7 +779,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
 
 bool CGameHandler::removeObject(const CGObjectInstance * obj, const PlayerColor & initiator)
 {
-	if (!obj || !getObj(obj->id))
+	if (!obj || !gameInfo().getObj(obj->id))
 	{
 		logGlobal->error("Something wrong, that object already has been removed or hasn't existed!");
 		return false;
@@ -791,11 +796,11 @@ bool CGameHandler::removeObject(const CGObjectInstance * obj, const PlayerColor
 
 bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode movementMode, bool transit, PlayerColor asker)
 {
-	const CGHeroInstance *h = getHero(hid);
+	const CGHeroInstance *h = gameInfo().getHero(hid);
 	// not turn of that hero or player can't simply teleport hero (at least not with this function)
 	if(!h || (asker != PlayerColor::NEUTRAL && movementMode != EMovementMode::STANDARD))
 	{
-		if(h && getStartInfo()->turnTimerInfo.isEnabled() && gameState().players.at(h->getOwner()).turnTimer.turnTimer == 0)
+		if(h && gameInfo().getStartInfo()->turnTimerInfo.isEnabled() && gameState().players.at(h->getOwner()).turnTimer.turnTimer == 0)
 			return true; //timer expired, no error
 		
 		logGlobal->error("Illegal call to move hero!");
@@ -811,7 +816,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		return false;
 	}
 
-	const TerrainTile t = *getTile(hmpos);
+	const TerrainTile t = *gameInfo().getTile(hmpos);
 	const int3 guardPos = gameState().guardingCreaturePosition(hmpos);
 	CGObjectInstance * objectToVisit = nullptr;
 	CGObjectInstance * guardian = nullptr;
@@ -819,9 +824,9 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	if (!t.visitableObjects.empty())
 		objectToVisit = gameState().getObjInstance(t.visitableObjects.back());
 
-	if (isInTheMap(guardPos))
+	if (gameInfo().isInTheMap(guardPos))
 	{
-		for (auto const & objectID : getTile(guardPos)->visitableObjects)
+		for (auto const & objectID : gameInfo().getTile(guardPos)->visitableObjects)
 		{
 			auto object = gameState().getObjInstance(objectID);
 
@@ -844,7 +849,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	tmh.movePoints = h->movementPointsRemaining();
 
 	//check if destination tile is available
-	auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gameState(), h, PathfinderOptions(*this));
+	auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gameState(), h, PathfinderOptions(gameInfo()));
 	auto ti = pathfinderHelper->getTurnInfo();
 
 	const bool canFly = ti->hasFlyingMovement() || (h->inBoat() && h->getBoat()->layer == EPathfindingLayer::AIR);
@@ -871,7 +876,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 
 	if (objectToVisit &&
 		objectToVisit->getOwner().isValidPlayer() &&
-		getPlayerRelations(objectToVisit->getOwner(), h->getOwner()) == PlayerRelations::ENEMIES &&
+		gameInfo().getPlayerRelations(objectToVisit->getOwner(), h->getOwner()) == PlayerRelations::ENEMIES &&
 		!turnOrder->isContactAllowed(objectToVisit->getOwner(), h->getOwner()))
 		return complainRet("You cannot move your hero there. This object belongs to another player and simultaneous turns are still active!");
 
@@ -907,7 +912,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		for(const auto & objID : gameState().getMap().getTile(h->visitablePos()).visitableObjects)
 			gameState().getObjInstance(objID)->onHeroLeave(*this, h);
 
-		this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadius(), ETileVisibility::HIDDEN, h->tempOwner);
+		gameInfo().getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadius(), ETileVisibility::HIDDEN, h->tempOwner);
 	};
 
 	auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards,
@@ -921,7 +926,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		if (leavingTile == LEAVING_TILE)
 			leaveTile();
 
-		if (lookForGuards == CHECK_FOR_GUARDS && isInTheMap(guardPos))
+		if (lookForGuards == CHECK_FOR_GUARDS && gameInfo().isInTheMap(guardPos))
 			tmh.attackedFrom = guardPos;
 
 		tmh.result = result;
@@ -931,7 +936,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		{ // Hero should be always able to visit any object he is staying on even if there are guards around
 			visitObjectOnTile(t, h);
 		}
-		else if (lookForGuards == CHECK_FOR_GUARDS && isInTheMap(guardPos))
+		else if (lookForGuards == CHECK_FOR_GUARDS && gameInfo().isInTheMap(guardPos))
 		{
 			objectVisited(guardian, h);
 
@@ -952,7 +957,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	{
 		for (ObjectInstanceID objectID : t.visitableObjects)
 		{
-			const CGObjectInstance * object = getObj(objectID);
+			const CGObjectInstance * object = gameInfo().getObj(objectID);
 
 			if(h->inBoat() && !object->isBlockedVisitable() && !h->getBoat()->onboardVisitAllowed)
 				return doMove(TryMoveHero::SUCCESS, this->IGNORE_GUARDS, DONT_VISIT_DEST, REMAINING_ON_TILE);
@@ -988,7 +993,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		if (blockingVisit()) // e.g. hero on the other side of teleporter
 			return true;
 
-		EGuardLook guardsCheck = (getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS) && movementMode == EMovementMode::DIMENSION_DOOR)
+		EGuardLook guardsCheck = (gameInfo().getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS) && movementMode == EMovementMode::DIMENSION_DOOR)
 			? CHECK_FOR_GUARDS
 			: IGNORE_GUARDS;
 
@@ -1040,8 +1045,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 
 bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker)
 {
-	const CGHeroInstance *h = getHero(hid);
-	const CGTownInstance *t = getTown(dstid);
+	const CGHeroInstance *h = gameInfo().getHero(hid);
+	const CGTownInstance *t = gameInfo().getTown(dstid);
 
 	if (!h || !t)
 		COMPLAIN_RET("Invalid call to teleportHero!");
@@ -1067,7 +1072,7 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
 
 void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owner)
 {
-	PlayerColor oldOwner = getOwner(obj->id);
+	PlayerColor oldOwner = gameState().getOwner(obj->id);
 
 	setObjPropertyID(obj->id, ObjProperty::OWNER, owner);
 
@@ -1090,7 +1095,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne
 	{
 		if (owner.isValidPlayer())
 		{
-			for (const CGTownInstance * t : getPlayerState(owner)->getTowns())
+			for (const CGTownInstance * t : gameInfo().getPlayerState(owner)->getTowns())
 			{
 				if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
 					setPortalDwelling(t);//set initial creatures for all portals of summoning
@@ -1185,7 +1190,7 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
 	if (remainerForTaking.empty())
 		return;
 
-	const auto * army = dynamic_cast<const CArmedInstance*>(getObj(objid));
+	const auto * army = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(objid));
 
 	for (const CStackBasicDescriptor &stackToTake : remainerForTaking)
 	{
@@ -1343,7 +1348,7 @@ void CGameHandler::giveHero(ObjectInstanceID id, PlayerColor player, ObjectInsta
 	sendAndApply(gh);
 
 	//Reveal fow around new hero, especially released from Prison
-	auto h = getHero(id);
+	auto h = gameInfo().getHero(id);
 	changeFogOfWar(h->getSightCenter(), h->getSightRadius(), player, ETileVisibility::REVEALED);
 }
 
@@ -1358,8 +1363,8 @@ void CGameHandler::changeObjPos(ObjectInstanceID objid, int3 newPos, const Playe
 
 void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID toHero)
 {
-	const CGHeroInstance * h1 = getHero(fromHero);
-	const CGHeroInstance * h2 = getHero(toHero);
+	const CGHeroInstance * h1 = gameInfo().getHero(fromHero);
+	const CGHeroInstance * h2 = gameInfo().getHero(toHero);
 	int h1_scholarSpellLevel = h1->valOfBonuses(BonusType::LEARN_MEETING_SPELL_LIMIT);
 	int h2_scholarSpellLevel = h2->valOfBonuses(BonusType::LEARN_MEETING_SPELL_LIMIT);
 
@@ -1459,10 +1464,10 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
 
 void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)
 {
-	auto h1 = getHero(hero1);
-	auto h2 = getHero(hero2);
+	auto h1 = gameInfo().getHero(hero1);
+	auto h2 = gameInfo().getHero(hero2);
 
-	if (getPlayerRelations(h1->getOwner(), h2->getOwner()) != PlayerRelations::ENEMIES)
+	if (gameInfo().getPlayerRelations(h1->getOwner(), h2->getOwner()) != PlayerRelations::ENEMIES)
 	{
 		auto exchange = std::make_shared<CGarrisonDialogQuery>(this, h1, h2);
 		ExchangeDialog hex;
@@ -1506,12 +1511,12 @@ void CGameHandler::sendAndApply(SetResources & pack)
 void CGameHandler::sendAndApply(NewStructures & pack)
 {
 	sendAndApply(static_cast<CPackForClient &>(pack));
-	checkVictoryLossConditionsForPlayer(getTown(pack.tid)->tempOwner);
+	checkVictoryLossConditionsForPlayer(gameInfo().getTown(pack.tid)->tempOwner);
 }
 
 bool CGameHandler::isPlayerOwns(const std::shared_ptr<CConnection> & connection, const CPackForServer * pack, ObjectInstanceID id)
 {
-	return pack->player == getOwner(id) && hasPlayerAt(getOwner(id), connection);
+	return pack->player == gameState().getOwner(id) && hasPlayerAt(gameState().getOwner(id), connection);
 }
 
 void CGameHandler::throwNotAllowedAction(const std::shared_ptr<CConnection> & connection)
@@ -1536,7 +1541,7 @@ void CGameHandler::throwIfWrongOwner(const std::shared_ptr<CConnection> & connec
 {
 	if(!isPlayerOwns(connection, pack, id))
 	{
-		wrongPlayerMessage(connection, pack, getOwner(id));
+		wrongPlayerMessage(connection, pack, gameState().getOwner(id));
 		throwNotAllowedAction(connection);
 	}
 }
@@ -1598,8 +1603,9 @@ bool CGameHandler::load(const std::string & filename)
 
 	try
 	{
-		CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourcePath(stem.to_string(), EResType::SAVEGAME)), this);
-		gs = std::make_shared<CGameState>(this);
+		CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourcePath(stem.to_string(), EResType::SAVEGAME)), gs.get());
+		gs = std::make_shared<CGameState>();
+		randomizer = std::make_unique<GameRandomizer>(*gs);
 		gs->loadGame(lf);
 		logGlobal->info("Loading server state");
 		lf.load(*this);
@@ -1653,7 +1659,7 @@ bool CGameHandler::bulkSplitStack(SlotID slotSrc, ObjectInstanceID srcOwner, si3
 	if(!slotSrc.validSlot() && complain(complainInvalidSlot))
 		return false;
 
-	const CArmedInstance * army = static_cast<const CArmedInstance*>(getObjInstance(srcOwner));
+	const CArmedInstance * army = static_cast<const CArmedInstance*>(gameInfo().getObjInstance(srcOwner));
 	const CCreatureSet & creatureSet = *army;
 
 	if((!vstd::contains(creatureSet.stacks, slotSrc) && complain(complainNoCreatures))
@@ -1697,7 +1703,7 @@ bool CGameHandler::bulkMergeStacks(SlotID slotSrc, ObjectInstanceID srcOwner)
 	if(!slotSrc.validSlot() && complain(complainInvalidSlot))
 		return false;
 
-	const CArmedInstance * army = static_cast<const CArmedInstance*>(getObjInstance(srcOwner));
+	const CArmedInstance * army = static_cast<const CArmedInstance*>(gameInfo().getObjInstance(srcOwner));
 	const CCreatureSet & creatureSet = *army;
 
 	if(!vstd::contains(creatureSet.stacks, slotSrc) && complain(complainNoCreatures))
@@ -1739,13 +1745,13 @@ bool CGameHandler::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destA
 	if(!srcSlot.validSlot() && complain(complainInvalidSlot))
 		return false;
 
-	const CArmedInstance * armySrc = static_cast<const CArmedInstance*>(getObjInstance(srcArmy));
+	const CArmedInstance * armySrc = static_cast<const CArmedInstance*>(gameInfo().getObjInstance(srcArmy));
 	const CCreatureSet & setSrc = *armySrc;
 
 	if(!vstd::contains(setSrc.stacks, srcSlot) && complain(complainNoCreatures))
 		return false;
 
-	const CArmedInstance * armyDest = static_cast<const CArmedInstance*>(getObjInstance(destArmy));
+	const CArmedInstance * armyDest = static_cast<const CArmedInstance*>(gameInfo().getObjInstance(destArmy));
 	const CCreatureSet & setDest = *armyDest;
 	auto freeSlots = setDest.getFreeSlotsQueue();
 
@@ -1826,7 +1832,7 @@ bool CGameHandler::bulkSplitAndRebalanceStack(SlotID slotSrc, ObjectInstanceID s
 	if(!slotSrc.validSlot() && complain(complainInvalidSlot))
 		return false;
 
-	const CArmedInstance * army = static_cast<const CArmedInstance*>(getObjInstance(srcOwner));
+	const CArmedInstance * army = static_cast<const CArmedInstance*>(gameInfo().getObjInstance(srcOwner));
 	const CCreatureSet & creatureSet = *army;
 
 	if(!vstd::contains(creatureSet.stacks, slotSrc) && complain(complainNoCreatures))
@@ -1912,8 +1918,8 @@ bool CGameHandler::bulkSplitAndRebalanceStack(SlotID slotSrc, ObjectInstanceID s
 
 bool CGameHandler::arrangeStacks(ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player)
 {
-	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(getObj(id1));
-	const CArmedInstance * s2 = static_cast<const CArmedInstance *>(getObj(id2));
+	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(gameInfo().getObj(id1));
+	const CArmedInstance * s2 = static_cast<const CArmedInstance *>(gameInfo().getObj(id2));
 
 	if (s1 == nullptr || s2 == nullptr)
 	{
@@ -2071,7 +2077,7 @@ bool CGameHandler::hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor
 
 bool CGameHandler::disbandCreature(ObjectInstanceID id, SlotID pos)
 {
-	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(getObjInstance(id));
+	const CArmedInstance * s1 = static_cast<const CArmedInstance *>(gameInfo().getObjInstance(id));
 	if (!vstd::contains(s1->stacks,pos))
 	{
 		complain("Illegal call to disbandCreature - no such stack in army!");
@@ -2084,7 +2090,7 @@ bool CGameHandler::disbandCreature(ObjectInstanceID id, SlotID pos)
 
 bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, bool force)
 {
-	const CGTownInstance * t = getTown(tid);
+	const CGTownInstance * t = gameInfo().getTown(tid);
 	if(!t)
 		COMPLAIN_RETF("No such town (ID=%s)!", tid);
 	if(!t->getTown()->buildings.count(requestedID))
@@ -2104,7 +2110,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 		switch(requestedBuilding->mode)
 		{
 		case CBuilding::BUILD_NORMAL :
-			if (canBuildStructure(t, requestedID) != EBuildingState::ALLOWED)
+			if (gameState().canBuildStructure(t, requestedID) != EBuildingState::ALLOWED)
 				COMPLAIN_RET("Cannot build that building!");
 			break;
 
@@ -2258,7 +2264,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 
 bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
 {
-	const CGTownInstance * t = getTown(tid);
+	const CGTownInstance * t = gameInfo().getTown(tid);
 
 	if(!t->hasBuilt(bid))
 		return false;
@@ -2292,7 +2298,7 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
 bool CGameHandler::razeStructure (ObjectInstanceID tid, BuildingID bid)
 {
 ///incomplete, simply erases target building
-	const CGTownInstance * t = getTown(tid);
+	const CGTownInstance * t = gameInfo().getTown(tid);
 	if(!t->hasBuilt(bid))
 		return false;
 	RazeStructures rs;
@@ -2316,7 +2322,7 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
 {
 	CGTownInstance *t = gameState().getTown(tid);
 
-	if(!getSettings().getBoolean(EGameSettings::TOWNS_SPELL_RESEARCH) && complain("Spell research not allowed!"))
+	if(!gameInfo().getSettings().getBoolean(EGameSettings::TOWNS_SPELL_RESEARCH) && complain("Spell research not allowed!"))
 		return false;
 	if (!t->spellResearchAllowed && complain("Spell research not allowed in this town!"))
 		return false;
@@ -2331,7 +2337,7 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
 
 	auto spells = t->spells.at(level);
 	
-	bool researchLimitExceeded = t->spellResearchCounterDay >= getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY).Vector()[level].Float();
+	bool researchLimitExceeded = t->spellResearchCounterDay >= gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY).Vector()[level].Float();
 	if(researchLimitExceeded && complain("Already researched today!"))
 		return false;
 
@@ -2343,11 +2349,11 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
 		return true;
 	}
 
-	auto costBase = TResources(getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
-	auto costExponent = getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
+	auto costBase = TResources(gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
+	auto costExponent = gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
 	auto cost = costBase * std::pow(t->spellResearchAcceptedCounter + 1, costExponent);
 
-	if(!getPlayerState(t->getOwner())->resources.canAfford(cost) && complain("Spell replacement cannot be afforded!"))
+	if(!gameInfo().getPlayerState(t->getOwner())->resources.canAfford(cost) && complain("Spell replacement cannot be afforded!"))
 		return false;
 
 	giveResources(t->getOwner(), -cost);
@@ -2368,10 +2374,10 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
 
 bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dstid, CreatureID crid, ui32 cram, si32 fromLvl, PlayerColor player)
 {
-	const CGDwelling * dwelling = dynamic_cast<const CGDwelling *>(getObj(objid));
-	const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(getObj(objid));
-	const CArmedInstance * army = dynamic_cast<const CArmedInstance *>(getObj(dstid));
-	const CGHeroInstance * hero = dynamic_cast<const CGHeroInstance *>(getObj(dstid));
+	const CGDwelling * dwelling = dynamic_cast<const CGDwelling *>(gameInfo().getObj(objid));
+	const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(gameInfo().getObj(objid));
+	const CArmedInstance * army = dynamic_cast<const CArmedInstance *>(gameInfo().getObj(dstid));
+	const CGHeroInstance * hero = dynamic_cast<const CGHeroInstance *>(gameInfo().getObj(dstid));
 	const CCreature * c = crid.toCreature();
 
 	const bool warMachine = c->warMachine != ArtifactID::NONE;
@@ -2416,7 +2422,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 	SlotID slot = army->getSlotFor(crid);
 
 	if ((!found && complain("Cannot recruit: no such creatures!"))
-		|| ((si32)cram  >  LIBRARY->creh->objects.at(crid)->maxAmount(getPlayerState(army->tempOwner)->resources) && complain("Cannot recruit: lack of resources!"))
+		|| ((si32)cram  >  LIBRARY->creh->objects.at(crid)->maxAmount(gameInfo().getPlayerState(army->tempOwner)->resources) && complain("Cannot recruit: lack of resources!"))
 		|| (cram<=0  &&  complain("Cannot recruit: cram <= 0!"))
 		|| (!slot.validSlot()  && !warMachine && complain("Cannot recruit: no available slot!")))
 	{
@@ -2465,15 +2471,15 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 
 bool CGameHandler::upgradeCreature(ObjectInstanceID objid, SlotID pos, CreatureID upgID)
 {
-	const CArmedInstance * obj = static_cast<const CArmedInstance *>(getObjInstance(objid));
+	const CArmedInstance * obj = static_cast<const CArmedInstance *>(gameInfo().getObjInstance(objid));
 	if (!obj->hasStackAtSlot(pos))
 	{
 		COMPLAIN_RET("Cannot upgrade, no stack at slot " + std::to_string(pos));
 	}
 	UpgradeInfo upgradeInfo(obj->getStackPtr(pos)->getId());
-	fillUpgradeInfo(obj, pos, upgradeInfo);
+	gameState().fillUpgradeInfo(obj, pos, upgradeInfo);
 	PlayerColor player = obj->tempOwner;
-	const PlayerState *p = getPlayerState(player);
+	const PlayerState *p = gameInfo().getPlayerState(player);
 	int crQuantity = obj->stacks.at(pos)->getCount();
 
 	//check if upgrade is possible
@@ -2498,7 +2504,7 @@ bool CGameHandler::upgradeCreature(ObjectInstanceID objid, SlotID pos, CreatureI
 
 bool CGameHandler::changeStackType(const StackLocation &sl, const CCreature *c)
 {
-	const CArmedInstance * obj = static_cast<const CArmedInstance *>(getObjInstance(sl.army));
+	const CArmedInstance * obj = static_cast<const CArmedInstance *>(gameInfo().getObjInstance(sl.army));
 
 	if (!obj->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Cannot find a stack to change type");
@@ -2545,7 +2551,7 @@ void CGameHandler::moveArmy(const CArmedInstance *src, const CArmedInstance *dst
 
 bool CGameHandler::swapGarrisonOnSiege(ObjectInstanceID tid)
 {
-	const CGTownInstance * town = getTown(tid);
+	const CGTownInstance * town = gameInfo().getTown(tid);
 
 	if(!town->getGarrisonHero() == !town->getVisitingHero())
 		return false;
@@ -2572,7 +2578,7 @@ bool CGameHandler::swapGarrisonOnSiege(ObjectInstanceID tid)
 
 bool CGameHandler::garrisonSwap(ObjectInstanceID tid)
 {
-	const CGTownInstance * town = getTown(tid);
+	const CGTownInstance * town = gameInfo().getTown(tid);
 	if (!town->getGarrisonHero() && town->getVisitingHero()) //visiting => garrison, merge armies: town army => hero army
 	{
 
@@ -2593,9 +2599,9 @@ bool CGameHandler::garrisonSwap(ObjectInstanceID tid)
 	}
 	else if (town->getGarrisonHero() && !town->getVisitingHero()) //move hero out of the garrison
 	{
-		int mapCap = getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
+		int mapCap = gameInfo().getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
 		//check if moving hero out of town will break wandering heroes limit
-		if (getHeroCount(town->getGarrisonHero()->tempOwner,false) >= mapCap)
+		if (gameInfo().getHeroCount(town->getGarrisonHero()->tempOwner,false) >= mapCap)
 		{
 			complain("Cannot move hero out of the garrison, there are already " + std::to_string(mapCap) + " wandering heroes!");
 			return false;
@@ -2628,8 +2634,8 @@ bool CGameHandler::garrisonSwap(ObjectInstanceID tid)
 // Function moves artifact from src to dst. If dst is not a backpack and is already occupied, old dst art goes to backpack and is replaced.
 bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocation & src, const ArtifactLocation & dst)
 {
-	const auto srcArtSet = getArtSet(src);
-	const auto dstArtSet = getArtSet(dst);
+	const auto srcArtSet = gameState().getArtSet(src);
+	const auto dstArtSet = gameState().getArtSet(dst);
 	assert(srcArtSet);
 	assert(dstArtSet);
 
@@ -2650,7 +2656,7 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
 
 	if(srcArtifact == nullptr)
 		COMPLAIN_RET("No artifact to move!");
-	if(isDstSlotOccupied && getOwner(src.artHolder) != getOwner(dst.artHolder) && !isDstSlotBackpack)
+	if(isDstSlotOccupied && gameState().getOwner(src.artHolder) != gameState().getOwner(dst.artHolder) && !isDstSlotBackpack)
 		COMPLAIN_RET("Can't touch artifact on hero of another player!");
 
 	// Check if src/dest slots are appropriate for the artifacts exchanged.
@@ -2688,7 +2694,7 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
 		ma.artsPack1.emplace_back(dstSlot, src.slot);
 	}
 
-	auto hero = getHero(dst.artHolder);
+	auto hero = gameInfo().getHero(dst.artHolder);
 	if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->getTypeId(), dstSlot))
 		giveHeroNewArtifact(hero, ArtifactID::SPELLBOOK, ArtifactPosition::SPELLBOOK);
 
@@ -2705,8 +2711,8 @@ bool CGameHandler::bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceI
 	if(!isAllowedExchange(srcId, dstId))
 		COMPLAIN_RET("That heroes cannot make any exchange!");
 
-	auto psrcSet = getArtSet(srcId);
-	auto pdstSet = getArtSet(dstId);
+	auto psrcSet = gameState().getArtSet(srcId);
+	auto pdstSet = gameState().getArtSet(dstId);
 	if((!psrcSet) || (!pdstSet))
 		COMPLAIN_RET("bulkMoveArtifacts: wrong hero's ID");
 
@@ -2715,7 +2721,7 @@ bool CGameHandler::bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceI
 	auto & slotsDstSrc = ma.artsPack1;
 
 	// Temporary fitting set for artifacts. Used to select available slots before sending data.
-	CArtifactFittingSet artFittingSet(gameState().cb, pdstSet->bearerType());
+	CArtifactFittingSet artFittingSet(&gameState(), pdstSet->bearerType());
 
 	auto moveArtifact = [this, &artFittingSet, dstId](const CArtifactInstance * artifact,
 		ArtifactPosition srcSlot, std::vector<MoveArtifactInfo> & slots) -> void
@@ -2728,7 +2734,7 @@ bool CGameHandler::bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceI
 			slots.emplace_back(srcSlot, dstSlot);
 
 			// TODO Shouldn't be here. Possibly in callback after equipping the artifact
-			if(auto dstHero = getHero(dstId))
+			if(auto dstHero = gameInfo().getHero(dstId))
 			{
 				if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, artifact->getTypeId(), dstSlot))
 					giveHeroNewArtifact(dstHero, ArtifactID::SPELLBOOK, ArtifactPosition::SPELLBOOK);
@@ -2802,7 +2808,7 @@ bool CGameHandler::bulkMoveArtifacts(const PlayerColor & player, ObjectInstanceI
 
 bool CGameHandler::manageBackpackArtifacts(const PlayerColor & player, const ObjectInstanceID heroID, const ManageBackpackArtifacts::ManageCmd & sortType)
 {
-	const auto artSet = getArtSet(heroID);
+	const auto artSet = gameState().getArtSet(heroID);
 	COMPLAIN_RET_FALSE_IF(artSet == nullptr, "manageBackpackArtifacts: wrong hero's ID");
 
 	BulkMoveArtifacts bma(player, heroID, heroID, false);
@@ -2886,7 +2892,7 @@ bool CGameHandler::manageBackpackArtifacts(const PlayerColor & player, const Obj
 
 bool CGameHandler::saveArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx)
 {
-	auto artSet = getArtSet(heroID);
+	auto artSet = gameState().getArtSet(heroID);
 	COMPLAIN_RET_FALSE_IF(artSet == nullptr, "saveArtifactsCostume: wrong hero's ID");
 
 	ChangeArtifactsCostume costume(player, costumeIdx);
@@ -2902,9 +2908,9 @@ bool CGameHandler::saveArtifactsCostume(const PlayerColor & player, const Object
 
 bool CGameHandler::switchArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx)
 {
-	const auto artSet = getArtSet(heroID);
+	const auto artSet = gameState().getArtSet(heroID);
 	COMPLAIN_RET_FALSE_IF(artSet == nullptr, "switchArtifactsCostume: wrong hero's ID");
-	const auto playerState = getPlayerState(player);
+	const auto playerState = gameInfo().getPlayerState(player);
 	COMPLAIN_RET_FALSE_IF(playerState == nullptr, "switchArtifactsCostume: wrong player");
 
 	if(auto costume = playerState->costumesArtifacts.find(costumeIdx); costume != playerState->costumesArtifacts.end())
@@ -2945,7 +2951,7 @@ bool CGameHandler::switchArtifactsCostume(const PlayerColor & player, const Obje
 				estimateBackpackSize++;
 			}
 		
-		const auto backpackCap = getSettings().getInteger(EGameSettings::HEROES_BACKPACK_CAP);
+		const auto backpackCap = gameInfo().getSettings().getInteger(EGameSettings::HEROES_BACKPACK_CAP);
 		if((backpackCap < 0 || estimateBackpackSize <= backpackCap) && !bma.artsPack0.empty())
 			sendAndApply(bma);
 	}
@@ -2962,7 +2968,7 @@ bool CGameHandler::switchArtifactsCostume(const PlayerColor & player, const Obje
  */
 bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
 {
-	const CGHeroInstance * hero = getHero(heroID);
+	const CGHeroInstance * hero = gameInfo().getHero(heroID);
 	const CArtifactInstance * destArtifact = hero->getArt(artifactSlot);
 
 	if(!destArtifact)
@@ -3014,7 +3020,7 @@ bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition a
 
 bool CGameHandler::eraseArtifactByClient(const ArtifactLocation & al)
 {
-	const auto * hero = getHero(al.artHolder);
+	const auto * hero = gameInfo().getHero(al.artHolder);
 	if(hero == nullptr)
 		COMPLAIN_RET("eraseArtifactByClient: wrong hero's ID");
 
@@ -3031,7 +3037,7 @@ bool CGameHandler::eraseArtifactByClient(const ArtifactLocation & al)
 
 bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
 {
-	const CGHeroInstance * hero = getHero(hid);
+	const CGHeroInstance * hero = gameInfo().getHero(hid);
 	COMPLAIN_RET_FALSE_IF(nullptr == hero, "Invalid hero index");
 	const CGTownInstance * town = hero->getVisitedTown();
 	COMPLAIN_RET_FALSE_IF(nullptr == town, "Hero not in town");
@@ -3039,7 +3045,7 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
 	if (aid==ArtifactID::SPELLBOOK)
 	{
 		if ((!town->hasBuilt(BuildingID::MAGES_GUILD_1) && complain("Cannot buy a spellbook, no mage guild in the town!"))
-		    || (getResource(hero->getOwner(), EGameResID::GOLD) < GameConstants::SPELLBOOK_GOLD_COST && complain("Cannot buy a spellbook, not enough gold!"))
+			|| (gameInfo().getResource(hero->getOwner(), EGameResID::GOLD) < GameConstants::SPELLBOOK_GOLD_COST && complain("Cannot buy a spellbook, not enough gold!"))
 		    || (hero->getArt(ArtifactPosition::SPELLBOOK) && complain("Cannot buy a spellbook, hero already has a one!"))
 		   )
 			return false;
@@ -3057,7 +3063,7 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
 		COMPLAIN_RET_FALSE_IF(art->getWarMachine() == CreatureID::NONE, "War machine artifact required");
 		COMPLAIN_RET_FALSE_IF(hero->hasArt(aid),"Hero already has this machine!");
 		const int price = art->getPrice();
-		COMPLAIN_RET_FALSE_IF(getPlayerState(hero->getOwner())->resources[EGameResID::GOLD] < price, "Not enough gold!");
+		COMPLAIN_RET_FALSE_IF(gameInfo().getPlayerState(hero->getOwner())->resources[EGameResID::GOLD] < price, "Not enough gold!");
 
 		if(town->isWarMachineAvailable(aid))
 		{
@@ -3092,7 +3098,7 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, GameRe
 	int b2;
 	m->getOffer(rid, aid, b1, b2, EMarketMode::RESOURCE_ARTIFACT);
 
-	if (getResource(h->tempOwner, rid) < b1)
+	if (gameInfo().getResource(h->tempOwner, rid) < b1)
 		COMPLAIN_RET("You can't afford to buy this artifact!");
 
 	giveResource(h->tempOwner, rid, -b1);
@@ -3163,7 +3169,7 @@ bool CGameHandler::buySecSkill(const IMarket *m, const CGHeroInstance *h, Second
 	if (!vstd::contains(m->availableItemsIds(EMarketMode::RESOURCE_SKILL), skill))
 		COMPLAIN_RET("That skill is unavailable!");
 
-	if (getResource(h->tempOwner, EGameResID::GOLD) < GameConstants::SKILL_GOLD_COST)//TODO: remove hardcoded resource\summ?
+	if (gameInfo().getResource(h->tempOwner, EGameResID::GOLD) < GameConstants::SKILL_GOLD_COST)//TODO: remove hardcoded resource\summ?
 		COMPLAIN_RET("You can't afford to buy this skill");
 
 	giveResource(h->tempOwner, EGameResID::GOLD, -GameConstants::SKILL_GOLD_COST);
@@ -3174,7 +3180,7 @@ bool CGameHandler::buySecSkill(const IMarket *m, const CGHeroInstance *h, Second
 
 bool CGameHandler::tradeResources(const IMarket *market, ui32 amountToSell, PlayerColor player, GameResID toSell, GameResID toBuy)
 {
-	TResourceCap haveToSell = getPlayerState(player)->resources[toSell];
+	TResourceCap haveToSell = gameInfo().getPlayerState(player)->resources[toSell];
 
 	vstd::amin(amountToSell, haveToSell); //can't trade more resources than have
 
@@ -3260,14 +3266,14 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
 
 bool CGameHandler::sendResources(ui32 val, PlayerColor player, GameResID r1, PlayerColor r2)
 {
-	const PlayerState *p2 = getPlayerState(r2, false);
+	const PlayerState *p2 = gameInfo().getPlayerState(r2, false);
 	if (!p2  ||  p2->status != EPlayerStatus::INGAME)
 	{
 		complain("Dest player must be in game!");
 		return false;
 	}
 
-	TResourceCap curRes1 = getPlayerState(player)->resources[r1];
+	TResourceCap curRes1 = gameInfo().getPlayerState(player)->resources[r1];
 
 	vstd::amin(val, curRes1);
 
@@ -3279,7 +3285,7 @@ bool CGameHandler::sendResources(ui32 val, PlayerColor player, GameResID r1, Pla
 
 bool CGameHandler::setFormation(ObjectInstanceID hid, EArmyFormation formation)
 {
-	const CGHeroInstance *h = getHero(hid);
+	const CGHeroInstance *h = gameInfo().getHero(hid);
 	if (!h)
 	{
 		logGlobal->error("Hero doesn't exist!");
@@ -3336,8 +3342,8 @@ bool CGameHandler::complain(const std::string &problem)
 void CGameHandler::showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits)
 {
 	//PlayerColor player = getOwner(hid);
-	auto upperArmy = dynamic_cast<const CArmedInstance*>(getObj(upobj));
-	auto lowerArmy = dynamic_cast<const CArmedInstance*>(getObj(hid));
+	auto upperArmy = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(upobj));
+	auto lowerArmy = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(hid));
 
 	assert(lowerArmy);
 	assert(upperArmy);
@@ -3374,8 +3380,8 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
 	if (id1 == id2)
 		return true;
 
-	const CGObjectInstance *o1 = getObj(id1);
-	const CGObjectInstance *o2 = getObj(id2);
+	const CGObjectInstance *o1 = gameInfo().getObj(id1);
+	const CGObjectInstance *o2 = gameInfo().getObj(id2);
 	if (!o1 || !o2)
 		return true; //arranging stacks within an object should be always allowed
 
@@ -3394,9 +3400,9 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
 				return true;
 		}
 
-		auto market = getMarket(id1);
+		auto market = gameState().getMarket(id1);
 		if(market == nullptr)
-			market = getMarket(id2);
+			market = gameState().getMarket(id2);
 		if(market)
 			return market->allowsTrade(EMarketMode::ARTIFACT_EXP);
 
@@ -3499,7 +3505,7 @@ void CGameHandler::objectVisitEnded(const ObjectInstanceID & heroObjectID, Playe
 
 bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
 {
-	const auto *obj = dynamic_cast<const IShipyard *>(getObj(objid));
+	const auto *obj = dynamic_cast<const IShipyard *>(gameInfo().getObj(objid));
 
 	if (obj->shipyardStatus() != IBoatGenerator::GOOD)
 	{
@@ -3509,7 +3515,7 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
 
 	TResources boatCost;
 	obj->getBoatCost(boatCost);
-	TResources available = getPlayerState(playerID)->resources;
+	TResources available = gameInfo().getPlayerState(playerID)->resources;
 
 	if (!available.canAfford(boatCost))
 	{
@@ -3533,7 +3539,7 @@ void CGameHandler::checkVictoryLossConditions(const std::set<PlayerColor> & play
 {
 	for (auto playerColor : playerColors)
 	{
-		if (getPlayerState(playerColor, false))
+		if (gameInfo().getPlayerState(playerColor, false))
 			checkVictoryLossConditionsForPlayer(playerColor);
 	}
 }
@@ -3550,7 +3556,7 @@ void CGameHandler::checkVictoryLossConditionsForAll()
 
 void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 {
-	const PlayerState * p = getPlayerState(player);
+	const PlayerState * p = gameInfo().getPlayerState(player);
 
 	if(!p || p->status != EPlayerStatus::INGAME) return;
 
@@ -3574,10 +3580,10 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			//one player won -> all enemies lost
 			for (auto i = gameState().players.cbegin(); i!=gameState().players.cend(); i++)
 			{
-				if (i->first != player && getPlayerState(i->first)->status == EPlayerStatus::INGAME)
+				if (i->first != player && gameInfo().getPlayerState(i->first)->status == EPlayerStatus::INGAME)
 				{
 					peg.player = i->first;
-					peg.victoryLossCheckResult = getPlayerRelations(player, i->first) == PlayerRelations::ALLIES ?
+					peg.victoryLossCheckResult = gameInfo().getPlayerRelations(player, i->first) == PlayerRelations::ALLIES ?
 								victoryLossCheckResult : victoryLossCheckResult.invert(); // ally of winner
 
 					InfoWindow iw;
@@ -3627,7 +3633,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 			//notify all players
 			for (auto pc : playerColors)
 			{
-				if (getPlayerState(pc)->status == EPlayerStatus::INGAME)
+				if (gameInfo().getPlayerState(pc)->status == EPlayerStatus::INGAME)
 				{
 					InfoWindow iw;
 					getVictoryLossMessage(player, victoryLossCheckResult.invert(), iw);
@@ -3793,7 +3799,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstanc
 
 bool CGameHandler::insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count)
 {
-	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+	auto army = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl.army));
 
 	if (army->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Slot is already taken!");
@@ -3812,7 +3818,7 @@ bool CGameHandler::insertNewStack(const StackLocation &sl, const CCreature *c, T
 
 bool CGameHandler::eraseStack(const StackLocation &sl, bool forceRemoval)
 {
-	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+	auto army = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl.army));
 
 	if (!army->hasStackAtSlot(sl.slot))
 		COMPLAIN_RET("Cannot find a stack to erase");
@@ -3833,7 +3839,7 @@ bool CGameHandler::eraseStack(const StackLocation &sl, bool forceRemoval)
 
 bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, ChangeValueMode mode)
 {
-	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+	auto army = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl.army));
 
 	TQuantity currentCount = army->getStackCount(sl.slot);
 	if ((mode == ChangeValueMode::ABSOLUTE && count < 0)
@@ -3861,7 +3867,7 @@ bool CGameHandler::changeStackCount(const StackLocation &sl, TQuantity count, Ch
 
 bool CGameHandler::addToSlot(const StackLocation &sl, const CCreature *c, TQuantity count)
 {
-	auto army = dynamic_cast<const CArmedInstance*>(getObj(sl.army));
+	auto army = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl.army));
 
 	const CCreature *slotC = army->getCreature(sl.slot);
 	if (!slotC) //slot is empty
@@ -3910,8 +3916,8 @@ void CGameHandler::tryJoiningArmy(const CArmedInstance *src, const CArmedInstanc
 
 bool CGameHandler::moveStack(const StackLocation &src, const StackLocation &dst, TQuantity count)
 {
-	auto srcArmy = dynamic_cast<const CArmedInstance*>(getObj(src.army));
-	auto dstArmy = dynamic_cast<const CArmedInstance*>(getObj(dst.army));
+	auto srcArmy = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(src.army));
+	auto dstArmy = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(dst.army));
 
 	if (!srcArmy->hasStackAtSlot(src.slot))
 		COMPLAIN_RET("No stack to move!");
@@ -3963,8 +3969,8 @@ void CGameHandler::castSpell(const spells::Caster * caster, SpellID spellID, con
 
 bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & sl2)
 {
-	auto army1 = dynamic_cast<const CArmedInstance*>(getObj(sl1.army));
-	auto army2 = dynamic_cast<const CArmedInstance*>(getObj(sl2.army));
+	auto army1 = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl1.army));
+	auto army2 = dynamic_cast<const CArmedInstance*>(gameInfo().getObj(sl2.army));
 
 	if(!army1->hasStackAtSlot(sl1.slot))
 	{
@@ -3988,11 +3994,11 @@ bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & s
 
 bool CGameHandler::putArtifact(const ArtifactLocation & al, const ArtifactInstanceID & id, std::optional<bool> askAssemble)
 {
-	const auto artInst = getArtInstance(id);
+	const auto artInst = gameInfo().getArtInstance(id);
 	assert(artInst && artInst->getType());
 	ArtifactLocation dst(al.artHolder, ArtifactPosition::PRE_FIRST);
 	dst.creature = al.creature;
-	auto putTo = getArtSet(al);
+	auto putTo = gameState().getArtSet(al);
 	assert(putTo);
 
 	if(al.slot == ArtifactPosition::FIRST_AVAILABLE)
@@ -4072,7 +4078,7 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID)
 {
 	std::vector<int3>::iterator tile;
 	std::vector<int3> tiles;
-	getFreeTiles(tiles);
+	gameState().getFreeTiles(tiles);
 	ui32 amount = (ui32)tiles.size() / 200; //Chance is 0.5% for each tile
 
 	RandomGeneratorUtil::randomShuffle(tiles, getRandomGenerator());
@@ -4135,11 +4141,11 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 
 	if (mode == ETileVisibility::HIDDEN)
 	{
-		getTilesInRange(tiles, center, radius, ETileVisibility::REVEALED, player);
+		gameInfo().getTilesInRange(tiles, center, radius, ETileVisibility::REVEALED, player);
 	}
 	else
 	{
-		getTilesInRange(tiles, center, radius, ETileVisibility::HIDDEN, player);
+		gameInfo().getTilesInRange(tiles, center, radius, ETileVisibility::HIDDEN, player);
 	}
 	changeFogOfWar(tiles, player, mode);
 }
@@ -4159,9 +4165,9 @@ void CGameHandler::changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerC
 		// do not hide tiles observed by owned objects. May lead to disastrous AI problems
 		// FIXME: this leads to a bug - shroud of darkness from Necropolis does can not override Skyship from Tower
 		std::unordered_set<int3> observedTiles;
-		auto p = getPlayerState(player);
+		auto p = gameInfo().getPlayerState(player);
 		for (auto obj : p->getOwnedObjects())
-			getTilesInRange(observedTiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::REVEALED, obj->getOwner());
+			gameInfo().getTilesInRange(observedTiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::REVEALED, obj->getOwner());
 
 		for (auto tile : observedTiles)
 			vstd::erase_if_present (fow.tiles, tile);
@@ -4179,7 +4185,7 @@ const CGHeroInstance * CGameHandler::getVisitingHero(const CGObjectInstance *obj
 	{
 		auto visit = std::dynamic_pointer_cast<const VisitQuery>(query);
 		if (visit && visit->visitedObject == obj->id)
-			return getHero(visit->visitingHero);
+			return gameInfo().getHero(visit->visitingHero);
 	}
 	return nullptr;
 }
@@ -4192,7 +4198,7 @@ const CGObjectInstance * CGameHandler::getVisitingObject(const CGHeroInstance *h
 	{
 		auto visit = std::dynamic_pointer_cast<const VisitQuery>(query);
 		if (visit && visit->visitingHero == hero->id)
-			return getObjInstance(visit->visitedObject);
+			return gameInfo().getObjInstance(visit->visitedObject);
 	}
 	return nullptr;
 }
@@ -4258,17 +4264,16 @@ vstd::RNG & CGameHandler::getRandomGenerator()
 	return randomizer->getDefault();
 }
 
-#if SCRIPTING_ENABLED
-scripting::Pool * CGameHandler::getGlobalContextPool() const
-{
-	return serverScripts.get();
-}
-
+//#if SCRIPTING_ENABLED
+//scripting::Pool * CGameHandler::getGlobalContextPool() const
+//{
+//	return serverScripts.get();
+//}
 //scripting::Pool * CGameHandler::getContextPool() const
 //{
 //	return serverScripts.get();
 //}
-#endif
+//#endif
 
 
 std::shared_ptr<CGObjectInstance> CGameHandler::createNewObject(const int3 & visitablePosition, MapObjectID objectID, MapObjectSubID subID)
@@ -4283,7 +4288,7 @@ std::shared_ptr<CGObjectInstance> CGameHandler::createNewObject(const int3 & vis
 
 	auto handler = LIBRARY->objtypeh->getHandlerFor(objectID, subID);
 
-	auto o = handler->create(gameState().cb, nullptr);
+	auto o = handler->create(&gameState(), nullptr);
 	handler->configureObject(o.get(), *randomizer);
 	assert(o->ID == objectID);
 	gameState().getMap().generateUniqueInstanceName(o.get());
@@ -4316,7 +4321,7 @@ void CGameHandler::createWanderingMonster(const int3 & visitablePosition, Creatu
 	cre->gainedArtifact = ArtifactID::NONE;
 	cre->identifier = -1;
 	cre->temppower = static_cast<int64_t>(unitSize) * 1000;
-	cre->addToSlot(SlotID(0), std::make_unique<CStackInstance>(gameState().cb, creature, unitSize));
+	cre->addToSlot(SlotID(0), std::make_unique<CStackInstance>(&gameState(), creature, unitSize));
 
 	newObject(createdObject, PlayerColor::NEUTRAL);
 }
@@ -4355,7 +4360,7 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance
 
 void CGameHandler::useChargedArtifactUsed(const ObjectInstanceID & heroObjectID, const SpellID & spellID)
 {
-	const auto hero = getHero(heroObjectID);
+	const auto hero = gameInfo().getHero(heroObjectID);
 	assert(hero);
 	assert(hero->canCastThisSpell(spellID.toSpell()));
 

+ 7 - 6
server/CGameHandler.h

@@ -55,7 +55,7 @@ class QueriesProcessor;
 class CObjectVisitQuery;
 class NewTurnProcessor;
 
-class CGameHandler : public CGameInfoCallback, public Environment, public IGameEventCallback
+class CGameHandler : public Environment, public IGameEventCallback
 {
 	CVCMIServer * lobby;
 
@@ -94,8 +94,9 @@ public:
 	bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 
-	CGameState & gameState() final { return *gs; }
-	const CGameState & gameState() const final { return *gs; }
+	IGameInfoCallback & gameInfo();
+	CGameState & gameState() { return *gs; }
+	const CGameState & gameState() const { return *gs; }
 
 	// Helpers to create new object of specified type
 
@@ -297,10 +298,10 @@ public:
 
 	vstd::RNG & getRandomGenerator() override;
 
-#if SCRIPTING_ENABLED
-	scripting::Pool * getGlobalContextPool() const override;
+//#if SCRIPTING_ENABLED
+//	scripting::Pool * getGlobalContextPool() const override;
 //	scripting::Pool * getContextPool() const override;
-#endif
+//#endif
 
 	friend class CVCMIServer;
 private:

+ 1 - 1
server/CVCMIServer.cpp

@@ -294,7 +294,7 @@ bool CVCMIServer::prepareToStartGame()
 void CVCMIServer::startGameImmediately()
 {
 	for(auto activeConnection : activeConnections)
-		activeConnection->enterGameplayConnectionMode(*gh->gs);
+		activeConnection->setCallback(&gh->gameInfo());
 
 	gh->start(si->mode == EStartMode::LOAD_GAME);
 	setState(EServerState::GAMEPLAY);

+ 3 - 2
server/NetPacksLobbyServer.cpp

@@ -19,6 +19,7 @@
 #include "../lib/campaign/CampaignState.h"
 #include "../lib/entities/faction/CTownHandler.h"
 #include "../lib/entities/faction/CFaction.h"
+#include "../lib/gameState/CGameState.h"
 #include "../lib/serializer/Connection.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/mapping/CMapHeader.h"
@@ -225,7 +226,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 		return;
 	}
 	
-	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getInitialStartInfo());
+	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->gameState().getInitialStartInfo());
 	pack.initializedGameState = srv.gh->gs;
 	result = true;
 }
@@ -240,7 +241,7 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGam
 		{
 			if(connection->connectionID == pack.clientId)
 			{
-				connection->enterGameplayConnectionMode(srv.gh->gameState());
+				connection->setCallback(&srv.gh->gameInfo());
 				srv.reconnectPlayer(pack.clientId);
 			}
 		}

+ 14 - 14
server/NetPacksServer.cpp

@@ -54,7 +54,7 @@ void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
 {
 	gh.throwIfWrongOwner(connection, &pack, pack.hid);
 	gh.throwIfPlayerNotActive(connection, &pack);
-	result = gh.removeObject(gh.getObj(pack.hid), pack.player);
+	result = gh.removeObject(gh.gameInfo().getObj(pack.hid), pack.player);
 }
 
 void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack)
@@ -181,7 +181,7 @@ void ApplyGhNetPackVisitor::visitUpgradeCreature(UpgradeCreature & pack)
 
 void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack)
 {
-	const CGTownInstance * town = gh.getTown(pack.tid);
+	const CGTownInstance * town = gh.gameInfo().getTown(pack.tid);
 	if(!gh.isPlayerOwns(connection, &pack, pack.tid) && !(town->getGarrisonHero() && gh.isPlayerOwns(connection, &pack, town->getGarrisonHero()->id)))
 		gh.throwNotAllowedAction(connection); //neither town nor garrisoned hero (if present) is ours
 	gh.throwIfPlayerNotActive(connection, &pack);
@@ -191,8 +191,8 @@ void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack)
 
 void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
 {
-	if(gh.getHero(pack.src.artHolder))
-		gh.throwIfWrongPlayer(connection, &pack, gh.getOwner(pack.src.artHolder)); //second hero can be ally
+	if(gh.gameInfo().getHero(pack.src.artHolder))
+		gh.throwIfWrongPlayer(connection, &pack, gh.gameState().getOwner(pack.src.artHolder)); //second hero can be ally
 	gh.throwIfPlayerNotActive(connection, &pack);
 
 	result = gh.moveArtifact(pack.player, pack.src, pack.dst);
@@ -200,7 +200,7 @@ void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
 
 void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack)
 {
-	if(gh.getMarket(pack.srcHero) == nullptr)
+	if(gh.gameState().getMarket(pack.srcHero) == nullptr)
 		gh.throwIfWrongOwner(connection, &pack, pack.srcHero);
 	if(pack.swap)
 		gh.throwIfWrongOwner(connection, &pack, pack.dstHero);
@@ -213,7 +213,7 @@ void ApplyGhNetPackVisitor::visitManageBackpackArtifacts(ManageBackpackArtifacts
 {
 	gh.throwIfPlayerNotActive(connection, &pack);
 
-	if(gh.getPlayerRelations(pack.player, gh.getOwner(pack.artHolder)) != PlayerRelations::ENEMIES)
+	if(gh.gameInfo().getPlayerRelations(pack.player, gh.gameState().getOwner(pack.artHolder)) != PlayerRelations::ENEMIES)
 		result = gh.manageBackpackArtifacts(pack.player, pack.artHolder, pack.cmd);
 }
 
@@ -235,7 +235,7 @@ void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack)
 
 void ApplyGhNetPackVisitor::visitEraseArtifactByClient(EraseArtifactByClient & pack)
 {
-	gh.throwIfWrongPlayer(connection, &pack, gh.getOwner(pack.al.artHolder));
+	gh.throwIfWrongPlayer(connection, &pack, gh.gameState().getOwner(pack.al.artHolder));
 	gh.throwIfPlayerNotActive(connection, &pack);
 	result = gh.eraseArtifactByClient(pack.al);
 }
@@ -249,9 +249,9 @@ void ApplyGhNetPackVisitor::visitBuyArtifact(BuyArtifact & pack)
 
 void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
 {
-	const CGObjectInstance * object = gh.getObj(pack.marketId);
-	const CGHeroInstance * hero = gh.getHero(pack.heroId);
-	const auto * market = gh.getMarket(pack.marketId);
+	const CGObjectInstance * object = gh.gameInfo().getObj(pack.marketId);
+	const CGHeroInstance * hero = gh.gameInfo().getHero(pack.heroId);
+	const auto * market = gh.gameState().getMarket(pack.marketId);
 
 	gh.throwIfWrongPlayer(connection, &pack);
 	gh.throwIfPlayerNotActive(connection, &pack);
@@ -295,7 +295,7 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
 		if (!object->visitableAt(hero->visitablePos()))
 			gh.throwAndComplain(connection, "Can not trade - object not visited!");
 
-		if (object->getOwner().isValidPlayer() && gh.getPlayerRelations(object->getOwner(), hero->getOwner()) == PlayerRelations::ENEMIES)
+		if (object->getOwner().isValidPlayer() && gh.gameInfo().getPlayerRelations(object->getOwner(), hero->getOwner()) == PlayerRelations::ENEMIES)
 			gh.throwAndComplain(connection, "Can not trade - market not owned!");
 	}
 
@@ -377,7 +377,7 @@ void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack)
 	gh.throwIfWrongPlayer(connection, &pack);
 	gh.throwIfPlayerNotActive(connection, &pack);
 
-	if(gh.getPlayerRelations(gh.getOwner(pack.objid), pack.player) == PlayerRelations::ENEMIES)
+	if(gh.gameInfo().getPlayerRelations(gh.gameState().getOwner(pack.objid), pack.player) == PlayerRelations::ENEMIES)
 		gh.throwAndComplain(connection, "Can't build boat at enemy shipyard");
 
 	result = gh.buildBoat(pack.objid, pack.player);
@@ -413,7 +413,7 @@ void ApplyGhNetPackVisitor::visitDigWithHero(DigWithHero & pack)
 	gh.throwIfWrongOwner(connection, &pack, pack.id);
 	gh.throwIfPlayerNotActive(connection, &pack);
 
-	result = gh.dig(gh.getHero(pack.id));
+	result = gh.dig(gh.gameInfo().getHero(pack.id));
 }
 
 void ApplyGhNetPackVisitor::visitCastAdvSpell(CastAdvSpell & pack)
@@ -424,7 +424,7 @@ void ApplyGhNetPackVisitor::visitCastAdvSpell(CastAdvSpell & pack)
 	if (!pack.sid.hasValue())
 		gh.throwNotAllowedAction(connection);
 
-	const CGHeroInstance * h = gh.getHero(pack.hid);
+	const CGHeroInstance * h = gh.gameInfo().getHero(pack.hid);
 	if(!h)
 		gh.throwNotAllowedAction(connection);
 

+ 1 - 1
server/ServerSpellCastEnvironment.cpp

@@ -81,7 +81,7 @@ void ServerSpellCastEnvironment::apply(CatapultAttack & pack)
 
 const IGameInfoCallback * ServerSpellCastEnvironment::getCb() const
 {
-	return gh;
+	return &gh->gameInfo();
 }
 
 const CMap * ServerSpellCastEnvironment::getMap() const

+ 12 - 12
server/TurnTimerHandler.cpp

@@ -29,7 +29,7 @@ TurnTimerHandler::TurnTimerHandler(CGameHandler & gh):
 
 void TurnTimerHandler::onGameplayStart(PlayerColor player)
 {
-	if(const auto * si = gameHandler.getStartInfo())
+	if(const auto * si = gameHandler.gameInfo().getStartInfo())
 	{
 		timers[player] = si->turnTimerInfo;
 		timers[player].turnTimer = 0;
@@ -66,7 +66,7 @@ void TurnTimerHandler::sendTimerUpdate(PlayerColor player)
 
 void TurnTimerHandler::onPlayerGetTurn(PlayerColor player)
 {
-	if(const auto * si = gameHandler.getStartInfo())
+	if(const auto * si = gameHandler.gameInfo().getStartInfo())
 	{
 		if(si->turnTimerInfo.isEnabled())
 		{
@@ -89,7 +89,7 @@ void TurnTimerHandler::prolongTimers(int durationMs)
 
 void TurnTimerHandler::update(int waitTimeMs)
 {
-	if(!gameHandler.getStartInfo()->turnTimerInfo.isEnabled())
+	if(!gameHandler.gameInfo().getStartInfo()->turnTimerInfo.isEnabled())
 		return;
 
 	for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
@@ -123,12 +123,12 @@ bool TurnTimerHandler::timerCountDown(int & timer, int initialTimer, PlayerColor
 
 void TurnTimerHandler::onPlayerMakingTurn(PlayerColor player, int waitTime)
 {
-	const auto * si = gameHandler.getStartInfo();
+	const auto * si = gameHandler.gameInfo().getStartInfo();
 	if(!si || !si->turnTimerInfo.isEnabled())
 		return;
 	
 	auto & timer = timers[player];
-	const auto * state = gameHandler.getPlayerState(player);
+	const auto * state = gameHandler.gameInfo().getPlayerState(player);
 	if(state && state->human && timer.isActive && !timer.isBattle && state->status == EPlayerStatus::INGAME)
 	{
 		// turn timers are only used if turn timer is non-zero
@@ -153,8 +153,8 @@ bool TurnTimerHandler::isPvpBattle(const BattleID & battleID) const
 	auto defender = gs.getBattle(battleID)->getSidePlayer(BattleSide::DEFENDER);
 	if(attacker.isValidPlayer() && defender.isValidPlayer())
 	{
-		const auto * attackerState = gameHandler.getPlayerState(attacker);
-		const auto * defenderState = gameHandler.getPlayerState(defender);
+		const auto * attackerState = gameHandler.gameInfo().getPlayerState(attacker);
+		const auto * defenderState = gameHandler.gameInfo().getPlayerState(defender);
 		if(attackerState && defenderState && attackerState->human && defenderState->human)
 			return true;
 	}
@@ -164,7 +164,7 @@ bool TurnTimerHandler::isPvpBattle(const BattleID & battleID) const
 void TurnTimerHandler::onBattleStart(const BattleID & battleID)
 {
 	const auto & gs = gameHandler.gameState();
-	const auto * si = gameHandler.getStartInfo();
+	const auto * si = gameHandler.gameInfo().getStartInfo();
 	if(!si)
 		return;
 
@@ -191,7 +191,7 @@ void TurnTimerHandler::onBattleStart(const BattleID & battleID)
 void TurnTimerHandler::onBattleEnd(const BattleID & battleID)
 {
 	const auto & gs = gameHandler.gameState();
-	const auto * si = gameHandler.getStartInfo();
+	const auto * si = gameHandler.gameInfo().getStartInfo();
 	if(!si)
 	{
 		assert(0);
@@ -219,7 +219,7 @@ void TurnTimerHandler::onBattleEnd(const BattleID & battleID)
 void TurnTimerHandler::onBattleNextStack(const BattleID & battleID, const CStack & stack)
 {
 	const auto & gs = gameHandler.gameState();
-	const auto * si = gameHandler.getStartInfo();
+	const auto * si = gameHandler.gameInfo().getStartInfo();
 	if(!si || !gs.getBattle(battleID))
 	{
 		assert(0);
@@ -245,7 +245,7 @@ void TurnTimerHandler::onBattleNextStack(const BattleID & battleID, const CStack
 void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime)
 {
 	const auto & gs = gameHandler.gameState();
-	const auto * si = gameHandler.getStartInfo();
+	const auto * si = gameHandler.gameInfo().getStartInfo();
 	if(!si)
 	{
 		assert(0);
@@ -273,7 +273,7 @@ void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime)
 	if(!player.isValidPlayer())
 		return;
 	
-	const auto * state = gameHandler.getPlayerState(player);
+	const auto * state = gameHandler.gameInfo().getPlayerState(player);
 	assert(state && state->status == EPlayerStatus::INGAME);
 	if(!state || state->status != EPlayerStatus::INGAME || !state->human)
 		return;

+ 2 - 2
server/battles/BattleActionProcessor.cpp

@@ -81,7 +81,7 @@ bool BattleActionProcessor::doSurrenderAction(const CBattleInfoCallback & battle
 		return false;
 	}
 
-	if (gameHandler->getResource(player, EGameResID::GOLD) < cost)
+	if (gameHandler->gameInfo().getResource(player, EGameResID::GOLD) < cost)
 	{
 		gameHandler->complain("Not enough gold to surrender!");
 		return false;
@@ -899,7 +899,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
 		}
 	}
 	//handle last hex separately for deviation
-	if (gameHandler->getSettings().getBoolean(EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES))
+	if (gameHandler->gameInfo().getSettings().getBoolean(EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES))
 	{
 		if (dest == battle::Unit::occupiedHex(start, curStack->doubleWide(), curStack->unitSide())
 			|| start == battle::Unit::occupiedHex(dest, curStack->doubleWide(), curStack->unitSide()))

+ 6 - 6
server/battles/BattleProcessor.cpp

@@ -110,7 +110,7 @@ void BattleProcessor::startBattle(const CArmedInstance *army1, const CArmedInsta
 	assert(battle);
 	
 	//add battle bonuses based from player state only when attacks neutral creatures
-	const auto * attackerInfo = gameHandler->getPlayerState(army1->getOwner(), false);
+	const auto * attackerInfo = gameHandler->gameInfo().getPlayerState(army1->getOwner(), false);
 	if(attackerInfo && !army2->getOwner().isValidPlayer())
 	{
 		for(auto bonus : attackerInfo->battleBonuses)
@@ -150,13 +150,13 @@ void BattleProcessor::startBattle(const CArmedInstance *army1, const CArmedInsta
 	startBattle(army1, army2, army2->visitablePos(),
 		army1->ID == Obj::HERO ? dynamic_cast<const CGHeroInstance*>(army1) : nullptr,
 		army2->ID == Obj::HERO ? dynamic_cast<const CGHeroInstance*>(army2) : nullptr,
-		BattleLayout::createDefaultLayout(gameHandler, army1, army2),
+		BattleLayout::createDefaultLayout(gameHandler->gameInfo(), army1, army2),
 		nullptr);
 }
 
 BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedInstance *> armies, BattleSideArray<const CGHeroInstance *> heroes, const BattleLayout & layout, const CGTownInstance *town)
 {
-	const auto & t = *gameHandler->getTile(tile);
+	const auto & t = *gameHandler->gameInfo().getTile(tile);
 	TerrainId terrain = t.getTerrainID();
 	if (town)
 		terrain = town->getNativeTerrain();
@@ -175,7 +175,7 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
 
 	//send info about battles
 	BattleStart bs;
-	bs.info = BattleInfo::setupBattle(gameHandler->gameState().cb, tile, terrain, battlefieldType, armies, heroes, layout, town);
+	bs.info = BattleInfo::setupBattle(&gameHandler->gameState(), tile, terrain, battlefieldType, armies, heroes, layout, town);
 	bs.battleID = gameHandler->gameState().nextBattleID;
 
 	engageIntoBattle(bs.info->getSide(BattleSide::ATTACKER).color);
@@ -184,8 +184,8 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
 	auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(bs.info->getSide(BattleSide::ATTACKER).color));
 	if(!lastBattleQuery)
 		lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(bs.info->getSide(BattleSide::DEFENDER).color));
-	bool isDefenderHuman = bs.info->getSide(BattleSide::DEFENDER).color.isValidPlayer() && gameHandler->getPlayerState(bs.info->getSide(BattleSide::DEFENDER).color)->isHuman();
-	bool isAttackerHuman = gameHandler->getPlayerState(bs.info->getSide(BattleSide::ATTACKER).color)->isHuman();
+	bool isDefenderHuman = bs.info->getSide(BattleSide::DEFENDER).color.isValidPlayer() && gameHandler->gameInfo().getPlayerState(bs.info->getSide(BattleSide::DEFENDER).color)->isHuman();
+	bool isAttackerHuman = gameHandler->gameInfo().getPlayerState(bs.info->getSide(BattleSide::ATTACKER).color)->isHuman();
 
 	bool onlyOnePlayerHuman = isDefenderHuman != isAttackerHuman;
 	bs.info->replayAllowed = lastBattleQuery == nullptr && onlyOnePlayerHuman;

+ 5 - 5
server/battles/BattleResultProcessor.cpp

@@ -143,7 +143,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle,
 
 void CasualtiesAfterBattle::updateArmy(CGameHandler *gh)
 {
-	if (gh->getObjInstance(army->id) == nullptr)
+	if (gh->gameInfo().getObjInstance(army->id) == nullptr)
 		throw std::runtime_error("Object " + army->getObjectName() + " is not on the map!");
 
 	for (TStackAndItsNewCount &ncount : newStackCounts)
@@ -269,8 +269,8 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle)
 	finishingBattles[battle.getBattle()->getBattleID()] = std::make_unique<FinishingBattleHelper>(battle, *battleResult, queriedPlayers);
 
 	// in battles against neutrals, 1st player can ask to replay battle manually
-	const auto * attackerPlayer = gameHandler->getPlayerState(battle.getBattle()->getSidePlayer(BattleSide::ATTACKER));
-	const auto * defenderPlayer = gameHandler->getPlayerState(battle.getBattle()->getSidePlayer(BattleSide::DEFENDER));
+	const auto * attackerPlayer = gameHandler->gameInfo().getPlayerState(battle.getBattle()->getSidePlayer(BattleSide::ATTACKER));
+	const auto * defenderPlayer = gameHandler->gameInfo().getPlayerState(battle.getBattle()->getSidePlayer(BattleSide::DEFENDER));
 	bool isAttackerHuman = attackerPlayer && attackerPlayer->isHuman();
 	bool isDefenderHuman = defenderPlayer && defenderPlayer->isHuman();
 	bool onlyOnePlayerHuman = isAttackerHuman != isDefenderHuman;
@@ -469,7 +469,7 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
 				for(const auto & artSlot : loserHero->getCommander()->artifactsWorn)
 					addArtifactToTransfer(packCommander, artSlot.first, artSlot.second.getArt());
 			}
-			auto armyObj = dynamic_cast<const CArmedInstance*>(gameHandler->getObj(finishingBattle->loserId));
+			auto armyObj = dynamic_cast<const CArmedInstance*>(gameHandler->gameInfo().getObj(finishingBattle->loserId));
 			for(const auto & armySlot : armyObj->stacks)
 			{
 				auto & packsArmy = resultsApplied.movingArtifacts.emplace_back(finishingBattle->victor, finishingBattle->loserId, finishingBattle->winnerId, false);
@@ -557,7 +557,7 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
 	{
 		RemoveObject ro(winnerHero->id, finishingBattle->loser);
 		gameHandler->sendAndApply(ro);
-		if(gameHandler->getSettings().getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))
+		if(gameHandler->gameInfo().getSettings().getBoolean(EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS))
 			gameHandler->heroPool->onHeroEscaped(finishingBattle->victor, winnerHero);
 	}
 

+ 12 - 12
server/processors/HeroPoolProcessor.cpp

@@ -147,9 +147,9 @@ void HeroPoolProcessor::onNewWeek(const PlayerColor & color)
 
 bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTypeID & heroToRecruit, const PlayerColor & player, const HeroTypeID & nextHero)
 {
-	const PlayerState * playerState = gameHandler->getPlayerState(player);
-	const CGObjectInstance * mapObject = gameHandler->getObj(objectID);
-	const CGTownInstance * town = gameHandler->getTown(objectID);
+	const PlayerState * playerState = gameHandler->gameInfo().getPlayerState(player);
+	const CGObjectInstance * mapObject = gameHandler->gameInfo().getObj(objectID);
+	const CGTownInstance * town = gameHandler->gameInfo().getTown(objectID);
 	const auto & heroesPool = gameHandler->gameState().heroesPool;
 
 	if (!mapObject && gameHandler->complain("Invalid map object!"))
@@ -161,15 +161,15 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 	if (playerState->resources[EGameResID::GOLD] < GameConstants::HERO_GOLD_COST && gameHandler->complain("Not enough gold for buying hero!"))
 		return false;
 
-	if (gameHandler->getHeroCount(player, false) >= gameHandler->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP) && gameHandler->complain("Cannot hire hero, too many wandering heroes already!"))
+	if (gameHandler->gameInfo().getHeroCount(player, false) >= gameHandler->gameInfo().getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP) && gameHandler->complain("Cannot hire hero, too many wandering heroes already!"))
 		return false;
 
-	if (gameHandler->getHeroCount(player, true) >= gameHandler->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && gameHandler->complain("Cannot hire hero, too many heroes garrisoned and wandering heroes present!"))
+	if (gameHandler->gameInfo().getHeroCount(player, true) >= gameHandler->gameInfo().getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP) && gameHandler->complain("Cannot hire hero, too many heroes garrisoned and wandering heroes present!"))
 		return false;
 
 	if (nextHero != HeroTypeID::NONE) // player attempts to invite next hero
 	{
-		if(!gameHandler->getSettings().getBoolean(EGameSettings::HEROES_TAVERN_INVITE) && gameHandler->complain("Inviting heroes not allowed!"))
+		if(!gameHandler->gameInfo().getSettings().getBoolean(EGameSettings::HEROES_TAVERN_INVITE) && gameHandler->complain("Inviting heroes not allowed!"))
 			return false;
 
 		if(!heroesPool->unusedHeroesFromPool().count(nextHero) && gameHandler->complain("Cannot invite specified hero!"))
@@ -181,7 +181,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 
 	if(town) //tavern in town
 	{
-		if(gameHandler->getPlayerRelations(mapObject->tempOwner, player) == PlayerRelations::ENEMIES && gameHandler->complain("Can't buy hero in enemy town!"))
+		if(gameHandler->gameInfo().getPlayerRelations(mapObject->tempOwner, player) == PlayerRelations::ENEMIES && gameHandler->complain("Can't buy hero in enemy town!"))
 			return false;
 
 		if(!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
@@ -201,7 +201,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 			return false;
 		}
 
-		if(gameHandler->getTile(mapObject->visitablePos())->visitableObjects.back() != mapObject->id && gameHandler->complain("Tavern entry must be unoccupied!"))
+		if(gameHandler->gameInfo().getTile(mapObject->visitablePos())->visitableObjects.back() != mapObject->id && gameHandler->complain("Tavern entry must be unoccupied!"))
 			return false;
 	}
 
@@ -227,11 +227,11 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 	hr.hid = recruitedHero->getHeroTypeID();
 	hr.player = player;
 	hr.tile = recruitedHero->convertFromVisitablePos(targetPos );
-	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->inBoat())
+	if(gameHandler->gameInfo().getTile(targetPos)->isWater() && !recruitedHero->inBoat())
 	{
 		//Create a new boat for hero
 		gameHandler->createBoat(targetPos, recruitedHero->getBoatType(), player);
-		hr.boatId = gameHandler->getTopObj(targetPos)->id;
+		hr.boatId = gameHandler->gameInfo().getTopObj(targetPos)->id;
 	}
 
 	// apply netpack -> this will remove hired hero from pool
@@ -257,7 +257,7 @@ std::vector<const CHeroClass *> HeroPoolProcessor::findAvailableClassesFor(const
 	std::vector<const CHeroClass *> result;
 
 	const auto & heroesPool = gameHandler->gameState().heroesPool;
-	FactionID factionID = gameHandler->getPlayerSettings(player)->castle;
+	FactionID factionID = gameHandler->gameInfo().getPlayerSettings(player)->castle;
 
 	for(const auto & elem : heroesPool->unusedHeroesFromPool())
 	{
@@ -302,7 +302,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo
 		return nullptr;
 	}
 
-	FactionID factionID = gameHandler->getPlayerSettings(player)->castle;
+	FactionID factionID = gameHandler->gameInfo().getPlayerSettings(player)->castle;
 	const auto & heroesPool = gameHandler->gameState().heroesPool;
 	const auto & currentTavern = heroesPool->getHeroesFor(player);
 

+ 14 - 14
server/processors/NewTurnProcessor.cpp

@@ -47,7 +47,7 @@ void NewTurnProcessor::handleTimeEvents(PlayerColor color)
 		if (!event.occursToday(gameHandler->gameState().day))
 			continue;
 
-		if (!event.affectsPlayer(color, gameHandler->getPlayerState(color)->isHuman()))
+		if (!event.affectsPlayer(color, gameHandler->gameInfo().getPlayerState(color)->isHuman()))
 			continue;
 
 		InfoWindow iw;
@@ -66,7 +66,7 @@ void NewTurnProcessor::handleTimeEvents(PlayerColor color)
 		//remove objects specified by event
 		for(const ObjectInstanceID objectIdToRemove : event.deletedObjectsInstances)
 		{
-			auto objectInstance = gameHandler->getObj(objectIdToRemove, false);
+			auto objectInstance = gameHandler->gameInfo().getObj(objectIdToRemove, false);
 			if(objectInstance != nullptr)
 				gameHandler->removeObject(objectInstance, PlayerColor::NEUTRAL);
 		}
@@ -82,7 +82,7 @@ void NewTurnProcessor::handleTownEvents(const CGTownInstance * town)
 			continue;
 
 		PlayerColor player = town->getOwner();
-		if (!event.affectsPlayer(player, gameHandler->getPlayerState(player)->isHuman()))
+		if (!event.affectsPlayer(player, gameHandler->gameInfo().getPlayerState(player)->isHuman()))
 			continue;
 
 		// dialog
@@ -178,7 +178,7 @@ void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
 	// check for 7 days without castle
 	gameHandler->checkVictoryLossConditionsForPlayer(which);
 
-	bool newWeek = gameHandler->getDate(Date::DAY_OF_WEEK) == 7; // end of 7th day
+	bool newWeek = gameHandler->gameInfo().getDate(Date::DAY_OF_WEEK) == 7; // end of 7th day
 
 	if (newWeek) //new heroes in tavern
 		gameHandler->heroPool->onNewWeek(which);
@@ -186,7 +186,7 @@ void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
 
 ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool newWeek)
 {
-	const auto & playerSettings = gameHandler->getPlayerSettings(playerID);
+	const auto & playerSettings = gameHandler->gameInfo().getPlayerSettings(playerID);
 	const PlayerState & state = gameHandler->gameState().players.at(playerID);
 	ResourceSet income;
 
@@ -515,7 +515,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
 			return { EWeekType::DEITYOFFIRE, CreatureID::IMP };
 	}
 
-	if(!gameHandler->getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS))
+	if(!gameHandler->gameInfo().getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS))
 		return { EWeekType::NORMAL, CreatureID::NONE};
 
 	int monthType = gameHandler->getRandomGenerator().nextInt(99);
@@ -523,7 +523,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
 	{
 		if (monthType < 40) //double growth
 		{
-			if (gameHandler->getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH))
+			if (gameHandler->gameInfo().getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH))
 			{
 				CreatureID creatureID = gameHandler->randomizer->rollCreature();
 				return { EWeekType::DOUBLE_GROWTH, creatureID};
@@ -645,9 +645,9 @@ NewTurn NewTurnProcessor::generateNewTurnPack()
 	n.creatureid = CreatureID::NONE;
 	n.day = gameHandler->gameState().day + 1;
 
-	bool firstTurn = !gameHandler->getDate(Date::DAY);
-	bool newWeek = gameHandler->getDate(Date::DAY_OF_WEEK) == 7; //day numbers are confusing, as day was not yet switched
-	bool newMonth = gameHandler->getDate(Date::DAY_OF_MONTH) == 28;
+	bool firstTurn = !gameHandler->gameInfo().getDate(Date::DAY);
+	bool newWeek = gameHandler->gameInfo().getDate(Date::DAY_OF_WEEK) == 7; //day numbers are confusing, as day was not yet switched
+	bool newMonth = gameHandler->gameInfo().getDate(Date::DAY_OF_MONTH) == 28;
 
 	if (!firstTurn)
 	{
@@ -691,9 +691,9 @@ void NewTurnProcessor::onNewTurn()
 {
 	NewTurn n = generateNewTurnPack();
 
-	bool firstTurn = !gameHandler->getDate(Date::DAY);
-	bool newWeek = gameHandler->getDate(Date::DAY_OF_WEEK) == 7; //day numbers are confusing, as day was not yet switched
-	bool newMonth = gameHandler->getDate(Date::DAY_OF_MONTH) == 28;
+	bool firstTurn = !gameHandler->gameInfo().getDate(Date::DAY);
+	bool newWeek = gameHandler->gameInfo().getDate(Date::DAY_OF_WEEK) == 7; //day numbers are confusing, as day was not yet switched
+	bool newMonth = gameHandler->gameInfo().getDate(Date::DAY_OF_MONTH) == 28;
 
 	gameHandler->sendAndApply(n);
 
@@ -713,7 +713,7 @@ void NewTurnProcessor::onNewTurn()
 		{
 			auto t = gameHandler->gameState().getTown(townID);
 			if (!t->getOwner().isValidPlayer())
-				updateNeutralTownGarrison(t, 1 + gameHandler->getDate(Date::DAY) / 7);
+				updateNeutralTownGarrison(t, 1 + gameHandler->gameInfo().getDate(Date::DAY) / 7);
 		}
 	}
 

+ 5 - 5
server/processors/PlayerMessageProcessor.cpp

@@ -50,7 +50,7 @@ void PlayerMessageProcessor::playerMessage(PlayerColor player, const std::string
 
 	if(handleCheatCode(message, player, currObj))
 	{
-		if(!gameHandler->getPlayerSettings(player)->isControlledByAI())
+		if(!gameHandler->gameInfo().getPlayerSettings(player)->isControlledByAI())
 		{
 			MetaString txt;
 			txt.appendLocalString(EMetaText::GENERAL_TXT, 260);
@@ -330,7 +330,7 @@ void PlayerMessageProcessor::startVoting(PlayerColor initiator, ECurrentChatVote
 
 	for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
 	{
-		auto state = gameHandler->getPlayerState(player, false);
+		auto state = gameHandler->gameInfo().getPlayerState(player, false);
 		if(state && state->isHuman() && initiator != player)
 			awaitingPlayers.insert(player);
 	}
@@ -690,7 +690,7 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
 	std::vector<std::string> words;
 	boost::split(words, boost::trim_copy(cheat), boost::is_any_of("\t\r\n "));
 
-	if (words.empty() || !gameHandler->getStartInfo()->extraOptionsInfo.cheatsAllowed)
+	if (words.empty() || !gameHandler->gameInfo().getStartInfo()->extraOptionsInfo.cheatsAllowed)
 		return false;
 
 	//Make cheat name case-insensitive, but keep words/parameters (e.g. creature name) as it
@@ -775,8 +775,8 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
 
 void PlayerMessageProcessor::executeCheatCode(const std::string & cheatName, PlayerColor player, ObjectInstanceID currObj, const std::vector<std::string> & words)
 {
-	const CGHeroInstance * hero = gameHandler->getHero(currObj);
-	const CGTownInstance * town = gameHandler->getTown(currObj);
+	const CGHeroInstance * hero = gameHandler->gameInfo().getHero(currObj);
+	const CGTownInstance * town = gameHandler->gameInfo().getTown(currObj);
 	if (!town && hero)
 		town = hero->getVisitedTown();
 

+ 21 - 21
server/processors/TurnOrderProcessor.cpp

@@ -33,14 +33,14 @@ int TurnOrderProcessor::simturnsTurnsMaxLimit() const
 {
 	if (simturnsMaxDurationDays)
 		return *simturnsMaxDurationDays;
-	return gameHandler->getStartInfo()->simturnsInfo.optionalTurns;
+	return gameHandler->gameInfo().getStartInfo()->simturnsInfo.optionalTurns;
 }
 
 int TurnOrderProcessor::simturnsTurnsMinLimit() const
 {
 	if (simturnsMinDurationDays)
 		return *simturnsMinDurationDays;
-	return gameHandler->getStartInfo()->simturnsInfo.requiredTurns;
+	return gameHandler->gameInfo().getStartInfo()->simturnsInfo.requiredTurns;
 }
 
 std::vector<TurnOrderProcessor::PlayerPair> TurnOrderProcessor::computeContactStatus() const
@@ -101,13 +101,13 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
 	boost::multi_array<bool, 3> leftReachability;
 	boost::multi_array<bool, 3> rightReachability;
 
-	int3 mapSize = gameHandler->getMapSize();
+	int3 mapSize = gameHandler->gameInfo().getMapSize();
 
 	leftReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
 	rightReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
 
-	const auto * leftInfo = gameHandler->getPlayerState(left, false);
-	const auto * rightInfo = gameHandler->getPlayerState(right, false);
+	const auto * leftInfo = gameHandler->gameInfo().getPlayerState(left, false);
+	const auto * rightInfo = gameHandler->gameInfo().getPlayerState(right, false);
 
 	for (auto obj : gameHandler->gameState().getMap().getObjects())
 	{
@@ -170,8 +170,8 @@ bool TurnOrderProcessor::isContactAllowed(PlayerColor active, PlayerColor waitin
 
 bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerColor waiting) const
 {
-	const auto * activeInfo = gameHandler->getPlayerState(active, false);
-	const auto * waitingInfo = gameHandler->getPlayerState(waiting, false);
+	const auto * activeInfo = gameHandler->gameInfo().getPlayerState(active, false);
+	const auto * waitingInfo = gameHandler->gameInfo().getPlayerState(waiting, false);
 
 	assert(active != waiting);
 	assert(activeInfo);
@@ -180,7 +180,7 @@ bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerC
 	if (activeInfo->human != waitingInfo->human)
 	{
 		// only one AI and one human can play simultaneously from single connection
-		if (!gameHandler->getStartInfo()->simturnsInfo.allowHumanWithAI)
+		if (!gameHandler->gameInfo().getStartInfo()->simturnsInfo.allowHumanWithAI)
 			return false;
 	}
 	else
@@ -190,13 +190,13 @@ bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerC
 			return false;
 	}
 
-	if (gameHandler->getDate(Date::DAY) < simturnsTurnsMinLimit())
+	if (gameHandler->gameInfo().getDate(Date::DAY) < simturnsTurnsMinLimit())
 		return true;
 
-	if (gameHandler->getDate(Date::DAY) > simturnsTurnsMaxLimit())
+	if (gameHandler->gameInfo().getDate(Date::DAY) > simturnsTurnsMaxLimit())
 		return false;
 
-	if (gameHandler->getStartInfo()->simturnsInfo.ignoreAlliedContacts && activeInfo->team == waitingInfo->team)
+	if (gameHandler->gameInfo().getStartInfo()->simturnsInfo.ignoreAlliedContacts && activeInfo->team == waitingInfo->team)
 		return true;
 
 	if (playersInContact(active, waiting))
@@ -207,8 +207,8 @@ bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerC
 
 bool TurnOrderProcessor::mustActBefore(PlayerColor left, PlayerColor right) const
 {
-	const auto * leftInfo = gameHandler->getPlayerState(left, false);
-	const auto * rightInfo = gameHandler->getPlayerState(right, false);
+	const auto * leftInfo = gameHandler->gameInfo().getPlayerState(left, false);
+	const auto * rightInfo = gameHandler->gameInfo().getPlayerState(right, false);
 
 	assert(left != right);
 	assert(leftInfo && rightInfo);
@@ -254,7 +254,7 @@ void TurnOrderProcessor::doStartNewDay()
 	bool activePlayer = false;
 	for (auto player : actedPlayers)
 	{
-		if (gameHandler->getPlayerState(player)->status == EPlayerStatus::INGAME)
+		if (gameHandler->gameInfo().getPlayerState(player)->status == EPlayerStatus::INGAME)
 			activePlayer = true;
 	}
 
@@ -272,8 +272,8 @@ void TurnOrderProcessor::doStartNewDay()
 
 void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
 {
-	assert(gameHandler->getPlayerState(which));
-	assert(gameHandler->getPlayerState(which)->status == EPlayerStatus::INGAME);
+	assert(gameHandler->gameInfo().getPlayerState(which));
+	assert(gameHandler->gameInfo().getPlayerState(which)->status == EPlayerStatus::INGAME);
 
 	// Only if player is actually starting his turn (and not loading from save)
 	if (!actingPlayers.count(which))
@@ -296,7 +296,7 @@ void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
 void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which)
 {
 	assert(isPlayerMakingTurn(which));
-	assert(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME);
+	assert(gameHandler->gameInfo().getPlayerStatus(which) == EPlayerStatus::INGAME);
 
 	actingPlayers.erase(which);
 	actedPlayers.insert(which);
@@ -340,7 +340,7 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
 		return false;
 	}
 
-	if(gameHandler->getPlayerStatus(which) != EPlayerStatus::INGAME)
+	if(gameHandler->gameInfo().getPlayerStatus(which) != EPlayerStatus::INGAME)
 	{
 		gameHandler->complain("Can not end turn for player that is not in game!");
 		return false;
@@ -356,7 +356,7 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
 
 	// it is possible that player have lost - e.g. spent 7 days without town
 	// in this case - don't call doEndPlayerTurn - turn transfer was already handled by onPlayerEndsGame
-	if(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME)
+	if(gameHandler->gameInfo().getPlayerStatus(which) == EPlayerStatus::INGAME)
 		doEndPlayerTurn(which);
 
 	return true;
@@ -402,10 +402,10 @@ bool TurnOrderProcessor::isPlayerAwaitsNewDay(PlayerColor which) const
 
 void TurnOrderProcessor::setMinSimturnsDuration(int days)
 {
-	simturnsMinDurationDays = gameHandler->getDate(Date::DAY) + days;
+	simturnsMinDurationDays = gameHandler->gameInfo().getDate(Date::DAY) + days;
 }
 
 void TurnOrderProcessor::setMaxSimturnsDuration(int days)
 {
-	simturnsMaxDurationDays = gameHandler->getDate(Date::DAY) + days;
+	simturnsMaxDurationDays = gameHandler->gameInfo().getDate(Date::DAY) + days;
 }

+ 2 - 2
server/queries/MapQueries.cpp

@@ -209,7 +209,7 @@ CTeleportDialogQuery::CTeleportDialogQuery(CGameHandler * owner, const TeleportD
 	CDialogQuery(owner)
 {
 	this->td = td;
-	addPlayer(gh->getHero(td.hero)->getOwner());
+	addPlayer(gh->gameInfo().getHero(td.hero)->getOwner());
 }
 
 CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp & Hlu, const CGHeroInstance * Hero):
@@ -266,7 +266,7 @@ void CHeroMovementQuery::onExposure(QueryPtr topQuery)
 		logGlobal->trace("Hero %s after victory over guard finishes visit to %s", hero->getNameTranslated(), tmh.end.toString());
 		//finish movement
 		visitDestAfterVictory = false;
-		gh->visitObjectOnTile(*gh->getTile(hero->convertToVisitablePos(tmh.end)), hero);
+		gh->visitObjectOnTile(*gh->gameInfo().getTile(hero->convertToVisitablePos(tmh.end)), hero);
 	}
 
 	owner->popIfTop(*this);

+ 4 - 5
test/game/CGameStateTest.cpp

@@ -50,8 +50,7 @@ public:
 
 	void SetUp() override
 	{
-		gameState = std::make_shared<CGameState>(gameCallback.get());
-		gameCallback->setGameState(gameState);
+		gameState = std::make_shared<CGameState>();
 		gameState->preInit(&services);
 	}
 
@@ -195,15 +194,15 @@ public:
 
 		int3 tile(4,4,0);
 
-		const auto & t = *gameCallback->getTile(tile);
+		const auto & t = *gameState->getTile(tile);
 
 		auto terrain = t.getTerrainID();
 		BattleField terType(0);
-		BattleLayout layout = BattleLayout::createDefaultLayout(gameState->cb, attacker, defender);
+		BattleLayout layout = BattleLayout::createDefaultLayout(*gameState, attacker, defender);
 
 		//send info about battles
 
-		auto battle = BattleInfo::setupBattle(gameState->cb, tile, terrain, terType, armedInstancies, heroes, layout, nullptr);
+		auto battle = BattleInfo::setupBattle(gameState.get(), tile, terrain, terType, armedInstancies, heroes, layout, nullptr);
 
 		BattleStart bs;
 		bs.info = std::move(battle);

+ 1 - 9
test/mock/mock_IGameCallback.cpp

@@ -17,15 +17,7 @@ GameCallbackMock::GameCallbackMock(UpperCallback * upperCallback_)
 
 }
 
-GameCallbackMock::~GameCallbackMock()
-{
-
-}
-
-void GameCallbackMock::setGameState(std::shared_ptr<CGameState> newGameState)
-{
-	gamestate = newGameState;
-}
+GameCallbackMock::~GameCallbackMock() = default;
 
 void GameCallbackMock::sendAndApply(CPackForClient & pack)
 {

+ 1 - 14
test/mock/mock_IGameCallback.h

@@ -17,27 +17,14 @@
 #include "../../lib/int3.h"
 #include "../../lib/ResourceSet.h"
 
-class GameCallbackMock : public CGameInfoCallback, public IGameEventCallback
+class GameCallbackMock : public IGameEventCallback
 {
-	std::shared_ptr<CGameState> gamestate;
 public:
 	using UpperCallback = ::ServerCallback;
 
 	GameCallbackMock(UpperCallback * upperCallback_);
 	virtual ~GameCallbackMock();
 
-	void setGameState(std::shared_ptr<CGameState> gameState);
-	CGameState & gameState() final { return *gamestate; }
-	const CGameState & gameState() const final { return *gamestate; }
-
-
-	///STUBS, to be removed as long as same methods moved from GameHandler
-
-	//all calls to such methods should be replaced with other object calls or actual netpacks
-	//currently they are declared in callbacks, overridden in GameHandler and stubbed in client
-
-	//TODO: fail all stub calls
-
 	void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) override {}
 	void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {}
 	void setRewardableObjectConfiguration(ObjectInstanceID mapObjectID, const Rewardable::Configuration & configuration) override {}

+ 1 - 1
test/netpacks/NetPackFixture.cpp

@@ -24,7 +24,7 @@ NetPackFixture::~NetPackFixture() = default;
 
 void NetPackFixture::setUp()
 {
-	gameState = std::make_shared<GameStateFake>(nullptr);
+    gameState = std::make_shared<GameStateFake>();
 }
 
 }