Merge branch 'staffsync' into 'master'

"Staffsync" command to test staff ghost playback

See merge request kart-krew-dev/ring-racers-internal!2744
This commit is contained in:
AJ Martinez 2025-08-20 02:08:56 +00:00
commit 2111e104ef
11 changed files with 255 additions and 5 deletions

View file

@ -1114,15 +1114,28 @@ void D_SRB2Loop(void)
//
// Wipes run an inner loop and artificially increase
// the measured time.
if (!ranwipe && frameskip < 3 && deltatics > 1.0)
if (staffsync)
{
frameskip++;
// SPECIAL: When checking staff demos for sync,
// draw as little as possible for speeeeeeed
if (frameskip < TICRATE*10)
frameskip++;
else
frameskip = 0;
}
else
{
frameskip = 0;
if (!ranwipe && frameskip < 3 && deltatics > 1.0)
{
frameskip++;
}
else
{
frameskip = 0;
}
}
if (world)
{
sincelastworld = 0.0;

View file

@ -193,6 +193,7 @@ static void Command_Eval(void);
static void Command_WriteTextmap(void);
static void Command_SnapshotMaps(void);
static void Command_Staffsync(void);
#ifdef DEVELOP
static void Command_FastForward(void);
@ -256,6 +257,8 @@ boolean forceresetplayers = false;
boolean deferencoremode = false;
boolean forcespecialstage = false;
boolean staffsync = false;
UINT8 splitscreen = 0;
INT32 adminplayers[MAXPLAYERS];
@ -451,6 +454,7 @@ void D_RegisterServerCommands(void)
COM_AddCommand("snapshotmap", Command_SnapshotMaps);
COM_AddCommand("snapshotmaps", Command_SnapshotMaps); // alias
COM_AddCommand("staffsync", Command_Staffsync);
// for master server connection
AddMServCommands();
@ -6776,6 +6780,141 @@ static void Command_SnapshotMaps(void)
CON_ToggleOff();
}
UINT32 staffsync_map = 0;
UINT32 staffsync_ghost = 0;
UINT32 staffsync_done = 0;
UINT32 staffsync_total = 0;
UINT32 staffsync_failed = 0;
staffsync_t staffsync_results[1024];
static void Command_Staffsync(void)
{
if (Playing())
{
CONS_Alert(CONS_ERROR, "This command cannot be used in-game. Return to the titlescreen first!\n");
return;
}
staffsync = true;
if (demo.playback)
return;
mapheader_t *mapheader;
staffbrief_t *staffbrief;
demo.loadfiles = false;
demo.ignorefiles = true;
mapheader = mapheaderinfo[staffsync_map];
// Test exit
if (false && staffsync_done == 9)
mapheader = NULL;
// Start of the run, init progress and report vars
if (staffsync_map == 0 && staffsync_ghost == 0)
{
staffsync_done = 0;
staffsync_total = 0;
staffsync_failed = 0;
memset(staffsync_results, 0, sizeof(staffsync_results));
for (INT32 i = 0; i < nummapheaders; i++)
{
if (mapheaderinfo[i] != NULL)
staffsync_total += mapheaderinfo[i]->ghostCount;
}
soundtest.shuffle = true;
S_UpdateSoundTestDef(false, false, true);
S_SoundTestPlay();
}
// The current configuration corresponds to a valid staff ghost, play it
if (mapheader != NULL && mapheader->ghostCount > 0 && staffsync_ghost < mapheader->ghostCount)
{
demo.timing = true;
g_singletics = true;
framecount = 0;
demostarttime = I_GetTime();
staffbrief = mapheader->ghostBrief[staffsync_ghost];
G_DoPlayDemoEx("", (staffbrief->wad << 16) | staffbrief->lump);
staffsync_ghost++;
staffsync_done++;
if (staffsync_done % 50 == 0)
{
S_UpdateSoundTestDef(false, true, true);
S_SoundTestPlay();
}
}
// We've exhausted map headers, report
if (mapheader == NULL)
{
CONS_Printf("========================================\n");
CONS_Printf("DESYNCING STAFF GHOSTS:\n");
UINT32 i = 0;
while (staffsync_results[i].reason != SYNC_NONE)
{
staffsync_t *result = &staffsync_results[i];
CONS_Printf("%s [%s] ", G_BuildMapName(result->map), result->name);
switch (result->reason)
{
case SYNC_CHARACTER:
CONS_Printf("(character/stats)");
break;
case SYNC_HEALTH:
CONS_Printf("(health)");
break;
case SYNC_ITEM:
CONS_Printf("(item/bumpers)");
break;
case SYNC_POSITION:
CONS_Printf("(position)");
break;
case SYNC_RNG:
CONS_Printf("(RNG class %d)", result->extra);
break;
}
CONS_Printf("\n");
i++;
}
CONS_Printf("========================================\n");
staffsync_ghost = 0;
staffsync_map = 0;
staffsync = false;
S_SoundTestTogglePause();
S_StartSound(NULL, sfx_tmxsuc);
S_StartSound(NULL, sfx_cftbl2);
CONS_Printf("%d / %d ghosts desync.\n", staffsync_failed, staffsync_done);
return;
}
// Out of ghosts for the current map, switch to the next map and re-exec
if (staffsync_ghost >= mapheader->ghostCount)
{
staffsync_ghost = 0;
staffsync_map++;
Command_Staffsync();
}
return;
}
#ifdef DEVELOP
static void Command_FastForward(void)
{

View file

@ -255,6 +255,17 @@ void Automate_Clear(void);
extern UINT32 livestudioaudience_timer;
void LiveStudioAudience(void);
typedef enum
{
SYNC_NONE,
SYNC_RNG,
SYNC_HEALTH,
SYNC_POSITION,
SYNC_ITEM,
SYNC_CHARACTER,
SYNC__MAX
} staffsync_reason_t;
void D_Cheat(INT32 playernum, INT32 cheat, ...);
// used for the player setup menu

View file

@ -238,6 +238,17 @@ extern UINT8 splitscreen;
extern int r_splitscreen;
extern boolean forceresetplayers, deferencoremode, forcespecialstage;
extern boolean staffsync;
extern UINT32 staffsync_map, staffsync_ghost, staffsync_done, staffsync_total, staffsync_failed;
struct staffsync_t
{
UINT32 map;
char name[MAXPLAYERNAME+1];
UINT32 reason;
UINT32 extra;
};
extern staffsync_t staffsync_results[1024];
// ========================================
// Internal parameters for sound rendering.

View file

@ -460,6 +460,9 @@ void F_RunWipe(UINT8 wipemode, UINT8 wipetype, boolean drawMenu, const char *col
lumpnum_t clump = LUMPERROR;
lighttable_t *fcolor = NULL;
if (staffsync)
return;
if (colormap != NULL)
clump = W_GetNumForName(colormap);

View file

@ -287,6 +287,21 @@ boolean G_ConsiderEndingDemoRead(void)
return true;
}
// Demo failed sync during a sync test! Log the failure to be reported later.
static void G_FailStaffSync(staffsync_reason_t reason, UINT32 extra)
{
if (!staffsync)
return;
if (staffsync_results[staffsync_failed].reason != 0)
return;
staffsync_results[staffsync_failed].map = gamemap;
memcpy(&staffsync_results[staffsync_failed].name, player_names[consoleplayer], sizeof(player_names[consoleplayer]));
staffsync_results[staffsync_failed].reason = reason;
staffsync_results[staffsync_failed].extra = extra;
}
void G_ReadDemoExtraData(void)
{
INT32 p, extradata, i;
@ -449,7 +464,11 @@ void G_ReadDemoExtraData(void)
P_SetRandSeed(static_cast<pr_class_t>(i), rng);
if (demosynced)
{
CONS_Alert(CONS_WARNING, "Demo playback has desynced (RNG class %d)!\n", i);
G_FailStaffSync(SYNC_RNG, i);
}
storesynced = false;
}
}
@ -1118,7 +1137,10 @@ void G_ConsGhostTic(INT32 playernum)
if (th != &thlist[THINK_MOBJ] && mobj->health != health) // Wasn't damaged?! This is desync! Fix it!
{
if (demosynced)
{
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced (health)!\n"));
G_FailStaffSync(SYNC_HEALTH, 0);
}
demosynced = false;
P_DamageMobj(mobj, players[0].mo, players[0].mo, 1, DMG_NORMAL);
}
@ -1180,7 +1202,10 @@ void G_ConsGhostTic(INT32 playernum)
if (ghostext[playernum].desyncframes >= 2)
{
if (demosynced)
{
CONS_Alert(CONS_WARNING, "Demo playback has desynced (player %s)!\n", player_names[playernum]);
G_FailStaffSync(SYNC_POSITION, 0);
}
demosynced = false;
P_UnsetThingPosition(testmo);
@ -1203,7 +1228,11 @@ void G_ConsGhostTic(INT32 playernum)
|| testmo->health != ghostext[playernum].health)
{
if (demosynced)
{
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced (item/bumpers)!\n"));
G_FailStaffSync(SYNC_HEALTH, 0);
}
demosynced = false;
players[playernum].itemtype = ghostext[playernum].itemtype;
@ -1217,7 +1246,11 @@ void G_ConsGhostTic(INT32 playernum)
demo.skinlist[ghostext[playernum].skinid].mapping != (UINT8)(((skin_t *)testmo->skin)->skinnum))
{
if (demosynced)
{
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced (Character/stats)!\n"));
G_FailStaffSync(SYNC_CHARACTER, 0);
}
demosynced = false;
testmo->skin = skins[demo.skinlist[ghostext[playernum].skinid].mapping];
@ -3904,6 +3937,12 @@ void G_StopDemo(void)
demo.waitingfortally = false;
g_singletics = false;
if (!demosynced && staffsync)
{
staffsync_failed++;
CONS_Printf("STAFF GHOST DESYNC on %s (%s)\n", G_BuildMapName(gamemap), player_names[consoleplayer]);
}
{
UINT8 i;
for (i = 0; i < MAXSPLITSCREENPLAYERS; ++i)

View file

@ -1882,7 +1882,14 @@ void G_Ticker(boolean run)
P_MapStart();
if (gamestate == GS_LEVEL && G_GetRetryFlag())
extern boolean demosynced;
if (demo.playback && staffsync && !demosynced)
{
G_ClearRetryFlag();
G_StopDemo();
Command_ExitGame_f();
}
else if (gamestate == GS_LEVEL && G_GetRetryFlag())
{
if (demo.playback == true)
{
@ -2042,6 +2049,8 @@ void G_Ticker(boolean run)
break;
case GS_MENU:
if (staffsync)
COM_BufInsertText("staffsync");
break;
case GS_INTRO:
@ -2075,6 +2084,9 @@ void G_Ticker(boolean run)
P_Ticker(run);
F_TitleScreenTicker(run);
if (staffsync)
COM_BufInsertText("staffsync");
break;
case GS_CEREMONY:

View file

@ -7670,6 +7670,20 @@ void K_drawKartHUD(void)
return;
}
if (staffsync)
{
V_DrawFadeScreen(31, 8);
V_DrawCenteredGamemodeString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 - 30, 0, 0, "Staff Ghost Sync Test");
UINT16 barw = BASEVIDWIDTH/2;
UINT16 barh = 8;
UINT16 bary = 10;
V_DrawFill(BASEVIDWIDTH/2 - barw/2, BASEVIDHEIGHT/2 - barh/2 + bary, barw, barh, 199);
V_DrawFill(BASEVIDWIDTH/2 - barw/2 + 1, BASEVIDHEIGHT/2 - barh/2 + 1 + bary, barw * staffsync_done / staffsync_total - 2, barh - 2, 176);
V_DrawCenteredThinString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 20, 0, va("Testing ghost %d of %d (%d failed). Choppy visuals are normal!\n", staffsync_done, staffsync_total, staffsync_failed));
V_DrawCenteredThinString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 30, 0, "See \"latest-log.txt\" for information on failing ghosts.\n");
return;
}
// Draw full screen stuff that turns off the rest of the HUD
if (R_GetViewNumber() == 0)
{

View file

@ -262,7 +262,8 @@ boolean S_SoundDisabled(void)
return (
sound_disabled ||
( window_notinfocus && ! (cv_bgaudio.value & 2) ) ||
(g_fast_forward > 0)
(g_fast_forward > 0) ||
staffsync
);
}

View file

@ -41,6 +41,7 @@ TYPEDEF (xcommand_t);
TYPEDEF (changeteam_packet_t);
TYPEDEF (changeteam_value_t);
TYPEDEF (scheduleTask_t);
TYPEDEF (staffsync_t);
// discord.h
TYPEDEF (discordRequest_t);

View file

@ -2656,6 +2656,12 @@ void Y_StartIntermission(void)
return;
}
if (staffsync)
{
Y_EndIntermission();
return;
}
G_SetGamestate(GS_INTERMISSION);
if (demo.playback)