Improved round-queue state net communication

- Only send from the server when an update to roundqueue state is relevant
- Perform sanity checking on reciept
- Initialise when map command is sent with roundqueue size greater than the client's
- Correct gametype/encore state on reciept
- Only permit from the server, forbid admin clients from providing it on penalty of kick
This commit is contained in:
toaster 2023-04-10 13:33:58 +01:00
parent 19ef96351a
commit a08683e819
4 changed files with 55 additions and 11 deletions

View file

@ -2578,12 +2578,18 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean p
flags |= 1<<2; flags |= 1<<2;
if (pforcespecialstage) if (pforcespecialstage)
flags |= 1<<3; flags |= 1<<3;
if (roundqueue.netcommunicate)
flags |= 1<<4;
WRITEUINT8(buf_p, flags); WRITEUINT8(buf_p, flags);
// roundqueue state if (roundqueue.netcommunicate)
WRITEUINT8(buf_p, roundqueue.position); {
WRITEUINT8(buf_p, roundqueue.size); // roundqueue state
WRITEUINT8(buf_p, roundqueue.roundnum); WRITEUINT8(buf_p, roundqueue.position);
WRITEUINT8(buf_p, roundqueue.size);
WRITEUINT8(buf_p, roundqueue.roundnum);
roundqueue.netcommunicate = false;
}
// new gametype value // new gametype value
WRITEUINT8(buf_p, newgametype); WRITEUINT8(buf_p, newgametype);
@ -3042,7 +3048,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
UINT8 flags; UINT8 flags;
INT32 presetplayer = 1, lastgametype; INT32 presetplayer = 1, lastgametype;
UINT8 skipprecutscene, pforcespecialstage; UINT8 skipprecutscene, pforcespecialstage;
boolean pencoremode; boolean pencoremode, hasroundqueuedata;
INT16 mapnumber; INT16 mapnumber;
forceresetplayers = deferencoremode = false; forceresetplayers = deferencoremode = false;
@ -3055,9 +3061,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
return; return;
} }
if (chmappending)
chmappending--;
flags = READUINT8(*cp); flags = READUINT8(*cp);
pencoremode = ((flags & 1) != 0); pencoremode = ((flags & 1) != 0);
@ -3068,9 +3071,38 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
pforcespecialstage = ((flags & (1<<3)) != 0); pforcespecialstage = ((flags & (1<<3)) != 0);
roundqueue.position = READUINT8(*cp); hasroundqueuedata = ((flags & (1<<4)) != 0);
roundqueue.size = READUINT8(*cp);
roundqueue.roundnum = READUINT8(*cp); if (hasroundqueuedata)
{
UINT8 position = READUINT8(*cp);
UINT8 size = READUINT8(*cp);
UINT8 roundnum = READUINT8(*cp);
if (playernum != serverplayer // Clients, even admin clients, don't have full roundqueue data
|| position > size // Sanity check A (intentionally not a >= comparison)
|| size > ROUNDQUEUE_MAX) // Sanity Check B (ditto)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal round-queue data received from %s\n"), player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL);
return;
}
roundqueue.position = position;
while (roundqueue.size < size)
{
// We wipe rather than provide full data to prevent bloating the packet,
// and because only this data is necessary for sync. ~toast 100423
memset(&roundqueue.entries[roundqueue.size], 0, sizeof(roundentry_t));
roundqueue.size++;
}
roundqueue.roundnum = roundnum; // no sanity checking required, server is authoriative
}
// No more kicks below this line, we can now start modifying state beyond this function.
if (chmappending)
chmappending--;
lastgametype = gametype; lastgametype = gametype;
gametype = READUINT8(*cp); gametype = READUINT8(*cp);
@ -3081,6 +3113,13 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
else if (gametype != lastgametype) else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (hasroundqueuedata && roundqueue.position > 0 && roundqueue.size > 0)
{
// ...we can evaluate CURRENT specifics for roundqueue data, though.
roundqueue.entries[roundqueue.position-1].gametype = gametype;
roundqueue.entries[roundqueue.position-1].encore = pencoremode;
}
if (!(gametyperules & GTR_ENCORE)) if (!(gametyperules & GTR_ENCORE))
pencoremode = false; pencoremode = false;

View file

@ -4173,6 +4173,9 @@ static void G_GetNextMap(void)
forceresetplayers = true; forceresetplayers = true;
} }
} }
// Make sure the next D_MapChange sends updated roundqueue state.
roundqueue.netcommunicate = true;
} }
else if (grandprixinfo.gp == true) else if (grandprixinfo.gp == true)
{ {

View file

@ -68,6 +68,7 @@ extern struct roundqueue
UINT8 roundnum; // Visible number on HUD UINT8 roundnum; // Visible number on HUD
UINT8 position; // Head position in the round queue UINT8 position; // Head position in the round queue
UINT8 size; // Number of entries in the round queue UINT8 size; // Number of entries in the round queue
boolean netcommunicate; // As server, should we net-communicate this in XD_MAP?
roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue
} roundqueue; } roundqueue;

View file

@ -138,6 +138,7 @@ void M_CupSelectHandler(INT32 choice)
memset(&roundqueue, 0, sizeof(struct roundqueue)); memset(&roundqueue, 0, sizeof(struct roundqueue));
G_GPCupIntoRoundQueue(newcup, levellist.newgametype, (boolean)cv_dummygpencore.value); G_GPCupIntoRoundQueue(newcup, levellist.newgametype, (boolean)cv_dummygpencore.value);
roundqueue.position = roundqueue.roundnum = 1; roundqueue.position = roundqueue.roundnum = 1;
roundqueue.netcommunicate = true; // relevant for future Online GP
paused = false; paused = false;