mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-01-18 07:22:28 +00:00
Even more SRB2Kart netcode improvement ports
- Prevent connection timeout during the waiting gamestate from the last commit. - Keep client connections alive during fades / other internal loops. - More consistently timeout clients when they reach the end of BACKUPTICS. - Dedicated servers will not run any game logic if no nodes are sending packets to it, to reduce CPU usage when there is no one interacting with your server. - Unlike SRB2Kart, the amount of time is configurable with the "dedicatedidletime" console variable. Setting this to 0 will disable this feature. - CL_SendClientCmd uses exact packet types instead of magic number offsets.
This commit is contained in:
parent
80bf4d6c2d
commit
4a735470b9
7 changed files with 235 additions and 53 deletions
274
src/d_clisrv.c
274
src/d_clisrv.c
|
@ -120,6 +120,8 @@ UINT8 hu_redownloadinggamestate = 0;
|
|||
// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
|
||||
boolean hu_stopped = false;
|
||||
|
||||
consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
|
||||
|
||||
UINT8 adminpassmd5[16];
|
||||
boolean adminpasswordset = false;
|
||||
|
||||
|
@ -4520,6 +4522,7 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
netconsole = 0;
|
||||
else
|
||||
netconsole = nodetoplayer[node];
|
||||
|
||||
#ifdef PARANOIA
|
||||
if (netconsole >= MAXPLAYERS)
|
||||
I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
|
||||
|
@ -4558,15 +4561,20 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
// Update the nettics
|
||||
nettics[node] = realend;
|
||||
|
||||
// Don't do anything for packets of type NODEKEEPALIVE?
|
||||
if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
|
||||
// This should probably still timeout though, as the node should always have a player 1 number
|
||||
if (netconsole == -1)
|
||||
break;
|
||||
|
||||
// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
|
||||
/// \todo Use a separate cvar for that kind of timeout?
|
||||
freezetimeout[node] = I_GetTime() + connectiontimeout;
|
||||
|
||||
// Don't do anything for packets of type NODEKEEPALIVE?
|
||||
// Sryder 2018/07/01: Update the freezetimeout still!
|
||||
if (netbuffer->packettype == PT_NODEKEEPALIVE
|
||||
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
|
||||
break;
|
||||
|
||||
// If we've alredy received a ticcmd for this tic, just submit it for the next one.
|
||||
tic_t faketic = maketic;
|
||||
if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
|
||||
|
@ -4630,6 +4638,21 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case PT_BASICKEEPALIVE:
|
||||
if (client)
|
||||
break;
|
||||
|
||||
// This should probably still timeout though, as the node should always have a player 1 number
|
||||
if (netconsole == -1)
|
||||
break;
|
||||
|
||||
// If a client sends this it should mean they are done receiving the savegame
|
||||
sendingsavegame[node] = false;
|
||||
|
||||
// As long as clients send keep alives, the server can keep running, so reset the timeout
|
||||
/// \todo Use a separate cvar for that kind of timeout?
|
||||
freezetimeout[node] = I_GetTime() + connectiontimeout;
|
||||
break;
|
||||
case PT_TEXTCMD2: // splitscreen special
|
||||
netconsole = nodetoplayer2[node];
|
||||
/* FALLTHRU */
|
||||
|
@ -5056,39 +5079,66 @@ static INT16 Consistancy(void)
|
|||
return (INT16)(ret & 0xFFFF);
|
||||
}
|
||||
|
||||
// confusing, but this DOESN'T send PT_NODEKEEPALIVE, it sends PT_BASICKEEPALIVE
|
||||
// used during wipes to tell the server that a node is still connected
|
||||
static void CL_SendClientKeepAlive(void)
|
||||
{
|
||||
netbuffer->packettype = PT_BASICKEEPALIVE;
|
||||
|
||||
HSendPacket(servernode, false, 0, 0);
|
||||
}
|
||||
|
||||
static void SV_SendServerKeepAlive(void)
|
||||
{
|
||||
INT32 n;
|
||||
|
||||
for (n = 1; n < MAXNETNODES; n++)
|
||||
{
|
||||
if (nodeingame[n])
|
||||
{
|
||||
netbuffer->packettype = PT_BASICKEEPALIVE;
|
||||
HSendPacket(n, false, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send the client packet to the server
|
||||
static void CL_SendClientCmd(void)
|
||||
{
|
||||
size_t packetsize = 0;
|
||||
boolean mis = false;
|
||||
|
||||
netbuffer->packettype = PT_CLIENTCMD;
|
||||
|
||||
if (cl_packetmissed)
|
||||
netbuffer->packettype++;
|
||||
{
|
||||
netbuffer->packettype = PT_CLIENTMIS;
|
||||
mis = true;
|
||||
}
|
||||
|
||||
netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
|
||||
netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
|
||||
|
||||
if (gamestate == GS_WAITINGPLAYERS)
|
||||
{
|
||||
// Send PT_NODEKEEPALIVE packet
|
||||
netbuffer->packettype += 4;
|
||||
netbuffer->packettype = (mis ? PT_NODEKEEPALIVEMIS : PT_NODEKEEPALIVE);
|
||||
packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
|
||||
HSendPacket(servernode, false, 0, packetsize);
|
||||
}
|
||||
else if (gamestate != GS_NULL && (addedtogame || dedicated))
|
||||
{
|
||||
packetsize = sizeof (clientcmd_pak);
|
||||
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
|
||||
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
|
||||
|
||||
// Send a special packet with 2 cmd for splitscreen
|
||||
if (splitscreen || botingame)
|
||||
{
|
||||
netbuffer->packettype += 2;
|
||||
G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
|
||||
netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD);
|
||||
packetsize = sizeof (client2cmd_pak);
|
||||
G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
|
||||
}
|
||||
else
|
||||
packetsize = sizeof (clientcmd_pak);
|
||||
|
||||
HSendPacket(servernode, false, 0, packetsize);
|
||||
}
|
||||
|
@ -5099,7 +5149,7 @@ static void CL_SendClientCmd(void)
|
|||
if (localtextcmd[0])
|
||||
{
|
||||
netbuffer->packettype = PT_TEXTCMD;
|
||||
M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
|
||||
M_Memcpy(netbuffer->u.textcmd, localtextcmd, localtextcmd[0]+1);
|
||||
// All extra data have been sent
|
||||
if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
|
||||
localtextcmd[0] = 0;
|
||||
|
@ -5479,28 +5529,11 @@ static inline void PingUpdate(void)
|
|||
pingmeasurecount = 1; //Reset count
|
||||
}
|
||||
|
||||
void NetUpdate(void)
|
||||
static tic_t gametime = 0;
|
||||
|
||||
static void UpdatePingTable(void)
|
||||
{
|
||||
static tic_t gametime = 0;
|
||||
static tic_t resptime = 0;
|
||||
tic_t nowtime;
|
||||
INT32 i;
|
||||
INT32 realtics;
|
||||
|
||||
nowtime = I_GetTime();
|
||||
realtics = nowtime - gametime;
|
||||
|
||||
if (realtics <= 0) // nothing new to update
|
||||
return;
|
||||
if (realtics > 5)
|
||||
{
|
||||
if (server)
|
||||
realtics = 1;
|
||||
else
|
||||
realtics = 5;
|
||||
}
|
||||
|
||||
gametime = nowtime;
|
||||
|
||||
if (server)
|
||||
{
|
||||
|
@ -5512,6 +5545,150 @@ void NetUpdate(void)
|
|||
realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
|
||||
pingmeasurecount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeouts to prevent definitive freezes from happenning
|
||||
static void HandleNodeTimeouts(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
if (server)
|
||||
{
|
||||
for (i = 1; i < MAXNETNODES; i++)
|
||||
if (nodeingame[i] && freezetimeout[i] < I_GetTime())
|
||||
Net_ConnectionTimeout(i);
|
||||
|
||||
// In case the cvar value was lowered
|
||||
if (joindelay)
|
||||
joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the network alive while not advancing tics!
|
||||
void NetKeepAlive(void)
|
||||
{
|
||||
tic_t nowtime;
|
||||
INT32 realtics;
|
||||
|
||||
nowtime = I_GetTime();
|
||||
realtics = nowtime - gametime;
|
||||
|
||||
// return if there's no time passed since the last call
|
||||
if (realtics <= 0) // nothing new to update
|
||||
return;
|
||||
|
||||
UpdatePingTable();
|
||||
|
||||
GetPackets();
|
||||
|
||||
#ifdef MASTERSERVER
|
||||
MasterClient_Ticker();
|
||||
#endif
|
||||
|
||||
if (client)
|
||||
{
|
||||
// send keep alive
|
||||
CL_SendClientKeepAlive();
|
||||
// No need to check for resynch because we aren't running any tics
|
||||
}
|
||||
else
|
||||
{
|
||||
SV_SendServerKeepAlive();
|
||||
}
|
||||
|
||||
// No else because no tics are being run and we can't resynch during this
|
||||
|
||||
Net_AckTicker();
|
||||
HandleNodeTimeouts();
|
||||
FileSendTicker();
|
||||
}
|
||||
|
||||
void NetUpdate(void)
|
||||
{
|
||||
static tic_t resptime = 0;
|
||||
tic_t nowtime;
|
||||
INT32 i;
|
||||
INT32 realtics;
|
||||
|
||||
nowtime = I_GetTime();
|
||||
realtics = nowtime - gametime;
|
||||
|
||||
if (realtics <= 0) // nothing new to update
|
||||
return;
|
||||
|
||||
if (realtics > 5)
|
||||
{
|
||||
if (server)
|
||||
realtics = 1;
|
||||
else
|
||||
realtics = 5;
|
||||
}
|
||||
|
||||
if (server && dedicated && gamestate == GS_LEVEL)
|
||||
{
|
||||
const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
|
||||
static tic_t dedicatedidletimeprev = 0;
|
||||
static tic_t dedicatedidle = 0;
|
||||
|
||||
if (dedicatedidletime > 0)
|
||||
{
|
||||
for (i = 1; i < MAXNETNODES; ++i)
|
||||
if (nodeingame[i])
|
||||
{
|
||||
if (dedicatedidle >= dedicatedidletime)
|
||||
{
|
||||
CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i);
|
||||
dedicatedidle = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MAXNETNODES)
|
||||
{
|
||||
if (leveltime == 2)
|
||||
{
|
||||
// On next tick...
|
||||
dedicatedidle = dedicatedidletime-1;
|
||||
}
|
||||
else if (dedicatedidle >= dedicatedidletime)
|
||||
{
|
||||
if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0))
|
||||
{
|
||||
CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
|
||||
dedicatedidle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
realtics = 0;
|
||||
}
|
||||
}
|
||||
else if ((dedicatedidle += realtics) >= dedicatedidletime)
|
||||
{
|
||||
const char *idlereason = "at round start";
|
||||
if (leveltime > 3)
|
||||
idlereason = va("for %d seconds", dedicatedidle/TICRATE);
|
||||
|
||||
CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason);
|
||||
realtics = 0;
|
||||
dedicatedidle = dedicatedidletime;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev)
|
||||
{
|
||||
CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n");
|
||||
}
|
||||
dedicatedidle = 0;
|
||||
}
|
||||
|
||||
dedicatedidletimeprev = dedicatedidletime;
|
||||
}
|
||||
|
||||
gametime = nowtime;
|
||||
|
||||
UpdatePingTable();
|
||||
|
||||
if (client)
|
||||
maketic = neededtic;
|
||||
|
@ -5543,25 +5720,26 @@ void NetUpdate(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!demoplayback)
|
||||
if (!demoplayback && realtics > 0)
|
||||
{
|
||||
INT32 counts;
|
||||
|
||||
hu_redownloadinggamestate = false;
|
||||
|
||||
firstticstosend = gametic;
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
if (nodeingame[i] && nettics[i] < firstticstosend)
|
||||
{
|
||||
firstticstosend = nettics[i];
|
||||
|
||||
if (maketic + 1 >= nettics[i] + BACKUPTICS)
|
||||
Net_ConnectionTimeout(i);
|
||||
}
|
||||
|
||||
// Don't erase tics not acknowledged
|
||||
counts = realtics;
|
||||
|
||||
firstticstosend = gametic;
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
if (!nodeingame[i])
|
||||
continue;
|
||||
if (nettics[i] < firstticstosend)
|
||||
firstticstosend = nettics[i];
|
||||
if (maketic + counts >= nettics[i] + (BACKUPTICS - TICRATE))
|
||||
Net_ConnectionTimeout(i);
|
||||
}
|
||||
|
||||
if (maketic + counts >= firstticstosend + BACKUPTICS)
|
||||
counts = firstticstosend+BACKUPTICS-maketic-1;
|
||||
|
||||
|
@ -5578,20 +5756,10 @@ void NetUpdate(void)
|
|||
}
|
||||
|
||||
Net_AckTicker();
|
||||
|
||||
// Handle timeouts to prevent definitive freezes from happenning
|
||||
if (server)
|
||||
{
|
||||
for (i = 1; i < MAXNETNODES; i++)
|
||||
if (nodeingame[i] && freezetimeout[i] < I_GetTime())
|
||||
Net_ConnectionTimeout(i);
|
||||
|
||||
// In case the cvar value was lowered
|
||||
if (joindelay)
|
||||
joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
|
||||
}
|
||||
HandleNodeTimeouts();
|
||||
|
||||
nowtime /= NEWTICRATERATIO;
|
||||
|
||||
if (nowtime > resptime)
|
||||
{
|
||||
resptime = nowtime;
|
||||
|
|
|
@ -77,6 +77,8 @@ typedef enum
|
|||
PT_ASKLUAFILE, // Client telling the server they don't have the file
|
||||
PT_HASLUAFILE, // Client telling the server they have the file
|
||||
|
||||
PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
|
||||
|
||||
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
|
||||
|
||||
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
|
||||
|
@ -398,6 +400,7 @@ extern tic_t servermaxping;
|
|||
extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
|
||||
extern consvar_t cv_resynchattempts, cv_blamecfail;
|
||||
extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
|
||||
extern consvar_t cv_dedicatedidletime;
|
||||
|
||||
// Used in d_net, the only dependence
|
||||
tic_t ExpandTics(INT32 low, INT32 node);
|
||||
|
@ -412,6 +415,9 @@ void SendKick(UINT8 playernum, UINT8 msg);
|
|||
// Create any new ticcmds and broadcast to other players.
|
||||
void NetUpdate(void);
|
||||
|
||||
// Maintain connections to nodes without timing them all out.
|
||||
void NetKeepAlive(void);
|
||||
|
||||
void SV_StartSinglePlayerServer(void);
|
||||
boolean SV_SpawnServer(void);
|
||||
void SV_StopServer(void);
|
||||
|
|
|
@ -869,6 +869,9 @@ static void DebugPrintpacket(const char *header)
|
|||
(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
|
||||
(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
|
||||
break;
|
||||
case PT_BASICKEEPALIVE:
|
||||
fprintf(debugfile, " wipetime\n");
|
||||
break;
|
||||
case PT_TEXTCMD:
|
||||
case PT_TEXTCMD2:
|
||||
fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]);
|
||||
|
|
|
@ -599,6 +599,7 @@ void D_RegisterServerCommands(void)
|
|||
CV_RegisterVar(&cv_joinnextround);
|
||||
CV_RegisterVar(&cv_showjoinaddress);
|
||||
CV_RegisterVar(&cv_blamecfail);
|
||||
CV_RegisterVar(&cv_dedicatedidletime);
|
||||
#endif
|
||||
|
||||
COM_AddCommand("ping", Command_Ping_f, COM_LUA);
|
||||
|
|
|
@ -614,6 +614,8 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
|
|||
|
||||
if (moviemode)
|
||||
M_SaveFrame();
|
||||
|
||||
NetKeepAlive(); // Update the network so we don't cause timeouts
|
||||
}
|
||||
|
||||
WipeInAction = false;
|
||||
|
|
|
@ -1915,6 +1915,7 @@ void G_PreLevelTitleCard(void)
|
|||
ST_runTitleCard();
|
||||
ST_preLevelTitleCardDrawer();
|
||||
I_FinishUpdate(); // page flip or blit buffer
|
||||
NetKeepAlive(); // Prevent timeouts
|
||||
|
||||
if (moviemode)
|
||||
M_SaveFrame();
|
||||
|
|
|
@ -7436,6 +7436,7 @@ static void P_RunSpecialStageWipe(void)
|
|||
lastwipetic = nowtime;
|
||||
if (moviemode) // make sure we save frames for the white hold too
|
||||
M_SaveFrame();
|
||||
NetKeepAlive(); // Prevent timeout
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue