View Issue Details

IDProjectCategoryView StatusLast Update
0000493Gameplay + OpenGL[All Projects] Featurepublic2017-03-29 07:28
Reporterhobomaster22 
Assigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionwon't fix 
Summary0000493: Write level statistics
DescriptionZDoom used to write level statistics in the old binary save game format that Doom Launcher would read to track statistics. The ability for Doom Launcher to track statistics with the new save game format has been lost. I am proposing a proper statistic feature that saves level statistics as a separate file.
Additional InformationI have edited the source code and leveraged the existing json code for save games to write an addition statistics file. Basically, it starts clean on a new game and writes all completed level statistics to a json file when a level is exited.

I have attached stats_test_code.txt with the necessary code changes to write level statistics. It may not be the best way but I'm hoping it's helpful. I have also attached stats.zip which is the output that is generated from completing e1m1 and e1m2.
TagsNo tags attached.

Relationships

Activities

hobomaster22

hobomaster22

2017-03-27 08:27

reporter  

stats.zip (287 bytes)
stats_test_code.txt (2,410 bytes)
g_level.h
------------------
struct FLevelStats
{
	FString MapName;

	int			maptime;		// time in the map
	int			totaltime;		// time in the game

	int			total_secrets;
	int			found_secrets;

	int			total_items;
	int			found_items;

	int			total_monsters;
	int			killed_monsters;
};

void LevelLocalToStat(FLevelStats& stat, FLevelLocals& level);

g_level.cpp
------------------
TArray<FLevelStats> levelstats; //level statistics since new game has started

void LevelLocalToStat(FLevelStats& stat, FLevelLocals& level)
{
	stat.MapName = level.MapName;
	stat.MapName.ToLower();
	stat.maptime = level.maptime;
	stat.totaltime = level.totaltime;

	stat.total_secrets = level.total_secrets;
	stat.found_secrets = level.found_secrets;

	stat.total_items = level.total_items;
	stat.found_items = level.found_items;

	stat.total_monsters = level.total_monsters;
	stat.killed_monsters = level.killed_monsters;
}

void G_NewInit ()
{
	...G_NewInit code
	
	levelstats.Clear();
}

void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill)
{
	FSerializer arc;

	if (arc.OpenWriter())
	{
		Renderer->StartSerialize(arc);

		FLevelStats stat;
		LevelLocalToStat(stat, level);
		levelstats.Push(stat);

		arc.BeginArray("levels");
		unsigned int count = levelstats.Size();
		for (unsigned int i = 0; i < count; i++)
			Serialize(arc, nullptr, levelstats[i]);
		arc.EndArray();

		Renderer->EndSerialize(arc);

		FCompressedBuffer buffer = arc.GetCompressedOutput();

		TArray<FString> filenames;
		TArray<FCompressedBuffer> content;
		filenames.Push("maps.json");
		content.Push(buffer);

		WriteZip("stats.zip", filenames, content);
	}

	....G_ChangeLevel
}

serializer.h
------------------
FSerializer &Serialize(FSerializer &arc, const char *key, FLevelStats &stats);

d_protocol.cpp
------------------
FSerializer &Serialize(FSerializer &arc, const char *key, FLevelStats &stats)
{
	if (arc.BeginObject(key))
	{
		arc("MapName", stats.MapName)
			("found_secrets", stats.found_secrets)
			("found_items", stats.found_items)
			("killed_monsters", stats.killed_monsters)
			("total_secrets", stats.total_secrets)
			("total_items", stats.total_items)
			("total_monsters", stats.total_monsters)
			("maptime", stats.maptime)
			("totaltime", stats.totaltime)
			.EndObject();
	}

	return arc;
}
stats_test_code.txt (2,410 bytes)
Graf Zahl

Graf Zahl

2017-03-28 15:15

administrator   ~0001140

Look what you can find in globals.json in the savegame:

    "statistics": {
        "startlevel": "map01",
        "levels": [{
            "totalkills": 19,
            "killcount": 19,
            "totalsecrets": 5,
            "secretcount": 0,
            "leveltime": 294,
            "levelname": "map01"
        },
        {
            "totalkills": 70,
            "killcount": 0,
            "totalsecrets": 1,
            "secretcount": 0,
            "leveltime": 95,
            "levelname": "MAP02"
        }]
    },


All there, except the items...
Doing this to a separate file comes with a large can of worms attached that are not trivially solvable.
hobomaster22

hobomaster22

2017-03-28 15:36

reporter   ~0001143

Thanks, this works. While I'm here I will bring up the issue of the last level in an episode will not be written to the statistics in a save game (like E1M8). Should I modify this request to add items and maybe a solution for writing statistics for the last level in an episode? Or should it be a new one entirely?
Graf Zahl

Graf Zahl

2017-03-28 18:24

administrator   ~0001155

That's because there is no save after the last level.
The statistics are gathered to be output to a text file that gets written at the end of an episode. That's not JSON but formatted for human readability.

Check these two CVARs:
CVAR(Int, savestatistics, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(String, statfile, "zdoomstat.txt", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)

It may make sense to replace or complement that with JSON, now that a parser is present, it would certainly make it easier to process that data by external tools.
hobomaster22

hobomaster22

2017-03-29 07:28

reporter   ~0001164

I had made a post about the savestatistics flag a while back. It would be incredibly useful if the savestatistics flag was changed to write the zdoomstat.txt file on every level exit rather than the end of an episode. This is more or less what I proposed. Like I said my exact implementation that I originally posted may not be the best, it was an example. If savestatistics could write the file on every level exit, json or not, that would be a huge improvement.

Issue History

Date Modified Username Field Change
2017-03-27 08:27 hobomaster22 New Issue
2017-03-27 08:27 hobomaster22 File Added: stats.zip
2017-03-27 08:27 hobomaster22 File Added: stats_test_code.txt
2017-03-28 15:15 Graf Zahl Note Added: 0001140
2017-03-28 15:15 Graf Zahl Status new => closed
2017-03-28 15:15 Graf Zahl Resolution open => won't fix
2017-03-28 15:36 hobomaster22 Note Added: 0001143
2017-03-28 18:24 Graf Zahl Note Added: 0001155
2017-03-29 07:28 hobomaster22 Note Added: 0001164