1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-21 00:19:29 +02:00

Statistics is now managed as part of CGameHandler

This commit is contained in:
Ivan Savenko
2025-05-20 21:40:05 +03:00
parent af20b39fe6
commit aa9b13b66a
8 changed files with 56 additions and 45 deletions

View File

@ -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;
}
if (!h.hasFeature(Handler::Version::SERVER_STATISTICS))
{
StatisticDataSet statistic;
h & statistic;
}
if(!h.saving && h.loadingGamestate)
restoreBonusSystemTree();

View File

@ -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;
}

View File

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

View File

@ -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!");

View File

@ -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())

View File

@ -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)

View File

@ -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);
}

View File

@ -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);