Let clients rejoin the server without losing their status

This is accomplished by simply preserving
the player's body after disconnecting.

Bodies will despawn after the number of minutes
specified by the "rejointimeout" console variable (float).
A value of 0 disables the feature completely.

Clients rejoining are identified by their IP address,
and may rejoin even if the server is full or joins are disabled,
for as long as their body remains.

From a technical standpoint, when the user disconnects,
the player they were controlling does not leave,
the underlying player_t just keeps working normally,
except it does not receive any input anymore.
When the user reconnects, they are simply "relinked"
to their player_t.

Those "soulless" players can be identified through
their "quittime" field, which is the number of tics
elapsed since the user disconnected, or zero
if still connected. "quittime" is exposed to Lua.
This commit is contained in:
Louis-Antoine 2020-01-22 03:05:08 +01:00
parent e9108de365
commit d374bf4f9b
13 changed files with 252 additions and 150 deletions

View file

@ -1407,7 +1407,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);

View file

@ -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
@ -391,7 +392,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 +438,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);
@ -1064,7 +1068,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;
}
}
@ -2403,6 +2407,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));
}
//
@ -2410,7 +2415,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.
@ -2423,9 +2428,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);
@ -2812,9 +2814,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))
{
@ -2874,6 +2879,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]);
@ -2942,7 +2948,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;
@ -2983,6 +2989,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);
}
@ -2991,6 +3015,9 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, N
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|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 };
@ -3060,6 +3087,7 @@ static void ResetNode(INT32 node)
nodewaiting[node] = 0;
playerpernode[node] = 0;
sendingsavegame[node] = false;
SV_InitResynchVars(node);
}
void SV_ResetServer(void)
@ -3075,13 +3103,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
@ -3089,6 +3112,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.
}
@ -3179,6 +3203,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)
{
@ -3195,13 +3250,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;
}
@ -3210,16 +3268,35 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
splitscreenplayer = newplayernum & 0x80;
newplayernum &= ~0x80;
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;
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
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);
// the server is creating my player
if (node == mynode)
{
@ -3236,28 +3313,66 @@ 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
if (!rejoined)
LUAh_PlayerJoin(newplayernum);
#endif
}
@ -3267,11 +3382,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++)
{
@ -3280,68 +3391,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
@ -3368,8 +3433,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++;
}
}
@ -3495,16 +3558,19 @@ 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.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."));
@ -3519,7 +3585,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;
@ -3956,7 +4022,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)));
@ -4075,6 +4141,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;
@ -4096,7 +4163,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;
@ -4114,7 +4181,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;
}
@ -4174,7 +4241,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;
@ -4186,7 +4253,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;
}
@ -4210,7 +4277,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)
@ -4406,7 +4473,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]);
@ -4582,6 +4649,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;
@ -4615,25 +4687,33 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
// create missed tic
static void SV_Maketic(void)
{
INT32 j;
for (j = 0; j < MAXNETNODES; j++)
if (playerpernode[j])
{
INT32 player = nodetoplayer[j];
if ((netcmds[maketic%BACKUPTICS][player].angleturn & TICCMD_RECEIVED) == 0)
{ // we didn't receive this tic
INT32 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])
for (i = 0; i < MAXPLAYERS; i++)
{
netcmds[maketic%BACKUPTICS][player] = netcmds[(maketic-1)%BACKUPTICS][player];
netcmds[maketic%BACKUPTICS][player].angleturn &= ~TICCMD_RECEIVED;
if (!playeringame[i])
continue;
// We didn't receive this tic
if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
{
ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i];
ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
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;
}
}
}
@ -4758,7 +4838,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);
}
}
/*

View file

@ -463,6 +463,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
{
@ -490,7 +491,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);

View file

@ -572,6 +572,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);
@ -1091,7 +1092,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);
}
}
@ -1449,7 +1450,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;
}
}
@ -2032,7 +2033,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;
}
@ -2142,7 +2143,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;
}
@ -2215,7 +2216,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;
}
@ -2278,7 +2279,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;
}
@ -2625,7 +2626,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
@ -2634,7 +2635,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;
@ -2667,7 +2668,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;
}
@ -2716,7 +2717,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!
@ -3000,7 +3001,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;
}
@ -3050,7 +3051,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;
}
@ -3124,7 +3125,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;
}
@ -3180,7 +3181,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;
}
@ -3338,7 +3339,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;
}
@ -3387,7 +3388,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;
}
@ -4170,7 +4171,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;
}

View file

@ -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

View file

@ -2307,6 +2307,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 skin;
UINT32 availabilities;
tic_t jointime;
tic_t quittime;
boolean spectator;
boolean outofcoop;
INT16 bot;
@ -2320,6 +2321,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));
@ -2391,6 +2393,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;
@ -2974,7 +2977,6 @@ void G_AddPlayer(INT32 playernum)
}
}
p->jointime = 0;
p->playerstate = PST_REBORN;
p->height = mobjinfo[MT_PLAYER].height;

View file

@ -659,7 +659,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;
}
@ -673,7 +673,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;
}
}
@ -2367,7 +2367,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");
@ -2566,7 +2566,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");
@ -2690,7 +2690,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");
@ -2721,7 +2721,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");
@ -2829,7 +2829,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");

View file

@ -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

View file

@ -91,7 +91,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
#endif

View file

@ -1295,7 +1295,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))))

View file

@ -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);

View file

@ -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);

View file

@ -590,10 +590,19 @@ 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 (server && players[i].quittime >= FixedMul(cv_rejointimeout.value, 60 * TICRATE))
SendKick(i, KICK_MSG_PLAYER_QUIT);
}
}
if (objectplacing)
{