浏览代码

Statistics is now managed as part of CGameHandler

Ivan Savenko 5 月之前
父节点
当前提交
aa9b13b66a

+ 5 - 3
lib/gameState/CGameState.h

@@ -83,8 +83,6 @@ public:
 	CBonusSystemNode globalEffects;
 	RumorState currentRumor;
 
-	StatisticDataSet statistic;
-
 	// NOTE: effectively AI mutex, only used by adventure map AI
 	static std::shared_mutex mutex;
 
@@ -185,7 +183,11 @@ public:
 			std::map<ArtifactID, int> allocatedArtifactsUnused;
 			h & allocatedArtifactsUnused;
 		}
-		h & statistic;
+		if (!h.hasFeature(Handler::Version::SERVER_STATISTICS))
+		{
+			StatisticDataSet statistic;
+			h & statistic;
+		}
 
 		if(!h.saving && h.loadingGamestate)
 			restoreBonusSystemTree();

+ 13 - 13
lib/gameState/GameStatistics.cpp

@@ -31,7 +31,7 @@ void StatisticDataSet::add(StatisticDataSetEntry entry)
 	data.push_back(entry);
 }
 
-StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, const CGameState * gs)
+StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, const CGameState * gs, const StatisticDataSet & accumulatedData)
 {
 	StatisticDataSetEntry data;
 
@@ -63,18 +63,18 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
 	data.numMines = Statistic::getNumMines(gs, ps);
 	data.score = scenarioHighScores.calculate().total;
 	data.maxHeroLevel = Statistic::findBestHero(gs, ps->color) ? Statistic::findBestHero(gs, ps->color)->level : 0;
-	data.numBattlesNeutral = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numBattlesNeutral : 0;
-	data.numBattlesPlayer = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numBattlesPlayer : 0;
-	data.numWinBattlesNeutral = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numWinBattlesNeutral : 0;
-	data.numWinBattlesPlayer = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numWinBattlesPlayer : 0;
-	data.numHeroSurrendered = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numHeroSurrendered : 0;
-	data.numHeroEscaped = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).numHeroEscaped : 0;
-	data.spentResourcesForArmy = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).spentResourcesForArmy : TResources();
-	data.spentResourcesForBuildings = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).spentResourcesForBuildings : TResources();
-	data.tradeVolume = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).tradeVolume : TResources();
-	data.eventCapturedTown = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).lastCapturedTownDay == gs->getDate(Date::DAY) : false;
-	data.eventDefeatedStrongestHero = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).lastDefeatedStrongestHeroDay == gs->getDate(Date::DAY) : false;
-	data.movementPointsUsed = gs->statistic.accumulatedValues.count(ps->color) ? gs->statistic.accumulatedValues.at(ps->color).movementPointsUsed : 0;
+	data.numBattlesNeutral = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numBattlesNeutral : 0;
+	data.numBattlesPlayer = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numBattlesPlayer : 0;
+	data.numWinBattlesNeutral = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numWinBattlesNeutral : 0;
+	data.numWinBattlesPlayer = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numWinBattlesPlayer : 0;
+	data.numHeroSurrendered = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numHeroSurrendered : 0;
+	data.numHeroEscaped = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).numHeroEscaped : 0;
+	data.spentResourcesForArmy = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).spentResourcesForArmy : TResources();
+	data.spentResourcesForBuildings = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).spentResourcesForBuildings : TResources();
+	data.tradeVolume = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).tradeVolume : TResources();
+	data.eventCapturedTown = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).lastCapturedTownDay == gs->getDate(Date::DAY) : false;
+	data.eventDefeatedStrongestHero = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).lastDefeatedStrongestHeroDay == gs->getDate(Date::DAY) : false;
+	data.movementPointsUsed = accumulatedData.accumulatedValues.count(ps->color) ? accumulatedData.accumulatedValues.at(ps->color).movementPointsUsed : 0;
 
 	return data;
 }

+ 2 - 2
lib/gameState/GameStatistics.h

@@ -100,8 +100,8 @@ struct DLL_LINKAGE StatisticDataSetEntry
 class DLL_LINKAGE StatisticDataSet
 {
 public:
-    void add(StatisticDataSetEntry entry);
-	static StatisticDataSetEntry createEntry(const PlayerState * ps, const CGameState * gs);
+	void add(StatisticDataSetEntry entry);
+	static StatisticDataSetEntry createEntry(const PlayerState * ps, const CGameState * gs, const StatisticDataSet & accumulatedData);
 	std::string toCsv(std::string sep) const;
 	std::string writeCsv() const;
 

+ 3 - 2
lib/serializer/ESerializationVersion.h

@@ -23,7 +23,7 @@
 /// - change 'CURRENT' to 'CURRENT = NEW_TEST_KEY'.
 ///
 /// To check for version in serialize() call use form
-/// if (h.version >= Handler::Version::NEW_TEST_KEY)
+/// if (h.hasFeature(Handler::Version::NEW_TEST_KEY))
 ///     h & newKey; // loading/saving save of a new version
 /// else
 ///     newKey = saneDefaultValue; // loading of old save
@@ -43,8 +43,9 @@ enum class ESerializationVersion : int32_t
 	FLAGGABLE_BONUS_SYSTEM_NODE, // flaggable objects now contain bonus system node
 	RANDOMIZATION_REWORK, // random rolls logic has been moved to server
 	CUSTOM_BONUS_ICONS, // support for custom icons in bonuses
+	SERVER_STATISTICS, // statistics now only saved on server
 
-	CURRENT = CUSTOM_BONUS_ICONS,
+	CURRENT = SERVER_STATISTICS,
 };
 
 static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");

+ 15 - 14
server/CGameHandler.cpp

@@ -519,6 +519,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
 	, complainInvalidSlot("Invalid slot accessed!")
 	, turnTimerHandler(std::make_unique<TurnTimerHandler>(*this))
 	, newTurnProcessor(std::make_unique<NewTurnProcessor>(this))
+	, statistics(std::make_unique<StatisticDataSet>())
 {
 	QID = 1;
 
@@ -620,7 +621,7 @@ void CGameHandler::addStatistics(StatisticDataSet &stat) const
 		if (elem.first == PlayerColor::NEUTRAL || !elem.first.isValidPlayer())
 			continue;
 
-		auto data = StatisticDataSet::createEntry(&elem.second, &gameState());
+		auto data = StatisticDataSet::createEntry(&elem.second, &gameState(), *statistics);
 
 		stat.add(data);
 	}
@@ -649,7 +650,7 @@ void CGameHandler::onNewTurn()
 	}
 	else
 	{
-		addStatistics(gameState().statistic); // write at end of turn
+		addStatistics(*statistics); // write at end of turn
 	}
 
 	for (const auto & townID : gameState().getMap().getAllTowns())
@@ -1035,7 +1036,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 
 		turnTimerHandler->setEndTurnAllowed(h->getOwner(), !movingOntoWater && !movingOntoObstacle);
 		doMove(TryMoveHero::SUCCESS, lookForGuards, visitDest, LEAVING_TILE);
-		gameState().statistic.accumulatedValues[asker].movementPointsUsed += tmh.movePoints;
+		statistics->accumulatedValues[asker].movementPointsUsed += tmh.movePoints;
 		return true;
 	}
 }
@@ -1079,7 +1080,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne
 	const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj);
 	if (town) //town captured
 	{
-		gameState().statistic.accumulatedValues[owner].lastCapturedTownDay = gameState().getDate(Date::DAY);
+		statistics->accumulatedValues[owner].lastCapturedTownDay = gameState().getDate(Date::DAY);
 
 		if (owner.isValidPlayer()) //new owner is real player
 		{
@@ -1489,7 +1490,7 @@ void CGameHandler::sendToAllClients(CPackForClient & pack)
 void CGameHandler::sendAndApply(CPackForClient & pack)
 {
 	sendToAllClients(pack);
-	gameState().apply(pack);
+	gs->apply(pack);
 	logNetwork->trace("\tApplied on gameState(): %s", typeid(pack).name());
 }
 
@@ -1646,8 +1647,8 @@ bool CGameHandler::load(const std::string & filename)
 		gameLobby().announceMessage(str);
 		return false;
 	}
-	gameState().preInit(LIBRARY);
-	gameState().updateOnLoad(gameLobby().si.get());
+	gs->preInit(LIBRARY);
+	gs->updateOnLoad(gameLobby().si.get());
 	return true;
 }
 
@@ -2229,7 +2230,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	if(!force)
 	{
 		giveResources(t->tempOwner, -requestedBuilding->resources);
-		gameState().statistic.accumulatedValues[t->tempOwner].spentResourcesForBuildings += requestedBuilding->resources;
+		statistics->accumulatedValues[t->tempOwner].spentResourcesForBuildings += requestedBuilding->resources;
 	}
 
 	//We know what has been built, apply changes. Do this as final step to properly update town window
@@ -2429,7 +2430,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
 	//recruit
 	TResources cost = (c->getFullRecruitCost() * cram);
 	giveResources(army->tempOwner, -cost);
-	gameState().statistic.accumulatedValues[army->tempOwner].spentResourcesForArmy += cost;
+	statistics->accumulatedValues[army->tempOwner].spentResourcesForArmy += cost;
 
 	SetAvailableCreatures sac;
 	sac.tid = objid;
@@ -2492,7 +2493,7 @@ bool CGameHandler::upgradeCreature(ObjectInstanceID objid, SlotID pos, CreatureI
 
 	//take resources
 	giveResources(player, -totalCost);
-	gameState().statistic.accumulatedValues[player].spentResourcesForArmy += totalCost;
+	statistics->accumulatedValues[player].spentResourcesForArmy += totalCost;
 
 	//upgrade creature
 	changeStackType(StackLocation(obj->id, pos), upgID.toCreature());
@@ -3194,8 +3195,8 @@ bool CGameHandler::tradeResources(const IMarket *market, ui32 amountToSell, Play
 	giveResource(player, toSell, -b1 * amountToBoy);
 	giveResource(player, toBuy, b2 * amountToBoy);
 
-	gameState().statistic.accumulatedValues[player].tradeVolume[toSell] += -b1 * amountToBoy;
-	gameState().statistic.accumulatedValues[player].tradeVolume[toBuy] += b2 * amountToBoy;
+	statistics->accumulatedValues[player].tradeVolume[toSell] += -b1 * amountToBoy;
+	statistics->accumulatedValues[player].tradeVolume[toBuy] += b2 * amountToBoy;
 
 	return true;
 }
@@ -3568,7 +3569,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 		PlayerEndsGame peg;
 		peg.player = player;
 		peg.victoryLossCheckResult = victoryLossCheckResult;
-		peg.statistic = StatisticDataSet(gameState().statistic);
+		peg.statistic = *statistics;
 		addStatistics(peg.statistic); // add last turn befor win / loss
 		sendAndApply(peg);
 
@@ -4288,7 +4289,7 @@ std::shared_ptr<CGObjectInstance> CGameHandler::createNewObject(const int3 & vis
 	auto o = handler->create(&gameInfo(), nullptr);
 	handler->configureObject(o.get(), *randomizer);
 	assert(o->ID == objectID);
-	gameState().getMap().generateUniqueInstanceName(o.get());
+	gs->getMap().generateUniqueInstanceName(o.get());
 
 	assert(!handler->getTemplates(terrainType).empty());
 	if (handler->getTemplates().empty())

+ 8 - 1
server/CGameHandler.h

@@ -27,6 +27,7 @@ class CCommanderInstance;
 class EVictoryLossCheckResult;
 class CRandomGenerator;
 class GameRandomizer;
+class StatisticDataSet;
 
 struct StartInfo;
 struct TerrainTile;
@@ -68,6 +69,7 @@ public:
 	std::unique_ptr<TurnTimerHandler> turnTimerHandler;
 	std::unique_ptr<NewTurnProcessor> newTurnProcessor;
 	std::unique_ptr<GameRandomizer> randomizer;
+	std::unique_ptr<StatisticDataSet> statistics;
 	std::shared_ptr<CGameState> gs;
 
 	//use enums as parameters, because doMove(sth, true, false, true) is not readable
@@ -96,7 +98,6 @@ public:
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 
 	IGameInfoCallback & gameInfo();
-	CGameState & gameState() { return *gs; }
 	const CGameState & gameState() const { return *gs; }
 
 	// Helpers to create new object of specified type
@@ -257,6 +258,12 @@ public:
 		h & *turnOrder;
 		h & *turnTimerHandler;
 
+		if (h.hasFeature(Handler::Version::SERVER_STATISTICS))
+		{
+			h & *statistics;
+		}
+
+
 #if SCRIPTING_ENABLED
 		JsonNode scriptsState;
 		if(h.saving)

+ 9 - 9
server/battles/BattleResultProcessor.cpp

@@ -343,21 +343,21 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 			if(!strongestHero || hero->exp > strongestHero->exp)
 				strongestHero = hero;
 		if(strongestHero->id == finishingBattle->loserId && strongestHero->level > 5)
-			gameHandler->gameState().statistic.accumulatedValues[finishingBattle->victor].lastDefeatedStrongestHeroDay = gameHandler->gameState().getDate(Date::DAY);
+			gameHandler->statistics->accumulatedValues[finishingBattle->victor].lastDefeatedStrongestHeroDay = gameHandler->gameState().getDate(Date::DAY);
 	}
 	if(battle.sideToPlayer(BattleSide::ATTACKER) == PlayerColor::NEUTRAL || battle.sideToPlayer(BattleSide::DEFENDER) == PlayerColor::NEUTRAL)
 	{
-		gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesNeutral++;
-		gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesNeutral++;
+		gameHandler->statistics->accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesNeutral++;
+		gameHandler->statistics->accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesNeutral++;
 		if(!finishingBattle->isDraw())
-			gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesNeutral++;
+			gameHandler->statistics->accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesNeutral++;
 	}
 	else
 	{
-		gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesPlayer++;
-		gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesPlayer++;
+		gameHandler->statistics->accumulatedValues[battle.sideToPlayer(BattleSide::ATTACKER)].numBattlesPlayer++;
+		gameHandler->statistics->accumulatedValues[battle.sideToPlayer(BattleSide::DEFENDER)].numBattlesPlayer++;
 		if(!finishingBattle->isDraw())
-			gameHandler->gameState().statistic.accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesPlayer++;
+			gameHandler->statistics->accumulatedValues[battle.sideToPlayer(finishingBattle->winnerSide)].numWinBattlesPlayer++;
 	}
 
 	BattleResultAccepted raccepted;
@@ -563,13 +563,13 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
 
 	if (result.result == EBattleResult::SURRENDER)
 	{
-		gameHandler->gameState().statistic.accumulatedValues[finishingBattle->loser].numHeroSurrendered++;
+		gameHandler->statistics->accumulatedValues[finishingBattle->loser].numHeroSurrendered++;
 		gameHandler->heroPool->onHeroSurrendered(finishingBattle->loser, loserHero);
 	}
 
 	if (result.result == EBattleResult::ESCAPE)
 	{
-		gameHandler->gameState().statistic.accumulatedValues[finishingBattle->loser].numHeroEscaped++;
+		gameHandler->statistics->accumulatedValues[finishingBattle->loser].numHeroEscaped++;
 		gameHandler->heroPool->onHeroEscaped(finishingBattle->loser, loserHero);
 	}
 

+ 1 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -149,7 +149,7 @@ void PlayerMessageProcessor::commandStatistic(PlayerColor player, const std::vec
 	if(!isHost)
 		return;
 
-	std::string path = gameHandler->gameState().statistic.writeCsv();
+	std::string path = gameHandler->statistics->writeCsv();
 
 	auto str = MetaString::createFromTextID("vcmi.broadcast.statisticFile");
 	str.replaceRawString(path);