mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-03 14:42:53 +00:00
Improve Demo end handing
- Demos/Ghosts that end before ticking once are now correctly ignored. (Resolves KartKrew/RingRacers#168) - There was code for discovering it on read! It was just placed slightly too early, probably due to the conversion for netreplays! I'm very mad! - As a preventative measure, demos *recorded* before ticking will simply not save in the first place. - This was also a frustratingly easy fix for the amount of headache it's caused us. - Reduced the amount of copypasted boilerplate by simplifying the places where DEMOMARKER can be written (and therefore read). - Previously, like half the write functions tried to guess their own output size and potentially end the demo at any point. - At best, this will grant us a few tics of reprireve for large netgames and MAYBE a handful of seconds for time attack, The Mode In Which The Aim Is To Go Fast. - Instead, double the size of the deadspace buffer extension and just check to see if we've crossed into that territory.
This commit is contained in:
parent
565733224f
commit
9e0510d674
3 changed files with 111 additions and 122 deletions
194
src/g_demo.cpp
194
src/g_demo.cpp
|
|
@ -270,6 +270,26 @@ static ticcmd_t oldcmd[MAXPLAYERS];
|
|||
|
||||
static mobj_t oldghost[MAXPLAYERS];
|
||||
|
||||
boolean G_ConsiderEndingDemoWrite(void)
|
||||
{
|
||||
// chill, we reserved extra memory so it's
|
||||
// "safe" to have written a bit past the end
|
||||
if (demobuf.p < demobuf.end)
|
||||
return false;
|
||||
|
||||
G_CheckDemoStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean G_ConsiderEndingDemoRead(void)
|
||||
{
|
||||
if (*demobuf.p != DEMOMARKER)
|
||||
return false;
|
||||
|
||||
G_CheckDemoStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void G_ReadDemoExtraData(void)
|
||||
{
|
||||
INT32 p, extradata, i;
|
||||
|
|
@ -460,13 +480,6 @@ void G_ReadDemoExtraData(void)
|
|||
|
||||
p = READUINT8(demobuf.p);
|
||||
}
|
||||
|
||||
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteDemoExtraData(void)
|
||||
|
|
@ -623,13 +636,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
|||
}
|
||||
|
||||
G_CopyTiccmd(cmd, &oldcmd[playernum], 1);
|
||||
|
||||
if (!(demoflags & DF_GHOST) && *demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
||||
|
|
@ -739,14 +745,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
|
|||
|
||||
WRITEUINT16(botziptic_p, botziptic);
|
||||
}
|
||||
|
||||
// attention here for the ticcmd size!
|
||||
// latest demos with mouse aiming byte in ticcmd
|
||||
if (!(demoflags & DF_GHOST) && ziptic_p > demobuf.end - 9)
|
||||
{
|
||||
G_CheckDemoStatus(); // no more space
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_GhostAddFlip(INT32 playernum)
|
||||
|
|
@ -794,8 +792,11 @@ void G_GhostAddHit(INT32 playernum, mobj_t *victim)
|
|||
|
||||
void G_WriteAllGhostTics(void)
|
||||
{
|
||||
boolean toobig = false;
|
||||
INT32 i, counter = leveltime;
|
||||
|
||||
if (!demobuf.p || !(demoflags & DF_GHOST))
|
||||
return; // No ghost data to write.
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
|
|
@ -811,22 +812,9 @@ void G_WriteAllGhostTics(void)
|
|||
|
||||
WRITEUINT8(demobuf.p, i);
|
||||
G_WriteGhostTic(players[i].mo, i);
|
||||
|
||||
// attention here for the ticcmd size!
|
||||
// latest demos with mouse aiming byte in ticcmd
|
||||
if (demobuf.p >= demobuf.end - (13 + 9 + 9))
|
||||
{
|
||||
toobig = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WRITEUINT8(demobuf.p, 0xFF);
|
||||
|
||||
if (toobig)
|
||||
{
|
||||
G_CheckDemoStatus(); // no more space
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
|
||||
|
|
@ -835,11 +823,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
|
|||
UINT8 *ziptic_p;
|
||||
UINT32 i;
|
||||
|
||||
if (!demobuf.p)
|
||||
return;
|
||||
if (!(demoflags & DF_GHOST))
|
||||
return; // No ghost data to write.
|
||||
|
||||
ziptic_p = demobuf.p++; // the ziptic, written at the end of this function
|
||||
|
||||
#define MAXMOM (0xFFFF<<8)
|
||||
|
|
@ -1061,7 +1044,7 @@ void G_ConsAllGhostTics(void)
|
|||
{
|
||||
UINT8 p;
|
||||
|
||||
if (!demobuf.p || !demo.deferstart)
|
||||
if (!demobuf.p || !(demoflags & DF_GHOST) || !demo.deferstart)
|
||||
return;
|
||||
|
||||
p = READUINT8(demobuf.p);
|
||||
|
|
@ -1071,13 +1054,6 @@ void G_ConsAllGhostTics(void)
|
|||
G_ConsGhostTic(p);
|
||||
p = READUINT8(demobuf.p);
|
||||
}
|
||||
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Uses ghost data to do consistency checks on your position.
|
||||
|
|
@ -1089,9 +1065,6 @@ void G_ConsGhostTic(INT32 playernum)
|
|||
mobj_t *testmo;
|
||||
UINT32 syncleeway;
|
||||
|
||||
if (!(demoflags & DF_GHOST))
|
||||
return; // No ghost data to use.
|
||||
|
||||
testmo = players[playernum].mo;
|
||||
|
||||
// Grab ghost data.
|
||||
|
|
@ -1282,13 +1255,6 @@ void G_ConsGhostTic(INT32 playernum)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
// end of demo data stream
|
||||
G_CheckDemoStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void G_GhostTicker(void)
|
||||
|
|
@ -1309,11 +1275,31 @@ void G_GhostTicker(void)
|
|||
continue;
|
||||
|
||||
readghosttic:
|
||||
#define follow g->mo->tracer
|
||||
|
||||
// Skip normal demo data.
|
||||
ziptic = READUINT8(g->p);
|
||||
xziptic = 0;
|
||||
|
||||
// Demo ends after ghost data.
|
||||
if (ziptic == DEMOMARKER)
|
||||
{
|
||||
fadeghost:
|
||||
g->mo->momx = g->mo->momy = g->mo->momz = 0;
|
||||
g->mo->fuse = TICRATE;
|
||||
if (follow)
|
||||
{
|
||||
follow->fuse = TICRATE;
|
||||
}
|
||||
|
||||
g->done = true;
|
||||
if (p)
|
||||
{
|
||||
p->next = g->next;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
while (ziptic != DW_END) // Get rid of extradata stuff
|
||||
{
|
||||
if (ziptic < MAXPLAYERS)
|
||||
|
|
@ -1414,9 +1400,11 @@ readghosttic:
|
|||
// Grab ghost data.
|
||||
ziptic = READUINT8(g->p);
|
||||
|
||||
if (ziptic == DEMOMARKER) // Had to end early for some reason
|
||||
goto fadeghost;
|
||||
if (ziptic == 0xFF)
|
||||
goto skippedghosttic; // Didn't write ghost info this frame
|
||||
else if (ziptic != 0)
|
||||
if (ziptic != 0)
|
||||
I_Error("Ghost is not a record attack ghost ZIPTIC"); //@TODO lmao don't blow up like this
|
||||
ziptic = READUINT8(g->p);
|
||||
|
||||
|
|
@ -1530,7 +1518,6 @@ readghosttic:
|
|||
g->mo->renderflags &= ~RF_DONTDRAW;
|
||||
}
|
||||
|
||||
#define follow g->mo->tracer
|
||||
if (ziptic & GZT_FOLLOW)
|
||||
{ // Even more...
|
||||
UINT8 followtic = READUINT8(g->p);
|
||||
|
|
@ -1620,28 +1607,6 @@ skippedghosttic:
|
|||
if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here.
|
||||
I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this
|
||||
|
||||
// Demo ends after ghost data.
|
||||
if (*g->p == DEMOMARKER)
|
||||
{
|
||||
g->mo->momx = g->mo->momy = g->mo->momz = 0;
|
||||
#if 0 // freeze frame (maybe more useful for time attackers) (2024-03-11: you leave it behind anyway!)
|
||||
g->mo->colorized = true;
|
||||
g->mo->fuse = 10*TICRATE;
|
||||
if (follow)
|
||||
follow->colorized = true;
|
||||
#else // dissapearing act
|
||||
g->mo->fuse = TICRATE;
|
||||
if (follow)
|
||||
follow->fuse = TICRATE;
|
||||
#endif
|
||||
g->done = true;
|
||||
if (p)
|
||||
{
|
||||
p->next = g->next;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the timer started, skip ahead until the ghost starts too.
|
||||
if (starttime <= leveltime && !g->linecrossed && G_TimeAttackStart())
|
||||
goto readghosttic;
|
||||
|
|
@ -1885,7 +1850,7 @@ void G_RecordDemo(const char *name)
|
|||
functions will check if they overran the buffer, but it
|
||||
should be safe enough because they'll think there's less
|
||||
memory than there actually is and stop early. */
|
||||
const size_t deadspace = 1024;
|
||||
const size_t deadspace = 2048;
|
||||
I_Assert(demobuf.size > deadspace);
|
||||
demobuf.size -= deadspace;
|
||||
demobuf.end -= deadspace;
|
||||
|
|
@ -3363,22 +3328,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
// Load "mapmusrng" used for altmusic selection
|
||||
mapmusrng = READUINT8(demobuf.p);
|
||||
|
||||
// Sigh ... it's an empty demo.
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
|
||||
CONS_Alert(CONS_ERROR, "%s", msg);
|
||||
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Z_Free(demo.skinlist);
|
||||
demo.skinlist = NULL;
|
||||
Z_Free(pdemoname);
|
||||
Z_Free(demobuf.buffer);
|
||||
demo.playback = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Z_Free(pdemoname);
|
||||
|
||||
memset(&oldcmd,0,sizeof(oldcmd));
|
||||
memset(&oldghost,0,sizeof(oldghost));
|
||||
memset(&ghostext,0,sizeof(ghostext));
|
||||
|
|
@ -3607,6 +3556,22 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
players[p].lastfakeskin = lastfakeskin[p];
|
||||
}
|
||||
|
||||
// Sigh ... it's an empty demo.
|
||||
if (*demobuf.p == DEMOMARKER)
|
||||
{
|
||||
snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname);
|
||||
CONS_Alert(CONS_ERROR, "%s", msg);
|
||||
M_StartMessage("Demo Playback", msg, NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Z_Free(demo.skinlist);
|
||||
demo.skinlist = NULL;
|
||||
Z_Free(pdemoname);
|
||||
Z_Free(demobuf.buffer);
|
||||
demo.playback = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Z_Free(pdemoname);
|
||||
|
||||
demo.deferstart = true;
|
||||
|
||||
CV_StealthSetValue(&cv_playbackspeed, 1);
|
||||
|
|
@ -3756,14 +3721,6 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
|||
|
||||
p++; // mapmusrng
|
||||
|
||||
if (*p == DEMOMARKER)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
|
||||
Z_Free(skinlist);
|
||||
P_SaveBufferFree(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once
|
||||
|
||||
// any invalidating flags?
|
||||
|
|
@ -3811,6 +3768,14 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
|
|||
return;
|
||||
}
|
||||
|
||||
if (*p == DEMOMARKER)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), defdemoname);
|
||||
Z_Free(skinlist);
|
||||
P_SaveBufferFree(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
gh = static_cast<demoghost*>(Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL));
|
||||
gh->sizes = ghostsizes;
|
||||
|
|
@ -4173,7 +4138,7 @@ boolean G_CheckDemoStatus(void)
|
|||
// Keep the demo open and don't boot to intermission
|
||||
// YET, pause demo playback.
|
||||
if (!demo.waitingfortally && modeattacking && exitcountdown)
|
||||
demo.waitingfortally = true;
|
||||
;
|
||||
else if (!demo.attract)
|
||||
G_FinishExitLevel();
|
||||
else
|
||||
|
|
@ -4191,6 +4156,8 @@ boolean G_CheckDemoStatus(void)
|
|||
D_SetDeferredStartTitle(true);
|
||||
}
|
||||
|
||||
demo.waitingfortally = true; // if we've returned early for some reason...
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4235,6 +4202,13 @@ void G_SaveDemo(void)
|
|||
if (currentMenu == &TitleEntryDef)
|
||||
M_ClearMenus(true);
|
||||
|
||||
if (!leveltime)
|
||||
{
|
||||
// Why would you save if nothing has been recorded
|
||||
G_ResetDemoRecording();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure extrainfo pointer is always available, even if no info is present.
|
||||
if (demoinfo_p && *(UINT32 *)demoinfo_p == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -172,6 +172,8 @@ extern UINT8 demo_writerng;
|
|||
boolean G_CompatLevel(UINT16 level);
|
||||
|
||||
// Record/playback tics
|
||||
boolean G_ConsiderEndingDemoRead(void);
|
||||
boolean G_ConsiderEndingDemoWrite(void);
|
||||
void G_ReadDemoExtraData(void);
|
||||
void G_WriteDemoExtraData(void);
|
||||
void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
|
||||
|
|
|
|||
37
src/p_tick.c
37
src/p_tick.c
|
|
@ -762,17 +762,23 @@ void P_Ticker(boolean run)
|
|||
|
||||
if (demo.recording)
|
||||
{
|
||||
G_WriteDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_WriteDemoTiccmd(&players[i].cmd, i);
|
||||
if (!G_ConsiderEndingDemoWrite())
|
||||
{
|
||||
G_WriteDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_WriteDemoTiccmd(&players[i].cmd, i);
|
||||
}
|
||||
}
|
||||
if (demo.playback && !demo.waitingfortally)
|
||||
{
|
||||
G_ReadDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_ReadDemoTiccmd(&players[i].cmd, i);
|
||||
if (!G_ConsiderEndingDemoRead())
|
||||
{
|
||||
G_ReadDemoExtraData();
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
G_ReadDemoTiccmd(&players[i].cmd, i);
|
||||
}
|
||||
}
|
||||
|
||||
LUA_ResetTicTimers();
|
||||
|
|
@ -1168,14 +1174,21 @@ void P_Ticker(boolean run)
|
|||
|
||||
if (demo.recording)
|
||||
{
|
||||
G_WriteAllGhostTics();
|
||||
|
||||
if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
|
||||
G_CheckDemoTitleEntry();
|
||||
|
||||
if (!G_ConsiderEndingDemoWrite())
|
||||
{
|
||||
G_WriteAllGhostTics();
|
||||
}
|
||||
}
|
||||
else if (demo.playback && !demo.waitingfortally) // Use Ghost data for consistency checks.
|
||||
else if (demo.playback && !demo.waitingfortally)
|
||||
{
|
||||
G_ConsAllGhostTics();
|
||||
if (!G_ConsiderEndingDemoRead())
|
||||
{
|
||||
// Use Ghost data for consistency checks.
|
||||
G_ConsAllGhostTics();
|
||||
}
|
||||
}
|
||||
|
||||
if (modeattacking)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue