Jelajahi Sumber

* simple mechanism for detecting desync after init
* moving stacks in tactics phase won't affect stack queue (part of #760)
* moved all boost headers in server to PCH

Michał W. Urbańczyk 13 tahun lalu
induk
melakukan
c698181c4c

+ 3 - 2
client/Client.cpp

@@ -334,13 +334,14 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 
 
 	ui32 seed, sum;
-	c >> si	>> sum >> seed;
+	si32 seedPostInit;
+	c >> si	>> sum >> seed >> seedPostInit;
 	tlog0 <<"\tSending/Getting info to/from the server: "<<tmh.getDiff()<<std::endl;
 	tlog0 << "\tUsing random seed: "<<seed << std::endl;
 
 	gs = const_cast<CGameInfo*>(CGI)->state;
 	gs->scenarioOps = si;
-	gs->init(si, sum, seed);
+	gs->init(si, sum, seed, seedPostInit);
 	tlog0 <<"Initializing GameState (together): "<<tmh.getDiff()<<std::endl;
 
 	if(gs->map)

+ 8 - 2
lib/CGameState.cpp

@@ -23,7 +23,7 @@
 #include "../lib/JsonNode.h"
 #include "GameConstants.h"
 
-boost::rand48 ran;
+DLL_LINKAGE boost::rand48 ran;
 class CGObjectInstance;
 
 #ifdef min
@@ -813,7 +813,7 @@ BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2],
 	return BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town);
 }
 
-void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
+void CGameState::init(StartInfo * si, ui32 checksum, int Seed, int expectedPostInitSeed /*= -1*/)
 {
 	struct HLP
 	{
@@ -1570,6 +1570,12 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
 
 
 	map->checkForObjectives(); //needs to be run when all objects are properly placed
+
+	if(expectedPostInitSeed >= 0)
+	{
+		int actualSeed = ran();
+		assert(expectedPostInitSeed == actualSeed); //RNG must be in the same state on all machines when initialization is done (otherwise we have desync)
+	}
 }
 
 int CGameState::battleGetBattlefieldType(int3 tile)

+ 1 - 1
lib/CGameState.h

@@ -373,7 +373,7 @@ public:
 
 	boost::shared_mutex *mx;
 
-	void init(StartInfo * si, ui32 checksum, int Seed);
+	void init(StartInfo * si, ui32 checksum, int Seed, int expectedPostInitSeed = -1);
 	void loadTownDInfos();
 	void randomizeObject(CGObjectInstance *cur);
 	std::pair<int,int> pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns <type, subtype>

+ 7 - 0
lib/NetPacksLib.cpp

@@ -1031,6 +1031,13 @@ DLL_LINKAGE void StartAction::applyGs( CGameState *gs )
 		return;
 	}
 
+	if(gs->curB->tacticDistance)
+	{
+		// moves in tactics phase do not affect creature status
+		// (tactics stack queue is managed by client)
+		return;
+	}
+
 	if(ba.actionType != BattleAction::HERO_SPELL) //don't check for stack if it's custom action by hero
 	{
 		assert(st);

+ 11 - 10
server/CGameHandler.cpp

@@ -21,9 +21,6 @@
 #include "../lib/VCMIDirs.h"
 #include "../client/CSoundBase.h"
 #include "CGameHandler.h"
-#include <boost/random/mersenne_twister.hpp>
-#include <boost/random/variate_generator.hpp>
-#include <boost/random/poisson_distribution.hpp>
 #include "../lib/CCreatureSet.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/GameConstants.h"
@@ -43,6 +40,7 @@
 #ifndef _MSC_VER
 #include <boost/thread/xtime.hpp>
 #endif
+#include <boost/random/linear_congruential.hpp>
 extern bool end2;
 #ifdef min
 #undef min
@@ -60,9 +58,6 @@ extern bool end2;
 
 CondSh<bool> battleMadeAction;
 CondSh<BattleResult *> battleResult(NULL);
-std::ptrdiff_t randomizer (ptrdiff_t i) {return rand() % i;}
-std::ptrdiff_t (*p_myrandom)(std::ptrdiff_t) = randomizer;
-
 template <typename T> class CApplyOnGH;
 
 class CBaseForGHApply
@@ -799,6 +794,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 
 CGameHandler::CGameHandler(void)
 {
+	seedInitial = seedPostInit = -1;
 	QID = 1;
 	//gs = NULL;
 	IObjectInterface::cb = this;
@@ -815,11 +811,16 @@ CGameHandler::~CGameHandler(void)
 	delete gs;
 }
 
-void CGameHandler::init(StartInfo *si, int Seed)
+void CGameHandler::init(StartInfo *si)
 {
+	extern DLL_LINKAGE boost::rand48 ran;
+	if(seedInitial < 0)
+		seedInitial = std::time(NULL);
+
 	gs = new CGameState();
 	tlog0 << "Gamestate created!" << std::endl;
-	gs->init(si, 0, Seed);
+	gs->init(si, 0, seedInitial);
+	seedPostInit = ran();
 	tlog0 << "Gamestate initialized!" << std::endl;
 
 	for(std::map<ui8,PlayerState>::iterator i = gs->players.begin(); i != gs->players.end(); i++)
@@ -1207,7 +1208,7 @@ void CGameHandler::run(bool resume)
 		if(!resume)
 		{
 			ui32 sum = gs->map ? gs->map->checksum : 612;
-			(*cc) << gs->initialOpts << sum << gs->seed; // gs->scenarioOps
+			(*cc) << gs->initialOpts << sum << gs->seed << seedPostInit; // gs->scenarioOps
 		}
 
 		(*cc) >> quantity; //how many players will be handled at that client
@@ -5619,7 +5620,7 @@ void CGameHandler::spawnWanderingMonsters(int creatureID)
 	std::vector<int3> tiles;
 	getFreeTiles(tiles);
 	ui32 amount = tiles.size() / 200; //Chance is 0.5% for each tile
-	std::random_shuffle(tiles.begin(), tiles.end(), p_myrandom);
+	std::random_shuffle(tiles.begin(), tiles.end());
 	tlog5 << "Spawning wandering monsters. Found " << tiles.size() << " free tiles. Creature type: " << creatureID << std::endl;
 	const CCreature *cre = VLC->creh->creatures[creatureID];
 	for (int i = 0; i < amount; ++i)

+ 3 - 1
server/CGameHandler.h

@@ -91,6 +91,8 @@ public:
 	PlayerStatuses states; //player color -> player state
 	std::set<CConnection*> conns;
 
+	si32 seedInitial, seedPostInit;
+
 	//queries stuff
 	boost::recursive_mutex gsm;
 	ui32 QID;
@@ -187,7 +189,7 @@ public:
 
 	void commitPackage(CPackForClient *pack) OVERRIDE;
 
-	void init(StartInfo *si, int Seed);
+	void init(StartInfo *si);
 	void handleConnection(std::set<int> players, CConnection &c);
 	int getPlayerAt(CConnection *c) const;
 

+ 2 - 12
server/CVCMIServer.cpp

@@ -1,14 +1,5 @@
 #include "StdInc.h"
 
-#include <boost/date_time/posix_time/posix_time_types.hpp> //no i/o just types
-#include <boost/random/linear_congruential.hpp>
-#include <boost/system/system_error.hpp>
-#include <boost/crc.hpp>
-#include <boost/interprocess/mapped_region.hpp>
-#include <boost/interprocess/shared_memory_object.hpp>
-
-
-
 #include "../lib/CCampaignHandler.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/Connection.h"
@@ -32,7 +23,6 @@
 #include "../lib/CObjectHandler.h"
 #include "../lib/GameConstants.h"
 
-#include <boost/asio.hpp>
 #include "../lib/UnlockGuard.h"
 
 std::string NAME_AFFIX = "server";
@@ -345,7 +335,7 @@ CGameHandler * CVCMIServer::initGhFromHostingConnection(CConnection &c)
 		c << ui8(0); //OK!
 	}
 
-	gh->init(&si,std::time(NULL));
+	gh->init(&si);
 	c.addStdVecItems(gh->gs);
 	gh->conns.insert(&c);
 
@@ -378,7 +368,7 @@ void CVCMIServer::newPregame()
 	{
 		CGameHandler gh;
 		gh.conns = cps->connections;
-		gh.init(cps->curStartInfo,std::clock());
+		gh.init(cps->curStartInfo);
 
 		BOOST_FOREACH(CConnection *c, gh.conns)
 			c->addStdVecItems(gh.gs);

+ 10 - 3
server/StdInc.h

@@ -2,6 +2,13 @@
 
 #include "../Global.h"
 
-// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
-
-// Here you can add specific libraries and macros which are specific to this project.
+#include <boost/crc.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp> //no i/o just types
+#include <boost/interprocess/mapped_region.hpp>
+#include <boost/interprocess/shared_memory_object.hpp>
+#include <boost/random/linear_congruential.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/poisson_distribution.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/asio.hpp>

+ 4 - 4
server/VCMI_server.vcxproj

@@ -94,7 +94,7 @@
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
-      <AdditionalOptions>/MP4 %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/MP4 %(AdditionalOptions)/Zm200</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MinimalRebuild>false</MinimalRebuild>
@@ -115,7 +115,7 @@
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <ClCompile>
-      <AdditionalOptions>/MP4 %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/MP4 %(AdditionalOptions)/Zm200</AdditionalOptions>
       <Optimization>Disabled</Optimization>
       <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MinimalRebuild>false</MinimalRebuild>
@@ -135,7 +135,7 @@
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|Win32'">
     <ClCompile>
-      <AdditionalOptions>/Oy- %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/Oy- %(AdditionalOptions)/Zm200</AdditionalOptions>
       <Optimization>MaxSpeed</Optimization>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
@@ -166,7 +166,7 @@
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'">
     <ClCompile>
-      <AdditionalOptions>/Oy- %(AdditionalOptions)</AdditionalOptions>
+      <AdditionalOptions>/Oy- %(AdditionalOptions)/Zm200</AdditionalOptions>
       <Optimization>MaxSpeed</Optimization>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>