mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-01-09 16:32:33 +00:00
Improve GP Backup handling to be more flexible
Since it's a user-facing feature and not a speedrunner's assistant...
- If an issue is discovered, make a CONS_Alert instead of a complete failure and let the user know
- Replaces the unhelpful error of "Savegame %s corrupted"
- Store skins and cups as strings, not IDs
- Permits playing with differing mod loadouts if everything important is still loaded
- Won't error for bots having not-loaded skins - those are converted into Eggrobos.
- Instead of storing full roundqueue data, rebuild from the cup and check if the important details match
- Prevents having to include map name strings
- Add minor version to GP backups
- Permits more flexibility in future
This commit is contained in:
parent
2eef39e079
commit
0d827ab05e
2 changed files with 101 additions and 51 deletions
21
src/g_game.c
21
src/g_game.c
|
|
@ -5654,10 +5654,14 @@ void G_SaveGameData(void)
|
|||
// G_InitFromSavegame
|
||||
// Can be called by the startup code or the menu task.
|
||||
//
|
||||
|
||||
#define SAV_VERSIONMINOR 1
|
||||
|
||||
void G_LoadGame(void)
|
||||
{
|
||||
char vcheck[VERSIONSIZE];
|
||||
char vcheck[VERSIONSIZE+1];
|
||||
char savename[255];
|
||||
UINT8 versionMinor;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
// memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots
|
||||
|
|
@ -5674,11 +5678,15 @@ void G_LoadGame(void)
|
|||
return;
|
||||
}
|
||||
|
||||
versionMinor = READUINT8(save.p);
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, "version %d", VERSION);
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck))
|
||||
|
||||
if (versionMinor != SAV_VERSIONMINOR
|
||||
|| memcmp(save.p, vcheck, VERSIONSIZE))
|
||||
{
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Save game %s from different version"), savename), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s is from\na different version."), savename), NULL, MM_NOTHING, NULL, NULL);
|
||||
P_SaveBufferFree(&save);
|
||||
return; // bad version
|
||||
}
|
||||
|
|
@ -5693,7 +5701,8 @@ void G_LoadGame(void)
|
|||
// dearchive all the modifications
|
||||
if (!P_LoadGame(&save))
|
||||
{
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s corrupted\n"), savename), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s could not be loaded.\n"
|
||||
"Check the console log for more info.\n"), savename), NULL, MM_NOTHING, NULL, NULL);
|
||||
Z_Free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
|
||||
|
|
@ -5723,7 +5732,7 @@ void G_SaveGame(void)
|
|||
|
||||
gameaction = ga_nothing;
|
||||
{
|
||||
char name[VERSIONSIZE];
|
||||
char name[VERSIONSIZE+1];
|
||||
size_t length;
|
||||
|
||||
if (P_SaveBufferAlloc(&save, SAVEGAMESIZE) == false)
|
||||
|
|
@ -5732,6 +5741,8 @@ void G_SaveGame(void)
|
|||
return;
|
||||
}
|
||||
|
||||
WRITEUINT8(save.p, SAV_VERSIONMINOR);
|
||||
|
||||
memset(name, 0, sizeof (name));
|
||||
sprintf(name, "version %d", VERSION);
|
||||
WRITEMEM(save.p, name, VERSIONSIZE);
|
||||
|
|
|
|||
131
src/p_saveg.c
131
src/p_saveg.c
|
|
@ -88,9 +88,18 @@ static inline void P_ArchivePlayer(savebuffer_t *save)
|
|||
WRITEUINT32(save->p, player->score);
|
||||
WRITEUINT16(save->p, player->totalring);
|
||||
|
||||
WRITEUINT8(save->p, player->skin);
|
||||
INT32 skin = player->skin;
|
||||
if (skin > numskins)
|
||||
skin = 0;
|
||||
|
||||
WRITESTRINGN(save->p, skins[skin].name, SKINNAMESIZE);
|
||||
|
||||
if (player->followerskin < 0 || player->followerskin >= numfollowers)
|
||||
WRITESTRINGN(save->p, "None", SKINNAMESIZE);
|
||||
else
|
||||
WRITESTRINGN(save->p, followers[player->followerskin].name, SKINNAMESIZE);
|
||||
|
||||
WRITEUINT16(save->p, player->skincolor);
|
||||
WRITEINT32(save->p, player->followerskin);
|
||||
WRITEUINT16(save->p, player->followercolor);
|
||||
|
||||
UINT8 i;
|
||||
|
|
@ -104,7 +113,11 @@ static inline void P_ArchivePlayer(savebuffer_t *save)
|
|||
|
||||
WRITEUINT8(save->p, i);
|
||||
|
||||
WRITEUINT8(save->p, players[i].skin);
|
||||
skin = players[i].skin;
|
||||
if (skin > numskins)
|
||||
skin = 0;
|
||||
|
||||
WRITESTRINGN(save->p, skins[skin].name, SKINNAMESIZE);
|
||||
|
||||
WRITEUINT8(save->p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(save->p, (UINT8)players[i].botvars.rival);
|
||||
|
|
@ -121,22 +134,48 @@ static boolean P_UnArchivePlayer(savebuffer_t *save)
|
|||
savedata.score = READUINT32(save->p);
|
||||
savedata.totalring = READUINT16(save->p);
|
||||
|
||||
savedata.skin = READUINT8(save->p);
|
||||
savedata.skincolor = READUINT16(save->p);
|
||||
savedata.followerskin = READINT32(save->p);
|
||||
savedata.followercolor = READUINT16(save->p);
|
||||
char skinname[SKINNAMESIZE+1];
|
||||
INT32 skin;
|
||||
|
||||
if (savedata.skin >= numskins)
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
skin = R_SkinAvailable(skinname);
|
||||
|
||||
if (skin == -1)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchivePlayer: Character \"%s\" is not currently loaded.\n", skinname);
|
||||
return false;
|
||||
}
|
||||
|
||||
savedata.skin = skin;
|
||||
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
savedata.followerskin = K_FollowerAvailable(skinname);
|
||||
|
||||
savedata.skincolor = READUINT16(save->p);
|
||||
savedata.followercolor = READUINT16(save->p);
|
||||
|
||||
memset(&savedata.bots, 0, sizeof(savedata.bots));
|
||||
|
||||
UINT8 pid;
|
||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
|
||||
while ((pid = READUINT8(save->p)) < MAXPLAYERS)
|
||||
{
|
||||
savedata.bots[pid].valid = true;
|
||||
savedata.bots[pid].skin = READUINT8(save->p);
|
||||
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
skin = R_SkinAvailable(skinname);
|
||||
|
||||
if (skin == -1)
|
||||
{
|
||||
// It is not worth destroying an otherwise good savedata over extra added skins.
|
||||
// Let's just say they didn't show up to the rematch, so some Eggrobos subbed in.
|
||||
CONS_Alert(CONS_WARNING, "P_UnArchivePlayer: Bot's character \"%s\" was not loaded, replacing with default \"%s\".\n", skinname, skins[defaultbotskin].name);
|
||||
skin = defaultbotskin;
|
||||
}
|
||||
|
||||
savedata.bots[pid].skin = skin;
|
||||
|
||||
savedata.bots[pid].difficulty = READUINT8(save->p);
|
||||
savedata.bots[pid].rival = (boolean)READUINT8(save->p);
|
||||
savedata.bots[pid].score = READUINT32(save->p);
|
||||
|
|
@ -5304,16 +5343,12 @@ static void P_NetUnArchiveSpecials(savebuffer_t *save)
|
|||
// =======================================================================
|
||||
static inline void P_ArchiveMisc(savebuffer_t *save)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
WRITESTRINGN(save->p, timeattackfolder, sizeof(timeattackfolder));
|
||||
|
||||
WRITEUINT8(save->p, grandprixinfo.gamespeed);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.encore);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.masterbots);
|
||||
|
||||
WRITEUINT16(save->p, grandprixinfo.cup->id);
|
||||
|
||||
{
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.players);
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.totalPlayers);
|
||||
|
|
@ -5338,18 +5373,12 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, (UINT8)grandprixinfo.rank.specialWon);
|
||||
}
|
||||
|
||||
WRITESTRINGL(save->p, grandprixinfo.cup->name, MAXCUPNAME);
|
||||
|
||||
WRITEUINT8(save->p, roundqueue.position);
|
||||
WRITEUINT8(save->p, roundqueue.size);
|
||||
WRITEUINT8(save->p, roundqueue.roundnum);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
WRITEUINT16(save->p, roundqueue.entries[i].mapnum);
|
||||
WRITEUINT8(save->p, roundqueue.entries[i].gametype);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].encore);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].rankrestricted);
|
||||
}
|
||||
|
||||
WRITEUINT8(save->p, (marathonmode & ~MA_INIT));
|
||||
|
||||
UINT32 writetime = marathontime;
|
||||
|
|
@ -5360,13 +5389,13 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
|
|||
|
||||
static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
||||
{
|
||||
UINT8 i;
|
||||
char testname[sizeof(timeattackfolder)];
|
||||
|
||||
READSTRINGN(save->p, testname, sizeof(testname));
|
||||
|
||||
if (strcmp(testname, timeattackfolder))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Corrupt mod ID.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -5383,18 +5412,6 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
|||
grandprixinfo.encore = (boolean)READUINT8(save->p);
|
||||
grandprixinfo.masterbots = (boolean)READUINT8(save->p);
|
||||
|
||||
UINT16 cupid = READUINT16(save->p);
|
||||
grandprixinfo.cup = kartcupheaders;
|
||||
while (grandprixinfo.cup)
|
||||
{
|
||||
if (grandprixinfo.cup->id == cupid)
|
||||
break;
|
||||
grandprixinfo.cup = grandprixinfo.cup->next;
|
||||
}
|
||||
|
||||
if (!grandprixinfo.cup)
|
||||
return false;
|
||||
|
||||
{
|
||||
grandprixinfo.rank.players = READUINT8(save->p);
|
||||
grandprixinfo.rank.totalPlayers = READUINT8(save->p);
|
||||
|
|
@ -5419,25 +5436,47 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
|||
grandprixinfo.rank.specialWon = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
char cupname[MAXCUPNAME];
|
||||
|
||||
// Find the relevant cup.
|
||||
READSTRINGL(save->p, cupname, sizeof(cupname));
|
||||
UINT32 hash = quickncasehash(cupname, MAXCUPNAME);
|
||||
|
||||
for (grandprixinfo.cup = kartcupheaders; grandprixinfo.cup; grandprixinfo.cup = grandprixinfo.cup->next)
|
||||
{
|
||||
if (grandprixinfo.cup->namehash != hash)
|
||||
continue;
|
||||
|
||||
if (strcmp(grandprixinfo.cup->name, cupname))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!grandprixinfo.cup)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Cup \"%s\" is not currently loaded.\n", cupname);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&roundqueue, 0, sizeof(roundqueue));
|
||||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
roundqueue.size = READUINT8(save->p);
|
||||
if (roundqueue.size > ROUNDQUEUE_MAX
|
||||
|| roundqueue.position > roundqueue.size)
|
||||
return false;
|
||||
G_GPCupIntoRoundQueue(grandprixinfo.cup, GT_RACE, grandprixinfo.encore);
|
||||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
UINT8 size = READUINT8(save->p);
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
if (roundqueue.size != size)
|
||||
{
|
||||
roundqueue.entries[i].mapnum = READUINT16(save->p);
|
||||
if (roundqueue.entries[i].mapnum >= nummapheaders)
|
||||
return false;
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Cup \"%s\"'s level composition has changed between game launches.\n", cupname);
|
||||
return false;
|
||||
}
|
||||
|
||||
roundqueue.entries[i].gametype = READUINT8(save->p);
|
||||
roundqueue.entries[i].encore = (boolean)READUINT8(save->p);
|
||||
roundqueue.entries[i].rankrestricted = (boolean)READUINT8(save->p);
|
||||
if (roundqueue.position == 0 || roundqueue.position > size)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Position in the round queue is invalid.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
marathonmode = READUINT8(save->p);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue