Make custom stats fully compatible between different versions, so new fields can be added in future without save corruptions
This commit is contained in:
parent
c8e78711a6
commit
1922870773
2 changed files with 94 additions and 14 deletions
|
|
@ -2,6 +2,7 @@
|
|||
#include "doomstat.h"
|
||||
#include "g_game.h"
|
||||
#include "byteptr.h"
|
||||
#include "z_zone.h"
|
||||
|
||||
kartstats_t kartstats = {0};
|
||||
|
||||
|
|
@ -65,6 +66,8 @@ void K_StatRound(void)
|
|||
if (demo.playback)
|
||||
return;
|
||||
|
||||
kartstats.somenewfield = 42;
|
||||
|
||||
int numplayers = 0;
|
||||
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
|
|
@ -87,23 +90,18 @@ void K_EraseStats(void)
|
|||
// The only field we want to remember
|
||||
boolean vanilla = kartstats.vanilla;
|
||||
|
||||
if (kartstats.copy)
|
||||
Z_Free(kartstats.copy);
|
||||
|
||||
memset(&kartstats, 0, sizeof(kartstats_t));
|
||||
|
||||
kartstats.vanilla = vanilla;
|
||||
}
|
||||
|
||||
void K_ReadStats(savebuffer_t *save, boolean vanilla)
|
||||
static void K_ReadStatsCustom(savebuffer_t *save)
|
||||
{
|
||||
memset(&kartstats, 0, sizeof(kartstats_t));
|
||||
|
||||
kartstats.vanilla = vanilla;
|
||||
|
||||
kartstats.totalplaytime = READUINT32(save->p);
|
||||
kartstats.matchesplayed = READUINT32(save->p);
|
||||
|
||||
// Vanilla only has those 2
|
||||
if (vanilla)
|
||||
return;
|
||||
kartstats.size = READUINT32(save->p);
|
||||
kartstats.version = READUINT32(save->p);
|
||||
|
||||
// So many similar-looking read's... scary...
|
||||
kartstats.raplaytime = READUINT32(save->p);
|
||||
|
|
@ -119,17 +117,51 @@ void K_ReadStats(savebuffer_t *save, boolean vanilla)
|
|||
kartstats.sinks = READUINT32(save->p);
|
||||
kartstats.sinked = READUINT32(save->p);
|
||||
kartstats.respawns = READUINT32(save->p);
|
||||
|
||||
// If kartstats gets updated, uncomment this and read next fields after this early return. Do same on next updates,
|
||||
// this way even data is read on different versions, it doesn't get corrupted (as long as fields aren't removed, which shouldn't happen)
|
||||
//if (kartstats.version < 2)
|
||||
// return;
|
||||
//
|
||||
//kartstats.somenewfield = READUINT32(save->p);
|
||||
}
|
||||
|
||||
void K_WriteStats(savebuffer_t *save, boolean vanilla)
|
||||
void K_ReadStats(savebuffer_t *save, boolean vanilla)
|
||||
{
|
||||
WRITEUINT32(save->p, kartstats.totalplaytime);
|
||||
WRITEUINT32(save->p, kartstats.matchesplayed);
|
||||
// Basically, free old kartstats.copy if needed and memset kartstats with zeros
|
||||
K_EraseStats();
|
||||
|
||||
kartstats.vanilla = vanilla;
|
||||
|
||||
kartstats.totalplaytime = READUINT32(save->p);
|
||||
kartstats.matchesplayed = READUINT32(save->p);
|
||||
|
||||
// Vanilla only has those 2
|
||||
if (vanilla)
|
||||
return;
|
||||
|
||||
// Save where block of custom stats starts
|
||||
UINT8 *customblockstart = save->p;
|
||||
|
||||
K_ReadStatsCustom(save);
|
||||
|
||||
// Make a copy of entire custom stats block, so unread values will still be written back
|
||||
kartstats.copy = Z_Malloc(kartstats.size, PU_STATIC, NULL);
|
||||
memcpy(kartstats.copy, customblockstart, kartstats.size);
|
||||
|
||||
// If there were extra fields we couldn't read, skip them
|
||||
save->p = customblockstart + kartstats.size;
|
||||
}
|
||||
|
||||
static void K_WriteStatsCustom(savebuffer_t *save)
|
||||
{
|
||||
// Update, if needed
|
||||
kartstats.version = max(kartstats.version, KARTSTATSVERSION);
|
||||
|
||||
WRITEUINT32(save->p, kartstats.size); // Note: if we will end up writing more that stored in there, this field in save file would be updated
|
||||
WRITEUINT32(save->p, kartstats.version);
|
||||
|
||||
// Version 1
|
||||
WRITEUINT32(save->p, kartstats.raplaytime);
|
||||
WRITEUINT32(save->p, kartstats.onlineplaytime);
|
||||
WRITEUINT32(save->p, kartstats.raceplaytime);
|
||||
|
|
@ -143,4 +175,42 @@ void K_WriteStats(savebuffer_t *save, boolean vanilla)
|
|||
WRITEUINT32(save->p, kartstats.sinks);
|
||||
WRITEUINT32(save->p, kartstats.sinked);
|
||||
WRITEUINT32(save->p, kartstats.respawns);
|
||||
|
||||
// No need for early returns, but please mark each new block of write's with version it corresponds to :3
|
||||
|
||||
// Version 2
|
||||
//WRITEUINT32(save->p, kartstats.somenewfield);
|
||||
}
|
||||
|
||||
void K_WriteStats(savebuffer_t *save, boolean vanilla)
|
||||
{
|
||||
WRITEUINT32(save->p, kartstats.totalplaytime);
|
||||
WRITEUINT32(save->p, kartstats.matchesplayed);
|
||||
|
||||
// Vanilla only has those 2
|
||||
if (vanilla)
|
||||
return;
|
||||
|
||||
// Now need to be careful... We save start of custom stats block
|
||||
UINT8 *customblockstart = save->p;
|
||||
|
||||
// Write everything we know how to write
|
||||
K_WriteStatsCustom(save);
|
||||
|
||||
// Calculate size of data that we wrote
|
||||
UINT32 size = (UINT32)(save->p - customblockstart);
|
||||
|
||||
// Now, if we wrote less data than save originally had, that means our copy that we kept in K_ReadStats
|
||||
// has stats from newer version that we need to append
|
||||
if (size < kartstats.size)
|
||||
{
|
||||
I_Assert(kartstats.copy != NULL); // If kartstats.copy is null, kartstats.size **always** should be 0, this can only happen if we create new save
|
||||
WRITEMEM(save->p, kartstats.copy + size, kartstats.size - size);
|
||||
}
|
||||
else if (size > kartstats.size) // No need to do anything if we wrote exactly same amount...
|
||||
{
|
||||
// ...but, if we wrote more, we need to update size field in header. **It's always located at start of block**
|
||||
// So we can just:
|
||||
WRITEUINT32(customblockstart, size);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include "d_player.h"
|
||||
#include "p_saveg.h"
|
||||
|
||||
#define KARTSTATSVERSION 1
|
||||
|
||||
typedef struct kartstats_s {
|
||||
// Remember if stats we loaded are vanilla ones or not
|
||||
boolean vanilla;
|
||||
|
|
@ -13,6 +15,12 @@ typedef struct kartstats_s {
|
|||
tic_t totalplaytime;
|
||||
UINT32 matchesplayed;
|
||||
|
||||
// Custom data, fields go in same order as they are written into game save
|
||||
|
||||
// IMPORTANT: size **must be first**, so K_WriteStats can overwrite it (not that we're gonna change order, but just in case)
|
||||
UINT32 size; // Size required for our stats. If older version reads save from newer one, it will just skip unread fields, avoiding reading corrupted values
|
||||
UINT32 version; // Version, so we know which fields we actually can read
|
||||
|
||||
tic_t raplaytime;
|
||||
tic_t onlineplaytime;
|
||||
tic_t raceplaytime;
|
||||
|
|
@ -26,6 +34,8 @@ typedef struct kartstats_s {
|
|||
UINT32 sinks; // Times hitting *others* by kitchen sink
|
||||
UINT32 sinked; // Times *being hit* by kitchen sink
|
||||
UINT32 respawns;
|
||||
|
||||
UINT8 *copy; // Copy of stats block that has been read from file. In case we're older version that reads newer save, we will keep values that we couldn't read
|
||||
} kartstats_t;
|
||||
|
||||
extern kartstats_t kartstats;
|
||||
|
|
|
|||
Loading…
Reference in a new issue