diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 08fe916e9..a784102ce 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4043,10 +4043,27 @@ static void HandleConnect(SINT8 node) UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); UINT8 connectedplayers = 0; - for (i = dedicated ? 1 : 0; i < MAXPLAYERS; i++) - if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player + { + // We use this to count players because it is affected by SV_AddWaitingPlayers when + // more than one client joins on the same tic, unlike playeringame and D_NumPlayers. + // UINT8_MAX denotes no node for that player. + + if (playernode[i] != UINT8_MAX) + { + // Sal: This hack sucks, but it should be safe. + // playeringame is set for bots immediately; they are deterministic instead of a netxcmd. + // If a bot is added with netxcmd in the future, then the node check is still here too. + // So at worst, a theoretical netxcmd added bot will block real joiners for the time + // it takes for the command to process, but not cause any horrifying player overwriting. + if (playeringame[i] && players[i].bot) + { + continue; + } + connectedplayers++; + } + } banrecord_t *ban = SV_GetBanByAddress(node); if (ban == NULL) diff --git a/src/k_kart.c b/src/k_kart.c index 0430d0dd6..d5e0c23a3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -13569,6 +13569,7 @@ void K_CheckSpectateStatus(boolean considermapreset) { UINT8 respawnlist[MAXPLAYERS]; UINT8 i, j, numingame = 0, numjoiners = 0; + UINT8 numhumans = 0, numbots = 0; // Maintain spectate wait timer for (i = 0; i < MAXPLAYERS; i++) @@ -13581,6 +13582,16 @@ void K_CheckSpectateStatus(boolean considermapreset) if (!players[i].spectator) { numingame++; + + if (players[i].bot) + { + numbots++; + } + else + { + numhumans++; + } + players[i].spectatewait = 0; players[i].spectatorReentry = 0; continue; @@ -13610,7 +13621,7 @@ void K_CheckSpectateStatus(boolean considermapreset) return; // DON'T allow if you've hit the in-game player cap - if (cv_maxplayers.value && numingame >= cv_maxplayers.value) + if (cv_maxplayers.value && numhumans >= cv_maxplayers.value) return; // Get the number of players in game, and the players to be de-spectated. @@ -13694,16 +13705,47 @@ void K_CheckSpectateStatus(boolean considermapreset) } } + const UINT8 previngame = numingame; + INT16 removeBotID = MAXPLAYERS - 1; + // Finally, we can de-spectate everyone! for (i = 0; i < numjoiners; i++) { - //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); - - P_SpectatorJoinGame(&players[respawnlist[i]]); - // Hit the in-game player cap while adding people? - if (cv_maxplayers.value && numingame+i >= cv_maxplayers.value) - break; + if (cv_maxplayers.value && numingame >= cv_maxplayers.value) + { + if (numbots > 0) + { + // Find a bot to kill to make room + while (removeBotID >= 0) + { + if (playeringame[removeBotID] && players[removeBotID].bot) + { + //CONS_Printf("bot %s kicked to make room on tic %d\n", player_names[removeBotID], leveltime); + CL_RemovePlayer(removeBotID, KR_LEAVE); + numbots--; + numingame--; + break; + } + + removeBotID--; + } + + if (removeBotID < 0) + { + break; + } + } + else + { + break; + } + } + + //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); + P_SpectatorJoinGame(&players[respawnlist[i]]); + numhumans++; + numingame++; } if (considermapreset == false) @@ -13712,7 +13754,7 @@ void K_CheckSpectateStatus(boolean considermapreset) // Reset the match when 2P joins 1P, DUEL mode // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled extern consvar_t cv_debugnewchallenger; - if (i > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2) && !cv_debugnewchallenger.value) + if (i > 0 && !mapreset && gamestate == GS_LEVEL && (previngame < 3 && numingame >= 2) && !cv_debugnewchallenger.value) { Music_Play("comeon"); // COME ON mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD