diff --git a/src/command.c b/src/command.c index 8c72eeaa3..dbdaceab5 100644 --- a/src/command.c +++ b/src/command.c @@ -1458,7 +1458,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum) // not from server or remote admin, must be hacked/buggy client CONS_Alert(CONS_WARNING, M_GetText("Illegal netvar command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } netid = READUINT16(*p); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 446f6b573..ee4e62b91 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -21,6 +21,7 @@ #include "d_net.h" #include "d_main.h" #include "g_game.h" +#include "st_stuff.h" #include "hu_stuff.h" #include "keys.h" #include "g_input.h" // JOY1 @@ -76,6 +77,7 @@ char motd[254], server_context[8]; // Message of the Day, Unique Context (even w // Server specific vars UINT8 playernode[MAXPLAYERS]; +char playeraddress[MAXPLAYERS][64]; // Minimum timeout for sending the savegame // The actual timeout will be longer depending on the savegame length @@ -391,7 +393,7 @@ static void ExtraDataTicker(void) { if (server) { - SendKick(i, KICK_MSG_CON_FAIL); + SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); } CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); @@ -437,6 +439,9 @@ void SendKick(UINT8 playernum, UINT8 msg) { UINT8 buf[2]; + if (!(server && cv_rejointimeout.value)) + msg &= ~KICK_MSG_KEEP_BODY; + buf[0] = playernum; buf[1] = msg; SendNetXCmd(XD_KICK, &buf, 2); @@ -1058,7 +1063,7 @@ static void SV_SendResynch(INT32 node) if (resynch_score[node] > (unsigned)cv_resynchattempts.value*250) { - SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL); + SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); resynch_score[node] = 0; } } @@ -2413,6 +2418,7 @@ void CL_ClearPlayer(INT32 playernum) if (players[playernum].mo) P_RemoveMobj(players[playernum].mo); memset(&players[playernum], 0, sizeof (player_t)); + memset(playeraddress[playernum], 0, sizeof(*playeraddress)); } // @@ -2420,7 +2426,7 @@ void CL_ClearPlayer(INT32 playernum) // // Removes a player from the current game // -static void CL_RemovePlayer(INT32 playernum, INT32 reason) +static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) { // Sanity check: exceptional cases (i.e. c-fails) can cause multiple // kick commands to be issued for the same player. @@ -2433,9 +2439,6 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason) playerpernode[node]--; if (playerpernode[node] <= 0) { - // If a resynch was in progress, well, it no longer needs to be. - SV_InitResynchVars(playernode[playernum]); - nodeingame[playernode[playernum]] = false; Net_CloseConnection(playernode[playernum]); ResetNode(node); @@ -2831,9 +2834,12 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) char buf[3 + MAX_REASONLENGTH]; char *reason = buf; kickreason_t kickreason = KR_KICK; + boolean keepbody; pnum = READUINT8(*p); msg = READUINT8(*p); + keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; + msg &= ~KICK_MSG_KEEP_BODY; if (pnum == serverplayer && IsPlayerAdmin(playernum)) { @@ -2893,6 +2899,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) */ pnum = playernum; msg = KICK_MSG_CON_FAIL; + keepbody = true; } //CONS_Printf("\x82%s ", player_names[pnum]); @@ -2912,7 +2919,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) switch (msg) { case KICK_MSG_GO_AWAY: - HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); + if (!players[pnum].quittime) + HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); kickreason = KR_KICK; break; case KICK_MSG_PING_HIGH: @@ -2961,7 +2969,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_TIMEOUT; break; case KICK_MSG_PLAYER_QUIT: - if (netgame) // not splitscreen/bots + if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); kickreason = KR_LEAVE; break; @@ -3002,6 +3010,24 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) else M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); } + else if (keepbody) + { + if (server && !demoplayback) + { + INT32 node = playernode[pnum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + playernode[pnum] = UINT8_MAX; + + players[pnum].quittime = 1; + } else CL_RemovePlayer(pnum, kickreason); } @@ -3010,6 +3036,9 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_rejointimeout = {"rejointimeout", "Off", CV_SAVE|CV_FLOAT, rejointimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "10", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; @@ -3079,6 +3108,7 @@ static void ResetNode(INT32 node) nodewaiting[node] = 0; playerpernode[node] = 0; sendingsavegame[node] = false; + SV_InitResynchVars(node); } void SV_ResetServer(void) @@ -3094,13 +3124,8 @@ void SV_ResetServer(void) tictoclear = maketic; for (i = 0; i < MAXNETNODES; i++) - { ResetNode(i); - // Make sure resynch status doesn't get carried over! - SV_InitResynchVars(i); - } - for (i = 0; i < MAXPLAYERS; i++) { #ifdef HAVE_BLUA @@ -3108,6 +3133,7 @@ void SV_ResetServer(void) #endif playeringame[i] = false; playernode[i] = UINT8_MAX; + memset(playeraddress[i], 0, sizeof(*playeraddress)); sprintf(player_names[i], "Player %d", i + 1); adminplayers[i] = -1; // Populate the entire adminplayers array with -1. } @@ -3198,6 +3224,37 @@ void D_QuitNetGame(void) #endif } +static INT32 FindRejoinerNum(SINT8 node) +{ + char strippednodeaddress[64]; + const char *nodeaddress; + char *port; + INT32 i; + + // Make sure there is no dead dress before proceeding to the stripping + if (!I_GetNodeAddress) + return -1; + nodeaddress = I_GetNodeAddress(node); + if (!nodeaddress) + return -1; + + // Strip the address of its port + strcpy(strippednodeaddress, nodeaddress); + port = strchr(strippednodeaddress, ':'); + if (port) + *port = '\0'; + + // Check if any player matches the stripped address + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX + && !strcmp(playeraddress[i], strippednodeaddress)) + return i; + } + + return -1; +} + // Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { @@ -3214,13 +3271,16 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) { INT16 node, newplayernum; boolean splitscreenplayer; + boolean rejoined; + player_t *newplayer; + char *port; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { // protect against hacked/buggy client CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3229,15 +3289,34 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) splitscreenplayer = newplayernum & 0x80; newplayernum &= ~0x80; - // Clear player before joining, lest some things get set incorrectly - // HACK: don't do this for splitscreen, it relies on preset values - if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - playeringame[newplayernum] = true; + rejoined = playeringame[newplayernum]; + + if (!rejoined) + { + // Clear player before joining, lest some things get set incorrectly + // HACK: don't do this for splitscreen, it relies on preset values + if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + if (server && I_GetNodeAddress) + { + strcpy(playeraddress[newplayernum], I_GetNodeAddress(node)); + port = strchr(playeraddress[newplayernum], ':'); + if (port) + *port = '\0'; + } + } + + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); - G_AddPlayer(newplayernum); - if (newplayernum+1 > doomcom->numslots) - doomcom->numslots = (INT16)(newplayernum+1); // the server is creating my player if (node == mynode) @@ -3255,29 +3334,67 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) secondarydisplayplayer = newplayernum; DEBFILE("spawning my brother\n"); if (botingame) - players[newplayernum].bot = 1; + newplayer->bot = 1; } D_SendPlayerConfig(); addedtogame = true; + + if (rejoined) + { + if (newplayer->mo) + { + if (!splitscreenplayer) + localangle = newplayer->mo->angle; + else + localangle2 = newplayer->mo->angle; + + newplayer->viewheight = 41*newplayer->height/48; + + if (newplayer->mo->eflags & MFE_VERTICALFLIP) + newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; + else + newplayer->viewz = newplayer->mo->z + newplayer->viewheight; + } + + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + + if (camera.chase && !splitscreenplayer) + P_ResetCamera(newplayer, &camera); + if (camera2.chase && splitscreenplayer) + P_ResetCamera(newplayer, &camera2); + } } if (netgame) { - if (server && cv_showjoinaddress.value) - { - const char *address; - if (I_GetNodeAddress && (address = I_GetNodeAddress(node)) != NULL) - HU_AddChatText(va("\x82*%s has joined the game (player %d) (%s)", player_names[newplayernum], newplayernum, address), false); // merge join notification + IP to avoid clogging console/chat. - } + char joinmsg[256]; + + if (rejoined) + strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); else - HU_AddChatText(va("\x82*%s has joined the game (player %d)", player_names[newplayernum], newplayernum), false); // if you don't wanna see the join address. + strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + + // Merge join notification + IP to avoid clogging console/chat + if (server && cv_showjoinaddress.value && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + if (address) + strcat(joinmsg, va(" (%s)", address)); + } + + HU_AddChatText(joinmsg, false); } if (server && multiplayer && motd[0] != '\0') COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); #ifdef HAVE_BLUA - LUAh_PlayerJoin(newplayernum); + if (!rejoined) + LUAh_PlayerJoin(newplayernum); #endif } @@ -3286,11 +3403,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) INT32 node, n, newplayer = false; UINT8 buf[2 + MAXPLAYERNAME]; UINT8 *p; - UINT8 newplayernum = 0; - - // What is the reason for this? Why can't newplayernum always be 0? - if (dedicated) - newplayernum = 1; + INT32 newplayernum; for (node = 0; node < MAXNETNODES; node++) { @@ -3299,68 +3412,22 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) { newplayer = true; - if (netgame) - // !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!! - // - // The line just after that comment is an awful, horrible, terrible, TERRIBLE hack. - // - // Basically, the fix I did in order to fix the download freezes happens - // to cause situations in which a player number does not match - // the node number associated to that player. - // That is totally normal, there is absolutely *nothing* wrong with that. - // Really. Player 7 being tied to node 29, for instance, is totally fine. - // - // HOWEVER. A few (broken) parts of the netcode do the TERRIBLE mistake - // of mixing up the concepts of node and player, resulting in - // incorrect handling of cases where a player is tied to a node that has - // a different number (which is a totally normal case, or at least should be). - // This incorrect handling can go as far as literally - // anyone from joining your server at all, forever. - // - // Given those two facts, there are two options available - // in order to let this download freeze fix be: - // 1) Fix the broken parts that assume a node is a player or similar bullshit. - // 2) Change the part this comment is located at, so that any player who joins - // is given the same number as their associated node. - // - // No need to say, 1) is by far the obvious best, whereas 2) is a terrible hack. - // Unfortunately, after trying 1), I most likely didn't manage to find all - // of those broken parts, and thus 2) has become the only safe option that remains. - // - // So I did this hack. - // - // If it isn't clear enough, in order to get rid of this ugly hack, - // you will have to fix all parts of the netcode that - // make a confusion between nodes and players. - // - // And if it STILL isn't clear enough, a node and a player - // is NOT the same thing. Never. NEVER. *NEVER*. - // - // And if someday you make the terrible mistake of - // daring to have the unforgivable idea to try thinking - // that a node might possibly be the same as a player, - // or that a player should have the same number as its node, - // be sure that I will somehow know about it and - // hunt you down tirelessly and make you regret it, - // even if you live on the other side of the world. - // - // TODO: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // \todo >>>>>>>>>> Remove this horrible hack as soon as possible <<<<<<<<<< - // TODO: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // - // !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!! - newplayernum = node; // OMFG SAY WELCOME TO TEH NEW HACK FOR FIX FIL DOWNLOAD!!1! - else // Don't use the hack if we don't have to + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) + { // search for a free playernum // we can't use playeringame since it is not updated here - for (; newplayernum < MAXPLAYERS; newplayernum++) + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) { + if (playeringame[newplayernum]) + continue; for (n = 0; n < MAXNETNODES; n++) if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) break; if (n == MAXNETNODES) break; } + } // should never happen since we check the playernum // before accepting the join @@ -3387,8 +3454,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); - // use the next free slot (we can't put playeringame[newplayernum] = true here) - newplayernum++; } } @@ -3514,8 +3579,11 @@ static size_t TotalTextCmdPerTic(tic_t tic) static void HandleConnect(SINT8 node) { char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 rejoinernum; INT32 i; + rejoinernum = FindRejoinerNum(node); + if (bannednode && bannednode[node]) SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server")); else if (netbuffer->u.clientcfg._255 != 255 || @@ -3527,9 +3595,9 @@ static void HandleConnect(SINT8 node) else if (netbuffer->u.clientcfg.version != VERSION || netbuffer->u.clientcfg.subversion != SUBVERSION) SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION)); - else if (!cv_allownewplayer.value && node) + else if (!cv_allownewplayer.value && node && rejoinernum == -1) SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment")); - else if (D_NumPlayers() >= cv_maxplayers.value) + else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1) SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value)); else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client? SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); @@ -3544,7 +3612,7 @@ static void HandleConnect(SINT8 node) for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) { strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], -1)) + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) { SV_SendRefuse(node, "Bad player name"); return; @@ -3985,7 +4053,7 @@ static void HandlePacketFromPlayer(SINT8 node) } else { - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", netconsole, realstart, consistancy[realstart%BACKUPTICS], SHORT(netbuffer->u.clientpak.consistancy))); @@ -4104,6 +4172,7 @@ static void HandlePacketFromPlayer(SINT8 node) kickmsg = KICK_MSG_TIMEOUT; else kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; SendKick(netconsole, kickmsg); nodetoplayer[node] = -1; @@ -4125,7 +4194,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = false; @@ -4143,7 +4212,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4203,7 +4272,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = true; @@ -4215,7 +4284,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4239,7 +4308,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } if (client) @@ -4435,7 +4504,7 @@ static void CL_SendClientCmd(void) packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); HSendPacket(servernode, false, 0, packetsize); } - else if (gamestate != GS_NULL) + else if (gamestate != GS_NULL && addedtogame) { G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); @@ -4611,6 +4680,11 @@ static void Local_Maketic(INT32 realtics) localcmds.angleturn |= TICCMD_RECEIVED; } +// This function is utter bullshit and is responsible for +// the random desynch that happens when a player spawns. +// This is because ticcmds are resent to clients if a packet +// was dropped, and thus modifying them can lead to several +// clients having their ticcmds set to different values. void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) { tic_t tic; @@ -4644,28 +4718,36 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) // create missed tic static void SV_Maketic(void) { - INT32 j; + INT32 i; - for (j = 0; j < MAXNETNODES; j++) - if (playerpernode[j]) + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + // We didn't receive this tic + if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) { - INT32 player = nodetoplayer[j]; - if ((netcmds[maketic%BACKUPTICS][player].angleturn & TICCMD_RECEIVED) == 0) - { // we didn't receive this tic - INT32 i; + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; - DEBFILE(va("MISS tic%4d for node %d\n", maketic, j)); -#if defined(PARANOIA) && 0 - CONS_Debug(DBG_NETPLAY, "Client Misstic %d\n", maketic); -#endif - // copy the old tic - for (i = 0; i < playerpernode[j]; i++, player = nodetoplayer2[j]) - { - netcmds[maketic%BACKUPTICS][player] = netcmds[(maketic-1)%BACKUPTICS][player]; - netcmds[maketic%BACKUPTICS][player].angleturn &= ~TICCMD_RECEIVED; - } + if (players[i].quittime) + { + // Copy the angle/aiming from the previous tic + // and empty the other inputs + memset(ticcmd, 0, sizeof(netcmds[0][0])); + ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; + ticcmd->aiming = prevticcmd->aiming; + } + else + { + DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->angleturn &= ~TICCMD_RECEIVED; } } + } // all tic are now proceed make the next maketic++; @@ -4787,7 +4869,7 @@ static inline void PingUpdate(void) // ok your net has been bad for too long, you deserve to die. { pingtimeout[i] = 0; - SendKick(i, KICK_MSG_PING_HIGH); + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); } } /* diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 408d0f8dd..df93fe31d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -485,6 +485,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_PING_HIGH 6 #define KICK_MSG_CUSTOM_KICK 7 #define KICK_MSG_CUSTOM_BAN 8 +#define KICK_MSG_KEEP_BODY 0x80 typedef enum { @@ -512,7 +513,9 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; -extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed; +extern consvar_t cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_rejointimeout; +extern consvar_t cv_resynchattempts, cv_blamecfail; +extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; // Used in d_net, the only dependence tic_t ExpandTics(INT32 low); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a597bad83..6b3662710 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -579,6 +579,7 @@ void D_RegisterServerCommands(void) // d_clisrv CV_RegisterVar(&cv_maxplayers); + CV_RegisterVar(&cv_rejointimeout); CV_RegisterVar(&cv_resynchattempts); CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); @@ -1124,7 +1125,7 @@ static void SetPlayerName(INT32 playernum, char *newname) { CONS_Printf(M_GetText("Player %d sent a bad name change\n"), playernum+1); if (server && netgame) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } } @@ -1482,7 +1483,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (kick) { CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -2022,7 +2023,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal map change received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2133,7 +2134,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal pause command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2208,7 +2209,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2271,7 +2272,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal clear scores command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2618,7 +2619,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // this should never happen unless the client is hacked/buggy CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } if (NetPacket.packet.verification) // Special marker that the server sent the request @@ -2627,7 +2628,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } playernum = NetPacket.packet.playernum; @@ -2660,7 +2661,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } return; } @@ -2719,7 +2720,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (server && ((NetPacket.packet.newteam < 0 || NetPacket.packet.newteam > 3) || error)) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } //Safety first! @@ -3011,7 +3012,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal verification received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3061,7 +3062,7 @@ static void Got_Removal(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal demotion received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3135,7 +3136,7 @@ static void Got_MotD_f(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal motd change received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); Z_Free(mymotd); return; } @@ -3191,7 +3192,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal runsoc command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3349,7 +3350,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick) { CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3398,7 +3399,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -4191,7 +4192,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal exitlevel command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } diff --git a/src/d_player.h b/src/d_player.h index 62f38193f..db55a9913 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -510,6 +510,7 @@ typedef struct player_s UINT8 bot; tic_t jointime; // Timer when player joins game to change skin/color + tic_t quittime; // Time elapsed since user disconnected, zero if connected #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/g_game.c b/src/g_game.c index f1b9b0b5c..f5d7cd2fb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2414,6 +2414,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 skin; UINT32 availabilities; tic_t jointime; + tic_t quittime; boolean spectator; boolean outofcoop; INT16 bot; @@ -2427,6 +2428,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) ctfteam = players[player].ctfteam; exiting = players[player].exiting; jointime = players[player].jointime; + quittime = players[player].quittime; spectator = players[player].spectator; outofcoop = players[player].outofcoop; pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); @@ -2498,6 +2500,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->pflags = pflags; p->ctfteam = ctfteam; p->jointime = jointime; + p->quittime = quittime; p->spectator = spectator; p->outofcoop = outofcoop; @@ -2651,73 +2654,24 @@ static boolean G_CheckSpot(INT32 playernum, mapthing_t *mthing) // or a not-so-appropriate spot, if it initially fails // due to a lack of starts open or something. // -void G_SpawnPlayer(INT32 playernum, boolean starpost) +void G_SpawnPlayer(INT32 playernum) { - mapthing_t *spawnpoint; - if (!playeringame[playernum]) return; P_SpawnPlayer(playernum); - - if (starpost) //Don't even bother with looking for a place to spawn. - { - P_MovePlayerToStarpost(playernum); -#ifdef HAVE_BLUA - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) -#endif - return; - } - - // -- CTF -- - // Order: CTF->DM->Coop - if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam) - { - if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindCoopStart(playernum); // fallback - } - - // -- DM/Tag/CTF-spectator/etc -- - // Order: DM->CTF->Coop - else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT)) - { - if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start - && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start - spawnpoint = G_FindCoopStart(playernum); // fallback - } - - // -- Other game modes -- - // Order: Coop->DM->CTF - else - { - if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindCTFStart(playernum); // fallback - } - - //No spawns found. ANYWHERE. - if (!spawnpoint) - { - if (nummapthings) - { - if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); - spawnpoint = &mapthings[0]; - } - else - { - if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); - //P_MovePlayerToSpawn handles this fine if the spawnpoint is NULL. - } - } - P_MovePlayerToSpawn(playernum, spawnpoint); - + G_MovePlayerToSpawnOrStarpost(playernum); #ifdef HAVE_BLUA LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) #endif +} +void G_MovePlayerToSpawnOrStarpost(INT32 playernum) +{ + if (players[playernum].starposttime) + P_MovePlayerToStarpost(playernum); + else + P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum)); } mapthing_t *G_FindCTFStart(INT32 playernum) @@ -2814,6 +2768,59 @@ mapthing_t *G_FindCoopStart(INT32 playernum) return NULL; } +mapthing_t *G_FindMapStart(INT32 playernum) +{ + mapthing_t *spawnpoint; + + if (!playeringame[playernum]) + return NULL; + + // -- CTF -- + // Order: CTF->DM->Coop + if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam) + { + if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start + && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start + spawnpoint = G_FindCoopStart(playernum); // fallback + } + + // -- DM/Tag/CTF-spectator/etc -- + // Order: DM->CTF->Coop + else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT)) + { + if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start + && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start + spawnpoint = G_FindCoopStart(playernum); // fallback + } + + // -- Other game modes -- + // Order: Coop->DM->CTF + else + { + if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start + && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start + spawnpoint = G_FindCTFStart(playernum); // fallback + } + + //No spawns found. ANYWHERE. + if (!spawnpoint) + { + if (nummapthings) + { + if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) + CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); + spawnpoint = &mapthings[0]; + } + else + { + if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) + CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); + } + } + + return spawnpoint; +} + // Go back through all the projectiles and remove all references to the old // player mobj, replacing them with the new one. void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo) @@ -2992,7 +2999,7 @@ void G_DoReborn(INT32 playernum) { if (!playeringame[i]) continue; - G_SpawnPlayer(i, (players[i].starposttime)); + G_SpawnPlayer(i); } // restore time in netgame (see also p_setup.c) @@ -3038,7 +3045,7 @@ void G_DoReborn(INT32 playernum) P_RemoveMobj(player->mo); } - G_SpawnPlayer(playernum, (player->starposttime)); + G_SpawnPlayer(playernum); if (oldmo) G_ChangePlayerReferences(oldmo, players[playernum].mo); } @@ -3080,7 +3087,6 @@ void G_AddPlayer(INT32 playernum) } } - p->jointime = 0; p->playerstate = PST_REBORN; p->height = mobjinfo[MT_PLAYER].height; @@ -3103,6 +3109,8 @@ boolean G_EnoughPlayersFinished(void) { if (!playeringame[i] || players[i].spectator || players[i].bot) continue; + if (players[i].quittime > 30 * TICRATE) + continue; if (players[i].lives <= 0) continue; diff --git a/src/g_game.h b/src/g_game.h index a589a8917..a4afac163 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -161,7 +161,9 @@ INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep); mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum); mapthing_t *G_FindCoopStart(INT32 playernum); -void G_SpawnPlayer(INT32 playernum, boolean starpost); +mapthing_t *G_FindMapStart(INT32 playernum); +void G_MovePlayerToSpawnOrStarpost(INT32 playernum); +void G_SpawnPlayer(INT32 playernum); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, but a warp test can start elsewhere diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 073b21b92..c51499282 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -654,7 +654,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -668,7 +668,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -2369,7 +2369,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 253, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER"); @@ -2568,7 +2568,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+128, y, ((players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); //else //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); @@ -2692,7 +2692,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); @@ -2723,7 +2723,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline supercheck = supercheckdef; strlcpy(name, tab[i].name, 7); - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); @@ -2831,7 +2831,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor strlcpy(name, tab[i].name, 7); if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); //else // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index f950ff2fe..48f2e20a8 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -87,7 +87,7 @@ deny: CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } // Wrapper for COM_AddCommand commands diff --git a/src/lua_hook.h b/src/lua_hook.h index 0ef21490e..94d2239f7 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -102,7 +102,7 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage -void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting +void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting void LUAh_IntermissionThinker(void); // Hook for Y_Ticker boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index e2c41f54f..306bf6839 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1439,7 +1439,7 @@ UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) return shouldCollide; } -void LUAh_PlayerQuit(player_t *plr, int reason) +void LUAh_PlayerQuit(player_t *plr, kickreason_t reason) { hook_p hookp; if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index c501fbbb2..1dd4c45b5 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -362,6 +362,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->bot); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); + else if (fastcmp(field,"quittime")) + lua_pushinteger(L, plr->quittime); #ifdef HWRENDER else if (fastcmp(field,"fovadd")) lua_pushfixed(L, plr->fovadd); @@ -701,6 +703,8 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"jointime")) plr->jointime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"quittime")) + plr->quittime = (tic_t)luaL_checkinteger(L, 3); #ifdef HWRENDER else if (fastcmp(field,"fovadd")) plr->fovadd = luaL_checkfixed(L, 3); diff --git a/src/p_enemy.c b/src/p_enemy.c index 763146d81..db297e684 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -746,6 +746,9 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->bot) continue; // ignore bots + if (player->quittime) + continue; // Ignore uncontrolled bodies + if (dist > 0 && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) continue; // Too far away diff --git a/src/p_inter.c b/src/p_inter.c index 71dcd70a1..8090b7406 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2257,9 +2257,9 @@ void P_CheckSurvivors(void) { if (players[i].spectator) spectators++; - else if (players[i].pflags & PF_TAGIT) + else if ((players[i].pflags & PF_TAGIT) && players[i].quittime < 30 * TICRATE) taggers++; - else if (!(players[i].pflags & PF_GAMETYPEOVER)) + else if (!(players[i].pflags & PF_GAMETYPEOVER) && players[i].quittime < 30 * TICRATE) { survivorarray[survivors] = i; survivors++; diff --git a/src/p_saveg.c b/src/p_saveg.c index 2b6a474bf..853856880 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -255,6 +255,7 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].onconveyor); WRITEUINT32(save_p, players[i].jointime); + WRITEUINT32(save_p, players[i].quittime); WRITEUINT16(save_p, flags); @@ -446,6 +447,7 @@ static void P_NetUnArchivePlayers(void) players[i].onconveyor = READINT32(save_p); players[i].jointime = READUINT32(save_p); + players[i].quittime = READUINT32(save_p); flags = READUINT16(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index c7f4cd81b..729ee00c2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3097,7 +3097,7 @@ static void P_InitTagGametype(void) //Also, you'd never have to loop through all 32 players slots to find anything ever again. for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].spectator) + if (playeringame[i] && !(players[i].spectator && players[i].quittime)) { playersactive[realnumplayers] = i; //stores the player's node in the array. realnumplayers++; @@ -3119,7 +3119,7 @@ static void P_InitTagGametype(void) if (players[playersactive[i]].mo) P_RemoveMobj(players[playersactive[i]].mo); - G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location. + G_SpawnPlayer(playersactive[i]); //respawn the lucky player in his dedicated spawn location. } static void P_SetupCamera(void) @@ -3297,7 +3297,7 @@ static void P_InitPlayers(void) G_DoReborn(i); else // gametype is GT_COOP or GT_RACE { - G_SpawnPlayer(i, players[i].starposttime); + G_SpawnPlayer(i); if (players[i].starposttime) P_ClearStarPost(players[i].starpostnum); } diff --git a/src/p_spec.c b/src/p_spec.c index e5b026a3d..84ccc29a5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4426,10 +4426,18 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers case 6: // Death Pit (Camera Mod) case 7: // Death Pit (No Camera Mod) if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); + { + if (player->quittime) + G_MovePlayerToSpawnOrStarpost(player - players); + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); + } break; case 8: // Instant Kill - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); + if (player->quittime) + G_MovePlayerToSpawnOrStarpost(player - players); + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); break; case 9: // Ring Drainer (Floor Touch) case 10: // Ring Drainer (No Floor Touch) diff --git a/src/p_tick.c b/src/p_tick.c index 9b56ee1c2..348f2557c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -590,10 +590,24 @@ void P_Ticker(boolean run) { INT32 i; - //Increment jointime even if paused. + // Increment jointime and quittime even if paused for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) - ++players[i].jointime; + { + players[i].jointime++; + + if (players[i].quittime) + { + players[i].quittime++; + + if (players[i].quittime == 30 * TICRATE) + P_CheckSurvivors(); + + if (server && players[i].quittime >= FixedMul(cv_rejointimeout.value, 60 * TICRATE) + && !(players[i].quittime % TICRATE)) + SendKick(i, KICK_MSG_PLAYER_QUIT); + } + } if (objectplacing) { diff --git a/src/p_user.c b/src/p_user.c index 74349e185..d229e8b78 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11756,6 +11756,8 @@ void P_PlayerThink(player_t *player) { if (!playeringame[i] || players[i].spectator || players[i].bot) continue; + if (players[i].quittime > 30 * TICRATE) + continue; if (players[i].lives <= 0) continue; @@ -12208,6 +12210,11 @@ void P_PlayerThink(player_t *player) player->pflags &= ~PF_USEDOWN; } + // IF PLAYER NOT HERE THEN FLASH END IF + if (player->quittime && player->powers[pw_flashing] < flashingtics - 1 + && !(G_TagGametype() && !(player->pflags & PF_TAGIT)) && !player->gotflag) + player->powers[pw_flashing] = flashingtics - 1; + // Counters, time dependent power ups. // Time Bonus & Ring Bonus count settings @@ -12251,12 +12258,12 @@ void P_PlayerThink(player_t *player) else player->powers[pw_underwater] = 0; } - else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer + else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer player->powers[pw_underwater]--; if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER))) player->powers[pw_spacetime] = 0; - else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer + else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer player->powers[pw_spacetime]--; if (player->powers[pw_gravityboots] && player->powers[pw_gravityboots] < UINT16_MAX)