Spectator re-entry timer

- On becoming a spectator in a netgame, there is a delay before you can de-spectate.
    - 30 seconds by default.
        - This can be changed using the cvar `spectatorreentry`.
    - ...unless there's only two people left including you, in which case it's three minutes!!
        - This can be changed using the cvar `duelspectatorreentry`.
        - If spectatorreentry is set to greater than duelspectatorrentry, the former is used instead.
    - This timer is wiped on mapload and intermission, so NEW CHALLENGER APPROACHING !! and level changes in general allow people in.
- General purpose cleanup of K_CheckSpectateStatus
This commit is contained in:
toaster 2023-04-13 21:17:05 +01:00
parent d50e3a1895
commit 15b0141700
8 changed files with 106 additions and 19 deletions

View file

@ -262,6 +262,10 @@ consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_NETVAR, C
static CV_PossibleValue_t maxplayers_cons_t[] = {{1, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; static CV_PossibleValue_t maxplayers_cons_t[] = {{1, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}};
consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_NETVAR, maxplayers_cons_t, NULL); consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_NETVAR, maxplayers_cons_t, NULL);
static CV_PossibleValue_t spectatorreentry_cons_t[] = {{0, "MIN"}, {10*60, "MAX"}, {0, NULL}};
consvar_t cv_spectatorreentry = CVAR_INIT ("spectatorreentry", "30", CV_NETVAR, spectatorreentry_cons_t, NULL);
consvar_t cv_duelspectatorreentry = CVAR_INIT ("duelspectatorreentry", "180", CV_NETVAR, spectatorreentry_cons_t, NULL);
consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL); consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL);
static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}}; static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}};
@ -788,6 +792,8 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_restrictskinchange); CV_RegisterVar(&cv_restrictskinchange);
CV_RegisterVar(&cv_allowteamchange); CV_RegisterVar(&cv_allowteamchange);
CV_RegisterVar(&cv_maxplayers); CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_spectatorreentry);
CV_RegisterVar(&cv_duelspectatorreentry);
CV_RegisterVar(&cv_respawntime); CV_RegisterVar(&cv_respawntime);
// d_clisrv // d_clisrv
@ -1594,6 +1600,35 @@ static void FinalisePlaystateChange(INT32 playernum)
// Clear player score and rings if a spectator. // Clear player score and rings if a spectator.
if (players[playernum].spectator) if (players[playernum].spectator)
{ {
// To attempt to discourage rage-spectators, we delay any rejoining.
// If you're engaging in a DUEL and quit early, in addition to the
// indignity of losing your PWR, you get a special extra-long delay.
if (netgame)
{
UINT8 pcount = 0;
if (cv_duelspectatorreentry.value > cv_spectatorreentry.value)
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
if (++pcount < 2)
continue;
break;
}
}
players[playernum].spectatorReentry =
(pcount == 1)
? (cv_duelspectatorreentry.value * TICRATE)
: (cv_spectatorreentry.value * TICRATE);
//CONS_Printf("player %u got re-entry of %u\n", playernum, players[playernum].spectatorReentry);
}
if (gametyperules & GTR_POINTLIMIT) // SRB2kart if (gametyperules & GTR_POINTLIMIT) // SRB2kart
{ {
players[playernum].roundscore = 0; players[playernum].roundscore = 0;

View file

@ -75,6 +75,7 @@ extern consvar_t cv_mute;
extern consvar_t cv_pause; extern consvar_t cv_pause;
extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime;
extern consvar_t cv_spectatorreentry, cv_duelspectatorreentry;
// SRB2kart items // SRB2kart items
extern consvar_t cv_items[NUMKARTRESULTS-1]; extern consvar_t cv_items[NUMKARTRESULTS-1];

View file

@ -691,6 +691,8 @@ struct player_t
tic_t jointime; // Timer when player joins game to change skin/color tic_t jointime; // Timer when player joins game to change skin/color
tic_t spectatorReentry;
UINT8 typing_timer; // Counts down while keystrokes are not emitted UINT8 typing_timer; // Counts down while keystrokes are not emitted
UINT8 typing_duration; // How long since resumed timer UINT8 typing_duration; // How long since resumed timer

View file

@ -2476,6 +2476,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
tic_t jointime; tic_t jointime;
tic_t spectatorReentry;
UINT8 splitscreenindex; UINT8 splitscreenindex;
boolean spectator; boolean spectator;
boolean bot; boolean bot;
@ -2654,6 +2656,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
saveroundconditions = true; saveroundconditions = true;
} }
spectatorReentry = (betweenmaps ? 0 : players[player].spectatorReentry);
if (!betweenmaps) if (!betweenmaps)
{ {
follower = players[player].follower; follower = players[player].follower;
@ -2734,6 +2738,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->botvars.rubberband = FRACUNIT; p->botvars.rubberband = FRACUNIT;
p->botvars.controller = UINT16_MAX; p->botvars.controller = UINT16_MAX;
p->spectatorReentry = spectatorReentry;
memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette)); memcpy(&p->itemRoulette, &itemRoulette, sizeof (p->itemRoulette));
memcpy(&p->respawn, &respawn, sizeof (p->respawn)); memcpy(&p->respawn, &respawn, sizeof (p->respawn));

View file

@ -11385,10 +11385,16 @@ void K_CheckSpectateStatus(void)
{ {
if (!playeringame[i]) if (!playeringame[i])
continue; continue;
if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN))
players[i].spectatewait++; players[i].spectatewait++;
else else
players[i].spectatewait = 0; players[i].spectatewait = 0;
if (gamestate != GS_LEVEL)
players[i].spectatorReentry = 0;
else if (players[i].spectatorReentry > 0)
players[i].spectatorReentry--;
} }
// No one's allowed to join // No one's allowed to join
@ -11404,22 +11410,45 @@ void K_CheckSpectateStatus(void)
if (!players[i].spectator) if (!players[i].spectator)
{ {
numingame++; numingame++;
if (cv_maxplayers.value && numingame >= cv_maxplayers.value) // DON'T allow if you've hit the in-game player cap
// DON'T allow if you've hit the in-game player cap
if (cv_maxplayers.value && numingame >= cv_maxplayers.value)
return; return;
if (gamestate != GS_LEVEL) // Allow if you're not in a level
// Allow if you're not in a level
if (gamestate != GS_LEVEL)
continue; continue;
if (players[i].exiting) // DON'T allow if anyone's exiting
// DON'T allow if anyone's exiting
if (players[i].exiting)
return; return;
if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet
// Allow if the match hasn't started yet
if (numingame < 2 || leveltime < starttime || mapreset)
continue; continue;
if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in
// DON'T allow if the match is 20 seconds in
if (leveltime > (starttime + 20*TICRATE))
return; return;
if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2) // DON'T allow if the race is at 2 laps
// DON'T allow if the race is at 2 laps
if ((gametyperules & GTR_CIRCUIT) && players[i].laps >= 2)
return; return;
continue; continue;
} }
else if (players[i].bot || !(players[i].pflags & PF_WANTSTOJOIN))
if (players[i].bot)
{
// Spectating bots are controlled by other mechanisms.
continue; continue;
}
if (!(players[i].pflags & PF_WANTSTOJOIN))
{
// This spectator does not want to join.
continue;
}
respawnlist[numjoiners++] = i; respawnlist[numjoiners++] = i;
} }
@ -11428,8 +11457,8 @@ void K_CheckSpectateStatus(void)
if (!numjoiners) if (!numjoiners)
return; return;
// Organize by spectate wait timer // Organize by spectate wait timer (if there's more than one to sort)
if (cv_maxplayers.value) if (cv_maxplayers.value && numjoiners > 1)
{ {
UINT8 oldrespawnlist[MAXPLAYERS]; UINT8 oldrespawnlist[MAXPLAYERS];
memcpy(oldrespawnlist, respawnlist, numjoiners); memcpy(oldrespawnlist, respawnlist, numjoiners);
@ -11454,17 +11483,25 @@ void K_CheckSpectateStatus(void)
} }
// Finally, we can de-spectate everyone! // Finally, we can de-spectate everyone!
for (i = 0; i < numjoiners; i++) for (i = 0, j = 0; i < numjoiners; i++)
{ {
if (cv_maxplayers.value && numingame+i >= cv_maxplayers.value) // Hit the in-game player cap while adding people? // This person has their reentry cooldown active.
break; if (netgame && players[respawnlist[i]].spectatorReentry > 0 && numingame > 0)
continue;
//CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime);
P_SpectatorJoinGame(&players[respawnlist[i]]); P_SpectatorJoinGame(&players[respawnlist[i]]);
j++; // j is being used as the number of players added
// Hit the in-game player cap while adding people?
if (cv_maxplayers.value && numingame+j >= cv_maxplayers.value)
break;
} }
// Reset the match when 2P joins 1P, DUEL mode // Reset the match when 2P joins 1P, DUEL mode
// Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled
if (!mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) // use previous i value if (j > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+j >= 2))
{ {
S_ChangeMusicInternal("chalng", false); // COME ON S_ChangeMusicInternal("chalng", false); // COME ON
mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD

View file

@ -500,6 +500,8 @@ static int player_get(lua_State *L)
lua_pushboolean(L, plr->bot); lua_pushboolean(L, plr->bot);
else if (fastcmp(field,"jointime")) else if (fastcmp(field,"jointime"))
lua_pushinteger(L, plr->jointime); lua_pushinteger(L, plr->jointime);
else if (fastcmp(field,"spectatorReentry"))
lua_pushinteger(L, plr->spectatorReentry);
else if (fastcmp(field,"splitscreenindex")) else if (fastcmp(field,"splitscreenindex"))
lua_pushinteger(L, plr->splitscreenindex); lua_pushinteger(L, plr->splitscreenindex);
#ifdef HWRENDER #ifdef HWRENDER
@ -880,6 +882,8 @@ static int player_set(lua_State *L)
return NOSET; return NOSET;
else if (fastcmp(field,"jointime")) else if (fastcmp(field,"jointime"))
return NOSET; return NOSET;
else if (fastcmp(field,"spectatorReentry"))
plr->spectatorReentry = (UINT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"splitscreenindex")) else if (fastcmp(field,"splitscreenindex"))
return NOSET; return NOSET;
#ifdef HWRENDER #ifdef HWRENDER

View file

@ -11682,12 +11682,7 @@ void P_SpawnPlayer(INT32 playernum)
else if (netgame && p->jointime <= 1 && pcount) else if (netgame && p->jointime <= 1 && pcount)
{ {
p->spectator = true; p->spectator = true;
p->spectatorReentry = 0;
#if 0
if (pcount == 1 || leveltime < starttime)
p->pflags |= PF_WANTSTOJOIN;
p->jointime = 2;
#endif
} }
else if (multiplayer && !netgame) else if (multiplayer && !netgame)
{ {
@ -11701,6 +11696,7 @@ void P_SpawnPlayer(INT32 playernum)
// Spawn as a spectator, // Spawn as a spectator,
// yes even in splitscreen mode // yes even in splitscreen mode
p->spectator = true; p->spectator = true;
if (playernum&1) p->skincolor = skincolor_redteam; if (playernum&1) p->skincolor = skincolor_redteam;
else p->skincolor = skincolor_blueteam; else p->skincolor = skincolor_blueteam;
@ -11722,7 +11718,9 @@ void P_SpawnPlayer(INT32 playernum)
{ {
// Fix stupid non spectator spectators. // Fix stupid non spectator spectators.
if (!p->spectator && !p->ctfteam) if (!p->spectator && !p->ctfteam)
{
p->spectator = true; p->spectator = true;
}
// Fix team colors. // Fix team colors.
// This code isn't being done right somewhere else. Oh well. // This code isn't being done right somewhere else. Oh well.

View file

@ -195,6 +195,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].jointime); WRITEUINT32(save->p, players[i].jointime);
WRITEUINT32(save->p, players[i].spectatorReentry);
WRITEUINT8(save->p, players[i].splitscreenindex); WRITEUINT8(save->p, players[i].splitscreenindex);
if (players[i].awayview.mobj) if (players[i].awayview.mobj)
@ -598,6 +600,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].jointime = READUINT32(save->p); players[i].jointime = READUINT32(save->p);
players[i].spectatorReentry = READUINT32(save->p);
players[i].splitscreenindex = READUINT8(save->p); players[i].splitscreenindex = READUINT8(save->p);
flags = READUINT16(save->p); flags = READUINT16(save->p);