浏览代码

rework netpacks to avoid double typeinfo

Andrii Danylchenko 2 年之前
父节点
当前提交
9f55666931

+ 2 - 0
client/CMakeLists.txt

@@ -212,6 +212,8 @@ set(client_HEADERS
 	CVideoHandler.h
 	Client.h
 	ClientCommandManager.h
+	ClientNetPackVisitors.h
+	LobbyClientNetPackVisitors.h
 	resource.h
 )
 

+ 55 - 17
client/CServerHandler.cpp

@@ -37,7 +37,7 @@
 #include "../lib/CConfigHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CThreadHelper.h"
-#include "../lib/NetPacks.h"
+#include "../lib/NetPackVisitor.h"
 #include "../lib/StartInfo.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CCampaignHandler.h"
@@ -53,6 +53,7 @@
 #include <boost/uuid/uuid_io.hpp>
 #include <boost/uuid/uuid_generators.hpp>
 #include "../lib/serializer/Cast.h"
+#include "LobbyClientNetPackVisitors.h"
 
 #include <vcmi/events/EventBus.h>
 
@@ -86,15 +87,21 @@ public:
 	bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
+		ApplyOnLobbyHandlerNetPackVisitor visitor(*handler);
+
 		logNetwork->trace("\tImmediately apply on lobby: %s", typeList.getTypeInfo(ptr)->name());
-		return ptr->applyOnLobbyHandler(handler);
+		ptr->visit(visitor);
+
+		return visitor.getResult();
 	}
 
 	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
+		ApplyOnLobbyScreenNetPackVisitor visitor(*handler, lobby);
+
 		logNetwork->trace("\tApply on lobby from queue: %s", typeList.getTypeInfo(ptr)->name());
-		ptr->applyOnLobbyScreen(lobby, handler);
+		ptr->visit(visitor);
 	}
 };
 
@@ -575,7 +582,7 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const
 	c->disableStackSendingByID();
 }
 
-void CServerHandler::startGameplay(CGameState * gameState)
+void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
 {
 	if(CMM)
 		CMM->disable();
@@ -767,6 +774,30 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
 	}
 }
 
+class ServerHandlerCPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+
+public:
+	ServerHandlerCPackVisitor(CServerHandler & handler)
+			:handler(handler)
+	{
+	}
+
+	virtual bool callTyped() override { return false; }
+
+	virtual void visitForLobby(CPackForLobby & lobbyPack) override
+	{
+		handler.visitForLobby(lobbyPack);
+	}
+
+	virtual void visitForClient(CPackForClient & clientPack) override
+	{
+		handler.visitForClient(clientPack);
+	}
+};
+
 void CServerHandler::threadHandleConnection()
 {
 	setThreadName("CServerHandler::threadHandleConnection");
@@ -787,20 +818,10 @@ void CServerHandler::threadHandleConnection()
 				// Though currently they'll be delivered and might cause crash.
 				vstd::clear_pointer(pack);
 			}
-			else if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
-			{
-				if(applier->getApplier(typeList.getTypeID(pack))->applyOnLobbyHandler(this, pack))
-				{
-					if(!settings["session"]["headless"].Bool())
-					{
-						boost::unique_lock<boost::recursive_mutex> lock(*mx);
-						packsForLobbyScreen.push_back(lobbyPack);
-					}
-				}
-			}
-			else if(auto clientPack = dynamic_ptr_cast<CPackForClient>(pack))
+			else
 			{
-				client->handlePack(clientPack);
+				ServerHandlerCPackVisitor visitor(*this);
+				pack->visit(visitor);
 			}
 		}
 	}
@@ -836,6 +857,23 @@ void CServerHandler::threadHandleConnection()
 	}
 }
 
+void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
+{
+	if(applier->getApplier(typeList.getTypeID(&lobbyPack))->applyOnLobbyHandler(this, &lobbyPack))
+	{
+		if(!settings["session"]["headless"].Bool())
+		{
+			boost::unique_lock<boost::recursive_mutex> lock(*mx);
+			packsForLobbyScreen.push_back(&lobbyPack);
+		}
+	}
+}
+
+void CServerHandler::visitForClient(CPackForClient & clientPack)
+{
+	client->handlePack(&clientPack);
+}
+
 void CServerHandler::threadRunServer()
 {
 #if !defined(VCMI_ANDROID) && !defined(VCMI_IOS)

+ 5 - 2
client/CServerHandler.h

@@ -25,13 +25,13 @@ class CGameState;
 struct ClientPlayer;
 struct CPack;
 struct CPackForLobby;
+struct CPackForClient;
 
 template<typename T> class CApplier;
 
 VCMI_LIB_NAMESPACE_END
 
 class CClient;
-
 class CBaseForLobbyApply;
 
 // TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
@@ -148,7 +148,7 @@ public:
 	void sendRestartGame() const override;
 	void sendStartGame(bool allowOnlyAI = false) const override;
 
-	void startGameplay(CGameState * gameState = nullptr);
+	void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
 	void endGameplay(bool closeConnection = true, bool restart = false);
 	void startCampaignScenario(std::shared_ptr<CCampaignState> cs = {});
 	void showServerError(std::string txt);
@@ -159,6 +159,9 @@ public:
 
 	void debugStartTest(std::string filename, bool save = false);
 	void restoreLastSession();
+
+	void visitForLobby(CPackForLobby & lobbyPack);
+	void visitForClient(CPackForClient & clientPack);
 };
 
 extern CServerHandler * CSH;

+ 5 - 2
client/Client.cpp

@@ -32,6 +32,7 @@
 #include "../lib/serializer/Connection.h"
 #include "../lib/serializer/CLoadIntegrityValidator.h"
 #include "../lib/NetPacks.h"
+#include "ClientNetPackVisitors.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CMap.h"
@@ -80,12 +81,14 @@ public:
 	void applyOnClAfter(CClient * cl, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		ptr->applyCl(cl);
+		ApplyClientNetPackVisitor visitor(*cl, *cl->gameState());
+		ptr->visit(visitor);
 	}
 	void applyOnClBefore(CClient * cl, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		ptr->applyFirstCl(cl);
+		ApplyFirstClientNetPackVisitor visitor(*cl, *cl->gameState());
+		ptr->visit(visitor);
 	}
 };
 

+ 4 - 1
client/ClientCommandManager.cpp

@@ -18,6 +18,7 @@
 #include "CServerHandler.h"
 #include "gui/CGuiHandler.h"
 #include "../lib/NetPacks.h"
+#include "ClientNetPackVisitors.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/CPlayerState.h"
@@ -427,7 +428,9 @@ void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
 	YourTurn yt;
 	yt.player = colorIdentifier;
 	yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
-	yt.applyCl(CSH->client);
+
+	ApplyClientNetPackVisitor visitor(*CSH->client, *CSH->client->gameState());
+	yt.visit(visitor);
 }
 
 void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)

+ 132 - 0
client/ClientNetPackVisitors.h

@@ -0,0 +1,132 @@
+/*
+ * ClientNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class CClient;
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class CGameState;
+
+VCMI_LIB_NAMESPACE_END
+
+class ApplyClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CClient & cl;
+	CGameState & gs;
+
+public:
+	ApplyClientNetPackVisitor(CClient & cl, CGameState & gs)
+		:cl(cl), gs(gs)
+	{
+	}
+
+	void visitSetResources(SetResources & pack) override;
+	void visitSetPrimSkill(SetPrimSkill & pack) override;
+	void visitSetSecSkill(SetSecSkill & pack) override;
+	void visitHeroVisitCastle(HeroVisitCastle & pack) override;
+	void visitSetMana(SetMana & pack) override;
+	void visitSetMovePoints(SetMovePoints & pack) override;
+	void visitFoWChange(FoWChange & pack) override;
+	void visitChangeStackCount(ChangeStackCount & pack) override;
+	void visitSetStackType(SetStackType & pack) override;
+	void visitEraseStack(EraseStack & pack) override;
+	void visitSwapStacks(SwapStacks & pack) override;
+	void visitInsertNewStack(InsertNewStack & pack) override;
+	void visitRebalanceStacks(RebalanceStacks & pack) override;
+	void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) override;
+	void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) override;
+	void visitPutArtifact(PutArtifact & pack) override;
+	void visitEraseArtifact(EraseArtifact & pack) override;
+	void visitMoveArtifact(MoveArtifact & pack) override;
+	void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) override;
+	void visitAssembledArtifact(AssembledArtifact & pack) override;
+	void visitDisassembledArtifact(DisassembledArtifact & pack) override;
+	void visitHeroVisit(HeroVisit & pack) override;
+	void visitNewTurn(NewTurn & pack) override;
+	void visitGiveBonus(GiveBonus & pack) override;
+	void visitChangeObjPos(ChangeObjPos & pack) override;
+	void visitPlayerEndsGame(PlayerEndsGame & pack) override;
+	void visitPlayerReinitInterface(PlayerReinitInterface & pack) override;
+	void visitRemoveBonus(RemoveBonus & pack) override;
+	void visitRemoveObject(RemoveObject & pack) override;
+	void visitTryMoveHero(TryMoveHero & pack) override;
+	void visitNewStructures(NewStructures & pack) override;
+	void visitRazeStructures(RazeStructures & pack) override;
+	void visitSetAvailableCreatures(SetAvailableCreatures & pack) override;
+	void visitSetHeroesInTown(SetHeroesInTown & pack) override;
+	void visitHeroRecruited(HeroRecruited & pack) override;
+	void visitGiveHero(GiveHero & pack) override;
+	void visitInfoWindow(InfoWindow & pack) override;
+	void visitSetObjectProperty(SetObjectProperty & pack) override;
+	void visitHeroLevelUp(HeroLevelUp & pack) override;
+	void visitCommanderLevelUp(CommanderLevelUp & pack) override;
+	void visitBlockingDialog(BlockingDialog & pack) override;
+	void visitGarrisonDialog(GarrisonDialog & pack) override;
+	void visitExchangeDialog(ExchangeDialog & pack) override;
+	void visitTeleportDialog(TeleportDialog & pack) override;
+	void visitMapObjectSelectDialog(MapObjectSelectDialog & pack) override;
+	void visitBattleStart(BattleStart & pack) override;
+	void visitBattleNextRound(BattleNextRound & pack) override;
+	void visitBattleSetActiveStack(BattleSetActiveStack & pack) override;
+	void visitBattleLogMessage(BattleLogMessage & pack) override;
+	void visitBattleTriggerEffect(BattleTriggerEffect & pack) override;
+	void visitBattleAttack(BattleAttack & pack) override;
+	void visitBattleSpellCast(BattleSpellCast & pack) override;
+	void visitSetStackEffect(SetStackEffect & pack) override;
+	void visitStacksInjured(StacksInjured & pack) override;
+	void visitBattleResultsApplied(BattleResultsApplied & pack) override;
+	void visitBattleUnitsChanged(BattleUnitsChanged & pack) override;
+	void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) override;
+	void visitCatapultAttack(CatapultAttack & pack) override;
+	void visitEndAction(EndAction & pack) override;
+	void visitPackageApplied(PackageApplied & pack) override;
+	void visitSystemMessage(SystemMessage & pack) override;
+	void visitPlayerBlocked(PlayerBlocked & pack) override;
+	void visitYourTurn(YourTurn & pack) override;
+	void visitSaveGameClient(SaveGameClient & pack) override;
+	void visitPlayerMessageClient(PlayerMessageClient & pack) override;
+	void visitShowInInfobox(ShowInInfobox & pack) override;
+	void visitAdvmapSpellCast(AdvmapSpellCast & pack) override;
+	void visitShowWorldViewEx(ShowWorldViewEx & pack) override;	
+	void visitOpenWindow(OpenWindow & pack) override;
+	void visitCenterView(CenterView & pack) override;
+	void visitNewObject(NewObject & pack) override;
+	void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) override;
+	void visitEntitiesChanged(EntitiesChanged & pack) override;
+};
+
+class ApplyFirstClientNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CClient & cl;
+	CGameState & gs;
+
+public:
+	ApplyFirstClientNetPackVisitor(CClient & cl, CGameState & gs)
+		:cl(cl), gs(gs)
+	{
+	}
+
+	virtual void visitChangeObjPos(ChangeObjPos & pack) override;
+	virtual void visitRemoveObject(RemoveObject & pack) override;
+	virtual void visitTryMoveHero(TryMoveHero & pack) override;
+	virtual void visitGiveHero(GiveHero & pack) override;
+	virtual void visitBattleStart(BattleStart & pack) override;
+	virtual void visitBattleNextRound(BattleNextRound & pack) override;
+	virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) override;
+	virtual void visitBattleResult(BattleResult & pack) override;
+	virtual void visitBattleStackMoved(BattleStackMoved & pack) override;
+	virtual void visitBattleAttack(BattleAttack & pack) override;
+	virtual void visitStartAction(StartAction & pack) override;
+};

+ 56 - 0
client/LobbyClientNetPackVisitors.h

@@ -0,0 +1,56 @@
+/*
+ * ClientNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class CClient;
+class CGameState;
+
+class ApplyOnLobbyHandlerNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+	bool result;
+
+public:
+	ApplyOnLobbyHandlerNetPackVisitor(CServerHandler & handler)
+		:handler(handler), result(true)
+	{
+	}
+
+	bool getResult() const { return result; }
+
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
+};
+
+class ApplyOnLobbyScreenNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CServerHandler & handler;
+	CLobbyScreen * lobby;
+
+public:
+	ApplyOnLobbyScreenNetPackVisitor(CServerHandler & handler, CLobbyScreen * lobby)
+		:handler(handler), lobby(lobby)
+	{
+	}
+
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) override;
+	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
+	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) override;
+};

文件差异内容过多而无法显示
+ 295 - 320
client/NetPacksClient.cpp


+ 54 - 56
client/NetPacksLobbyClient.cpp

@@ -9,6 +9,7 @@
  */
 #include "StdInc.h"
 
+#include "LobbyClientNetPackVisitors.h"
 #include "lobby/CSelectionBase.h"
 #include "lobby/CLobbyScreen.h"
 
@@ -28,123 +29,120 @@
 #include "../lib/NetPacksLobby.h"
 #include "../lib/serializer/Connection.h"
 
-bool LobbyClientConnected::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
 {
+	result = false;
+
 	// Check if it's LobbyClientConnected for our client
-	if(uuid == handler->c->uuid)
+	if(pack.uuid == handler.c->uuid)
 	{
-		handler->c->connectionID = clientId;
+		handler.c->connectionID = pack.clientId;
 		if(!settings["session"]["headless"].Bool())
-			GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler->screenType));
-		handler->state = EClientState::LOBBY;
-		return true;
+			GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
+		handler.state = EClientState::LOBBY;
 	}
-	return false;
-}
-
-void LobbyClientConnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
-{
 }
 
-bool LobbyClientDisconnected::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	if(clientId != c->connectionID)
-		return false;
+	if(pack.clientId != pack.c->connectionID)
+	{
+		result = false;
+		return;
+	}
 
-	handler->stopServerConnection();
-	return true;
+	handler.stopServerConnection();
 }
 
-void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
 	if(GH.listInt.size())
 		GH.popInts(1);
 }
 
-void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack)
 {
 	if(lobby && lobby->card)
 	{
-		lobby->card->chat->addNewMessage(playerName + ": " + message);
+		lobby->card->chat->addNewMessage(pack.playerName + ": " + pack.message);
 		lobby->card->setChat(true);
 		if(lobby->buttonChat)
 			lobby->buttonChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
 	}
 }
 
-void LobbyGuiAction::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack)
 {
-	if(!lobby || !handler->isGuest())
+	if(!lobby || !handler.isGuest())
 		return;
 
-	switch(action)
+	switch(pack.action)
 	{
-	case NO_TAB:
+	case LobbyGuiAction::NO_TAB:
 		lobby->toggleTab(lobby->curTab);
 		break;
-	case OPEN_OPTIONS:
+	case LobbyGuiAction::OPEN_OPTIONS:
 		lobby->toggleTab(lobby->tabOpt);
 		break;
-	case OPEN_SCENARIO_LIST:
+	case LobbyGuiAction::OPEN_SCENARIO_LIST:
 		lobby->toggleTab(lobby->tabSel);
 		break;
-	case OPEN_RANDOM_MAP_OPTIONS:
+	case LobbyGuiAction::OPEN_RANDOM_MAP_OPTIONS:
 		lobby->toggleTab(lobby->tabRand);
 		break;
 	}
 }
 
-bool LobbyEndGame::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
 {
-	if(handler->state == EClientState::GAMEPLAY)
+	if(handler.state == EClientState::GAMEPLAY)
 	{
-		handler->endGameplay(closeConnection, restart);
+		handler.endGameplay(pack.closeConnection, pack.restart);
 	}
 	
-	if(restart)
-		handler->sendStartGame();
-	
-	return true;
+	if(pack.restart)
+		handler.sendStartGame();
 }
 
-bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(clientId != -1 && clientId != handler->c->connectionID)
-		return false;
+	if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
+	{
+		result = false;
+		return;
+	}
 	
-	handler->state = EClientState::STARTING;
-	if(handler->si->mode != StartInfo::LOAD_GAME || clientId == handler->c->connectionID)
+	handler.state = EClientState::STARTING;
+	if(handler.si->mode != StartInfo::LOAD_GAME || pack.clientId == handler.c->connectionID)
 	{
-		auto modeBackup = handler->si->mode;
-		handler->si = initializedStartInfo;
-		handler->si->mode = modeBackup;
+		auto modeBackup = handler.si->mode;
+		handler.si = pack.initializedStartInfo;
+		handler.si->mode = modeBackup;
 	}
 	if(settings["session"]["headless"].Bool())
-		handler->startGameplay(initializedGameState);
-	return true;
+		handler.startGameplay(pack.initializedGameState);
 }
 
-void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(clientId != -1 && clientId != handler->c->connectionID)
+	if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
 		return;
 	
-	GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, handler, initializedGameState));
+	GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, &handler, pack.initializedGameState));
 }
 
-bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler)
+void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)
 {
-	hostChanged = state.hostClientId != handler->hostClientId;
-	static_cast<LobbyState &>(*handler) = state;
-	return true;
+	pack.hostChanged = pack.state.hostClientId != handler.hostClientId;
+	static_cast<LobbyState &>(handler) = pack.state;
 }
 
-void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState & pack)
 {
 	if(!lobby) //stub: ignore message for game mode
 		return;
 		
-	if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
+	if(!lobby->bonusSel && handler.si->campState && handler.state == EClientState::LOBBY_CAMPAIGN)
 	{
 		lobby->bonusSel = std::make_shared<CBonusSelection>();
 		GH.pushInt(lobby->bonusSel);
@@ -155,15 +153,15 @@ void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
 	else
 		lobby->updateAfterStateChange();
 
-	if(hostChanged)
-		lobby->toggleMode(handler->isHost());
+	if(pack.hostChanged)
+		lobby->toggleMode(handler.isHost());
 }
 
-void LobbyShowMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
+void ApplyOnLobbyScreenNetPackVisitor::visitLobbyShowMessage(LobbyShowMessage & pack)
 {
 	if(!lobby) //stub: ignore message for game mode
 		return;
 	
 	lobby->buttonStart->block(false);
-	handler->showServerError(message);
+	handler.showServerError(pack.message);
 }

+ 1 - 0
cmake_modules/VCMI_lib.cmake

@@ -439,6 +439,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/NetPacksBase.h
 		${MAIN_LIB_DIR}/NetPacks.h
 		${MAIN_LIB_DIR}/NetPacksLobby.h
+		${MAIN_LIB_DIR}/NetPackVisitor.h
 		${MAIN_LIB_DIR}/ObstacleHandler.h
 		${MAIN_LIB_DIR}/PathfinderUtil.h
 		${MAIN_LIB_DIR}/Point.h

+ 165 - 0
lib/NetPackVisitor.h

@@ -0,0 +1,165 @@
+/*
+ * CServerHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "NetPacks.h"
+#include "NetPacksLobby.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ICPackVisitor
+{
+public:
+	virtual bool callTyped() { return true; }
+
+	virtual void visitForLobby(CPackForLobby & pack) {}
+	virtual void visitForServer(CPackForServer & pack) {}
+	virtual void visitForClient(CPackForClient & pack) {}
+	virtual void visitPackageApplied(PackageApplied & pack) {}
+	virtual void visitSystemMessage(SystemMessage & pack) {}
+	virtual void visitPlayerBlocked(PlayerBlocked & pack) {}
+	virtual void visitPlayerCheated(PlayerCheated & pack) {}
+	virtual void visitYourTurn(YourTurn & pack) {}
+	virtual void visitEntitiesChanged(EntitiesChanged & pack) {}
+	virtual void visitSetResources(SetResources & pack) {}
+	virtual void visitSetPrimSkill(SetPrimSkill & pack) {}
+	virtual void visitSetSecSkill(SetSecSkill & pack) {}
+	virtual void visitHeroVisitCastle(HeroVisitCastle & pack) {}
+	virtual void visitChangeSpells(ChangeSpells & pack) {}
+	virtual void visitSetMana(SetMana & pack) {}
+	virtual void visitSetMovePoints(SetMovePoints & pack) {}
+	virtual void visitFoWChange(FoWChange & pack) {}
+	virtual void visitSetAvailableHeroes(SetAvailableHeroes & pack) {}
+	virtual void visitGiveBonus(GiveBonus & pack) {}
+	virtual void visitChangeObjPos(ChangeObjPos & pack) {}
+	virtual void visitPlayerEndsGame(PlayerEndsGame & pack) {}
+	virtual void visitPlayerReinitInterface(PlayerReinitInterface & pack) {}
+	virtual void visitRemoveBonus(RemoveBonus & pack) {}
+	virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {}
+	virtual void visitAddQuest(AddQuest & pack) {}
+	virtual void visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack) {}
+	virtual void visitUpdateMapEvents(UpdateMapEvents & pack) {}
+	virtual void visitUpdateCastleEvents(UpdateCastleEvents & pack) {}
+	virtual void visitChangeFormation(ChangeFormation & pack) {}
+	virtual void visitRemoveObject(RemoveObject & pack) {}
+	virtual void visitTryMoveHero(TryMoveHero & pack) {}
+	virtual void visitNewStructures(NewStructures & pack) {}
+	virtual void visitRazeStructures(RazeStructures & pack) {}
+	virtual void visitSetAvailableCreatures(SetAvailableCreatures & pack) {}
+	virtual void visitSetHeroesInTown(SetHeroesInTown & pack) {}
+	virtual void visitHeroRecruited(HeroRecruited & pack) {}
+	virtual void visitGiveHero(GiveHero & pack) {}
+	virtual void visitCatapultAttack(CatapultAttack & pack) {}
+	virtual void visitOpenWindow(OpenWindow & pack) {}
+	virtual void visitNewObject(NewObject & pack) {}
+	virtual void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) {}
+	virtual void visitNewArtifact(NewArtifact & pack) {}
+	virtual void visitChangeStackCount(ChangeStackCount & pack) {}
+	virtual void visitSetStackType(SetStackType & pack) {}
+	virtual void visitEraseStack(EraseStack & pack) {}
+	virtual void visitSwapStacks(SwapStacks & pack) {}
+	virtual void visitInsertNewStack(InsertNewStack & pack) {}
+	virtual void visitRebalanceStacks(RebalanceStacks & pack) {}
+	virtual void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) {}
+	virtual void visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack) {}
+	virtual void visitPutArtifact(PutArtifact & pack) {}
+	virtual void visitEraseArtifact(EraseArtifact & pack) {}
+	virtual void visitMoveArtifact(MoveArtifact & pack) {}
+	virtual void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) {}
+	virtual void visitAssembledArtifact(AssembledArtifact & pack) {}
+	virtual void visitDisassembledArtifact(DisassembledArtifact & pack) {}
+	virtual void visitHeroVisit(HeroVisit & pack) {}
+	virtual void visitNewTurn(NewTurn & pack) {}
+	virtual void visitInfoWindow(InfoWindow & pack) {}
+	virtual void visitSetObjectProperty(SetObjectProperty & pack) {}
+	virtual void visitChangeObjectVisitors(ChangeObjectVisitors & pack) {}
+	virtual void visitPrepareHeroLevelUp(PrepareHeroLevelUp & pack) {}
+	virtual void visitHeroLevelUp(HeroLevelUp & pack) {}
+	virtual void visitCommanderLevelUp(CommanderLevelUp & pack) {}
+	virtual void visitBlockingDialog(BlockingDialog & pack) {}
+	virtual void visitGarrisonDialog(GarrisonDialog & pack) {}
+	virtual void visitExchangeDialog(ExchangeDialog & pack) {}
+	virtual void visitTeleportDialog(TeleportDialog & pack) {}
+	virtual void visitMapObjectSelectDialog(MapObjectSelectDialog & pack) {}
+	virtual void visitBattleStart(BattleStart & pack) {}
+	virtual void visitBattleNextRound(BattleNextRound & pack) {}
+	virtual void visitBattleSetActiveStack(BattleSetActiveStack & pack) {}
+	virtual void visitBattleResult(BattleResult & pack) {}
+	virtual void visitBattleLogMessage(BattleLogMessage & pack) {}
+	virtual void visitBattleStackMoved(BattleStackMoved & pack) {}
+	virtual void visitBattleUnitsChanged(BattleUnitsChanged & pack) {}
+	virtual void visitBattleAttack(BattleAttack & pack) {}
+	virtual void visitStartAction(StartAction & pack) {}
+	virtual void visitEndAction(EndAction & pack) {}
+	virtual void visitBattleSpellCast(BattleSpellCast & pack) {}
+	virtual void visitSetStackEffect(SetStackEffect & pack) {}
+	virtual void visitStacksInjured(StacksInjured & pack) {}
+	virtual void visitBattleResultsApplied(BattleResultsApplied & pack) {}
+	virtual void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) {}
+	virtual void visitBattleSetStackProperty(BattleSetStackProperty & pack) {}
+	virtual void visitBattleTriggerEffect(BattleTriggerEffect & pack) {}
+	virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) {}
+	virtual void visitShowInInfobox(ShowInInfobox & pack) {}
+	virtual void visitAdvmapSpellCast(AdvmapSpellCast & pack) {}
+	virtual void visitShowWorldViewEx(ShowWorldViewEx & pack) {}
+	virtual void visitEndTurn(EndTurn & pack) {}
+	virtual void visitDismissHero(DismissHero & pack) {}
+	virtual void visitMoveHero(MoveHero & pack) {}
+	virtual void visitCastleTeleportHero(CastleTeleportHero & pack) {}
+	virtual void visitArrangeStacks(ArrangeStacks & pack) {}
+	virtual void visitBulkMoveArmy(BulkMoveArmy & pack) {}
+	virtual void visitBulkSplitStack(BulkSplitStack & pack) {}
+	virtual void visitBulkMergeStacks(BulkMergeStacks & pack) {}
+	virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) {}
+	virtual void visitDisbandCreature(DisbandCreature & pack) {}
+	virtual void visitBuildStructure(BuildStructure & pack) {}
+	virtual void visitRazeStructure(RazeStructure & pack) {}
+	virtual void visitRecruitCreatures(RecruitCreatures & pack) {}
+	virtual void visitUpgradeCreature(UpgradeCreature & pack) {}
+	virtual void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) {}
+	virtual void visitExchangeArtifacts(ExchangeArtifacts & pack) {}
+	virtual void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) {}
+	virtual void visitAssembleArtifacts(AssembleArtifacts & pack) {}
+	virtual void visitBuyArtifact(BuyArtifact & pack) {}
+	virtual void visitTradeOnMarketplace(TradeOnMarketplace & pack) {}
+	virtual void visitSetFormation(SetFormation & pack) {}
+	virtual void visitHireHero(HireHero & pack) {}
+	virtual void visitBuildBoat(BuildBoat & pack) {}
+	virtual void visitQueryReply(QueryReply & pack) {}
+	virtual void visitMakeAction(MakeAction & pack) {}
+	virtual void visitMakeCustomAction(MakeCustomAction & pack) {}
+	virtual void visitDigWithHero(DigWithHero & pack) {}
+	virtual void visitCastAdvSpell(CastAdvSpell & pack) {}
+	virtual void visitSaveGame(SaveGame & pack) {}
+	virtual void visitSaveGameClient(SaveGameClient & pack) {}
+	virtual void visitPlayerMessage(PlayerMessage & pack) {}
+	virtual void visitPlayerMessageClient(PlayerMessageClient & pack) {}
+	virtual void visitCenterView(CenterView & pack) {}
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) {}
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) {}
+	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) {}
+	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) {}
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) {}
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) {}
+	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) {}
+	virtual void visitLobbyUpdateState(LobbyUpdateState & pack) {}
+	virtual void visitLobbySetMap(LobbySetMap & pack) {}
+	virtual void visitLobbySetCampaign(LobbySetCampaign & pack) {}
+	virtual void visitLobbySetCampaignMap(LobbySetCampaignMap & pack) {}
+	virtual void visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack) {}
+	virtual void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) {}
+	virtual void visitLobbySetPlayer(LobbySetPlayer & pack) {}
+	virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) {}
+	virtual void visitLobbySetDifficulty(LobbySetDifficulty & pack) {}
+	virtual void visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) {}
+	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) {}
+};
+
+VCMI_LIB_NAMESPACE_END

文件差异内容过多而无法显示
+ 234 - 191
lib/NetPacks.h


+ 30 - 20
lib/NetPacksBase.h

@@ -17,6 +17,9 @@
 
 class CClient;
 class CGameHandler;
+class CLobbyScreen;
+class CServerHandler;
+class CVCMIServer;
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -30,6 +33,8 @@ class CArtifactSet;
 class CBonusSystemNode;
 struct ArtSlotInfo;
 
+class ICPackVisitor;
+
 struct DLL_LINKAGE CPack
 {
 	std::shared_ptr<CConnection> c; // Pointer to connection that pack received from
@@ -45,27 +50,31 @@ struct DLL_LINKAGE CPack
 
 	void applyGs(CGameState * gs)
 	{}
+
+	void visit(ICPackVisitor & cpackVisitor);
+
+protected:
+	/// <summary>
+	/// For basic types of netpacks hierarchy like CPackForClient. Called first.
+	/// </summary>
+	virtual void visitBasic(ICPackVisitor & cpackVisitor);
+
+	/// <summary>
+	/// For leaf types of netpacks hierarchy. Called after visitBasic.
+	/// </summary>
+	virtual void visitTyped(ICPackVisitor & cpackVisitor);
 };
 
-struct CPackForClient : public CPack
+struct DLL_LINKAGE CPackForClient : public CPack
 {
-	CGameState* GS(CClient *cl);
-	void applyFirstCl(CClient *cl)//called before applying to gs
-	{}
-	void applyCl(CClient *cl)//called after applying to gs
-	{}
+protected:
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
 };
 
-struct CPackForServer : public CPack
+struct DLL_LINKAGE CPackForServer : public CPack
 {
 	mutable PlayerColor player = PlayerColor::NEUTRAL;
 	mutable si32 requestID;
-	CGameState * GS(CGameHandler * gh);
-	bool applyGh(CGameHandler *gh) //called after applying to gs
-	{
-		logGlobal->error("Should not happen... applying plain CPackForServer");
-		return false;
-	}
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -74,14 +83,15 @@ struct CPackForServer : public CPack
 	}
 
 protected:
-	void throwNotAllowedAction();
-	void throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id);
-	void throwOnWrongPlayer(CGameHandler * gh, PlayerColor player);
-	void throwAndComplain(CGameHandler * gh, std::string txt);
-	bool isPlayerOwns(CGameHandler * gh, ObjectInstanceID id);
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
+};
 
-private:
-	void wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedplayer);
+struct DLL_LINKAGE CPackForLobby : public CPack
+{
+	virtual bool isForServer() const;
+
+protected:
+	virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
 };
 
 struct DLL_LINKAGE MetaString

文件差异内容过多而无法显示
+ 761 - 32
lib/NetPacksLib.cpp


+ 42 - 85
lib/NetPacksLobby.h

@@ -13,7 +13,6 @@
 
 #include "StartInfo.h"
 
-class CLobbyScreen;
 class CServerHandler;
 class CVCMIServer;
 
@@ -25,40 +24,16 @@ struct StartInfo;
 class CMapGenOptions;
 struct ClientPlayer;
 
-struct CPackForLobby : public CPack
+struct DLL_LINKAGE CLobbyPackToPropagate : public CPackForLobby
 {
-	bool checkClientPermissions(CVCMIServer * srv) const
-	{
-		return false;
-	}
-
-	bool applyOnServer(CVCMIServer * srv)
-	{
-		return true;
-	}
-
-	void applyOnServerAfterAnnounce(CVCMIServer * srv) {}
-
-	bool applyOnLobbyHandler(CServerHandler * handler)
-	{
-		return true;
-	}
-
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) {}
-};
-
-struct CLobbyPackToPropagate : public CPackForLobby
-{
-
 };
 
-struct CLobbyPackToServer : public CPackForLobby
+struct DLL_LINKAGE CLobbyPackToServer : public CPackForLobby
 {
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
+	virtual bool isForServer() const override;
 };
 
-struct LobbyClientConnected : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyClientConnected : public CLobbyPackToPropagate
 {
 	// Set by client before sending pack to server
 	std::string uuid;
@@ -68,11 +43,7 @@ struct LobbyClientConnected : public CLobbyPackToPropagate
 	int clientId = -1;
 	int hostClientId = -1;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -85,16 +56,13 @@ struct LobbyClientConnected : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyClientDisconnected : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyClientDisconnected : public CLobbyPackToPropagate
 {
 	int clientId;
 	bool shutdownServer = false;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -103,12 +71,11 @@ struct LobbyClientDisconnected : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyChatMessage : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyChatMessage : public CLobbyPackToPropagate
 {
 	std::string playerName, message;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -117,14 +84,14 @@ struct LobbyChatMessage : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyGuiAction : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyGuiAction : public CLobbyPackToPropagate
 {
 	enum EAction : ui8 {
 		NONE, NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS
 	} action = NONE;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -132,14 +99,11 @@ struct LobbyGuiAction : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyEndGame : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyEndGame : public CLobbyPackToPropagate
 {
 	bool closeConnection = false, restart = false;
 	
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 	
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -148,18 +112,14 @@ struct LobbyEndGame : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyStartGame : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyStartGame : public CLobbyPackToPropagate
 {
 	// Set by server
 	std::shared_ptr<StartInfo> initializedStartInfo = nullptr;
 	CGameState * initializedGameState = nullptr;
 	int clientId = -1; //-1 means to all clients
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	void applyOnServerAfterAnnounce(CVCMIServer * srv);
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -172,13 +132,11 @@ struct LobbyStartGame : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyChangeHost : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyChangeHost : public CLobbyPackToPropagate
 {
 	int newHostConnectionId = -1;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
-	bool applyOnServerAfterAnnounce(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -186,13 +144,12 @@ struct LobbyChangeHost : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbyUpdateState : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyUpdateState : public CLobbyPackToPropagate
 {
 	LobbyState state;
 	bool hostChanged = false; // Used on client-side only
 
-	bool applyOnLobbyHandler(CServerHandler * handler);
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -200,13 +157,14 @@ struct LobbyUpdateState : public CLobbyPackToPropagate
 	}
 };
 
-struct LobbySetMap : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetMap : public CLobbyPackToServer
 {
 	std::shared_ptr<CMapInfo> mapInfo;
 	std::shared_ptr<CMapGenOptions> mapGenOpts;
 
 	LobbySetMap() : mapInfo(nullptr), mapGenOpts(nullptr) {}
-	bool applyOnServer(CVCMIServer * srv);
+
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -215,11 +173,11 @@ struct LobbySetMap : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaign : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaign : public CLobbyPackToServer
 {
 	std::shared_ptr<CCampaignState> ourCampaign;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -227,11 +185,11 @@ struct LobbySetCampaign : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaignMap : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaignMap : public CLobbyPackToServer
 {
 	int mapId = -1;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -239,11 +197,11 @@ struct LobbySetCampaignMap : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetCampaignBonus : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetCampaignBonus : public CLobbyPackToServer
 {
 	int bonusId = -1;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -251,15 +209,14 @@ struct LobbySetCampaignBonus : public CLobbyPackToServer
 	}
 };
 
-struct LobbyChangePlayerOption : public CLobbyPackToServer
+struct DLL_LINKAGE LobbyChangePlayerOption : public CLobbyPackToServer
 {
 	enum EWhat : ui8 {UNKNOWN, TOWN, HERO, BONUS};
 	ui8 what = UNKNOWN;
 	si8 direction = 0; //-1 or +1
 	PlayerColor color = PlayerColor::CANNOT_DETERMINE;
 
-	bool checkClientPermissions(CVCMIServer * srv) const;
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -269,11 +226,11 @@ struct LobbyChangePlayerOption : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetPlayer : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetPlayer : public CLobbyPackToServer
 {
 	PlayerColor clickedColor = PlayerColor::CANNOT_DETERMINE;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -281,11 +238,11 @@ struct LobbySetPlayer : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetTurnTime : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetTurnTime : public CLobbyPackToServer
 {
 	ui8 turnTime = 0;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -293,11 +250,11 @@ struct LobbySetTurnTime : public CLobbyPackToServer
 	}
 };
 
-struct LobbySetDifficulty : public CLobbyPackToServer
+struct DLL_LINKAGE LobbySetDifficulty : public CLobbyPackToServer
 {
 	ui8 difficulty = 0;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -305,12 +262,12 @@ struct LobbySetDifficulty : public CLobbyPackToServer
 	}
 };
 
-struct LobbyForceSetPlayer : public CLobbyPackToServer
+struct DLL_LINKAGE LobbyForceSetPlayer : public CLobbyPackToServer
 {
 	ui8 targetConnectedPlayer = -1;
 	PlayerColor targetPlayerColor = PlayerColor::CANNOT_DETERMINE;
 
-	bool applyOnServer(CVCMIServer * srv);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -319,11 +276,11 @@ struct LobbyForceSetPlayer : public CLobbyPackToServer
 	}
 };
 
-struct LobbyShowMessage : public CLobbyPackToPropagate
+struct DLL_LINKAGE LobbyShowMessage : public CLobbyPackToPropagate
 {
 	std::string message;
 	
-	void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler);
+	virtual void visitTyped(ICPackVisitor & visitor) override;
 	
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{

+ 62 - 6
server/CGameHandler.cpp

@@ -30,7 +30,7 @@
 #include "../lib/CStack.h"
 #include "../lib/battle/BattleInfo.h"
 #include "../lib/CondSh.h"
-#include "../lib/NetPacks.h"
+#include "ServerNetPackVisitors.h"
 #include "../lib/VCMI_Lib.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapService.h"
@@ -196,7 +196,7 @@ template <typename T> class CApplyOnGH;
 class CBaseForGHApply
 {
 public:
-	virtual bool applyOnGH(CGameHandler * gh, void * pack) const =0;
+	virtual bool applyOnGH(CGameHandler * gh, CGameState * gs, void * pack) const =0;
 	virtual ~CBaseForGHApply(){}
 	template<typename U> static CBaseForGHApply *getApplier(const U * t=nullptr)
 	{
@@ -207,12 +207,16 @@ public:
 template <typename T> class CApplyOnGH : public CBaseForGHApply
 {
 public:
-	bool applyOnGH(CGameHandler * gh, void * pack) const override
+	bool applyOnGH(CGameHandler * gh, CGameState * gs, void * pack) const override
 	{
 		T *ptr = static_cast<T*>(pack);
 		try
 		{
-			return ptr->applyGh(gh);
+			ApplyGhNetPackVisitor applier(*gh, *gs);
+
+			ptr->visit(applier);
+
+			return applier.getResult();
 		}
 		catch(ExceptionNotAllowedAction & e)
 		{
@@ -230,7 +234,7 @@ template <>
 class CApplyOnGH<CPack> : public CBaseForGHApply
 {
 public:
-	bool applyOnGH(CGameHandler * gh, void * pack) const override
+	bool applyOnGH(CGameHandler * gh, CGameState * gs, void * pack) const override
 	{
 		logGlobal->error("Cannot apply on GH plain CPack!");
 		assert(0);
@@ -1358,7 +1362,7 @@ void CGameHandler::handleReceivedPack(CPackForServer * pack)
 	}
 	else if(apply)
 	{
-		const bool result = apply->applyOnGH(this, pack);
+		const bool result = apply->applyOnGH(this, this->gs, pack);
 		if(result)
 			logGlobal->trace("Message %s successfully applied!", typeid(*pack).name());
 		else
@@ -2933,6 +2937,58 @@ void CGameHandler::sendAndApply(NewStructures * pack)
 	checkVictoryLossConditionsForPlayer(getTown(pack->tid)->tempOwner);
 }
 
+bool CGameHandler::isPlayerOwns(CPackForServer * pack, ObjectInstanceID id)
+{
+	return getPlayerAt(pack->c) == getOwner(id);
+}
+
+void CGameHandler::throwNotAllowedAction(CPackForServer * pack)
+{
+	if(pack->c)
+	{
+		SystemMessage temp_message("You are not allowed to perform this action!");
+		pack->c->sendPack(&temp_message);
+	}
+	logNetwork->error("Player is not allowed to perform this action!");
+	throw ExceptionNotAllowedAction();
+}
+
+void CGameHandler::wrongPlayerMessage(CPackForServer * pack, PlayerColor expectedplayer)
+{
+	std::ostringstream oss;
+	oss << "You were identified as player " << getPlayerAt(pack->c) << " while expecting " << expectedplayer;
+	logNetwork->error(oss.str());
+	if(pack->c)
+	{
+		SystemMessage temp_message(oss.str());
+		pack->c->sendPack(&temp_message);
+	}
+}
+
+void CGameHandler::throwOnWrongOwner(CPackForServer * pack, ObjectInstanceID id)
+{
+	if(!isPlayerOwns(pack, id))
+	{
+		wrongPlayerMessage(pack, getOwner(id));
+		throwNotAllowedAction(pack);
+	}
+}
+
+void CGameHandler::throwOnWrongPlayer(CPackForServer * pack, PlayerColor player)
+{
+	if(!hasPlayerAt(player, pack->c) && player != getPlayerAt(pack->c))
+	{
+		wrongPlayerMessage(pack, player);
+		throwNotAllowedAction(pack);
+	}
+}
+
+void CGameHandler::throwAndComplain(CPackForServer * pack, std::string txt)
+{
+	complain(txt);
+	throwNotAllowedAction(pack);
+}
+
 void CGameHandler::save(const std::string & filename)
 {
 	logGlobal->info("Saving to %s", filename);

+ 7 - 0
server/CGameHandler.h

@@ -303,6 +303,13 @@ public:
 	void sendAndApply(SetResources * pack);
 	void sendAndApply(NewStructures * pack);
 
+	void wrongPlayerMessage(CPackForServer * pack, PlayerColor expectedplayer);
+	void throwNotAllowedAction(CPackForServer * pack);
+	void throwOnWrongOwner(CPackForServer * pack, ObjectInstanceID id);
+	void throwOnWrongPlayer(CPackForServer * pack, PlayerColor player);
+	void throwAndComplain(CPackForServer * pack, std::string txt);
+	bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id);
+
 	struct FinishingBattleHelper
 	{
 		FinishingBattleHelper();

+ 2 - 0
server/CMakeLists.txt

@@ -14,6 +14,8 @@ set(server_HEADERS
 		CGameHandler.h
 		CQuery.h
 		CVCMIServer.h
+		LobbyNetPackVisitors.h
+		ServerNetPackVisitors.h
 )
 
 assign_source_group(${server_SRCS} ${server_HEADERS})

+ 45 - 12
server/CVCMIServer.cpp

@@ -27,6 +27,8 @@
 #include "../lib/StartInfo.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/rmg/CMapGenOptions.h"
+#include "../lib/NetPackVisitor.h"
+#include "LobbyNetPackVisitors.h"
 #ifdef VCMI_ANDROID
 #include <jni.h>
 #include <android/log.h>
@@ -81,10 +83,17 @@ public:
 	bool applyOnServerBefore(CVCMIServer * srv, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		if(ptr->checkClientPermissions(srv))
+		ClientPermissionsCheckerNetPackVisitor checker(*srv);
+		ptr->visit(checker);
+
+		if(checker.getResult())
 		{
 			boost::unique_lock<boost::mutex> stateLock(srv->stateMutex);
-			return ptr->applyOnServer(srv);
+			ApplyOnServerNetPackVisitor applier(*srv);
+			
+			ptr->visit(applier);
+
+			return applier.getResult();
 		}
 		else
 			return false;
@@ -93,7 +102,8 @@ public:
 	void applyOnServerAfter(CVCMIServer * srv, void * pack) const override
 	{
 		T * ptr = static_cast<T *>(pack);
-		ptr->applyOnServerAfterAnnounce(srv);
+		ApplyOnServerAfterAnnounceNetPackVisitor applier(*srv);
+		ptr->visit(applier);
 	}
 };
 
@@ -363,6 +373,35 @@ void CVCMIServer::connectionAccepted(const boost::system::error_code & ec)
 	startAsyncAccept();
 }
 
+class CVCMIServerPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CVCMIServer & handler;
+	CGameHandler & gh;
+
+public:
+	CVCMIServerPackVisitor(CVCMIServer & handler, CGameHandler & gh)
+			:handler(handler), gh(gh)
+	{
+	}
+
+	virtual bool callTyped() override { return false; }
+
+	virtual void visitForLobby(CPackForLobby & packForLobby) override
+	{
+		handler.handleReceivedPack(std::unique_ptr<CPackForLobby>(&packForLobby));
+	}
+
+	virtual void visitForServer(CPackForServer & serverPack) override
+	{
+		gh.handleReceivedPack(&serverPack);
+	}
+
+	virtual void visitForClient(CPackForClient & clientPack) override
+	{
+	}
+};
+
 void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
 {
 	setThreadName("CVCMIServer::handleConnection");
@@ -394,15 +433,9 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
 				}
 				break;
 			}
-			
-			if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
-			{
-				handleReceivedPack(std::unique_ptr<CPackForLobby>(lobbyPack));
-			}
-			else if(auto serverPack = dynamic_ptr_cast<CPackForServer>(pack))
-			{
-				gh->handleReceivedPack(serverPack);
-			}
+
+			CVCMIServerPackVisitor visitor(*this, *this->gh);
+			pack->visit(visitor);
 		}
 #ifndef _MSC_VER
 	 }

+ 92 - 0
server/LobbyNetPackVisitors.h

@@ -0,0 +1,92 @@
+/*
+ * LobbyNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class ClientPermissionsCheckerNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	bool result;
+	CVCMIServer & srv;
+
+public:
+	ClientPermissionsCheckerNetPackVisitor(CVCMIServer & srv)
+		:srv(srv), result(false)
+	{
+	}
+
+	bool getResult() const
+	{
+		return result;
+	}
+
+	virtual void visitForLobby(CPackForLobby & pack) override;
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) override;
+	virtual void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) override;
+	virtual void visitLobbyChatMessage(LobbyChatMessage & pack) override;
+	virtual void visitLobbyGuiAction(LobbyGuiAction & pack) override;
+};
+
+class ApplyOnServerAfterAnnounceNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	CVCMIServer & srv;
+
+public:
+	ApplyOnServerAfterAnnounceNetPackVisitor(CVCMIServer & srv)
+		:srv(srv)
+	{
+	}
+
+	virtual void visitForLobby(CPackForLobby & pack) override;
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) override;
+};
+
+class ApplyOnServerNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	bool result;
+	CVCMIServer & srv;
+
+public:
+	ApplyOnServerNetPackVisitor(CVCMIServer & srv)
+		:srv(srv), result(true)
+	{
+	}
+
+	bool getResult() const
+	{
+		return result;
+	}
+
+	virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
+	virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
+	virtual void visitLobbySetMap(LobbySetMap & pack) override;
+	virtual void visitLobbySetCampaign(LobbySetCampaign & pack) override;
+	virtual void visitLobbySetCampaignMap(LobbySetCampaignMap & pack) override;
+	virtual void visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack) override;
+	virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
+	virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
+	virtual void visitLobbyChangeHost(LobbyChangeHost & pack) override;
+	virtual void visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) override;
+	virtual void visitLobbySetPlayer(LobbySetPlayer & pack) override;
+	virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) override;
+	virtual void visitLobbySetDifficulty(LobbySetDifficulty & pack) override;
+	virtual void visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) override;
+};

+ 199 - 148
server/NetPacksLobbyServer.cpp

@@ -8,6 +8,7 @@
  *
  */
 #include "StdInc.h"
+#include "LobbyNetPackVisitors.h"
 
 #include "CVCMIServer.h"
 #include "CGameHandler.h"
@@ -21,328 +22,378 @@
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMapInfo.h"
 
-bool CLobbyPackToServer::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack)
 {
-	return srv->isClientHost(c->connectionID);
+	if(pack.isForServer())
+	{
+		result = srv.isClientHost(pack.c->connectionID);
+	}
 }
 
-void CLobbyPackToServer::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitForLobby(CPackForLobby & pack)
 {
 	// Propogate options after every CLobbyPackToServer
-	srv->updateAndPropagateLobbyState();
+	if(pack.isForServer())
+	{
+		srv.updateAndPropagateLobbyState();
+	}
 }
 
-bool LobbyClientConnected::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
 {
-	if(srv->gh)
+	if(srv.gh)
 	{
-		for(auto & connection : srv->hangingConnections)
+		for(auto & connection : srv.hangingConnections)
 		{
-			if(connection->uuid == uuid)
+			if(connection->uuid == pack.uuid)
 			{
-				return true;
+				{
+				result = true;
+				return;
+				}
 			}
 		}
 	}
 	
-	if(srv->state == EServerState::LOBBY)
-		return true;
+	if(srv.state == EServerState::LOBBY)
+		{
+		result = true;
+		return;
+		}
 	
 	//disconnect immediately and ignore this client
-	srv->connections.erase(c);
-	if(c && c->isOpen())
+	srv.connections.erase(pack.c);
+	if(pack.c && pack.c->isOpen())
+	{
+		pack.c->close();
+		pack.c->connected = false;
+	}
 	{
-		c->close();
-		c->connected = false;
+	result = false;
+	return;
 	}
-	return false;
 }
 
-bool LobbyClientConnected::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
 {
-	if(srv->gh)
+	if(srv.gh)
 	{
-		for(auto & connection : srv->hangingConnections)
+		for(auto & connection : srv.hangingConnections)
 		{
-			if(connection->uuid == uuid)
+			if(connection->uuid == pack.uuid)
 			{
 				logNetwork->info("Reconnection player");
-				c->connectionID = connection->connectionID;
-				for(auto & playerConnection : srv->gh->connections)
+				pack.c->connectionID = connection->connectionID;
+				for(auto & playerConnection : srv.gh->connections)
 				{
 					for(auto & existingConnection : playerConnection.second)
 					{
 						if(existingConnection == connection)
 						{
 							playerConnection.second.erase(existingConnection);
-							playerConnection.second.insert(c);
+							playerConnection.second.insert(pack.c);
 							break;
 						}
 					}
 				}
-				srv->hangingConnections.erase(connection);
+				srv.hangingConnections.erase(connection);
 				break;
 			}
 		}
 	}
 	
-	srv->clientConnected(c, names, uuid, mode);
+	srv.clientConnected(pack.c, pack.names, pack.uuid, pack.mode);
 	// Server need to pass some data to newly connected client
-	clientId = c->connectionID;
-	mode = srv->si->mode;
-	hostClientId = srv->hostClientId;
-	return true;
+	pack.clientId = pack.c->connectionID;
+	pack.mode = srv.si->mode;
+	pack.hostClientId = srv.hostClientId;
+
+	result = true;
 }
 
-void LobbyClientConnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack)
 {
 	// FIXME: we need to avoid senting something to client that not yet get answer for LobbyClientConnected
 	// Until UUID set we only pass LobbyClientConnected to this client
-	c->uuid = uuid;
-	srv->updateAndPropagateLobbyState();
-	if(srv->state == EServerState::GAMEPLAY)
+	pack.c->uuid = pack.uuid;
+	srv.updateAndPropagateLobbyState();
+	if(srv.state == EServerState::GAMEPLAY)
 	{
 		//immediately start game
 		std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame);
-		startGameForReconnectedPlayer->initializedStartInfo = srv->si;
-		startGameForReconnectedPlayer->initializedGameState = srv->gh->gameState();
-		startGameForReconnectedPlayer->clientId = c->connectionID;
-		srv->addToAnnounceQueue(std::move(startGameForReconnectedPlayer));
+		startGameForReconnectedPlayer->initializedStartInfo = srv.si;
+		startGameForReconnectedPlayer->initializedGameState = srv.gh->gameState();
+		startGameForReconnectedPlayer->clientId = pack.c->connectionID;
+		srv.addToAnnounceQueue(std::move(startGameForReconnectedPlayer));
 	}
 }
 
-bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	if(clientId != c->connectionID)
-		return false;
+	if(pack.clientId != pack.c->connectionID)
+	{
+		result = false;
+		return;
+	}
 
-	if(shutdownServer)
+	if(pack.shutdownServer)
 	{
-		if(!srv->cmdLineOptions.count("run-by-client"))
-			return false;
+		if(!srv.cmdLineOptions.count("run-by-client"))
+		{
+			result = false;
+			return;
+		}
 
-		if(c->uuid != srv->cmdLineOptions["uuid"].as<std::string>())
-			return false;
+		if(pack.c->uuid != srv.cmdLineOptions["uuid"].as<std::string>())
+		{
+			result = false;
+			return;
+		}
 	}
 
-	return true;
+	result = true;
 }
 
-bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	srv->clientDisconnected(c);
-	c->close();
-	c->connected = false;
-	return true;
+	srv.clientDisconnected(pack.c);
+	pack.c->close();
+	pack.c->connected = false;
+
+	result = true;
 }
 
-void LobbyClientDisconnected::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
 {
-	if(c && c->isOpen())
+	if(pack.c && pack.c->isOpen())
 	{
-		boost::unique_lock<boost::mutex> lock(*c->mutexWrite);
-		c->close();
-		c->connected = false;
+		boost::unique_lock<boost::mutex> lock(*pack.c->mutexWrite);
+		pack.c->close();
+		pack.c->connected = false;
 	}
 
-	if(shutdownServer)
+	if(pack.shutdownServer)
 	{
 		logNetwork->info("Client requested shutdown, server will close itself...");
-		srv->state = EServerState::SHUTDOWN;
+		srv.state = EServerState::SHUTDOWN;
 		return;
 	}
-	else if(srv->connections.empty())
+	else if(srv.connections.empty())
 	{
 		logNetwork->error("Last connection lost, server will close itself...");
-		srv->state = EServerState::SHUTDOWN;
+		srv.state = EServerState::SHUTDOWN;
 	}
-	else if(c == srv->hostClient)
+	else if(pack.c == srv.hostClient)
 	{
 		auto ph = std::make_unique<LobbyChangeHost>();
-		auto newHost = *RandomGeneratorUtil::nextItem(srv->connections, CRandomGenerator::getDefault());
+		auto newHost = *RandomGeneratorUtil::nextItem(srv.connections, CRandomGenerator::getDefault());
 		ph->newHostConnectionId = newHost->connectionID;
-		srv->addToAnnounceQueue(std::move(ph));
+		srv.addToAnnounceQueue(std::move(ph));
 	}
-	srv->updateAndPropagateLobbyState();
+	srv.updateAndPropagateLobbyState();
 }
 
-bool LobbyChatMessage::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack)
 {
-	return true;
+	result = true;
 }
 
-bool LobbySetMap::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
 {
-	if(srv->state != EServerState::LOBBY)
-		return false;
+	if(srv.state != EServerState::LOBBY)
+	{
+		result = false;
+		return;
+	}
+
+	srv.updateStartInfoOnMapChange(pack.mapInfo, pack.mapGenOpts);
 
-	srv->updateStartInfoOnMapChange(mapInfo, mapGenOpts);
-	return true;
+	result = true;
 }
 
-bool LobbySetCampaign::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack)
 {
-	srv->si->mapname = ourCampaign->camp->header.filename;
-	srv->si->mode = StartInfo::CAMPAIGN;
-	srv->si->campState = ourCampaign;
-	srv->si->turnTime = 0;
-	bool isCurrentMapConquerable = ourCampaign->currentMap && ourCampaign->camp->conquerable(*ourCampaign->currentMap);
-	for(int i = 0; i < ourCampaign->camp->scenarios.size(); i++)
+	srv.si->mapname = pack.ourCampaign->camp->header.filename;
+	srv.si->mode = StartInfo::CAMPAIGN;
+	srv.si->campState = pack.ourCampaign;
+	srv.si->turnTime = 0;
+	bool isCurrentMapConquerable = pack.ourCampaign->currentMap && pack.ourCampaign->camp->conquerable(*pack.ourCampaign->currentMap);
+	for(int i = 0; i < pack.ourCampaign->camp->scenarios.size(); i++)
 	{
-		if(ourCampaign->camp->conquerable(i))
+		if(pack.ourCampaign->camp->conquerable(i))
 		{
-			if(!isCurrentMapConquerable || (isCurrentMapConquerable && i == *ourCampaign->currentMap))
+			if(!isCurrentMapConquerable || (isCurrentMapConquerable && i == *pack.ourCampaign->currentMap))
 			{
-				srv->setCampaignMap(i);
+				srv.setCampaignMap(i);
 			}
 		}
 	}
-	return true;
+	
+	result = true;
 }
 
-bool LobbySetCampaignMap::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetCampaignMap(LobbySetCampaignMap & pack)
 {
-	srv->setCampaignMap(mapId);
-	return true;
+	srv.setCampaignMap(pack.mapId);
+
+	result = true;
 }
 
-bool LobbySetCampaignBonus::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack)
 {
-	srv->setCampaignBonus(bonusId);
-	return true;
+	srv.setCampaignBonus(pack.bonusId);
+
+	result = true;
 }
 
-bool LobbyGuiAction::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack)
 {
-	return srv->isClientHost(c->connectionID);
+	result = srv.isClientHost(pack.c->connectionID);
 }
 
-bool LobbyEndGame::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
 {
-	return srv->isClientHost(c->connectionID);
+	result = srv.isClientHost(pack.c->connectionID);
 }
 
-bool LobbyEndGame::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
 {
-	srv->prepareToRestart();
-	return true;
+	srv.prepareToRestart();
+
+	result = true;
 }
 
-void LobbyEndGame::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
 {
-	boost::unique_lock<boost::mutex> stateLock(srv->stateMutex);
-	for(auto & c : srv->connections)
+	boost::unique_lock<boost::mutex> stateLock(srv.stateMutex);
+	for(auto & c : srv.connections)
 	{
 		c->enterLobbyConnectionMode();
 		c->disableStackSendingByID();
 	}
 }
 
-bool LobbyStartGame::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	return srv->isClientHost(c->connectionID);
+	result = srv.isClientHost(pack.c->connectionID);
 }
 
-bool LobbyStartGame::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
 	try
 	{
-		srv->verifyStateBeforeStart(true);
+		srv.verifyStateBeforeStart(true);
 	}
 	catch(...)
 	{
-		return false;
+		result = false;
+		return;
 	}
 	// Server will prepare gamestate and we announce StartInfo to clients
-	if(!srv->prepareToStartGame())
-		return false;
+	if(!srv.prepareToStartGame())
+	{
+		result = false;
+		return;
+	}
 	
-	initializedStartInfo = std::make_shared<StartInfo>(*srv->gh->getStartInfo(true));
-	initializedGameState = srv->gh->gameState();
+	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true));
+	pack.initializedGameState = srv.gh->gameState();
 
-	return true;
+	result = true;
 }
 
-void LobbyStartGame::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
 {
-	if(clientId == -1) //do not restart game for single client only
-		srv->startGameImmidiately();
+	if(pack.clientId == -1) //do not restart game for single client only
+		srv.startGameImmidiately();
 	else
 	{
-		for(auto & c : srv->connections)
+		for(auto & c : srv.connections)
 		{
-			if(c->connectionID == clientId)
+			if(c->connectionID == pack.clientId)
 			{
-				c->enterGameplayConnectionMode(srv->gh->gameState());
-				srv->reconnectPlayer(clientId);
+				c->enterGameplayConnectionMode(srv.gh->gameState());
+				srv.reconnectPlayer(pack.clientId);
 			}
 		}
 	}
 }
 
-bool LobbyChangeHost::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
 {
-	return srv->isClientHost(c->connectionID);
+	result = srv.isClientHost(pack.c->connectionID);
 }
 
-bool LobbyChangeHost::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
 {
-	return true;
+	result = true;
 }
 
-bool LobbyChangeHost::applyOnServerAfterAnnounce(CVCMIServer * srv)
+void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack)
 {
-	return srv->passHost(newHostConnectionId);
+	auto result = srv.passHost(pack.newHostConnectionId);
+	
+	if(!result)
+	{
+		logGlobal->error("passHost returned false. What does it mean?");
+	}
 }
 
-bool LobbyChangePlayerOption::checkClientPermissions(CVCMIServer * srv) const
+void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack)
 {
-	if(srv->isClientHost(c->connectionID))
-		return true;
+	if(srv.isClientHost(pack.c->connectionID))
+	{
+		result = true;
+		return;
+	}
 
-	if(vstd::contains(srv->getAllClientPlayers(c->connectionID), color))
-		return true;
+	if(vstd::contains(srv.getAllClientPlayers(pack.c->connectionID), pack.color))
+	{
+		result = true;
+		return;
+	}
 
-	return false;
+	result = false;
 }
 
-bool LobbyChangePlayerOption::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack)
 {
-	switch(what)
+	switch(pack.what)
 	{
-	case TOWN:
-		srv->optionNextCastle(color, direction);
+	case LobbyChangePlayerOption::TOWN:
+		srv.optionNextCastle(pack.color, pack.direction);
 		break;
-	case HERO:
-		srv->optionNextHero(color, direction);
+	case LobbyChangePlayerOption::HERO:
+		srv.optionNextHero(pack.color, pack.direction);
 		break;
-	case BONUS:
-		srv->optionNextBonus(color, direction);
+	case LobbyChangePlayerOption::BONUS:
+		srv.optionNextBonus(pack.color, pack.direction);
 		break;
 	}
-	return true;
+	
+	result = true;
 }
 
-bool LobbySetPlayer::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetPlayer(LobbySetPlayer & pack)
 {
-	srv->setPlayer(clickedColor);
-	return true;
+	srv.setPlayer(pack.clickedColor);
+	result = true;
 }
 
-bool LobbySetTurnTime::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetTurnTime(LobbySetTurnTime & pack)
 {
-	srv->si->turnTime = turnTime;
-	return true;
+	srv.si->turnTime = pack.turnTime;
+	result = true;
 }
 
-bool LobbySetDifficulty::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbySetDifficulty(LobbySetDifficulty & pack)
 {
-	srv->si->difficulty = vstd::abetween(difficulty, 0, 4);
-	return true;
+	srv.si->difficulty = vstd::abetween(pack.difficulty, 0, 4);
+	result = true;
 }
 
-bool LobbyForceSetPlayer::applyOnServer(CVCMIServer * srv)
+void ApplyOnServerNetPackVisitor::visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack)
 {
-	srv->si->playerInfos[targetPlayerColor].connectedPlayerIDs.insert(targetConnectedPlayer);
-	return true;
+	srv.si->playerInfos[pack.targetPlayerColor].connectedPlayerIDs.insert(pack.targetConnectedPlayer);
+	result = true;
 }

+ 164 - 218
server/NetPacksServer.cpp

@@ -8,7 +8,7 @@
  *
  */
 #include "StdInc.h"
-#include "../lib/NetPacks.h"
+#include "ServerNetPackVisitors.h"
 
 #include "CGameHandler.h"
 #include "../lib/IGameCallback.h"
@@ -22,384 +22,330 @@
 #include "../lib/spells/ISpellMechanics.h"
 #include "../lib/serializer/Cast.h"
 
-bool CPackForServer::isPlayerOwns(CGameHandler * gh, ObjectInstanceID id)
+void ApplyGhNetPackVisitor::visitSaveGame(SaveGame & pack)
 {
-	return gh->getPlayerAt(c) == gh->getOwner(id);
+	gh.save(pack.fname);
+	logGlobal->info("Game has been saved as %s", pack.fname);
+	result = true;
 }
 
-void CPackForServer::throwNotAllowedAction()
+void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack)
 {
-	if(c)
+	PlayerColor currentPlayer = gs.currentPlayer;
+	if(pack.player != currentPlayer)
 	{
-		SystemMessage temp_message("You are not allowed to perform this action!");
-		c->sendPack(&temp_message);
-	}
-	logNetwork->error("Player is not allowed to perform this action!");
-	throw ExceptionNotAllowedAction();
-}
+		if(gh.getPlayerStatus(pack.player) == EPlayerStatus::INGAME)
+			gh.throwAndComplain(&pack, "pack.player attempted to end turn for another pack.player!");
 
-void CPackForServer::wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedplayer)
-{
-	std::ostringstream oss;
-	oss << "You were identified as player " << gh->getPlayerAt(c) << " while expecting " << expectedplayer;
-	logNetwork->error(oss.str());
-	if(c)
-	{
-		SystemMessage temp_message(oss.str());
-		c->sendPack(&temp_message);
-	}
-}
+		logGlobal->debug("pack.player attempted to end turn after game over. Ignoring this request.");
 
-void CPackForServer::throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id)
-{
-	if(!isPlayerOwns(gh, id))
-	{
-		wrongPlayerMessage(gh, gh->getOwner(id));
-		throwNotAllowedAction();
+		result = true;
+		return;
 	}
-}
 
-void CPackForServer::throwOnWrongPlayer(CGameHandler * gh, PlayerColor player)
-{
-	if(!gh->hasPlayerAt(player, c) && player != gh->getPlayerAt(c))
-	{
-		wrongPlayerMessage(gh, player);
-		throwNotAllowedAction();
-	}
-}
+	gh.throwOnWrongPlayer(&pack, pack.player);
+	if(gh.queries.topQuery(pack.player))
+		gh.throwAndComplain(&pack, "Cannot end turn before resolving queries!");
 
-void CPackForServer::throwAndComplain(CGameHandler * gh, std::string txt)
-{
-	gh->complain(txt);
-	throwNotAllowedAction();
+	gh.states.setFlag(gs.currentPlayer, &PlayerStatus::makingTurn, false);
+	result = true;
 }
 
-
-CGameState * CPackForServer::GS(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
 {
-	return gh->gs;
+	gh.throwOnWrongOwner(&pack, pack.hid);
+	result = gh.removeObject(gh.getObj(pack.hid));
 }
 
-bool SaveGame::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack)
 {
-	gh->save(fname);
-	logGlobal->info("Game has been saved as %s", fname);
-	return true;
+	gh.throwOnWrongOwner(&pack, pack.hid);
+	result = gh.moveHero(pack.hid, pack.dest, 0, pack.transit, gh.getPlayerAt(pack.c));
 }
 
-bool EndTurn::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitCastleTeleportHero(CastleTeleportHero & pack)
 {
-	PlayerColor currentPlayer = GS(gh)->currentPlayer;
-	if(player != currentPlayer)
-	{
-		if(gh->getPlayerStatus(player) == EPlayerStatus::INGAME)
-			throwAndComplain(gh, "Player attempted to end turn for another player!");
+	gh.throwOnWrongOwner(&pack, pack.hid);
 
-		logGlobal->debug("Player attempted to end turn after game over. Ignoring this request.");
-
-		return true;
-	}
-
-	throwOnWrongPlayer(gh, player);
-	if(gh->queries.topQuery(player))
-		throwAndComplain(gh, "Cannot end turn before resolving queries!");
-
-	gh->states.setFlag(GS(gh)->currentPlayer, &PlayerStatus::makingTurn, false);
-	return true;
-}
-
-bool DismissHero::applyGh(CGameHandler * gh)
-{
-	throwOnWrongOwner(gh, hid);
-	return gh->removeObject(gh->getObj(hid));
+	result = gh.teleportHero(pack.hid, pack.dest, pack.source, gh.getPlayerAt(pack.c));
 }
 
-bool MoveHero::applyGh(CGameHandler * gh)
-{
-	throwOnWrongOwner(gh, hid);
-	return gh->moveHero(hid, dest, 0, transit, gh->getPlayerAt(c));
-}
-
-bool CastleTeleportHero::applyGh(CGameHandler * gh)
-{
-	throwOnWrongOwner(gh, hid);
-
-	return gh->teleportHero(hid, dest, source, gh->getPlayerAt(c));
-}
-
-bool ArrangeStacks::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitArrangeStacks(ArrangeStacks & pack)
 {
 	//checks for owning in the gh func
-	return gh->arrangeStacks(id1, id2, what, p1, p2, val, gh->getPlayerAt(c));
+	result = gh.arrangeStacks(pack.id1, pack.id2, pack.what, pack.p1, pack.p2, pack.val, gh.getPlayerAt(pack.c));
 }
 
-bool BulkMoveArmy::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBulkMoveArmy(BulkMoveArmy & pack)
 {
-	return gh->bulkMoveArmy(srcArmy, destArmy, srcSlot);
+	result = gh.bulkMoveArmy(pack.srcArmy, pack.destArmy, pack.srcSlot);
 }
 
-bool BulkSplitStack::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBulkSplitStack(BulkSplitStack & pack)
 {
-	return gh->bulkSplitStack(src, srcOwner, amount);
+	result = gh.bulkSplitStack(pack.src, pack.srcOwner, pack.amount);
 }
 
-bool BulkMergeStacks::applyGh(CGameHandler* gh)
+void ApplyGhNetPackVisitor::visitBulkMergeStacks(BulkMergeStacks & pack)
 {
-	return gh->bulkMergeStacks(src, srcOwner);
+	result = gh.bulkMergeStacks(pack.src, pack.srcOwner);
 }
 
-bool BulkSmartSplitStack::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBulkSmartSplitStack(BulkSmartSplitStack & pack)
 {
-	return gh->bulkSmartSplitStack(src, srcOwner);
+	result = gh.bulkSmartSplitStack(pack.src, pack.srcOwner);
 }
 
-bool DisbandCreature::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitDisbandCreature(DisbandCreature & pack)
 {
-	throwOnWrongOwner(gh, id);
-	return gh->disbandCreature(id, pos);
+	gh.throwOnWrongOwner(&pack, pack.id);
+	result = gh.disbandCreature(pack.id, pack.pos);
 }
 
-bool BuildStructure::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBuildStructure(BuildStructure & pack)
 {
-	throwOnWrongOwner(gh, tid);
-	return gh->buildStructure(tid, bid);
+	gh.throwOnWrongOwner(&pack, pack.tid);
+	result = gh.buildStructure(pack.tid, pack.bid);
 }
 
-bool RecruitCreatures::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitRecruitCreatures(RecruitCreatures & pack)
 {
-	return gh->recruitCreatures(tid, dst, crid, amount, level);
+	result = gh.recruitCreatures(pack.tid, pack.dst, pack.crid, pack.amount, pack.level);
 }
 
-bool UpgradeCreature::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitUpgradeCreature(UpgradeCreature & pack)
 {
-	throwOnWrongOwner(gh, id);
-	return gh->upgradeCreature(id, pos, cid);
+	gh.throwOnWrongOwner(&pack, pack.id);
+	result = gh.upgradeCreature(pack.id, pack.pos, pack.cid);
 }
 
-bool GarrisonHeroSwap::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack)
 {
-	const CGTownInstance * town = gh->getTown(tid);
-	if(!isPlayerOwns(gh, tid) && !(town->garrisonHero && isPlayerOwns(gh, town->garrisonHero->id)))
-		throwNotAllowedAction(); //neither town nor garrisoned hero (if present) is ours
-	return gh->garrisonSwap(tid);
+	const CGTownInstance * town = gh.getTown(pack.tid);
+	if(!gh.isPlayerOwns(&pack, pack.tid) && !(town->garrisonHero && gh.isPlayerOwns(&pack, town->garrisonHero->id)))
+		gh.throwNotAllowedAction(&pack); //neither town nor garrisoned hero (if present) is ours
+	result = gh.garrisonSwap(pack.tid);
 }
 
-bool ExchangeArtifacts::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
 {
-	throwOnWrongPlayer(gh, src.owningPlayer()); //second hero can be ally
-	return gh->moveArtifact(src, dst);
+	gh.throwOnWrongPlayer(&pack, pack.src.owningPlayer()); //second hero can be ally
+	result = gh.moveArtifact(pack.src, pack.dst);
 }
 
-bool BulkExchangeArtifacts::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack)
 {
-	const CGHeroInstance * pSrcHero = gh->getHero(srcHero);
-	throwOnWrongPlayer(gh, pSrcHero->getOwner());
-	return gh->bulkMoveArtifacts(srcHero, dstHero, swap);
+	const CGHeroInstance * pSrcHero = gh.getHero(pack.srcHero);
+	gh.throwOnWrongPlayer(&pack, pSrcHero->getOwner());
+	result = gh.bulkMoveArtifacts(pack.srcHero, pack.dstHero, pack.swap);
 }
 
-bool AssembleArtifacts::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack)
 {
-	throwOnWrongOwner(gh, heroID);
-	return gh->assembleArtifacts(heroID, artifactSlot, assemble, assembleTo);
+	gh.throwOnWrongOwner(&pack, pack.heroID);
+	result = gh.assembleArtifacts(pack.heroID, pack.artifactSlot, pack.assemble, pack.assembleTo);
 }
 
-bool BuyArtifact::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBuyArtifact(BuyArtifact & pack)
 {
-	throwOnWrongOwner(gh, hid);
-	return gh->buyArtifact(hid, aid);
+	gh.throwOnWrongOwner(&pack, pack.hid);
+	result = gh.buyArtifact(pack.hid, pack.aid);
 }
 
-bool TradeOnMarketplace::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
 {
-	const CGObjectInstance * market = gh->getObj(marketId);
+	const CGObjectInstance * market = gh.getObj(pack.marketId);
 	if(!market)
-		throwAndComplain(gh, "Invalid market object");
-	const CGHeroInstance * hero = gh->getHero(heroId);
+		gh.throwAndComplain(&pack, "Invalid market object");
+	const CGHeroInstance * hero = gh.getHero(pack.heroId);
 
 	//market must be owned or visited
 	const IMarket * m = IMarket::castFrom(market);
 
 	if(!m)
-		throwAndComplain(gh, "market is not-a-market! :/");
+		gh.throwAndComplain(&pack, "market is not-a-market! :/");
 
 	PlayerColor player = market->tempOwner;
 
 	if(player >= PlayerColor::PLAYER_LIMIT)
-		player = gh->getTile(market->visitablePos())->visitableObjects.back()->tempOwner;
+		player = gh.getTile(market->visitablePos())->visitableObjects.back()->tempOwner;
 
 	if(player >= PlayerColor::PLAYER_LIMIT)
-		throwAndComplain(gh, "No player can use this market!");
+		gh.throwAndComplain(&pack, "No player can use this market!");
 
-	bool allyTownSkillTrade = (mode == EMarketMode::RESOURCE_SKILL && gh->getPlayerRelations(player, hero->tempOwner) == PlayerRelations::ALLIES);
+	bool allyTownSkillTrade = (pack.mode == EMarketMode::RESOURCE_SKILL && gh.getPlayerRelations(player, hero->tempOwner) == PlayerRelations::ALLIES);
 
 	if(hero && (!(player == hero->tempOwner || allyTownSkillTrade)
 		|| hero->visitablePos() != market->visitablePos()))
-		throwAndComplain(gh, "This hero can't use this marketplace!");
+		gh.throwAndComplain(&pack, "This hero can't use this marketplace!");
 
 	if(!allyTownSkillTrade)
-		throwOnWrongPlayer(gh, player);
+		gh.throwOnWrongPlayer(&pack, player);
 
-	bool result = true;
+	result = true;
 
-	switch(mode)
+	switch(pack.mode)
 	{
 	case EMarketMode::RESOURCE_RESOURCE:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->tradeResources(m, val[i], player, r1[i], r2[i]);
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.tradeResources(m, pack.val[i], player, pack.r1[i], pack.r2[i]);
 		break;
 	case EMarketMode::RESOURCE_PLAYER:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->sendResources(val[i], player, static_cast<Res::ERes>(r1[i]), PlayerColor(r2[i]));
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.sendResources(pack.val[i], player, static_cast<Res::ERes>(pack.r1[i]), PlayerColor(pack.r2[i]));
 		break;
 	case EMarketMode::CREATURE_RESOURCE:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->sellCreatures(val[i], m, hero, SlotID(r1[i]), static_cast<Res::ERes>(r2[i]));
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.sellCreatures(pack.val[i], m, hero, SlotID(pack.r1[i]), static_cast<Res::ERes>(pack.r2[i]));
 		break;
 	case EMarketMode::RESOURCE_ARTIFACT:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->buyArtifact(m, hero, static_cast<Res::ERes>(r1[i]), ArtifactID(r2[i]));
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.buyArtifact(m, hero, static_cast<Res::ERes>(pack.r1[i]), ArtifactID(pack.r2[i]));
 		break;
 	case EMarketMode::ARTIFACT_RESOURCE:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->sellArtifact(m, hero, ArtifactInstanceID(r1[i]), static_cast<Res::ERes>(r2[i]));
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.sellArtifact(m, hero, ArtifactInstanceID(pack.r1[i]), static_cast<Res::ERes>(pack.r2[i]));
 		break;
 	case EMarketMode::CREATURE_UNDEAD:
-		for(int i = 0; i < r1.size(); ++i)
-			result &= gh->transformInUndead(m, hero, SlotID(r1[i]));
+		for(int i = 0; i < pack.r1.size(); ++i)
+			result &= gh.transformInUndead(m, hero, SlotID(pack.r1[i]));
 		break;
 	case EMarketMode::RESOURCE_SKILL:
-		for(int i = 0; i < r2.size(); ++i)
-			result &= gh->buySecSkill(m, hero, SecondarySkill(r2[i]));
+		for(int i = 0; i < pack.r2.size(); ++i)
+			result &= gh.buySecSkill(m, hero, SecondarySkill(pack.r2[i]));
 		break;
 	case EMarketMode::CREATURE_EXP:
 	{
-		std::vector<SlotID> slotIDs(r1.begin(), r1.end());
-		std::vector<ui32> count(val.begin(), val.end());
-		return gh->sacrificeCreatures(m, hero, slotIDs, count);
+		std::vector<SlotID> slotIDs(pack.r1.begin(), pack.r1.end());
+		std::vector<ui32> count(pack.val.begin(), pack.val.end());
+		result = gh.sacrificeCreatures(m, hero, slotIDs, count);
+		return;
 	}
 	case EMarketMode::ARTIFACT_EXP:
 	{
-		std::vector<ArtifactPosition> positions(r1.begin(), r1.end());
-		return gh->sacrificeArtifact(m, hero, positions);
+		std::vector<ArtifactPosition> positions(pack.r1.begin(), pack.r1.end());
+		result = gh.sacrificeArtifact(m, hero, positions);
+		return;
 	}
 	default:
-		throwAndComplain(gh, "Unknown exchange mode!");
+		gh.throwAndComplain(&pack, "Unknown exchange pack.mode!");
 	}
-
-	return result;
 }
 
-bool SetFormation::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitSetFormation(SetFormation & pack)
 {
-	throwOnWrongOwner(gh, hid);
-	return gh->setFormation(hid, formation);
+	gh.throwOnWrongOwner(&pack, pack.hid);
+	result = gh.setFormation(pack.hid, pack.formation);
 }
 
-bool HireHero::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitHireHero(HireHero & pack)
 {
-	const CGObjectInstance * obj = gh->getObj(tid);
+	const CGObjectInstance * obj = gh.getObj(pack.tid);
 	const CGTownInstance * town = dynamic_ptr_cast<CGTownInstance>(obj);
-	if(town && PlayerRelations::ENEMIES == gh->getPlayerRelations(obj->tempOwner, gh->getPlayerAt(c)))
-		throwAndComplain(gh, "Can't buy hero in enemy town!");
+	if(town && PlayerRelations::ENEMIES == gh.getPlayerRelations(obj->tempOwner, gh.getPlayerAt(pack.c)))
+		gh.throwAndComplain(&pack, "Can't buy hero in enemy town!");
 
-	return gh->hireHero(obj, hid, player);
+	result = gh.hireHero(obj, pack.hid, pack.player);
 }
 
-bool BuildBoat::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitBuildBoat(BuildBoat & pack)
 {
-	if(gh->getPlayerRelations(gh->getOwner(objid), gh->getPlayerAt(c)) == PlayerRelations::ENEMIES)
-		throwAndComplain(gh, "Can't build boat at enemy shipyard");
+	if(gh.getPlayerRelations(gh.getOwner(pack.objid), gh.getPlayerAt(pack.c)) == PlayerRelations::ENEMIES)
+		gh.throwAndComplain(&pack, "Can't build boat at enemy shipyard");
 
-	return gh->buildBoat(objid);
+	result = gh.buildBoat(pack.objid);
 }
 
-bool QueryReply::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitQueryReply(QueryReply & pack)
 {
-	auto playerToConnection = gh->connections.find(player);
-	if(playerToConnection == gh->connections.end())
-		throwAndComplain(gh, "No such player!");
-	if(!vstd::contains(playerToConnection->second, c))
-		throwAndComplain(gh, "Message came from wrong connection!");
-	if(qid == QueryID(-1))
-		throwAndComplain(gh, "Cannot answer the query with id -1!");
-
-	assert(vstd::contains(gh->states.players, player));
-	return gh->queryReply(qid, reply, player);
+	auto playerToConnection = gh.connections.find(pack.player);
+	if(playerToConnection == gh.connections.end())
+		gh.throwAndComplain(&pack, "No such pack.player!");
+	if(!vstd::contains(playerToConnection->second, pack.c))
+		gh.throwAndComplain(&pack, "Message came from wrong connection!");
+	if(pack.qid == QueryID(-1))
+		gh.throwAndComplain(&pack, "Cannot answer the query with pack.id -1!");
+
+	assert(vstd::contains(gh.states.players, pack.player));
+
+	result = gh.queryReply(pack.qid, pack.reply, pack.player);
 }
 
-bool MakeAction::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitMakeAction(MakeAction & pack)
 {
-	const BattleInfo * b = GS(gh)->curB;
+	const BattleInfo * b = gs.curB;
 	if(!b)
-		throwNotAllowedAction();
+		gh.throwNotAllowedAction(&pack);
 
 	if(b->tacticDistance)
 	{
-		if(ba.actionType != EActionType::WALK && ba.actionType != EActionType::END_TACTIC_PHASE
-			&& ba.actionType != EActionType::RETREAT && ba.actionType != EActionType::SURRENDER)
-			throwNotAllowedAction();
-		if(!vstd::contains(gh->connections[b->sides[b->tacticsSide].color], c))
-			throwNotAllowedAction();
+		if(pack.ba.actionType != EActionType::WALK && pack.ba.actionType != EActionType::END_TACTIC_PHASE
+			&& pack.ba.actionType != EActionType::RETREAT && pack.ba.actionType != EActionType::SURRENDER)
+			gh.throwNotAllowedAction(&pack);
+		if(!vstd::contains(gh.connections[b->sides[b->tacticsSide].color], pack.c))
+			gh.throwNotAllowedAction(&pack);
 	}
 	else
 	{
 		auto active = b->battleActiveUnit();
 		if(!active)
-			throwNotAllowedAction();
+			gh.throwNotAllowedAction(&pack);
 		auto unitOwner = b->battleGetOwner(active);
-		if(!vstd::contains(gh->connections[unitOwner], c))
-			throwNotAllowedAction();
+		if(!vstd::contains(gh.connections[unitOwner], pack.c))
+			gh.throwNotAllowedAction(&pack);
 	}
-	return gh->makeBattleAction(ba);
+
+	result = gh.makeBattleAction(pack.ba);
 }
 
-bool MakeCustomAction::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitMakeCustomAction(MakeCustomAction & pack)
 {
-	const BattleInfo * b = GS(gh)->curB;
+	const BattleInfo * b = gs.curB;
 	if(!b)
-		throwNotAllowedAction();
+		gh.throwNotAllowedAction(&pack);
 	if(b->tacticDistance)
-		throwNotAllowedAction();
+		gh.throwNotAllowedAction(&pack);
 	auto active = b->battleActiveUnit();
 	if(!active)
-		throwNotAllowedAction();
+		gh.throwNotAllowedAction(&pack);
 	auto unitOwner = b->battleGetOwner(active);
-	if(!vstd::contains(gh->connections[unitOwner], c))
-		throwNotAllowedAction();
-	if(ba.actionType != EActionType::HERO_SPELL)
-		throwNotAllowedAction();
-	return gh->makeCustomAction(ba);
+	if(!vstd::contains(gh.connections[unitOwner], pack.c))
+		gh.throwNotAllowedAction(&pack);
+	if(pack.ba.actionType != EActionType::HERO_SPELL)
+		gh.throwNotAllowedAction(&pack);
+
+	result = gh.makeCustomAction(pack.ba);
 }
 
-bool DigWithHero::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitDigWithHero(DigWithHero & pack)
 {
-	throwOnWrongOwner(gh, id);
-	return gh->dig(gh->getHero(id));
+	gh.throwOnWrongOwner(&pack, pack.id);
+	result = gh.dig(gh.getHero(pack.id));
 }
 
-bool CastAdvSpell::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitCastAdvSpell(CastAdvSpell & pack)
 {
-	throwOnWrongOwner(gh, hid);
+	gh.throwOnWrongOwner(&pack, pack.hid);
 
-	const CSpell * s = sid.toSpell();
+	const CSpell * s = pack.sid.toSpell();
 	if(!s)
-		throwNotAllowedAction();
-	const CGHeroInstance * h = gh->getHero(hid);
+		gh.throwNotAllowedAction(&pack);
+	const CGHeroInstance * h = gh.getHero(pack.hid);
 	if(!h)
-		throwNotAllowedAction();
+		gh.throwNotAllowedAction(&pack);
 
 	AdventureSpellCastParameters p;
 	p.caster = h;
-	p.pos = pos;
+	p.pos = pack.pos;
 
-	return s->adventureCast(gh->spellEnv, p);
+	result = s->adventureCast(gh.spellEnv, p);
 }
 
-bool PlayerMessage::applyGh(CGameHandler * gh)
+void ApplyGhNetPackVisitor::visitPlayerMessage(PlayerMessage & pack)
 {
-	if(!player.isSpectator()) // TODO: clearly not a great way to verify permissions
-		throwOnWrongPlayer(gh, player);
+	if(!pack.player.isSpectator()) // TODO: clearly not a great way to verify permissions
+		gh.throwOnWrongPlayer(&pack, pack.player);
 	
-	gh->playerMessage(player, text, currObj);
-	return true;
+	gh.playerMessage(pack.player, pack.text, pack.currObj);
+	result = true;
 }

+ 61 - 0
server/ServerNetPackVisitors.h

@@ -0,0 +1,61 @@
+/*
+ * ServerNetPackVisitors.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../lib/NetPackVisitor.h"
+
+class ApplyGhNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
+{
+private:
+	bool result;
+	CGameHandler & gh;
+	CGameState & gs;
+
+public:
+	ApplyGhNetPackVisitor(CGameHandler & gh, CGameState & gs)
+		:gh(gh), gs(gs), result(false)
+	{
+	}
+
+	bool getResult() const
+	{
+		return result;
+	}
+
+	virtual void visitSaveGame(SaveGame & pack) override;
+	virtual void visitEndTurn(EndTurn & pack) override;
+	virtual void visitDismissHero(DismissHero & pack) override;
+	virtual void visitMoveHero(MoveHero & pack) override;
+	virtual void visitCastleTeleportHero(CastleTeleportHero & pack) override;
+	virtual void visitArrangeStacks(ArrangeStacks & pack) override;
+	virtual void visitBulkMoveArmy(BulkMoveArmy & pack) override;
+	virtual void visitBulkSplitStack(BulkSplitStack & pack) override;
+	virtual void visitBulkMergeStacks(BulkMergeStacks & pack) override;
+	virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) override;
+	virtual void visitDisbandCreature(DisbandCreature & pack) override;
+	virtual void visitBuildStructure(BuildStructure & pack) override;
+	virtual void visitRecruitCreatures(RecruitCreatures & pack) override;
+	virtual void visitUpgradeCreature(UpgradeCreature & pack) override;
+	virtual void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) override;
+	virtual void visitExchangeArtifacts(ExchangeArtifacts & pack) override;
+	virtual void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) override;
+	virtual void visitAssembleArtifacts(AssembleArtifacts & pack) override;
+	virtual void visitBuyArtifact(BuyArtifact & pack) override;
+	virtual void visitTradeOnMarketplace(TradeOnMarketplace & pack) override;
+	virtual void visitSetFormation(SetFormation & pack) override;
+	virtual void visitHireHero(HireHero & pack) override;
+	virtual void visitBuildBoat(BuildBoat & pack) override;
+	virtual void visitQueryReply(QueryReply & pack) override;
+	virtual void visitMakeAction(MakeAction & pack) override;
+	virtual void visitMakeCustomAction(MakeCustomAction & pack) override;
+	virtual void visitDigWithHero(DigWithHero & pack) override;
+	virtual void visitCastAdvSpell(CastAdvSpell & pack) override;
+	virtual void visitPlayerMessage(PlayerMessage & pack) override;
+};

部分文件因为文件数量过多而无法显示