Send script-defined statistics to the masterserver

After GameOver(), the global function CollectStatistics() is called
which in turn calls CollectStats() on all definitions and the Scenario.
The results are collected into a proplist and sent to the masterserver
as JSON.

The intended purpose is to collect statistics like weapon kill counts
and evaluate them across all online games to improve balancing.
alut-include-path
Lukas Werling 2017-02-17 23:04:16 +01:00
parent ee0b1c2599
commit 87ee44964c
6 changed files with 50 additions and 0 deletions

View File

@ -0,0 +1,28 @@
/*--
CollectStatistics.c
Authors: Luchs
Global entry point for statistics collection for the masterserver.
--*/
// This function is called after the round ends. The return value is passed to
// the masterserver.
global func CollectStatistics()
{
var result = {};
var i = 0, def, stats;
while (def = GetDefinition(i++))
{
stats = def->~CollectStats();
if (stats != nil)
result[def->GetName(true)] = stats;
}
stats = Scenario->~CollectStats();
if (stats != nil)
result.Scenario = stats;
if (GetLength(GetProperties(result)))
return result;
return nil;
}

View File

@ -249,6 +249,7 @@ void C4RoundResults::Clear()
sNetResult.Clear();
eNetResult = NR_None;
fHideSettlementScore=false;
Statistics.Clear();
}
void C4RoundResults::Init()
@ -308,6 +309,19 @@ void C4RoundResults::EvaluateGame()
int32_t iFirstLocalPlayer = pFirstLocalPlayer ? pFirstLocalPlayer->Number : NO_OWNER;
EvaluateGoals(Goals, FulfilledGoals, iFirstLocalPlayer);
iPlayingTime = Game.Time;
// collect statistics
try
{
C4AulParSet Pars;
auto stats = ::ScriptEngine.GetPropList()->Call(PSF_CollectStatistics, &Pars);
if (stats != C4VNull)
Statistics = stats.ToJSON();
}
catch (C4JSONSerializationError& e)
{
DebugLogF("ERROR: cannot serialize CollectStatistics() result: %s", e.what());
}
}
void C4RoundResults::EvaluateNetwork(C4RoundResults::NetResult eNetResult, const char *szResultMsg)

View File

@ -149,6 +149,7 @@ private:
// scenario-specific
StdCopyStrBuf sCustomEvaluationStrings;
StdCopyStrBuf Statistics; // stats collected by script at the end of the round
public:
C4RoundResults() : iPlayingTime(0) {}
@ -190,6 +191,8 @@ public:
void SetLeaguePerformance(int32_t iNewPerf, int32_t idPlayer = 0);
int32_t GetLeaguePerformance(int32_t idPlayer = 0) const;
StdCopyStrBuf GetStatistics() const { return Statistics; }
const C4RoundResultsPlayers &GetPlayers() const { return Players; }
const char *GetCustomEvaluationStrings() const { return sCustomEvaluationStrings.getData(); }
NetResult GetNetResult() const { return eNetResult; }

View File

@ -102,6 +102,8 @@ bool C4ValueToMatrix(const C4ValueArray& array, StdMeshMatrix* matrix);
#define PSF_CommandFailure "~CommandFailure" // string command, pTarget, iTx, iTy, pTarget2, iData
#define PSF_OnCompletionChange "~OnCompletionChange" // int old_con, int new_con
#define PSF_CollectStatistics "CollectStatistics"
// Effect callbacks
#define PSF_FxStart "Fx%sStart" // C4Object *pTarget, int iEffectNumber, int iTemp, C4Value vVar1, C4Value vVar2, C4Value vVar3, C4Value vVar4

View File

@ -80,6 +80,7 @@ void C4Network2Reference::InitLocal()
IsEditor = !!::Application.isEditor;
NetpuncherGameID = ::Network.getNetpuncherGameID();
NetpuncherAddr = ::Network.getNetpuncherAddr();
Statistics = ::Game.RoundResults.GetStatistics();
Game.Set();
// Addresses
@ -129,6 +130,7 @@ void C4Network2Reference::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(OfficialServer, "OfficialServer", false));
pComp->Value(mkNamingAdapt(NetpuncherGameID, "NetpuncherID", C4NetpuncherID(), false, false));
pComp->Value(mkNamingAdapt(NetpuncherAddr, "NetpuncherAddr", "", false, false));
pComp->Value(mkNamingAdapt(Statistics, "Statistics", "", false, false));
pComp->Value(Parameters);
}

View File

@ -54,6 +54,7 @@ private:
bool IsEditor;
C4NetpuncherID NetpuncherGameID;
StdCopyStrBuf NetpuncherAddr;
StdCopyStrBuf Statistics;
// Engine information
C4GameVersion Game;