コミットメタ情報

リビジョンb88c12f029eaa4afdb60890f97cdecf56ee34b6e (tree)
日時2021-10-19 12:30:04
作者Adam Kaminski <kaminskiadam9@gmai...>
コミッターAdam Kaminski

ログメッセージ

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

変更サマリ

差分

diff -r 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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 0878f44e527a -r b88c12f029ea 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
@@ -333,6 +333,9 @@
333333 ulNumMoveCMDs++;
334334 }
335335
336+ // [AK] Check if we should process this client's movement commands.
337+ bool bProcessMoveCMD = SERVER_ShouldProcessMoveCommand( ulIdx, ulNumMoveCMDs );
338+
336339 // [AK] Handle the skip correction. If it explicity returns false, then we won't process two
337340 // movement commands during this tic for the client.
338341 if (( sv_smoothplayers ) && ( SERVER_HandleSkipCorrection( ulIdx, ulNumMoveCMDs ) == false ))
@@ -342,9 +345,10 @@
342345 {
343346 // Process only one movement command.
344347 const bool bMovement = client->MoveCMDs[0]->isMoveCmd( );
345- client->MoveCMDs[0]->process( ulIdx );
346348
347- if ( bMovement )
349+ // [AK] Only update the last movement command if we're supposed to be processing any
350+ // movement commands in the buffer at this time.
351+ if (( bProcessMoveCMD ) && ( bMovement ))
348352 {
349353 if ( client->LastMoveCMD != NULL )
350354 delete client->LastMoveCMD;
@@ -353,8 +357,14 @@
353357 client->LastMoveCMD = new ClientMoveCommand( *static_cast<ClientMoveCommand *>( client->MoveCMDs[0] ));
354358 }
355359
356- delete client->MoveCMDs[0];
357- client->MoveCMDs.Delete(0);
360+ // [AK] Only process movement commands if we're allowed to at this time. On the other
361+ // hand, we can still process other commands in the buffer.
362+ if (( bProcessMoveCMD ) || ( bMovement == false ))
363+ {
364+ client->MoveCMDs[0]->process( ulIdx );
365+ delete client->MoveCMDs[0];
366+ client->MoveCMDs.Delete( 0 );
367+ }
358368
359369 if ( bMovement == true )
360370 break;
diff -r 0878f44e527a -r b88c12f029ea 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
@@ -142,7 +142,7 @@
142142
143143 static bool server_Ignore( BYTESTREAM_s *pByteStream );
144144 static bool server_Say( BYTESTREAM_s *pByteStream );
145-static bool server_ClientMove( BYTESTREAM_s *pByteStream );
145+static bool server_ClientMove( BYTESTREAM_s *pByteStream, bool bSentBackup );
146146 static bool server_MissingPacket( BYTESTREAM_s *pByteStream );
147147 static bool server_UpdateClientPing( BYTESTREAM_s *pByteStream );
148148 static bool server_WeaponSelect( BYTESTREAM_s *pByteStream );
@@ -4561,7 +4561,7 @@
45614561 pszString = GetStringCLCC ( static_cast<CLCC> ( lCommand ) );
45624562 else
45634563 {
4564- if (( sv_showcommands >= 2 ) && ( lCommand == CLC_CLIENTMOVE ))
4564+ if (( sv_showcommands >= 2 ) && ( lCommand == CLC_CLIENTMOVE || lCommand == CLC_CLIENTMOVEBACKUP ))
45654565 return;
45664566 if (( sv_showcommands >= 3 ) && ( lCommand == CLC_PONG ))
45674567 return;
@@ -4743,11 +4743,12 @@
47434743 // Client is talking.
47444744 return ( server_Say( pByteStream ));
47454745 case CLC_CLIENTMOVE:
4746+ case CLC_CLIENTMOVEBACKUP:
47464747 {
47474748 bool bPlayerKicked;
47484749
47494750 // Client is sending movement information.
4750- bPlayerKicked = server_ClientMove( pByteStream );
4751+ bPlayerKicked = server_ClientMove( pByteStream, lCommand == CLC_CLIENTMOVEBACKUP );
47514752
47524753 if ( g_aClients[g_lCurrentClient].lLastMoveTick == gametic )
47534754 g_aClients[g_lCurrentClient].lOverMovementLevel++;
@@ -5230,6 +5231,40 @@
52305231
52315232 //*****************************************************************************
52325233 //
5234+bool SERVER_ShouldProcessMoveCommand( ULONG ulClient, ULONG ulNumMoveCMDs )
5235+{
5236+ // [AK] If the client isn't sending us backup commands, then always process movement commands.
5237+ if ( g_aClients[ulClient].ulNumExpectedCMDs == 1 )
5238+ return true;
5239+
5240+ // [AK] If the client is sending us backup commands and their tic buffer is getting too full, we
5241+ // must always process their movement commands. This condition is true when the number of commands
5242+ // stored in the buffer exceeds the number of commands we expect them to send us per tic.
5243+ if ( ulNumMoveCMDs >= g_aClients[ulClient].ulNumExpectedCMDs )
5244+ return true;
5245+
5246+ // [AK] Don't execute this block if we already processed a movement commands on this tic.
5247+ if ( g_aClients[ulClient].lLastMoveTickProcess != gametic )
5248+ {
5249+ // [AK] We have received enough movement commands from this client to process them, but we can only
5250+ // do this once per tic. Otherwise, their tic buffer will become too empty for us to process any
5251+ // backup commands properly.
5252+ if ( g_aClients[ulClient].ulNumSentCMDs == g_aClients[ulClient].ulNumExpectedCMDs )
5253+ return true;
5254+
5255+ // [AK] Are we still waiting for the client to send us more movement commands than we expect? In case
5256+ // case they suffer from packet loss, we don't want to wait forever to receive all of them , so we'll
5257+ // just have process whatever we have. To do this, increment the counter once per tic, which will
5258+ // eventually satisfy the condition above this one.
5259+ if ( g_aClients[ulClient].lLastMoveTick != gametic )
5260+ g_aClients[ulClient].ulNumSentCMDs++;
5261+ }
5262+
5263+ return false;
5264+}
5265+
5266+//*****************************************************************************
5267+//
52335268 CLIENT_PLAYER_DATA_s::CLIENT_PLAYER_DATA_s ( player_t *player )
52345269 {
52355270 PositionData = MOVE_THING_DATA_s( player->mo );
@@ -5393,6 +5428,8 @@
53935428
53945429 // [AK] We want to reset this client's last backtrace tic only when we reset their tic buffer.
53955430 g_aClients[ulClient].lLastBacktraceTic = 0;
5431+ // [AK] Also reset the backup command counters.
5432+ g_aClients[ulClient].ulNumSentCMDs = g_aClients[ulClient].ulNumExpectedCMDs = 1;
53965433
53975434 SERVER_ResetClientExtrapolation( ulClient );
53985435 }
@@ -5651,7 +5688,7 @@
56515688
56525689 //*****************************************************************************
56535690 //
5654-static bool server_ClientMove( BYTESTREAM_s *pByteStream )
5691+static bool server_ClientMove( BYTESTREAM_s *pByteStream, bool bSentBackup )
56555692 {
56565693 // Don't timeout.
56575694 g_aClients[g_lCurrentClient].ulLastCommandTic = gametic;
@@ -5660,7 +5697,43 @@
56605697 // in a buffer. This way we can limit the amount of movement commands
56615698 // we process for a player in a given tic to prevent the player from
56625699 // seemingly teleporting in case too many movement commands arrive at once.
5663- return server_ParseBufferedCommand<ClientMoveCommand> ( pByteStream );
5700+ if ( !bSentBackup )
5701+ {
5702+ g_aClients[g_lCurrentClient].ulNumSentCMDs = 1;
5703+ g_aClients[g_lCurrentClient].ulNumExpectedCMDs = 1;
5704+
5705+ return server_ParseBufferedCommand<ClientMoveCommand>( pByteStream );
5706+ }
5707+
5708+ // [AK] If we get to this point, that means the client also wanted to
5709+ // send us backup movement commands. Here, we'll determine how many commands
5710+ // they actually sent us (ulNumSentCMDs), and how many commands they're
5711+ // trying to send to us per tic (ulNumExpectedCMDs = cl_backupcommands + 1).
5712+ ULONG ulNumSentCMDs = pByteStream->ReadShortByte( 4 );
5713+ ULONG ulNumExpectedCMDs = pByteStream->ReadShortByte( 4 );
5714+
5715+ ULONG ulOldBufferSize = g_aClients[g_lCurrentClient].MoveCMDs.Size( );
5716+ bool result = false;
5717+
5718+ // [AK] Parse all of the movement commands, but also remember if the client
5719+ // needs to be kicked at all or not.
5720+ for ( unsigned int i = 1; i <= ulNumSentCMDs; i++ )
5721+ {
5722+ if ( server_ParseBufferedCommand<ClientMoveCommand>( pByteStream ))
5723+ result = true;
5724+ }
5725+
5726+ // [AK] We only want to update the number of sent/expected commands from
5727+ // the client if these movement commands weren't treated as late commands
5728+ // because of the skip correction. We can figure this out by checking if
5729+ // the size of the tic buffer changed at all.
5730+ if ( ulOldBufferSize != g_aClients[g_lCurrentClient].MoveCMDs.Size( ))
5731+ {
5732+ g_aClients[g_lCurrentClient].ulNumSentCMDs = ulNumSentCMDs;
5733+ g_aClients[g_lCurrentClient].ulNumExpectedCMDs = ulNumExpectedCMDs;
5734+ }
5735+
5736+ return result;
56645737 }
56655738
56665739 ClientMoveCommand::ClientMoveCommand ( BYTESTREAM_s *pByteStream )
diff -r 0878f44e527a -r b88c12f029ea 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
@@ -458,6 +458,14 @@
458458 // we need to know how much thrust we need to add back in case the backtrace succeeds.
459459 fixed_t backtraceThrust[3];
460460
461+ // [AK] How many movement commands did this player send us in the last packet? If this is greater than
462+ // one, that means they also sent us backups of older commands.
463+ ULONG ulNumSentCMDs;
464+
465+ // [AK] How many movement commands are we expecting from this player? If this is greater than one, that
466+ // means we're expecting them to also send us backups of older commands.
467+ ULONG ulNumExpectedCMDs;
468+
461469 // [BB] Variables for the account system
462470 FString username;
463471 unsigned int clientSessionID;
@@ -618,6 +626,7 @@
618626 void SERVER_KillCheat( const char* what );
619627 void STACK_ARGS SERVER_PrintWarning( const char* format, ... ) GCCPRINTF( 1, 2 );
620628 void SERVER_FlagsetChanged( FIntCVar& flagset, int maxflags = 2 );
629+bool SERVER_ShouldProcessMoveCommand( ULONG ulClient, ULONG ulNumMoveCMDs );
621630 bool SERVER_HandleSkipCorrection( ULONG ulClient, ULONG ulNumMoveCMDs );
622631 bool SERVER_IsBacktracingPlayer( ULONG ulClient );
623632 void SERVER_ResetClientTicBuffer( ULONG ulClient, bool bClearMoveCMDs = true );
diff -r 0878f44e527a -r b88c12f029ea 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 // =================================================================================================
旧リポジトリブラウザで表示