Skip to content

Commit 830353d

Browse files
committed
feat(stats): Store match stats with last replay
1 parent 926ffff commit 830353d

13 files changed

Lines changed: 1536 additions & 2 deletions

File tree

Generals/Code/GameEngine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ set(GAMEENGINE_SRC
105105
Include/Common/StackDump.h
106106
Include/Common/StateMachine.h
107107
Include/Common/StatsCollector.h
108+
Include/Common/StatsExporter.h
108109
# Include/Common/STLTypedefs.h
109110
# Include/Common/StreamingArchiveFile.h
110111
# Include/Common/SubsystemInterface.h
@@ -586,6 +587,7 @@ set(GAMEENGINE_SRC
586587
Source/Common/SkirmishBattleHonors.cpp
587588
Source/Common/StateMachine.cpp
588589
Source/Common/StatsCollector.cpp
590+
Source/Common/StatsExporter.cpp
589591
# Source/Common/System/ArchiveFile.cpp
590592
# Source/Common/System/ArchiveFileSystem.cpp
591593
# Source/Common/System/AsciiString.cpp

Generals/Code/GameEngine/Include/Common/ScoreKeeper.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ class ScoreKeeper : public Snapshot
9696
// for battle honor calculation. done once at the end of each online game
9797
Int getTotalUnitsBuilt( KindOfMaskType validMask, KindOfMaskType invalidMask );
9898

99+
// TheSuperHackers @feature hrich 10/03/2026 Public accessors for game stats export.
100+
typedef std::map<const ThingTemplate *, Int> ObjectCountMap;
101+
Int getUnitsDestroyedByPlayer( Int idx ) const { return m_totalUnitsDestroyed[idx]; }
102+
Int getBuildingsDestroyedByPlayer( Int idx ) const { return m_totalBuildingsDestroyed[idx]; }
103+
const ObjectCountMap& getObjectsBuilt() const { return m_objectsBuilt; }
104+
const ObjectCountMap* getObjectsDestroyedArray() const { return m_objectsDestroyed; }
105+
const ObjectCountMap& getObjectsLost() const { return m_objectsLost; }
106+
const ObjectCountMap& getObjectsCaptured() const { return m_objectsCaptured; }
107+
99108
protected:
100109

101110
// snapshot methods
@@ -119,7 +128,6 @@ class ScoreKeeper : public Snapshot
119128

120129
Int m_myPlayerIdx; ///< We need to not score kills on ourselves... so we need to know who we are
121130

122-
typedef std::map<const ThingTemplate *, Int> ObjectCountMap;
123131
typedef ObjectCountMap::iterator ObjectCountMapIt;
124132
ObjectCountMap m_objectsBuilt; ///< How many and what kinds of objects did we build
125133
ObjectCountMap m_objectsDestroyed[MAX_PLAYER_COUNT]; ///< How many and what kinds and who's did we kill
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
** Command & Conquer Generals(tm)
3+
** Copyright 2026 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
// TheSuperHackers @feature hrich 10/03/2026 Game stats JSON exporter.
20+
21+
#pragma once
22+
23+
class AsciiString;
24+
25+
/// Export game statistics as a JSON file alongside the replay file.
26+
/// @param replayDir Directory containing replays (e.g. "[UserDataPath]/Replays/")
27+
/// @param replayFileName Replay filename with extension (e.g. "LastReplay.rep")
28+
void ExportGameStatsJSON(const AsciiString& replayDir, const AsciiString& replayFileName);
29+
30+
/// Collect a time-series snapshot of all players' stats (called every game logic frame).
31+
/// Snapshots are taken every 30 frames (~1 second) and stored in memory.
32+
void StatsExporterCollectSnapshot();
33+
34+
/// Clear all stored time-series snapshots (called at game start/reset).
35+
void StatsExporterClearSnapshots();

Generals/Code/GameEngine/Source/Common/Recorder.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
#include "Common/CRCDebug.h"
4848
#include "Common/OptionPreferences.h"
4949
#include "Common/version.h"
50+
// TheSuperHackers @feature hrich 10/03/2026 Export game stats as JSON alongside replay file.
51+
#include "Common/StatsExporter.h"
5052

5153
constexpr const char s_genrep[] = "GENREP";
5254
constexpr const UnsignedInt replayBufferBytes = 8192;
@@ -731,6 +733,9 @@ void RecorderClass::stopRecording() {
731733
if (m_archiveReplays)
732734
archiveReplay(m_fileName);
733735
}
736+
// TheSuperHackers @feature hrich 10/03/2026 Export game stats as JSON alongside replay file.
737+
if (!m_fileName.isEmpty())
738+
ExportGameStatsJSON(getReplayDir(), m_fileName);
734739
m_fileName.clear();
735740
}
736741

0 commit comments

Comments
 (0)