Browse Source

First semi-working version of antilag for hero movement

Ivan Savenko 3 months ago
parent
commit
241da5937d

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -269,7 +269,7 @@ void AIGateway::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance
 	NET_EVENT_HANDLER;
 }
 
-void AIGateway::tileHidden(const std::unordered_set<int3> & pos)
+void AIGateway::tileHidden(const FowTilesType & pos)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
@@ -277,7 +277,7 @@ void AIGateway::tileHidden(const std::unordered_set<int3> & pos)
 	nullkiller->memory->removeInvisibleObjects(myCb.get());
 }
 
-void AIGateway::tileRevealed(const std::unordered_set<int3> & pos)
+void AIGateway::tileRevealed(const FowTilesType & pos)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;

+ 2 - 2
AI/Nullkiller/AIGateway.h

@@ -106,7 +106,7 @@ public:
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void centerView(int3 pos, int focusTime) override;
-	void tileHidden(const std::unordered_set<int3> & pos) override;
+	void tileHidden(const FowTilesType & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
@@ -121,7 +121,7 @@ public:
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
-	void tileRevealed(const std::unordered_set<int3> & pos) override;
+	void tileRevealed(const FowTilesType & pos) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -286,7 +286,7 @@ void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * to
 	//moveCreaturesToHero(town);
 }
 
-void VCAI::tileHidden(const std::unordered_set<int3> & pos)
+void VCAI::tileHidden(const FowTilesType & pos)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
@@ -295,7 +295,7 @@ void VCAI::tileHidden(const std::unordered_set<int3> & pos)
 	clearPathsInfo();
 }
 
-void VCAI::tileRevealed(const std::unordered_set<int3> & pos)
+void VCAI::tileRevealed(const FowTilesType & pos)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;

+ 2 - 2
AI/VCAI/VCAI.h

@@ -147,7 +147,7 @@ public:
 	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
 	void heroInGarrisonChange(const CGTownInstance * town) override;
 	void centerView(int3 pos, int focusTime) override;
-	void tileHidden(const std::unordered_set<int3> & pos) override;
+	void tileHidden(const FowTilesType & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
 	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
@@ -162,7 +162,7 @@ public:
 	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
 	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
 	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
-	void tileRevealed(const std::unordered_set<int3> & pos) override;
+	void tileRevealed(const FowTilesType & pos) override;
 	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
 	void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override;
 	void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;

+ 14 - 6
client/AntilagServer.cpp

@@ -10,12 +10,15 @@
 #include "StdInc.h"
 #include "AntilagServer.h"
 
+#include "CServerHandler.h"
+#include "Client.h"
 #include "GameEngine.h"
 
 #include "../server/CGameHandler.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/serializer/GameConnection.h"
+#include "GameInstance.h"
 
 int ConnectionPackWriter::write(const std::byte * data, unsigned size)
 {
@@ -123,11 +126,14 @@ bool AntilagServer::verifyReply(const CPackForClient & pack)
 	if (packageReceived)
 	{
 		assert(currentPackageID == invalidPackageID);
-		assert(!predictedReplies.empty());
-		const auto & nextPrediction = predictedReplies.front();
-		assert(nextPrediction.senderID == packageReceived->player);
-		assert(nextPrediction.requestID == packageReceived->requestID);
-		currentPackageID = packageReceived->requestID;
+
+		if (!predictedReplies.empty() && predictedReplies.front().requestID == packageReceived->requestID)
+		{
+			[[maybe_unused]] const auto & nextPrediction = predictedReplies.front();
+			assert(nextPrediction.senderID == packageReceived->player);
+			assert(nextPrediction.requestID == packageReceived->requestID);
+			currentPackageID = packageReceived->requestID;
+		}
 	}
 
 	if (currentPackageID == invalidPackageID)
@@ -197,9 +203,11 @@ void AntilagServer::applyPack(CPackForClient & pack)
 	BinarySerializer serializer(&packWriter);
 	serializer & &pack;
 	predictedReplies.back().writtenPacks.push_back(std::move(packWriter));
+
+	GAME->server().client->handlePack(pack);
 }
 
 void AntilagServer::sendPack(CPackForClient & pack, GameConnectionID connectionID)
 {
-	// TODO
+	applyPack(pack);
 }

+ 3 - 3
client/CPlayerInterface.cpp

@@ -1184,14 +1184,14 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 	ENGINE->windows().pushWindow(wnd);
 }
 
-void CPlayerInterface::tileRevealed(const std::unordered_set<int3> &pos)
+void CPlayerInterface::tileRevealed(const FowTilesType &pos)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
 	adventureInt->onMapTilesChanged(pos);
 }
 
-void CPlayerInterface::tileHidden(const std::unordered_set<int3> &pos)
+void CPlayerInterface::tileHidden(const FowTilesType &pos)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	adventureInt->onMapTilesChanged(pos);
@@ -1341,7 +1341,7 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 
 		//redraw minimap if owner changed
 		std::set<int3> pos = obj->getBlockedPos();
-		std::unordered_set<int3> upos(pos.begin(), pos.end());
+		FowTilesType upos(pos.begin(), pos.end());
 		adventureInt->onMapTilesChanged(upos);
 
 		assert(cb->getTownsInfo().size() == localState->getOwnedTowns().size());

+ 2 - 2
client/CPlayerInterface.h

@@ -126,8 +126,8 @@ protected: // Call-ins from server, should not be called directly, but only via
 	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
 	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
 	void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell
-	void tileHidden(const std::unordered_set<int3> &pos) override; //called when given tiles become hidden under fog of war
-	void tileRevealed(const std::unordered_set<int3> &pos) override; //called when fog of war disappears from given tiles
+	void tileHidden(const FowTilesType &pos) override; //called when given tiles become hidden under fog of war
+	void tileRevealed(const FowTilesType &pos) override; //called when fog of war disappears from given tiles
 	void newObject(const CGObjectInstance * obj) override;
 	void availableArtifactsChanged(const CGBlackMarket *bm = nullptr) override; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	void yourTurn(QueryID queryID) override;

+ 5 - 5
client/CServerHandler.cpp

@@ -617,8 +617,8 @@ void CServerHandler::startGameplay(std::shared_ptr<CGameState> gameState)
 	if(GAME->mainmenu())
 		GAME->mainmenu()->disable();
 
-	if (isGuest())
-		antilagServer = std::make_unique<AntilagServer>(getNetworkHandler(), gameState);
+	//if (isGuest())
+	networkLagCompensator = std::make_unique<AntilagServer>(getNetworkHandler(), gameState);
 
 	switch(si->mode)
 	{
@@ -944,7 +944,7 @@ void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
 
 void CServerHandler::visitForClient(CPackForClient & clientPack)
 {
-	if (antilagServer && antilagServer->verifyReply(clientPack))
+	if (networkLagCompensator && networkLagCompensator->verifyReply(clientPack))
 		return;
 
 	client->handlePack(clientPack);
@@ -968,8 +968,8 @@ bool CServerHandler::inGame() const
 
 void CServerHandler::sendGamePack(const CPackForServer & pack) const
 {
-	if (antilagServer)
-		antilagServer->tryPredictReply(pack);
+	if (networkLagCompensator)
+		networkLagCompensator->tryPredictReply(pack);
 
 	logicConnection->sendPack(pack);
 }

+ 1 - 1
client/CServerHandler.h

@@ -102,7 +102,7 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
 	std::unique_ptr<GlobalLobbyClient> lobbyClient;
 	std::unique_ptr<GameChatHandler> gameChat;
 	std::unique_ptr<IServerRunner> serverRunner;
-	std::unique_ptr<AntilagServer> antilagServer;
+	std::unique_ptr<AntilagServer> networkLagCompensator;
 	std::shared_ptr<CMapInfo> mapToStart;
 	std::vector<std::string> localPlayerNames;
 

+ 1 - 1
client/HeroMovementController.cpp

@@ -167,7 +167,7 @@ void HeroMovementController::onTryMoveHero(const CGHeroInstance * hero, const Tr
 		GAME->interface()->localState->hasPath(hero) &&
 		GAME->interface()->localState->getPath(hero).lastNode().coord == details.attackedFrom;
 
-	std::unordered_set<int3> changedTiles {
+	FowTilesType changedTiles {
 		hero->convertToVisitablePos(details.start),
 		hero->convertToVisitablePos(details.end)
 	};

+ 1 - 1
client/adventureMap/AdventureMapInterface.cpp

@@ -351,7 +351,7 @@ void AdventureMapInterface::onHeroOrderChanged()
 	widget->getHeroList()->updateWidget();
 }
 
-void AdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
+void AdventureMapInterface::onMapTilesChanged(boost::optional<FowTilesType> positions)
 {
 	if (positions)
 		widget->getMinimap()->updateTiles(*positions);

+ 2 - 1
client/adventureMap/AdventureMapInterface.h

@@ -22,6 +22,7 @@ struct CGPathNode;
 struct ObjectPosInfo;
 struct Component;
 class int3;
+using FowTilesType = std::set<int3>;
 
 VCMI_LIB_NAMESPACE_END
 
@@ -131,7 +132,7 @@ public:
 	void onCurrentPlayerChanged(PlayerColor playerID);
 
 	/// Called by PlayerInterface when specific map tile changed and must be updated on minimap
-	void onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions);
+	void onMapTilesChanged(boost::optional<FowTilesType> positions);
 
 	/// Called by PlayerInterface when hero starts movement
 	void onHeroMovementStarted(const CGHeroInstance * hero);

+ 1 - 1
client/adventureMap/CMinimap.cpp

@@ -276,7 +276,7 @@ void CMinimap::updateVisibleHeroes()
 	}
 }
 
-void CMinimap::updateTiles(const std::unordered_set<int3> & positions)
+void CMinimap::updateTiles(const FowTilesType & positions)
 {
 	if(minimap)
 	{

+ 2 - 1
client/adventureMap/CMinimap.h

@@ -13,6 +13,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 class ColorRGBA;
+using FowTilesType = std::set<int3>;
 VCMI_LIB_NAMESPACE_END
 
 class Canvas;
@@ -73,6 +74,6 @@ public:
 
 	void showAll(Canvas & to) override;
 
-	void updateTiles(const std::unordered_set<int3> & positions);
+	void updateTiles(const FowTilesType & positions);
 };
 

+ 1 - 1
client/adventureMap/MapAudioPlayer.cpp

@@ -163,7 +163,7 @@ void MapAudioPlayer::updateAmbientSounds()
 	};
 
 	int3 pos = currentSelection->getSightCenter();
-	std::unordered_set<int3> tiles;
+	FowTilesType tiles;
 	GAME->interface()->cb->getVisibleTilesInRange(tiles, pos, ENGINE->sound().ambientGetRange(), int3::DIST_CHEBYSHEV);
 	for(int3 tile : tiles)
 	{

+ 3 - 3
lib/callback/CGameInfoCallback.cpp

@@ -716,7 +716,7 @@ const TeamState * CGameInfoCallback::getPlayerTeam( PlayerColor color ) const
 	}
 }
 
-void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
+void CGameInfoCallback::getVisibleTilesInRange(FowTilesType &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
 {
 	gameState().getTilesInRange(tiles, pos, radious, ETileVisibility::REVEALED, *getPlayerID(),  distanceFormula);
 }
@@ -814,7 +814,7 @@ void CGameInfoCallback::getFreeTiles(std::vector<int3> & tiles) const
 	}
 }
 
-void CGameInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
+void CGameInfoCallback::getTilesInRange(FowTilesType & tiles,
 											  const int3 & pos,
 											  int radious,
 											  ETileVisibility mode,
@@ -851,7 +851,7 @@ void CGameInfoCallback::getTilesInRange(std::unordered_set<int3> & tiles,
 	}
 }
 
-void CGameInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std::optional<PlayerColor> Player, int level, const std::function<bool(const TerrainTile *)> & filter) const
+void CGameInfoCallback::getAllTiles(FowTilesType & tiles, std::optional<PlayerColor> Player, int level, const std::function<bool(const TerrainTile *)> & filter) const
 {
 	if(Player.has_value() && !Player->isValidPlayer())
 	{

+ 3 - 3
lib/callback/CGameInfoCallback.h

@@ -75,7 +75,7 @@ public:
 	bool isTileGuardedUnchecked(int3 tile) const override;
 	const TerrainTile * getTile(int3 tile, bool verbose = true) const override;
 	const TerrainTile * getTileUnchecked(int3 tile) const override;
-	void getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
+	void getVisibleTilesInRange(FowTilesType &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
 	void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
 	EDiggingStatus getTileDigStatus(int3 tile, bool verbose = true) const override;
 	bool checkForVisitableDir(const int3 & src, const int3 & dst) const override;
@@ -103,8 +103,8 @@ public:
 
 	//used for random spawns
 	void getFreeTiles(std::vector<int3> &tiles) const;
-	void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const override;
-	void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const override;
+	void getTilesInRange(FowTilesType & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const override;
+	void getAllTiles(FowTilesType &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const override;
 
 	void getAllowedSpells(std::vector<SpellID> &out, std::optional<ui16> level = std::nullopt) const;
 

+ 2 - 2
lib/callback/EditorCallback.cpp

@@ -97,12 +97,12 @@ std::vector<const CGObjectInstance*> EditorCallback::getGuardingCreatures(int3)
 	THROW_EDITOR_UNSUPPORTED;
 }
 
-void EditorCallback::getTilesInRange(std::unordered_set<int3> &, const int3 &, int, ETileVisibility, std::optional<PlayerColor>, int3::EDistanceFormula) const
+void EditorCallback::getTilesInRange(FowTilesType &, const int3 &, int, ETileVisibility, std::optional<PlayerColor>, int3::EDistanceFormula) const
 {
 	THROW_EDITOR_UNSUPPORTED;
 }
 
-void EditorCallback::getAllTiles(std::unordered_set<int3> &, std::optional<PlayerColor>, int, const std::function<bool(const TerrainTile *)> &) const
+void EditorCallback::getAllTiles(FowTilesType &, std::optional<PlayerColor>, int, const std::function<bool(const TerrainTile *)> &) const
 {
 	THROW_EDITOR_UNSUPPORTED;
 }

+ 2 - 2
lib/callback/EditorCallback.h

@@ -40,8 +40,8 @@ public:
 	bool checkForVisitableDir(const int3 & src, const int3 & dst) const override;
 	std::vector<const CGObjectInstance*> getGuardingCreatures(int3 pos) const override;
 
-	void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player, int3::EDistanceFormula formula) const override;
-	void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const override;
+	void getTilesInRange(FowTilesType & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player, int3::EDistanceFormula formula) const override;
+	void getAllTiles(FowTilesType &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const override;
 
 	std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const override;
 	std::vector<ObjectInstanceID> getTeleportChannelEntrances(TeleportChannelID id, PlayerColor player) const override;

+ 2 - 1
lib/callback/IGameEventCallback.h

@@ -34,6 +34,7 @@ class CCreatureSet;
 class CGObjectInstance;
 class IObjectInterface;
 
+using FowTilesType = std::set<int3>;
 enum class EOpenWindowMode : uint8_t;
 
 namespace Rewardable
@@ -111,7 +112,7 @@ public:
 	virtual void sendAndApply(CPackForClient & pack) = 0;
 	virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
 	virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) = 0;
-	virtual void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) = 0;
+	virtual void changeFogOfWar(const FowTilesType &tiles, PlayerColor player, ETileVisibility mode) = 0;
 
 	virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0;
 

+ 3 - 2
lib/callback/IGameEventsReceiver.h

@@ -32,6 +32,7 @@ class EVictoryLossCheckResult;
 class IShipyard;
 class IMarket;
 
+using FowTilesType = std::set<int3>;
 enum class EInfoWindowMode : uint8_t;
 
 class DLL_LINKAGE IGameEventsReceiver
@@ -76,8 +77,8 @@ public:
 	virtual void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) {};
 	virtual void showQuestLog(){};
 	virtual void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID){}; //called when a hero casts a spell
-	virtual void tileHidden(const std::unordered_set<int3> &pos){};
-	virtual void tileRevealed(const std::unordered_set<int3> &pos){};
+	virtual void tileHidden(const FowTilesType &pos){};
+	virtual void tileRevealed(const FowTilesType &pos){};
 	virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
 	virtual void availableArtifactsChanged(const CGBlackMarket *bm = nullptr){}; //bm may be nullptr, then artifacts are changed in the global pool (used by merchants in towns)
 	virtual void centerView (int3 pos, int focusTime){};

+ 4 - 2
lib/callback/IGameInfoCallback.h

@@ -40,6 +40,8 @@ class CGTeleport;
 class CGTownInstance;
 class IMarket;
 
+using FowTilesType = std::set<int3>;
+
 #if SCRIPTING_ENABLED
 namespace scripting
 {
@@ -151,10 +153,10 @@ public:
 	virtual bool isTileGuardedUnchecked(int3 tile) const = 0;
 
 	/// Returns all tiles within specified range with specific tile visibility mode
-	virtual void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const = 0;
+	virtual void getTilesInRange(FowTilesType & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const = 0;
 
 	/// returns all tiles on given level (-1 - both levels, otherwise number of level)
-	virtual void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const = 0;
+	virtual void getAllTiles(FowTilesType &tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter) const = 0;
 
 	virtual std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player)  const  = 0;
 	virtual std::vector<ObjectInstanceID> getTeleportChannelEntrances(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const  = 0;

+ 1 - 1
lib/gameState/CGameState.cpp

@@ -676,7 +676,7 @@ void CGameState::initFogOfWar()
 			if(!vstd::contains(elem.second.players, obj->getOwner()))
 				continue; //not a flagged object
 
-			std::unordered_set<int3> tiles;
+			FowTilesType tiles;
 			getTilesInRange(tiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::HIDDEN, obj->tempOwner);
 			for(const int3 & tile : tiles)
 			{

+ 1 - 1
lib/gameState/GameStatePackVisitor.cpp

@@ -189,7 +189,7 @@ void GameStatePackVisitor::visitFoWChange(FoWChange & pack)
 
 	if (pack.mode == ETileVisibility::HIDDEN) //do not hide too much
 	{
-		std::unordered_set<int3> tilesRevealed;
+		FowTilesType tilesRevealed;
 		for (auto & o : gs.getMap().getObjects())
 		{
 			if (o->asOwnable())

+ 10 - 2
lib/networkPacks/PacksForClient.h

@@ -48,6 +48,8 @@ class BattleInfo;
 // For now it's will be there till teleports code refactored and moved into own file
 using TTeleportExitsList = std::vector<std::pair<ObjectInstanceID, int3>>;
 
+using FowTilesType = std::set<int3>;
+
 /***********************************************************************************************************/
 struct DLL_LINKAGE PackageApplied : public CPackForClient
 {
@@ -405,7 +407,7 @@ struct DLL_LINKAGE SetMovePoints : public CPackForClient
 
 struct DLL_LINKAGE FoWChange : public CPackForClient
 {
-	std::unordered_set<int3> tiles;
+	FowTilesType tiles;
 	PlayerColor player;
 	ETileVisibility mode;
 	bool waitForDialogs = false;
@@ -659,7 +661,7 @@ struct DLL_LINKAGE TryMoveHero : public CPackForClient
 	/// Hero anchor position to which hero moves
 	int3 end;
 	/// Tiles that were revealed by this move
-	std::unordered_set<int3> fowRevealed;
+	FowTilesType fowRevealed;
 	/// If hero moves on guarded tile, this field will be set to visitable pos of attacked wandering monster
 	int3 attackedFrom;
 
@@ -679,6 +681,12 @@ struct DLL_LINKAGE TryMoveHero : public CPackForClient
 		h & movePoints;
 		h & fowRevealed;
 		h & attackedFrom;
+
+		std::string fow;
+		for (const auto & tile : fowRevealed)
+			fow += tile.toString() + ", ";
+
+		logGlobal->info("OI %d, mp %d, res %d, start %s, end %s, attack %s, fow %s", id.getNum(), movePoints, static_cast<int>(result), start.toString(), end.toString(), attackedFrom.toString(), fow);
 	}
 };
 

+ 3 - 1
lib/pathfinder/CPathfinder.h

@@ -20,6 +20,8 @@ class TurnInfo;
 class CGTeleport;
 struct PathfinderOptions;
 
+using FowTilesType = std::set<int3>;
+
 // Optimized storage - tile can have 0-8 neighbour tiles
 // static_vector uses fixed, preallocated storage (capacity) and dynamic size
 // this avoid dynamic allocations on huge number of neighbour list queries
@@ -78,7 +80,7 @@ public:
 		PATROL_LOCKED = 1,
 		PATROL_RADIUS
 	} patrolState;
-	std::unordered_set<int3> patrolTiles;
+	FowTilesType patrolTiles;
 
 	int turn;
 	PlayerColor owner;

+ 1 - 1
lib/rewardable/Interface.cpp

@@ -76,7 +76,7 @@ void Rewardable::Interface::grantRewardBeforeLevelup(IGameEventCallback & gameEv
 			return score > 0;
 		};
 
-		std::unordered_set<int3> tiles;
+		FowTilesType tiles;
 		if (props.radius > 0)
 		{
 			cb->getTilesInRange(tiles, hero->getSightCenter(), props.radius, ETileVisibility::HIDDEN, hero->getOwner());

+ 6 - 6
lib/serializer/GameConnection.cpp

@@ -19,7 +19,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class ConnectionPackWriter final : public IBinaryWriter
+class GameConnectionPackWriter final : public IBinaryWriter
 {
 public:
 	std::vector<std::byte> buffer;
@@ -27,7 +27,7 @@ public:
 	int write(const std::byte * data, unsigned size) final;
 };
 
-class ConnectionPackReader final : public IBinaryReader
+class GameConnectionPackReader final : public IBinaryReader
 {
 public:
 	const std::vector<std::byte> * buffer;
@@ -36,13 +36,13 @@ public:
 	int read(std::byte * data, unsigned size) final;
 };
 
-int ConnectionPackWriter::write(const std::byte * data, unsigned size)
+int GameConnectionPackWriter::write(const std::byte * data, unsigned size)
 {
 	buffer.insert(buffer.end(), data, data + size);
 	return size;
 }
 
-int ConnectionPackReader::read(std::byte * data, unsigned size)
+int GameConnectionPackReader::read(std::byte * data, unsigned size)
 {
 	if (position + size > buffer->size())
 		throw std::runtime_error("End of file reached when reading received network pack!");
@@ -54,8 +54,8 @@ int ConnectionPackReader::read(std::byte * data, unsigned size)
 
 GameConnection::GameConnection(std::weak_ptr<INetworkConnection> networkConnection)
 	: networkConnection(networkConnection)
-	, packReader(std::make_unique<ConnectionPackReader>())
-	, packWriter(std::make_unique<ConnectionPackWriter>())
+	, packReader(std::make_unique<GameConnectionPackReader>())
+	, packWriter(std::make_unique<GameConnectionPackWriter>())
 	, deserializer(std::make_unique<BinaryDeserializer>(packReader.get()))
 	, serializer(std::make_unique<BinarySerializer>(packWriter.get()))
 {

+ 5 - 5
lib/serializer/GameConnection.h

@@ -19,20 +19,20 @@ class BinaryDeserializer;
 class BinarySerializer;
 struct CPack;
 class INetworkConnection;
-class ConnectionPackReader;
-class ConnectionPackWriter;
+class GameConnectionPackReader;
+class GameConnectionPackWriter;
 class CGameState;
 class IGameInfoCallback;
 
 /// Wrapper class for game connection
 /// Handles serialization and deserialization of data received from network
-class DLL_LINKAGE GameConnection final
+class DLL_LINKAGE GameConnection final : boost::noncopyable
 {
 	/// Non-owning pointer to underlying connection
 	std::weak_ptr<INetworkConnection> networkConnection;
 
-	std::unique_ptr<ConnectionPackReader> packReader;
-	std::unique_ptr<ConnectionPackWriter> packWriter;
+	std::unique_ptr<GameConnectionPackReader> packReader;
+	std::unique_ptr<GameConnectionPackWriter> packWriter;
 	std::unique_ptr<BinaryDeserializer> deserializer;
 	std::unique_ptr<BinarySerializer> serializer;
 

+ 4 - 4
server/CGameHandler.cpp

@@ -1560,7 +1560,7 @@ void CGameHandler::throwIfWrongOwner(GameConnectionID connectionID, const CPackF
 
 void CGameHandler::throwIfPlayerNotActive(GameConnectionID connectionID, const CPackForServer * pack)
 {
-	if (!turnOrder->isPlayerMakingTurn(pack->player))
+	if (!vstd::contains(gs->actingPlayers, pack->player))
 		throwNotAllowedAction(connectionID);
 }
 
@@ -4069,7 +4069,7 @@ void CGameHandler::removeAfterVisit(const ObjectInstanceID & id)
 
 void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode)
 {
-	std::unordered_set<int3> tiles;
+	FowTilesType tiles;
 
 	if (mode == ETileVisibility::HIDDEN)
 	{
@@ -4082,7 +4082,7 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
 	changeFogOfWar(tiles, player, mode);
 }
 
-void CGameHandler::changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode)
+void CGameHandler::changeFogOfWar(const FowTilesType &tiles, PlayerColor player, ETileVisibility mode)
 {
 	if (tiles.empty())
 		return;
@@ -4095,7 +4095,7 @@ void CGameHandler::changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerC
 	if (mode == ETileVisibility::HIDDEN)
 	{
 		// do not hide tiles observed by owned objects. May lead to disastrous AI problems
-		std::unordered_set<int3> observedTiles;
+		FowTilesType observedTiles;
 		const auto * p = gameInfo().getPlayerState(player);
 		for (const auto * obj : p->getOwnedObjects())
 			gameInfo().getTilesInRange(observedTiles, obj->getSightCenter(), obj->getSightRadius(), ETileVisibility::REVEALED, obj->getOwner());

+ 1 - 1
server/CGameHandler.h

@@ -173,7 +173,7 @@ public:
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override;
 
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) override;
-	void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player,ETileVisibility mode) override;
+	void changeFogOfWar(const FowTilesType &tiles, PlayerColor player,ETileVisibility mode) override;
 	
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override;
 	void useChargeBasedSpell(const ObjectInstanceID & heroObjectID, const SpellID & spellID);

+ 1 - 1
test/mock/mock_IGameEventCallback.h

@@ -80,7 +80,7 @@ public:
 	void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {}
 	void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {} //when two heroes meet on adventure map
 	void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) override {}
-	void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) override {}
+	void changeFogOfWar(const FowTilesType &tiles, PlayerColor player, ETileVisibility mode) override {}
 	void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {}
 
 	///useful callback methods

+ 2 - 2
test/mock/mock_IGameInfoCallback.h

@@ -66,8 +66,8 @@ public:
 	MOCK_CONST_METHOD1(getTopObj, const CGObjectInstance *(int3 pos));
 	MOCK_CONST_METHOD2(getTileDigStatus, EDiggingStatus(int3 tile, bool verbose));
 	MOCK_CONST_METHOD1(calculatePaths, void(const std::shared_ptr<PathfinderConfig> & config));
-	MOCK_CONST_METHOD6(getTilesInRange, void( std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player, int3::EDistanceFormula formula));
-	MOCK_CONST_METHOD4(getAllTiles, void(std::unordered_set<int3> & tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter));
+	MOCK_CONST_METHOD6(getTilesInRange, void(FowTilesType & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player, int3::EDistanceFormula formula));
+	MOCK_CONST_METHOD4(getAllTiles, void(FowTilesType & tiles, std::optional<PlayerColor> player, int level, const std::function<bool(const TerrainTile *)> & filter));
 	MOCK_CONST_METHOD2(getVisibleTeleportObjects, std::vector<ObjectInstanceID>(std::vector<ObjectInstanceID> ids, PlayerColor player));
 	MOCK_CONST_METHOD2(getTeleportChannelEntrances, std::vector<ObjectInstanceID>(TeleportChannelID id, PlayerColor Player));
 	MOCK_CONST_METHOD2(getTeleportChannelExits, std::vector<ObjectInstanceID>(TeleportChannelID id, PlayerColor Player));