• R/O
  • SSH

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

This is a fork of Zandronum used on servers hosted by The Sentinels Playground (TSPG).


Commit MetaInfo

Revisiónfda0458f443afe5e8cc906df39e97b49f44aeb9a (tree)
Tiempo2021-10-19 12:30:04
AutorAdam Kaminski <kaminskiadam9@gmai...>
CommiterAdam Kaminski

Log Message

Added packet loss mitigation, which the client can control using the CVar "cl_backupcommands".

Cambiar Resumen

Diferencia incremental

diff -r 629a821a3980 -r fda0458f443a docs/zandronum-history.txt
--- a/docs/zandronum-history.txt Mon Oct 18 23:27:43 2021 -0400
+++ b/docs/zandronum-history.txt Mon Oct 18 23:30:04 2021 -0400
@@ -59,6 +59,7 @@
5959 + - Added the EVENT script type: GAMEEVENT_PLAYERCONNECT, indicating when a client or bot joins the server. [Kaminsky]
6060 + - Added the EVENT script type GAMEEVENT_ACTOR_SPAWNED and GAMEVENT_ACTOR_DAMAGED, which are triggered just before an actor's first tic and when an actor takes damage. Note that for performance reasons, these events are disabled by default so modders have to enable them by themselves. [Kaminsky]
6161 + - Added DMFlags: "sv_shootthroughallies" and "sv_dontpushallies", so a player's attacks can pass through and not push their allies. [Kaminsky]
62++ - Added packet loss mitigation, which the client can control using the CVar "cl_backupcommands". [Kaminsky]
6263 - - Fixed: Bots tries to jump to reach item when sv_nojump is true. [sleep]
6364 - - Fixed: ACS function SetSkyScrollSpeed didn't work online. [Edward-san]
6465 - - Fixed: color codes in callvote reasons weren't terminated properly. [Dusk]
diff -r 629a821a3980 -r fda0458f443a src/cl_commands.cpp
--- a/src/cl_commands.cpp Mon Oct 18 23:27:43 2021 -0400
+++ b/src/cl_commands.cpp Mon Oct 18 23:30:04 2021 -0400
@@ -76,6 +76,9 @@
7676 static bool g_bIgnoreWeaponSelect = false;
7777 SDWORD g_sdwCheckCmd = 0;
7878
79+// [AK] Backups of the last few movement commands we sent to the server.
80+static RingBuffer<CLIENT_MOVE_COMMAND_s, MAX_BACKUP_COMMANDS> g_BackupMoveCMDs;
81+
7982 //*****************************************************************************
8083 // FUNCTIONS
8184
@@ -90,6 +93,13 @@
9093
9194 //*****************************************************************************
9295 //
96+void CLIENT_ClearBackupCommands( void )
97+{
98+ g_BackupMoveCMDs.clear( );
99+}
100+
101+//*****************************************************************************
102+//
93103 void CLIENT_IgnoreWeaponSelect( bool bIgnore )
94104 {
95105 g_bIgnoreWeaponSelect = bIgnore;
@@ -415,8 +425,42 @@
415425 // [AK] Create the movement command for the current tic.
416426 CLIENT_MOVE_COMMAND_s moveCMD = clientcommand_CreateMoveCommand( );
417427
418- CLIENT_GetLocalBuffer( )->ByteStream.WriteByte( CLC_CLIENTMOVE );
419- clientcommand_WriteMoveCommandToBuffer( moveCMD );
428+ // [AK] If we don't want to send backup commands, send only this one and that's it.
429+ if ( cl_backupcommands == 0 )
430+ {
431+ CLIENT_GetLocalBuffer( )->ByteStream.WriteByte( CLC_CLIENTMOVE );
432+ clientcommand_WriteMoveCommandToBuffer( moveCMD );
433+ }
434+ else
435+ {
436+ // [AK] Save the movement command from this tic for future use.
437+ g_BackupMoveCMDs.put( moveCMD );
438+
439+ ULONG ulNumSavedCMDs = 0;
440+ ULONG ulNumExpectedCMDs = cl_backupcommands + 1;
441+
442+ // [AK] Determine how many movement commands we now have saved in the buffer.
443+ for ( unsigned int i = 0; i < MAX_BACKUP_COMMANDS; i++ )
444+ {
445+ if ( g_BackupMoveCMDs.getOldestEntry( i ).ulGametic != 0 )
446+ ulNumSavedCMDs++;
447+ }
448+
449+ ULONG ulNumCMDsToSend = MIN( ulNumSavedCMDs, ulNumExpectedCMDs );
450+ CLIENT_GetLocalBuffer( )->ByteStream.WriteByte( CLC_CLIENTMOVEBACKUP );
451+
452+ // [AK] We need to tell the server the number of movement commands we sent, and
453+ // up to how many movment commands we actually want to send.
454+ CLIENT_GetLocalBuffer( )->ByteStream.WriteShortByte( ulNumCMDsToSend, 4 );
455+ CLIENT_GetLocalBuffer( )->ByteStream.WriteShortByte( ulNumExpectedCMDs, 4 );
456+
457+ // [AK] Older movement commands must be written to the buffer before newer ones.
458+ for ( int i = ulNumCMDsToSend; i >= 1; i-- )
459+ {
460+ moveCMD = g_BackupMoveCMDs.getOldestEntry( MAX_BACKUP_COMMANDS - i );
461+ clientcommand_WriteMoveCommandToBuffer( moveCMD );
462+ }
463+ }
420464 }
421465
422466 //*****************************************************************************
diff -r 629a821a3980 -r fda0458f443a src/cl_commands.h
--- a/src/cl_commands.h Mon Oct 18 23:27:43 2021 -0400
+++ b/src/cl_commands.h Mon Oct 18 23:30:04 2021 -0400
@@ -70,6 +70,7 @@
7070 // PROTOTYPES
7171
7272 void CLIENT_ResetFloodTimers( void );
73+void CLIENT_ClearBackupCommands( void );
7374 void CLIENT_IgnoreWeaponSelect( bool bIgnore );
7475 bool CLIENT_GetIgnoreWeaponSelect( void );
7576 bool CLIENT_AllowSVCheatMessage( void );
diff -r 629a821a3980 -r fda0458f443a src/cl_main.cpp
--- a/src/cl_main.cpp Mon Oct 18 23:27:43 2021 -0400
+++ b/src/cl_main.cpp Mon Oct 18 23:30:04 2021 -0400
@@ -169,6 +169,16 @@
169169 // [JS] Always makes us ready when we are in intermission.
170170 CVAR( Bool, cl_autoready, false, CVAR_ARCHIVE )
171171
172+// [AK] Let the user send backup copies of old commands, in case of packet loss.
173+CUSTOM_CVAR( Int, cl_backupcommands, 0, CVAR_ARCHIVE )
174+{
175+ if ( self < 0 )
176+ self = 0;
177+
178+ if ( self > MAX_BACKUP_COMMANDS - 1 )
179+ self = MAX_BACKUP_COMMANDS - 1;
180+}
181+
172182 //*****************************************************************************
173183 // PROTOTYPES
174184
@@ -3407,6 +3417,8 @@
34073417 ( priorState == PST_REBORN ) || ( priorState == PST_REBORNNOINVENTORY ))
34083418 {
34093419 g_ulFirstSpawnedTic = gametic;
3420+ // [AK] Any backup commands we have saved are now invalid, so remove them.
3421+ CLIENT_ClearBackupCommands( );
34103422 }
34113423 }
34123424 else
diff -r 629a821a3980 -r fda0458f443a src/cl_main.h
--- a/src/cl_main.h Mon Oct 18 23:27:43 2021 -0400
+++ b/src/cl_main.h Mon Oct 18 23:30:04 2021 -0400
@@ -61,6 +61,9 @@
6161 #define CONNECTION_RESEND_TIME ( 3 * TICRATE )
6262 #define GAMESTATE_RESEND_TIME ( 3 * TICRATE )
6363
64+// [AK] The maximum number of move commands we're allowed to send per tic.
65+#define MAX_BACKUP_COMMANDS 3
66+
6467 //*****************************************************************************
6568 enum CONNECTIONSTATE_e
6669 {
@@ -211,6 +214,7 @@
211214 EXTERN_CVAR( String, cl_password )
212215 EXTERN_CVAR( String, cl_joinpassword )
213216 EXTERN_CVAR( Bool, cl_hitscandecalhack )
217+EXTERN_CVAR( Int, cl_backupcommands ) // [AK]
214218
215219 // Not in cl_main.cpp, but this seems like a good enough place for it.
216220 EXTERN_CVAR( Int, cl_skins )
diff -r 629a821a3980 -r fda0458f443a src/network_enums.h
--- a/src/network_enums.h Mon Oct 18 23:27:43 2021 -0400
+++ b/src/network_enums.h Mon Oct 18 23:30:04 2021 -0400
@@ -407,6 +407,7 @@
407407 ENUM_ELEMENT( CLC_ENDCHAT ),
408408 ENUM_ELEMENT( CLC_SAY ),
409409 ENUM_ELEMENT( CLC_CLIENTMOVE ),
410+ ENUM_ELEMENT( CLC_CLIENTMOVEBACKUP ),
410411 ENUM_ELEMENT( CLC_MISSINGPACKET ),
411412 ENUM_ELEMENT( CLC_PONG ),
412413 ENUM_ELEMENT( CLC_WEAPONSELECT ),
diff -r 629a821a3980 -r fda0458f443a src/p_tick.cpp
--- a/src/p_tick.cpp Mon Oct 18 23:27:43 2021 -0400
+++ b/src/p_tick.cpp Mon Oct 18 23:30:04 2021 -0400
@@ -334,6 +334,9 @@
334334 ulNumMoveCMDs++;
335335 }
336336
337+ // [AK] Check if we should process this client's movement commands.
338+ bool bProcessMoveCMD = SERVER_ShouldProcessMoveCommand( ulIdx, ulNumMoveCMDs );
339+
337340 // [AK] Handle the skip correction. If it explicity returns false, then we won't process two
338341 // movement commands during this tic for the client.
339342 if ((sv_smoothplayers) && (SERVER_HandleSkipCorrection(ulIdx, ulNumMoveCMDs) == false))
@@ -343,9 +346,10 @@
343346 {
344347 // Process only one movement command.
345348 const bool bMovement = client->MoveCMDs[0]->isMoveCmd( );
346- client->MoveCMDs[0]->process( ulIdx );
347349
348- if ( bMovement )
350+ // [AK] Only update the last movement command if we're supposed to be processing any
351+ // movement commands in the buffer at this time.
352+ if (( bProcessMoveCMD ) && ( bMovement ))
349353 {
350354 if ( client->LastMoveCMD != NULL )
351355 delete client->LastMoveCMD;
@@ -354,8 +358,14 @@
354358 client->LastMoveCMD = new ClientMoveCommand( *static_cast<ClientMoveCommand *>( client->MoveCMDs[0] ));
355359 }
356360
357- delete client->MoveCMDs[0];
358- client->MoveCMDs.Delete(0);
361+ // [AK] Only process movement commands if we're allowed to at this time. On the other
362+ // hand, we can still process other commands in the buffer.
363+ if (( bProcessMoveCMD ) || ( bMovement == false ))
364+ {
365+ client->MoveCMDs[0]->process( ulIdx );
366+ delete client->MoveCMDs[0];
367+ client->MoveCMDs.Delete( 0 );
368+ }
359369
360370 if ( bMovement == true )
361371 break;
diff -r 629a821a3980 -r fda0458f443a src/sv_main.cpp
--- a/src/sv_main.cpp Mon Oct 18 23:27:43 2021 -0400
+++ b/src/sv_main.cpp Mon Oct 18 23:30:04 2021 -0400
@@ -145,7 +145,7 @@
145145
146146 static bool server_Ignore( BYTESTREAM_s *pByteStream );
147147 static bool server_Say( BYTESTREAM_s *pByteStream );
148-static bool server_ClientMove( BYTESTREAM_s *pByteStream );
148+static bool server_ClientMove( BYTESTREAM_s *pByteStream, bool bSentBackup );
149149 static bool server_MissingPacket( BYTESTREAM_s *pByteStream );
150150 static bool server_UpdateClientPing( BYTESTREAM_s *pByteStream );
151151 static bool server_WeaponSelect( BYTESTREAM_s *pByteStream );
@@ -4644,7 +4644,7 @@
46444644 pszString = GetStringCLCC ( static_cast<CLCC> ( lCommand ) );
46454645 else
46464646 {
4647- if (( sv_showcommands >= 2 ) && ( lCommand == CLC_CLIENTMOVE ))
4647+ if (( sv_showcommands >= 2 ) && ( lCommand == CLC_CLIENTMOVE || lCommand == CLC_CLIENTMOVEBACKUP ))
46484648 return;
46494649 if (( sv_showcommands >= 3 ) && ( lCommand == CLC_PONG ))
46504650 return;
@@ -4826,11 +4826,12 @@
48264826 // Client is talking.
48274827 return ( server_Say( pByteStream ));
48284828 case CLC_CLIENTMOVE:
4829+ case CLC_CLIENTMOVEBACKUP:
48294830 {
48304831 bool bPlayerKicked;
48314832
48324833 // Client is sending movement information.
4833- bPlayerKicked = server_ClientMove( pByteStream );
4834+ bPlayerKicked = server_ClientMove( pByteStream, lCommand == CLC_CLIENTMOVEBACKUP );
48344835
48354836 if ( g_aClients[g_lCurrentClient].lLastMoveTick == gametic )
48364837 g_aClients[g_lCurrentClient].lOverMovementLevel++;
@@ -5313,6 +5314,40 @@
53135314
53145315 //*****************************************************************************
53155316 //
5317+bool SERVER_ShouldProcessMoveCommand( ULONG ulClient, ULONG ulNumMoveCMDs )
5318+{
5319+ // [AK] If the client isn't sending us backup commands, then always process movement commands.
5320+ if ( g_aClients[ulClient].ulNumExpectedCMDs == 1 )
5321+ return true;
5322+
5323+ // [AK] If the client is sending us backup commands and their tic buffer is getting too full, we
5324+ // must always process their movement commands. This condition is true when the number of commands
5325+ // stored in the buffer exceeds the number of commands we expect them to send us per tic.
5326+ if ( ulNumMoveCMDs >= g_aClients[ulClient].ulNumExpectedCMDs )
5327+ return true;
5328+
5329+ // [AK] Don't execute this block if we already processed a movement commands on this tic.
5330+ if ( g_aClients[ulClient].lLastMoveTickProcess != gametic )
5331+ {
5332+ // [AK] We have received enough movement commands from this client to process them, but we can only
5333+ // do this once per tic. Otherwise, their tic buffer will become too empty for us to process any
5334+ // backup commands properly.
5335+ if ( g_aClients[ulClient].ulNumSentCMDs == g_aClients[ulClient].ulNumExpectedCMDs )
5336+ return true;
5337+
5338+ // [AK] Are we still waiting for the client to send us more movement commands than we expect? In case
5339+ // case they suffer from packet loss, we don't want to wait forever to receive all of them , so we'll
5340+ // just have process whatever we have. To do this, increment the counter once per tic, which will
5341+ // eventually satisfy the condition above this one.
5342+ if ( g_aClients[ulClient].lLastMoveTick != gametic )
5343+ g_aClients[ulClient].ulNumSentCMDs++;
5344+ }
5345+
5346+ return false;
5347+}
5348+
5349+//*****************************************************************************
5350+//
53165351 CLIENT_PLAYER_DATA_s::CLIENT_PLAYER_DATA_s ( player_t *player )
53175352 {
53185353 PositionData = MOVE_THING_DATA_s( player->mo );
@@ -5476,6 +5511,8 @@
54765511
54775512 // [AK] We want to reset this client's last backtrace tic only when we reset their tic buffer.
54785513 g_aClients[ulClient].lLastBacktraceTic = 0;
5514+ // [AK] Also reset the backup command counters.
5515+ g_aClients[ulClient].ulNumSentCMDs = g_aClients[ulClient].ulNumExpectedCMDs = 1;
54795516
54805517 SERVER_ResetClientExtrapolation( ulClient );
54815518 }
@@ -5740,7 +5777,7 @@
57405777
57415778 //*****************************************************************************
57425779 //
5743-static bool server_ClientMove( BYTESTREAM_s *pByteStream )
5780+static bool server_ClientMove( BYTESTREAM_s *pByteStream, bool bSentBackup )
57445781 {
57455782 // Don't timeout.
57465783 g_aClients[g_lCurrentClient].ulLastCommandTic = gametic;
@@ -5749,7 +5786,43 @@
57495786 // in a buffer. This way we can limit the amount of movement commands
57505787 // we process for a player in a given tic to prevent the player from
57515788 // seemingly teleporting in case too many movement commands arrive at once.
5752- return server_ParseBufferedCommand<ClientMoveCommand> ( pByteStream );
5789+ if ( !bSentBackup )
5790+ {
5791+ g_aClients[g_lCurrentClient].ulNumSentCMDs = 1;
5792+ g_aClients[g_lCurrentClient].ulNumExpectedCMDs = 1;
5793+
5794+ return server_ParseBufferedCommand<ClientMoveCommand>( pByteStream );
5795+ }
5796+
5797+ // [AK] If we get to this point, that means the client also wanted to
5798+ // send us backup movement commands. Here, we'll determine how many commands
5799+ // they actually sent us (ulNumSentCMDs), and how many commands they're
5800+ // trying to send to us per tic (ulNumExpectedCMDs = cl_backupcommands + 1).
5801+ ULONG ulNumSentCMDs = pByteStream->ReadShortByte( 4 );
5802+ ULONG ulNumExpectedCMDs = pByteStream->ReadShortByte( 4 );
5803+
5804+ ULONG ulOldBufferSize = g_aClients[g_lCurrentClient].MoveCMDs.Size( );
5805+ bool result = false;
5806+
5807+ // [AK] Parse all of the movement commands, but also remember if the client
5808+ // needs to be kicked at all or not.
5809+ for ( unsigned int i = 1; i <= ulNumSentCMDs; i++ )
5810+ {
5811+ if ( server_ParseBufferedCommand<ClientMoveCommand>( pByteStream ))
5812+ result = true;
5813+ }
5814+
5815+ // [AK] We only want to update the number of sent/expected commands from
5816+ // the client if these movement commands weren't treated as late commands
5817+ // because of the skip correction. We can figure this out by checking if
5818+ // the size of the tic buffer changed at all.
5819+ if ( ulOldBufferSize != g_aClients[g_lCurrentClient].MoveCMDs.Size( ))
5820+ {
5821+ g_aClients[g_lCurrentClient].ulNumSentCMDs = ulNumSentCMDs;
5822+ g_aClients[g_lCurrentClient].ulNumExpectedCMDs = ulNumExpectedCMDs;
5823+ }
5824+
5825+ return result;
57535826 }
57545827
57555828 ClientMoveCommand::ClientMoveCommand ( BYTESTREAM_s *pByteStream )
diff -r 629a821a3980 -r fda0458f443a src/sv_main.h
--- a/src/sv_main.h Mon Oct 18 23:27:43 2021 -0400
+++ b/src/sv_main.h Mon Oct 18 23:30:04 2021 -0400
@@ -454,6 +454,14 @@
454454 // [AK] The number of tics we extrapolated this player's movement.
455455 ULONG ulExtrapolatedTics;
456456
457+ // [AK] How many movement commands did this player send us in the last packet? If this is greater than
458+ // one, that means they also sent us backups of older commands.
459+ ULONG ulNumSentCMDs;
460+
461+ // [AK] How many movement commands are we expecting from this player? If this is greater than one, that
462+ // means we're expecting them to also send us backups of older commands.
463+ ULONG ulNumExpectedCMDs;
464+
457465 // [BB] Variables for the account system
458466 FString username;
459467 unsigned int clientSessionID;
@@ -614,6 +622,7 @@
614622 void SERVER_KillCheat( const char* what );
615623 void STACK_ARGS SERVER_PrintWarning( const char* format, ... ) GCCPRINTF( 1, 2 );
616624 void SERVER_FlagsetChanged( FIntCVar& flagset, int maxflags = 2 );
625+bool SERVER_ShouldProcessMoveCommand( ULONG ulClient, ULONG ulNumMoveCMDs );
617626 bool SERVER_HandleSkipCorrection( ULONG ulClient, ULONG ulNumMoveCMDs );
618627 bool SERVER_IsBacktracingPlayer( ULONG ulClient );
619628 void SERVER_ResetClientTicBuffer( ULONG ulClient, bool bClearMoveCMDs = true );
diff -r 629a821a3980 -r fda0458f443a wadsrc/static/menudef.za
--- a/wadsrc/static/menudef.za Mon Oct 18 23:27:43 2021 -0400
+++ b/wadsrc/static/menudef.za Mon Oct 18 23:30:04 2021 -0400
@@ -93,6 +93,13 @@
9393 1.0, "DSL"
9494 }
9595
96+OptionValue ZA_BackupRate
97+{
98+ 0.0, "None"
99+ 1.0, "One per tick"
100+ 2.0, "Two per tick"
101+}
102+
96103 OptionMenu ZA_NetworkOptions
97104 {
98105 Title "NETWORK OPTIONS"
@@ -101,9 +108,21 @@
101108 Option "Unlagged", "cl_unlagged", "OnOff"
102109 Option "Unlag Type", "cl_ping_unlagged", "ZA_UnlagType"
103110 Option "Update Rate", "cl_ticsperupdate", "ZA_UpdateRate"
111+ Option "Send backup commands", "cl_backupcommands", "ZA_BackupRate"
104112 Option "Hitscan decals", "cl_hitscandecalhack", "OnOff"
105113 Option "Clientside puffs", "cl_clientsidepuffs", "OnOff"
106114 Option "Display packet loss", "cl_showpacketloss", "YesNo"
115+
116+ // [AK] Sending backup commands comes with drawbacks, such as increased outbound
117+ // net traffic and their commands being delayed on the server's end. If the client
118+ // decides to enable this, they should be made aware of the consequences too.
119+ StaticText " "
120+ StaticText "--- WARNING ---"
121+ StaticText "Sending backup commands helps mitigate packet loss", 1
122+ StaticText "but increases outbound net traffic and delays your input!", 1
123+ StaticText " "
124+ StaticText "You should only enable them if you're actually", 1
125+ StaticText "suffering from severe packet loss!", 1
107126 }
108127
109128 // =================================================================================================