Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2534 lines
72 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "server_pch.h"
  7. #include "framesnapshot.h"
  8. #include "checksum_engine.h"
  9. #include "sv_main.h"
  10. #include "GameEventManager.h"
  11. #include "networkstringtable.h"
  12. #include "demo.h"
  13. #include "PlayerState.h"
  14. #include "tier0/vprof.h"
  15. #include "sv_packedentities.h"
  16. #include "LocalNetworkBackdoor.h"
  17. #include "testscriptmgr.h"
  18. #include "hltvserver.h"
  19. #if defined( REPLAY_ENABLED )
  20. #include "replayserver.h"
  21. #endif
  22. #include "pr_edict.h"
  23. #include "logofile_shared.h"
  24. #include "dt_send_eng.h"
  25. #include "sv_plugin.h"
  26. #include "download.h"
  27. #include "cmodel_engine.h"
  28. #include "tier1/commandbuffer.h"
  29. #include "gl_cvars.h"
  30. #include "tier2/tier2.h"
  31. #include "matchmaking/imatchframework.h"
  32. #include "audio/public/vox.h" // TERROR: for net_showreliablesounds
  33. #include "SoundEmitterSystem/isoundemittersystembase.h"
  34. #include "ihltv.h"
  35. #include "tier1/utlstringtoken.h"
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  39. static ConVar sv_timeout( "sv_timeout", "65", 0, "After this many seconds without a message from a client, the client is dropped" );
  40. static ConVar sv_maxrate( "sv_maxrate", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Max bandwidth rate allowed on server, 0 == unlimited", true, 0, true, MAX_RATE );
  41. static ConVar sv_minrate( "sv_minrate", STRINGIFY( MIN_RATE ), FCVAR_REPLICATED | FCVAR_RELEASE, "Min bandwidth rate allowed on server, 0 == unlimited", true, 0, true, MAX_RATE );
  42. ConVar sv_maxupdaterate( "sv_maxupdaterate", "64", FCVAR_REPLICATED | FCVAR_RELEASE, "Maximum updates per second that the server will allow" ); // we need to be able to set max rate to 128
  43. ConVar sv_minupdaterate( "sv_minupdaterate", "64", FCVAR_REPLICATED | FCVAR_RELEASE, "Minimum updates per second that the server will allow" );
  44. ConVar sv_stressbots("sv_stressbots", "0", FCVAR_RELEASE, "If set to 1, the server calculates data and fills packets to bots. Used for perf testing.");
  45. ConVar sv_replaybots( "sv_replaybots", "1", FCVAR_RELEASE, "If set to 1, the server records data needed to replay network stream from bot's perspective" );
  46. static ConVar sv_allowdownload ("sv_allowdownload", "1", FCVAR_RELEASE, "Allow clients to download files");
  47. static ConVar sv_allowupload ("sv_allowupload", "1", FCVAR_RELEASE, "Allow clients to upload customizations files");
  48. ConVar sv_sendtables ( "sv_sendtables", "0", FCVAR_DEVELOPMENTONLY, "Force full sendtable sending path." );
  49. #if HLTV_REPLAY_ENABLED
  50. ConVar spec_replay_enable( "spec_replay_enable", "0", FCVAR_RELEASE|FCVAR_REPLICATED, "Enable Killer Replay, requires hltv server running." );
  51. #endif
  52. ConVar spec_replay_message_time( "spec_replay_message_time", "9.5", FCVAR_RELEASE | FCVAR_REPLICATED, "How long to show the message about Killer Replay after death. The best setting is a bit shorter than spec_replay_autostart_delay + spec_replay_leadup_time + spec_replay_winddown_time" );
  53. ConVar spec_replay_rate_limit( "spec_replay_rate_limit", "3", FCVAR_RELEASE | FCVAR_REPLICATED, "Minimum allowable pause between replay requests in seconds" );
  54. static ConVar ss_voice_hearpartner( "ss_voice_hearpartner", "0", 0, "Route voice between splitscreen players on same system." );
  55. ConVar sv_max_dropped_packets_to_process( "sv_max_dropped_packets_to_process", "10", FCVAR_RELEASE, "Max dropped packets to process. Lower settings prevent lagged players from simulating too far in the past. Setting of 0 disables cap." );
  56. ConVar cl_allowdownload( "cl_allowdownload", "1", FCVAR_ARCHIVE, "Client downloads customization files" );
  57. static ConVar sv_quota_stringcmdspersecond( "sv_quota_stringcmdspersecond", "40", FCVAR_RELEASE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands" );
  58. // TERROR:
  59. static ConVar net_showreliablesounds( "net_showreliablesounds", "0", FCVAR_CHEAT );
  60. ConVar replay_debug( "replay_debug", "0", FCVAR_RELEASE | FCVAR_REPLICATED);
  61. ConVar sv_allow_legacy_cmd_execution_from_client( "sv_allow_legacy_cmd_execution_from_client", "0", FCVAR_RELEASE, "Enables old concommand execution behavior allowing remote clients to run any command not explicitly flagged as disallowed." );
  62. extern ConVar sv_maxreplay;
  63. extern ConVar tv_snapshotrate;
  64. extern ConVar tv_transmitall;
  65. extern ConVar sv_pure_kick_clients;
  66. extern ConVar sv_pure_trace;
  67. #if defined( REPLAY_ENABLED )
  68. extern ConVar replay_snapshotrate;
  69. extern ConVar replay_transmitall;
  70. #endif
  71. // static ConVar sv_failuretime( "sv_failuretime", "0.5", 0, "After this long without a packet from client, don't send any more until client starts sending again" );
  72. static const char * s_clcommands[] =
  73. {
  74. "status",
  75. "pause",
  76. "setpause",
  77. "unpause",
  78. "ping",
  79. "rpt_server_enable",
  80. "rpt_client_enable",
  81. #ifndef DEDICATED
  82. "rpt",
  83. "rpt_connect",
  84. "rpt_password",
  85. "rpt_screenshot",
  86. "rpt_download_log",
  87. #endif
  88. "ss_connect",
  89. "ss_disconnect",
  90. #if defined( REPLAY_ENABLED )
  91. "request_replay_demo",
  92. #endif
  93. NULL,
  94. };
  95. // Used on the server and on the client to bound its cl_rate cvar.
  96. int ClampClientRate( int nRate )
  97. {
  98. // Apply mod specific clamps
  99. if ( sv_maxrate.GetInt() > 0 )
  100. {
  101. nRate = MIN( nRate, sv_maxrate.GetInt() );
  102. }
  103. if ( sv_minrate.GetInt() > 0 )
  104. {
  105. nRate = MAX( nRate, sv_minrate.GetInt() );
  106. }
  107. // Apply overall clamp
  108. nRate = clamp( nRate, MIN_RATE, MAX_RATE );
  109. return nRate;
  110. }
  111. // Validate minimum number of required clients to be connected to a server
  112. enum ValidateMinRequiredClients_t
  113. {
  114. VALIDATE_SPAWN,
  115. VALIDATE_DISCONNECT
  116. };
  117. void SV_ValidateMinRequiredClients( ValidateMinRequiredClients_t eReason )
  118. {
  119. // FIXME: This gives false positives for drops and disconnects. (kwd)
  120. return;
  121. if ( !IsX360() && !sv.IsDedicatedForXbox() )
  122. return;
  123. static ConVarRef s_director_min_start_players( "director_min_start_players", true );
  124. if ( !s_director_min_start_players.IsValid() )
  125. return;
  126. int numRequiredByDirector = s_director_min_start_players.GetInt();
  127. if ( numRequiredByDirector <= 0 )
  128. return;
  129. switch ( eReason )
  130. {
  131. case VALIDATE_SPAWN:
  132. {
  133. // If at least one client has already spawned in the server, if there is a required
  134. // minimum number of players and some of the players are not yet connected to server
  135. // then most likely they dropped out or failed to connect, need to lower the minimum
  136. // number of required players and proceed with game.
  137. int numConnected = sv.GetClientCount();
  138. int numInState = 0;
  139. // Determine how many clients are above signon state NEW
  140. for ( int j = 0 ; j < numConnected ; j++ )
  141. {
  142. IClient *client = sv.GetClient( j );
  143. if ( !client )
  144. continue;
  145. if ( !client->IsSpawned() )
  146. continue;
  147. ++ numInState;
  148. }
  149. if ( numRequiredByDirector > numInState )
  150. {
  151. s_director_min_start_players.SetValue( numInState );
  152. ConMsg( "SV_ValidateMinRequiredClients: spawn: lowered min start players to %d.\n",
  153. numInState );
  154. }
  155. }
  156. break;
  157. case VALIDATE_DISCONNECT:
  158. {
  159. // If somebody disconnects from the server we decrement the minimum number of players required
  160. -- numRequiredByDirector;
  161. s_director_min_start_players.SetValue( numRequiredByDirector );
  162. ConMsg( "SV_ValidateMinRequiredClients: disconnect: lowered min start players to %d.\n",
  163. numRequiredByDirector );
  164. }
  165. break;
  166. }
  167. }
  168. CGameClient::CGameClient(int slot, CBaseServer *pServer )
  169. {
  170. Clear();
  171. m_nClientSlot = slot;
  172. m_nEntityIndex = slot+1;
  173. m_Server = pServer;
  174. m_pCurrentFrame = NULL;
  175. m_bIsInReplayMode = false;
  176. // NULL out data we'll never use.
  177. memset( &m_PrevPackInfo, 0, sizeof( m_PrevPackInfo ) );
  178. m_PrevPackInfo.m_pTransmitEdict = &m_PrevTransmitEdict;
  179. m_flTimeClientBecameFullyConnected = -1.0f;
  180. m_flLastClientCommandQuotaStart = -1.0f;
  181. m_numClientCommandsInQuota = 0;
  182. }
  183. CGameClient::~CGameClient()
  184. {
  185. }
  186. bool CGameClient::CLCMsg_ClientInfo( const CCLCMsg_ClientInfo& msg )
  187. {
  188. BaseClass::CLCMsg_ClientInfo( msg );
  189. if ( m_bIsHLTV )
  190. {
  191. Disconnect( "CLCMsg_ClientInfo: SourceTV can not connect to game directly.\n" );
  192. return false;
  193. }
  194. #if defined( REPLAY_ENABLED )
  195. if ( m_bIsReplay )
  196. {
  197. Disconnect( "CLCMsg_ClientInfo: Replay can not connect to game directly.\n" );
  198. return false;
  199. }
  200. #endif
  201. if ( sv_allowupload.GetBool() )
  202. {
  203. // download all missing customizations files from this client;
  204. DownloadCustomizations();
  205. }
  206. return true;
  207. }
  208. bool CGameClient::CLCMsg_Move( const CCLCMsg_Move& msg )
  209. {
  210. // Don't process usercmds until the client is active. If we do, there can be weird behavior
  211. // like the game trying to send reliable messages to the client and having those messages discarded.
  212. if ( !IsActive() )
  213. return true;
  214. if ( m_LastMovementTick == sv.m_nTickCount )
  215. {
  216. // Only one movement command per frame, someone is cheating.
  217. return true;
  218. }
  219. m_LastMovementTick = sv.m_nTickCount;
  220. INetChannel *netchan = sv.GetBaseUserForSplitClient( this )->m_NetChannel;
  221. int totalcmds = msg.num_backup_commands() + msg.num_new_commands();
  222. // Decrement drop count by held back packet count
  223. int netdrop = netchan->GetDropNumber();
  224. // Dropped packet count is reported by clients
  225. if ( sv_max_dropped_packets_to_process.GetInt() )
  226. netdrop = Clamp( netdrop, 0, sv_max_dropped_packets_to_process.GetInt() );
  227. bool ignore = !sv.IsActive();
  228. #ifdef DEDICATED
  229. bool paused = sv.IsPaused();
  230. #else
  231. bool paused = sv.IsPaused() || ( !sv.IsMultiplayer() && Con_IsVisible() );
  232. #endif
  233. // Make sure player knows of correct server time
  234. g_ServerGlobalVariables.curtime = sv.GetTime();
  235. g_ServerGlobalVariables.frametime = host_state.interval_per_tick;
  236. // COM_Log( "sv.log", " executing %i move commands from client starting with command %i(%i)\n",
  237. // numcmds,
  238. // m_Client->netchan->incoming_sequence,
  239. // m_Client->netchan->incoming_sequence & SV_UPDATE_MASK );
  240. bf_read DataIn( &msg.data()[0], msg.data().size() );
  241. serverGameClients->ProcessUsercmds
  242. (
  243. edict, // Player edict
  244. &DataIn,
  245. msg.num_new_commands(),
  246. totalcmds, // Commands in packet
  247. netdrop, // Number of dropped commands
  248. ignore, // Don't actually run anything
  249. paused // Run, but don't actually do any movement
  250. );
  251. if ( DataIn.IsOverflowed() )
  252. {
  253. Disconnect( "ProcessUsercmds: Overflowed reading usercmd data (check sending and receiving code for mismatches)!\n" );
  254. return false;
  255. }
  256. return true;
  257. }
  258. bool CGameClient::CLCMsg_VoiceData( const CCLCMsg_VoiceData& msg )
  259. {
  260. serverGameClients->ClientVoice( edict );
  261. SV_BroadcastVoiceData( this, msg );
  262. return true;
  263. }
  264. bool CGameClient::CLCMsg_CmdKeyValues( const CCLCMsg_CmdKeyValues& msg )
  265. {
  266. KeyValues *keyvalues = CmdKeyValuesHelper::CLCMsg_GetKeyValues( msg );
  267. KeyValues::AutoDelete autodelete_keyvalues( keyvalues );
  268. serverGameClients->ClientCommandKeyValues( edict, keyvalues );
  269. if ( IsX360() || NET_IsDedicatedForXbox() )
  270. {
  271. // See if a player was removed
  272. if ( !Q_stricmp( keyvalues->GetName(), "OnPlayerRemovedFromSession" ) )
  273. {
  274. XUID xuid = keyvalues->GetUint64( "xuid", 0ull );
  275. for ( int iPlayerIndex = 0; iPlayerIndex < sv.GetClientCount(); iPlayerIndex++ )
  276. {
  277. CBaseClient *pClient = (CBaseClient *) sv.GetClient( iPlayerIndex );
  278. if ( !pClient || !xuid || pClient->GetClientXuid() != xuid )
  279. continue;
  280. pClient->Disconnect( "Player removed from host session\n" );
  281. return true;
  282. }
  283. }
  284. }
  285. MEM_ALLOC_CREDIT();
  286. KeyValues *pEvent = new KeyValues( "Server::CmdKeyValues" );
  287. pEvent->AddSubKey( autodelete_keyvalues.Detach() );
  288. pEvent->SetPtr( "edict", edict );
  289. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pEvent );
  290. return true;
  291. }
  292. bool CGameClient::CLCMsg_HltvReplay( const CCLCMsg_HltvReplay &msg )
  293. {
  294. int nRequest = msg.request();
  295. if ( nRequest == REPLAY_EVENT_STUCK_NEED_FULL_UPDATE )
  296. {
  297. if ( m_nForceWaitForTick > 0 )
  298. {
  299. // <sergiy> if we are indeed waiting for tick confirmation, the client may indeed be stuck. Let them have another update. This is prone to a mildly annoying attack: client can go into a loop requesting updates, which will raise server's CPU usage and traffic and may cause server to skip ticks on high-load casual servers. So later on, I should probably rate-limit this. But hopefully I'll find why the client gets stuck before too long.
  300. UpdateAcknowledgedFramecount( -1 );
  301. }
  302. }
  303. else if ( nRequest )
  304. {
  305. ClientReplayEventParams_t params( nRequest );
  306. if ( params.m_flSlowdownRate > 0.01f && params.m_flSlowdownRate < 10.0f && params.m_flSlowdownLength > 0.01f && params.m_flSlowdownLength <= 5.0f )
  307. {
  308. // keep defaults in suspicious cases
  309. params.m_flSlowdownRate = msg.slowdown_rate();
  310. params.m_flSlowdownLength = msg.slowdown_length();
  311. }
  312. params.m_nPrimaryTargetEntIndex = msg.primary_target_ent_index();
  313. params.m_flEventTime = msg.event_time();
  314. serverGameClients->ClientReplayEvent( edict, params );
  315. }
  316. else
  317. {
  318. if ( IsHltvReplay() )
  319. m_HltvReplayStats.nUserCancels++;
  320. StopHltvReplay();
  321. }
  322. return true;
  323. }
  324. bool CGameClient::SVCMsg_UserMessage( const CSVCMsg_UserMessage &msg )
  325. {
  326. serverGameClients->ClientSvcUserMessage( edict, msg.msg_type(), msg.passthrough(), msg.msg_data().size(), &msg.msg_data()[0] );
  327. return true;
  328. }
  329. bool CGameClient::CLCMsg_RespondCvarValue( const CCLCMsg_RespondCvarValue& msg )
  330. {
  331. if ( msg.cookie() > 0 )
  332. {
  333. if ( g_pServerPluginHandler )
  334. g_pServerPluginHandler->OnQueryCvarValueFinished( ( EQueryCvarValueStatus )msg.cookie(), edict, ( EQueryCvarValueStatus )msg.status_code(), msg.name().c_str(), msg.value().c_str() );
  335. }
  336. else
  337. {
  338. // Negative cookie means the game DLL asked for the value.
  339. if ( serverGameDLL && g_bServerGameDLLGreaterThanV5 )
  340. {
  341. #ifdef REL_TO_STAGING_MERGE_TODO
  342. serverGameDLL->OnQueryCvarValueFinished( msg.cookie(), edict, msg.status_code(), msg.name().c_str(), msg.value().c_str() );
  343. #endif
  344. }
  345. }
  346. return true;
  347. }
  348. #include "pure_server.h"
  349. bool CGameClient::CLCMsg_FileCRCCheck( const CCLCMsg_FileCRCCheck& msg )
  350. {
  351. // Ignore this message if we're not in pure server mode...
  352. if ( !sv.IsInPureServerMode() )
  353. return true;
  354. char warningStr[1024] = {0};
  355. // first check against all the other files users have sent
  356. FileHash_t filehash;
  357. V_memcpy( filehash.m_md5contents.bits, msg.md5().c_str(), MD5_DIGEST_LENGTH );
  358. filehash.m_crcIOSequence = msg.crc();
  359. filehash.m_eFileHashType = msg.file_hash_type();
  360. filehash.m_cbFileLen = msg.file_len();
  361. filehash.m_nPackFileNumber = msg.pack_file_number();
  362. filehash.m_PackFileID = msg.pack_file_id();
  363. const char *path = CCLCMsg_FileCRCCheck_t::GetPath( msg );
  364. const char *fileName = CCLCMsg_FileCRCCheck_t::GetFileName( msg );
  365. if ( g_PureFileTracker.DoesFileMatch( path, fileName, msg.file_fraction(), &filehash, GetNetworkID() ) )
  366. {
  367. // track successful file
  368. }
  369. else
  370. {
  371. V_snprintf( warningStr, sizeof( warningStr ), "Pure server: file [%s]\\%s does not match the server's file.", path, fileName );
  372. }
  373. // still ToDo:
  374. // 1. make sure the user sends some files
  375. // 2. make sure the user doesnt skip any files
  376. // 3. make sure the user sends the right files...
  377. if ( warningStr[0] )
  378. {
  379. if ( serverGameDLL )
  380. {
  381. serverGameDLL->OnPureServerFileValidationFailure( edict, path, fileName, filehash.m_crcIOSequence, filehash.m_eFileHashType,
  382. filehash.m_cbFileLen, filehash.m_nPackFileNumber, filehash.m_PackFileID );
  383. }
  384. if ( sv_pure_kick_clients.GetInt() )
  385. {
  386. Disconnect( warningStr );
  387. }
  388. else
  389. {
  390. ClientPrintf( "Warning: %s\n", warningStr );
  391. if ( sv_pure_trace.GetInt() >= 1 )
  392. {
  393. Msg( "[%s] %s\n", GetNetworkIDString(), warningStr );
  394. }
  395. }
  396. }
  397. else
  398. {
  399. if ( sv_pure_trace.GetInt() == 2 )
  400. {
  401. Msg( "Pure server CRC check: client %s passed check for [%s]\\%s\n", GetClientName(), path, fileName );
  402. }
  403. }
  404. return true;
  405. }
  406. void CGameClient::DownloadCustomizations()
  407. {
  408. if ( !cl_allowdownload.GetBool() )
  409. return; // client doesn't want to download any customizations
  410. for ( int i=0; i<MAX_CUSTOM_FILES; i++ )
  411. {
  412. if ( m_nCustomFiles[i].crc == 0 )
  413. continue; // slot not used
  414. CCustomFilename hexname( m_nCustomFiles[i].crc );
  415. if ( g_pFileSystem->FileExists( hexname.m_Filename ) )
  416. continue; // we already have it
  417. // we don't have it, request download from client
  418. m_nCustomFiles[i].reqID = m_NetChannel->RequestFile( hexname.m_Filename, false );
  419. }
  420. }
  421. void CGameClient::Connect(const char * szName, int nUserID, INetChannel *pNetChannel, bool bFakePlayer, CrossPlayPlatform_t clientPlatform, const CMsg_CVars *pVecCvars /*= NULL*/)
  422. {
  423. BaseClass::Connect( szName, nUserID, pNetChannel, bFakePlayer, clientPlatform, pVecCvars );
  424. edict = EDICT_NUM( m_nEntityIndex );
  425. // init PackInfo
  426. m_PackInfo.m_pClientEnt = edict;
  427. m_PackInfo.m_nPVSSize = sizeof( m_PackInfo.m_PVS );
  428. // fire global game event
  429. IGameEvent *event = g_GameEventManager.CreateEvent( "player_connect" );
  430. {
  431. event->SetInt( "userid", m_UserID );
  432. event->SetInt( "index", m_nClientSlot );
  433. event->SetString( "name", m_Name );
  434. event->SetUint64( "xuid", GetClientXuid() );
  435. event->SetString( "networkid", GetNetworkIDString() );
  436. // event->SetString( "address", m_NetChannel?m_NetChannel->GetAddress():"none" );
  437. event->SetInt( "bot", m_bFakePlayer?1:0 );
  438. g_GameEventManager.FireEvent( event );
  439. }
  440. }
  441. static ConVar sv_maxclientframes( "sv_maxclientframes", "128" );
  442. static ConVar sv_extra_client_connect_time( "sv_extra_client_connect_time", "15.0", 0,
  443. "Seconds after client connect during which extra frames are buffered to prevent non-delta'd update" );
  444. void CGameClient::SetupPackInfo( CFrameSnapshot *pSnapshot )
  445. {
  446. Assert( !IsHltvReplay() );
  447. // Compute Vis for each client
  448. m_PackInfo.m_nPVSSize = (GetCollisionBSPData()->numclusters + 7) / 8;
  449. serverGameClients->ClientSetupVisibility( (edict_t *)m_pViewEntity,
  450. m_PackInfo.m_pClientEnt, m_PackInfo.m_PVS, m_PackInfo.m_nPVSSize );
  451. // This is the frame we are creating, i.e., the next
  452. // frame after the last one that the client acknowledged
  453. m_pCurrentFrame = AllocateFrame();
  454. m_pCurrentFrame->Init( pSnapshot );
  455. m_PackInfo.m_pTransmitEdict = &m_pCurrentFrame->transmit_entity;
  456. // if this client is the HLTV or Replay client, add the nocheck PVS bit array
  457. // normal clients don't need that extra array
  458. #if defined( REPLAY_ENABLED )
  459. if ( IsHLTV() || IsReplay() )
  460. #else
  461. if ( IsHLTV() )
  462. #endif
  463. {
  464. // the hltv client doesn't has a ClientFrame list
  465. m_pCurrentFrame->transmit_always = new CBitVec<MAX_EDICTS>;
  466. m_PackInfo.m_pTransmitAlways = m_pCurrentFrame->transmit_always;
  467. }
  468. else
  469. {
  470. m_PackInfo.m_pTransmitAlways = NULL;
  471. }
  472. // Add frame to ClientFrame list
  473. int nMaxFrames = MAX( sv_maxclientframes.GetInt(), MAX_CLIENT_FRAMES );
  474. // Only do this on dedicated servers (360 dedicated servers are !IsX360 so this check isn't strictly necessary)
  475. // and non-x360 am servers due to concerns over memory growth on the consoles.
  476. if ( ( !IsX360() || sv.IsDedicated() ) &&
  477. ( m_flTimeClientBecameFullyConnected != -1.0f ) &&
  478. ( realtime - m_flTimeClientBecameFullyConnected ) < sv_extra_client_connect_time.GetFloat() )
  479. {
  480. // For 15 seconds, the max will go from 128 (default) to 450 (assuming 0.0333 world tick interval)
  481. // or to 960 (assuming 0.015625, 64 fps).
  482. // In practice during changelevel on 360 I've seen it get up to 210 or so which is only 60% greater
  483. // than the 128 frame max default, so 450 seems like it should capture all cases.
  484. nMaxFrames = MAX( nMaxFrames, (int)( sv_extra_client_connect_time.GetFloat() / m_Server->GetTickInterval() ) );
  485. // Msg( "Allowing up to %d frames for player for %f more seconds\n", nMaxFrames, realtime - m_flTimeClientBecameFullyConnected );
  486. }
  487. if ( sv_maxreplay.GetFloat() > 0 )
  488. {
  489. // if the server has replay features enabled, allow a way bigger frame buffer
  490. nMaxFrames = MAX( nMaxFrames, sv_maxreplay.GetFloat() / m_Server->GetTickInterval() );
  491. }
  492. // During the startup period we retain additional frames. Once nMaxFrames drops we need to
  493. // purge the extra frames or else we may permanently be using too much memory.
  494. int frameCount = AddClientFrame( m_pCurrentFrame );
  495. while ( nMaxFrames < frameCount )
  496. {
  497. // If the client has more than nMaxFrames frames, the server will start to eat too much memory.
  498. RemoveOldestFrame();
  499. --frameCount;
  500. }
  501. m_PackInfo.m_AreasNetworked = 0;
  502. int areaCount = g_AreasNetworked.Count();
  503. for ( int j = 0; j < areaCount; j++ )
  504. {
  505. // Msg("CGameClient::SetupPackInfo: too much areas (%i)", areaCount );
  506. AssertOnce( m_PackInfo.m_AreasNetworked < MAX_WORLD_AREAS );
  507. if ( m_PackInfo.m_AreasNetworked >= MAX_WORLD_AREAS )
  508. break;
  509. m_PackInfo.m_Areas[m_PackInfo.m_AreasNetworked] = g_AreasNetworked[ j ];
  510. m_PackInfo.m_AreasNetworked++;
  511. }
  512. CM_SetupAreaFloodNums( m_PackInfo.m_AreaFloodNums, &m_PackInfo.m_nMapAreas );
  513. }
  514. ConVar spec_replay_rate_base( "spec_replay_rate_base", "1", FCVAR_RELEASE | FCVAR_REPLICATED, "Base time scale of Killer Replay.Experimental." );
  515. void CGameClient::SetupHltvFrame( int nServerTick )
  516. {
  517. Assert( m_nHltvReplayDelay && m_pHltvReplayServer );
  518. int nReplayTick = nServerTick - m_nHltvReplayDelay;
  519. CClientFrame *pFrame = m_pHltvReplayServer->ExpandAndGetClientFrame( nReplayTick, false );
  520. if ( !pFrame )
  521. return;
  522. m_pCurrentFrame = pFrame;
  523. }
  524. void CGameClient::SetupPrevPackInfo()
  525. {
  526. Assert( !IsHltvReplay() );
  527. memcpy( &m_PrevTransmitEdict, m_PackInfo.m_pTransmitEdict, sizeof( m_PrevTransmitEdict ) );
  528. // Copy the relevant fields into m_PrevPackInfo.
  529. m_PrevPackInfo.m_AreasNetworked = m_PackInfo.m_AreasNetworked;
  530. memcpy( m_PrevPackInfo.m_Areas, m_PackInfo.m_Areas, sizeof( m_PackInfo.m_Areas[0] ) * m_PackInfo.m_AreasNetworked );
  531. m_PrevPackInfo.m_nPVSSize = m_PackInfo.m_nPVSSize;
  532. memcpy( m_PrevPackInfo.m_PVS, m_PackInfo.m_PVS, m_PackInfo.m_nPVSSize );
  533. m_PrevPackInfo.m_nMapAreas = m_PackInfo.m_nMapAreas;
  534. memcpy( m_PrevPackInfo.m_AreaFloodNums, m_PackInfo.m_AreaFloodNums, m_PackInfo.m_nMapAreas * sizeof( m_PackInfo.m_nMapAreas ) );
  535. }
  536. /*
  537. ================
  538. CheckRate
  539. Make sure channel rate for active client is within server bounds
  540. ================
  541. */
  542. void CGameClient::SetRate(int nRate, bool bForce )
  543. {
  544. if ( !bForce )
  545. {
  546. nRate = ClampClientRate( nRate );
  547. }
  548. BaseClass::SetRate( nRate, bForce );
  549. }
  550. void CGameClient::SetUpdateRate( float fUpdateRate, bool bForce )
  551. {
  552. if ( !bForce )
  553. {
  554. if ( CHLTVServer *hltv = GetAnyConnectedHltvServer() )
  555. {
  556. // Clients connected to our HLTV server will receive updates at tv_snapshotrate
  557. fUpdateRate = hltv->GetSnapshotRate();
  558. }
  559. else
  560. {
  561. if ( sv_maxupdaterate.GetFloat() > 0 )
  562. {
  563. fUpdateRate = clamp( fUpdateRate, 1, sv_maxupdaterate.GetFloat() );
  564. }
  565. if ( sv_minupdaterate.GetInt() > 0 )
  566. {
  567. fUpdateRate = clamp( fUpdateRate, sv_minupdaterate.GetFloat(), 128.0f );
  568. }
  569. }
  570. }
  571. BaseClass::SetUpdateRate( fUpdateRate, bForce );
  572. }
  573. void CGameClient::UpdateUserSettings()
  574. {
  575. // set voice loopback
  576. m_bVoiceLoopback = m_ConVars->GetInt( "voice_loopback", 0 ) != 0;
  577. BaseClass::UpdateUserSettings();
  578. // Give entity dll a chance to look at the changes.
  579. // Do this after BaseClass::UpdateUserSettings() so name changes like prepending a (1)
  580. // take effect before the server dll sees the name.
  581. g_pServerPluginHandler->ClientSettingsChanged( edict );
  582. }
  583. //-----------------------------------------------------------------------------
  584. // Purpose: A File has been received, if it's a logo, send it on to any other players who need it
  585. // and return true, otherwise, return false
  586. // Input : *cl -
  587. // *filename -
  588. // Output : Returns true on success, false on failure.
  589. /*-----------------------------------------------------------------------------
  590. bool CGameClient::ProcessIncomingLogo( const char *filename )
  591. {
  592. char crcfilename[ 512 ];
  593. char logohex[ 16 ];
  594. Q_binarytohex( (byte *)&logo, sizeof( logo ), logohex, sizeof( logohex ) );
  595. Q_snprintf( crcfilename, sizeof( crcfilename ), "materials/decals/downloads/%s.vtf", logohex );
  596. // It's not a logo file?
  597. if ( Q_strcasecmp( filename, crcfilename ) )
  598. {
  599. return false;
  600. }
  601. // First, make sure crc is valid
  602. CRC32_t check;
  603. CRC_File( &check, crcfilename );
  604. if ( check != logo )
  605. {
  606. ConMsg( "Incoming logo file didn't match player's logo CRC, ignoring\n" );
  607. // Still note that it was a logo!
  608. return true;
  609. }
  610. // Okay, looks good, see if any other players need this logo file
  611. SV_SendLogo( check );
  612. return true;
  613. } */
  614. bool CGameClient::IsHearingClient( int index ) const
  615. {
  616. #if defined( REPLAY_ENABLED )
  617. if ( IsHLTV() || IsReplay() )
  618. #else
  619. if ( IsHLTV() )
  620. #endif
  621. return true;
  622. if ( index == GetPlayerSlot() )
  623. return m_bVoiceLoopback;
  624. CGameClient *pClient = sv.Client( index );
  625. // Don't send voice from one splitscreen partner to another on the same box
  626. if ( !ss_voice_hearpartner.GetBool() &&
  627. IsSplitScreenPartner( pClient ) )
  628. {
  629. return false;
  630. }
  631. return pClient->m_VoiceStreams.Get( GetPlayerSlot() ) != 0;
  632. }
  633. bool CGameClient::IsProximityHearingClient( int index ) const
  634. {
  635. CGameClient *pClient = sv.Client( index );
  636. return pClient->m_VoiceProximity.Get( GetPlayerSlot() ) != 0;
  637. }
  638. void CGameClient::Inactivate( void )
  639. {
  640. if ( edict && !edict->IsFree() )
  641. {
  642. m_Server->RemoveClientFromGame( this );
  643. }
  644. if ( IsHLTV() )
  645. {
  646. if ( CHLTVServer *hltv = GetAnyConnectedHltvServer() )
  647. {
  648. hltv->Changelevel( true );
  649. }
  650. }
  651. m_nHltvReplayDelay = 0;
  652. m_pHltvReplayServer = NULL;
  653. m_nHltvReplayStopAt = 0;
  654. m_nHltvReplayStartAt = 0;
  655. m_nHltvLastSendTick = 0; // last send tick, don't send ticks twice
  656. #if defined( REPLAY_ENABLED )
  657. if ( IsReplay() )
  658. {
  659. replay->Changelevel();
  660. }
  661. #endif
  662. BaseClass::Inactivate();
  663. m_Sounds.Purge();
  664. ConVarRef voice_verbose( "voice_verbose" );
  665. if ( voice_verbose.GetBool() )
  666. {
  667. Msg( "* CGameClient::Inactivate: Clearing m_VoiceStreams/m_VoiceProximity for %s (%s)\n", GetClientName(), GetNetChannel() ? GetNetChannel()->GetAddress() : "null" );
  668. }
  669. m_VoiceStreams.ClearAll();
  670. m_VoiceProximity.ClearAll();
  671. DeleteClientFrames( -1 ); // delete all
  672. }
  673. bool CGameClient::UpdateAcknowledgedFramecount(int tick)
  674. {
  675. // free old client frames which won't be used anymore
  676. if ( tick != m_nDeltaTick )
  677. {
  678. // delta tick changed, free all frames smaller than tick
  679. int removeTick = tick;
  680. if ( sv_maxreplay.GetFloat() > 0 )
  681. removeTick -= (sv_maxreplay.GetFloat() / m_Server->GetTickInterval() ); // keep a replay buffer
  682. if ( removeTick > 0 )
  683. {
  684. DeleteClientFrames( removeTick );
  685. }
  686. }
  687. return BaseClass::UpdateAcknowledgedFramecount( tick );
  688. }
  689. void CGameClient::Clear()
  690. {
  691. if ( m_bIsHLTV )
  692. {
  693. if ( CHLTVServer *hltv = GetAnyConnectedHltvServer() )
  694. {
  695. hltv->Shutdown();
  696. }
  697. }
  698. #if defined( REPLAY_ENABLED )
  699. if ( m_bIsReplay )
  700. {
  701. replay->Shutdown();
  702. }
  703. #endif
  704. BaseClass::Clear();
  705. m_HltvQueuedMessages.PurgeAndDeleteElements();
  706. // free all frames
  707. DeleteClientFrames( -1 );
  708. m_Sounds.Purge();
  709. ConVarRef voice_verbose( "voice_verbose" );
  710. if ( voice_verbose.GetBool() )
  711. {
  712. Msg( "* CGameClient::Clear: Clearing m_VoiceStreams/m_VoiceProximity for %s (%s)\n", GetClientName(), GetNetChannel() ? GetNetChannel()->GetAddress() : "null" );
  713. }
  714. m_VoiceStreams.ClearAll();
  715. m_VoiceProximity.ClearAll();
  716. edict = NULL;
  717. m_pViewEntity = NULL;
  718. m_bVoiceLoopback = false;
  719. m_LastMovementTick = 0;
  720. m_nSoundSequence = 0;
  721. m_flTimeClientBecameFullyConnected = -1.0f;
  722. m_flLastClientCommandQuotaStart = -1.0f;
  723. m_numClientCommandsInQuota = 0;
  724. m_nHltvReplayDelay = 0;
  725. m_pHltvReplayServer = NULL;
  726. m_nHltvReplayStopAt = 0;
  727. m_nHltvReplayStartAt = 0;
  728. m_nHltvLastSendTick = 0;
  729. m_flHltvLastReplayRequestTime = -spec_replay_message_time.GetFloat();
  730. }
  731. void CGameClient::Reconnect( void )
  732. {
  733. // If the client was connected before, tell the game .dll to disconnect him/her.
  734. sv.RemoveClientFromGame( this );
  735. BaseClass::Reconnect();
  736. }
  737. void CGameClient::PerformDisconnection( const char *pReason )
  738. {
  739. // notify other clients of player leaving the game
  740. // send the username and network id so we don't depend on the CBasePlayer pointer
  741. IGameEvent *event = g_GameEventManager.CreateEvent( "player_disconnect" );
  742. if ( event )
  743. {
  744. event->SetInt("userid", GetUserID() );
  745. event->SetString("reason", pReason );
  746. event->SetString("name", GetClientName() );
  747. event->SetUint64("xuid", GetClientXuid() );
  748. event->SetString("networkid", GetNetworkIDString() );
  749. g_GameEventManager.FireEvent( event );
  750. }
  751. m_Server->RemoveClientFromGame( this );
  752. int nDisconnectSignonState = GetSignonState();
  753. BaseClass::PerformDisconnection( pReason );
  754. if ( nDisconnectSignonState >= SIGNONSTATE_NEW )
  755. {
  756. SV_ValidateMinRequiredClients( VALIDATE_DISCONNECT );
  757. }
  758. m_nHltvReplayDelay = 0;
  759. m_pHltvReplayServer = NULL;
  760. m_nHltvReplayStopAt = 0;
  761. m_nHltvReplayStartAt = 0;
  762. m_nHltvLastSendTick = 0;
  763. }
  764. HltvReplayStats_t m_DisconnectedClientsHltvReplayStats;
  765. void CGameClient::Disconnect( const char *fmt )
  766. {
  767. // Remember what state we had when "Disconnect" got called
  768. int nDisconnectSignonState = GetSignonState();
  769. if ( nDisconnectSignonState == SIGNONSTATE_NONE )
  770. return; // no recursion
  771. m_DisconnectedClientsHltvReplayStats += m_HltvReplayStats;
  772. m_HltvReplayStats.Reset();
  773. BaseClass::Disconnect( fmt );
  774. }
  775. bool CGameClient::ProcessSignonStateMsg( int state, int spawncount )
  776. {
  777. if ( state == SIGNONSTATE_SPAWN || state == SIGNONSTATE_CHANGELEVEL )
  778. {
  779. StopHltvReplay();
  780. }
  781. else if ( state == SIGNONSTATE_CONNECTED )
  782. {
  783. if ( !CheckConnect() )
  784. return false;
  785. // Allow long enough time-out to load a map
  786. float flTimeout = SIGNON_TIME_OUT;
  787. if ( sv.IsDedicatedForXbox() )
  788. flTimeout = SIGNON_TIME_OUT_360;
  789. m_NetChannel->SetTimeout( flTimeout );
  790. m_NetChannel->SetFileTransmissionMode( false );
  791. m_NetChannel->SetMaxBufferSize( true, NET_MAX_PAYLOAD );
  792. }
  793. else if ( state == SIGNONSTATE_NEW )
  794. {
  795. if ( !sv.IsMultiplayer() )
  796. {
  797. // local client as received and create string tables,
  798. // now link server tables to client tables
  799. SV_InstallClientStringTableMirrors();
  800. }
  801. }
  802. else if ( state == SIGNONSTATE_FULL )
  803. {
  804. if ( sv.m_bLoadgame )
  805. {
  806. // If this game was loaded from savegame, finish restoring game now
  807. sv.FinishRestore();
  808. }
  809. m_NetChannel->SetTimeout( sv_timeout.GetFloat() ); // use smaller timeout limit
  810. m_NetChannel->SetFileTransmissionMode( true );
  811. g_pServerPluginHandler->ClientFullyConnect( edict );
  812. }
  813. return BaseClass::ProcessSignonStateMsg( state, spawncount );
  814. }
  815. void CGameClient::SendSound( SoundInfo_t &sound, bool isReliable )
  816. {
  817. #if defined( REPLAY_ENABLED )
  818. if ( IsFakeClient() && !IsHLTV() && !IsReplay() && !IsSplitScreenUser() )
  819. #else
  820. if ( IsFakeClient() && !IsHLTV() && !IsSplitScreenUser() )
  821. #endif
  822. {
  823. return; // dont send sound messages to bots
  824. }
  825. // don't send sound messages while client is replay mode
  826. if ( m_bIsInReplayMode )
  827. {
  828. return;
  829. }
  830. // reliable sounds are send as single messages
  831. if ( isReliable )
  832. {
  833. CSVCMsg_Sounds_t *sndmsg = new CSVCMsg_Sounds_t;
  834. m_nSoundSequence = ( m_nSoundSequence + 1 ) & SOUND_SEQNUMBER_MASK; // increase own sound sequence counter
  835. sound.nSequenceNumber = 0; // don't transmit nSequenceNumber for reliable sounds
  836. sndmsg->set_reliable_sound( true );
  837. sound.WriteDelta( NULL, *sndmsg, sv.GetFinalTickTime() );
  838. if ( net_showreliablesounds.GetBool() )
  839. {
  840. const char *name = "<Unknown>";
  841. if ( sound.bIsSentence )
  842. {
  843. name = VOX_SentenceNameFromIndex( sound.nSoundNum );
  844. }
  845. else
  846. {
  847. if( sound.nFlags & SND_IS_SCRIPTHANDLE )
  848. {
  849. name = sound.pszName;
  850. }
  851. else
  852. {
  853. name = sv.GetSound( sound.nSoundNum );
  854. }
  855. }
  856. Warning( "reliable%s %s %d/%d/%d/%s\n",
  857. ((sound.nFlags & SND_STOP) != 0)?" stop":"",
  858. (sound.bIsSentence)?"sentence":"sound",
  859. sound.nEntityIndex, sound.nChannel, sound.nSoundNum, name );
  860. }
  861. // send reliable sound as single message
  862. SendNetMsg( *sndmsg, true );
  863. if ( m_nHltvReplayDelay )
  864. m_HltvQueuedMessages.AddToTail( sndmsg );
  865. else
  866. delete sndmsg;
  867. return;
  868. }
  869. sound.nSequenceNumber = m_nSoundSequence;
  870. m_Sounds.AddToTail( sound ); // queue sounds until snapshot is send
  871. }
  872. void CGameClient::WriteGameSounds( bf_write &buf, int nMaxSounds )
  873. {
  874. if ( m_Sounds.Count() <= 0 )
  875. return;
  876. CSVCMsg_Sounds_t msg;
  877. msg.SetReliable( false );
  878. int nSoundCount = FillSoundsMessage( msg, nMaxSounds );
  879. msg.WriteToBuffer( buf );
  880. if ( IsTracing() )
  881. {
  882. TraceNetworkData( buf, "Sounds [count=%d]", nSoundCount );
  883. }
  884. }
  885. static ConVar sv_sound_discardextraunreliable( "sv_sound_discardextraunreliable", "1" );
  886. int CGameClient::FillSoundsMessage(CSVCMsg_Sounds &msg, int nMaxSounds )
  887. {
  888. int i, count = m_Sounds.Count();
  889. // Discard events if we have too many to signal with 8 bits
  890. if ( count > nMaxSounds )
  891. count = nMaxSounds;
  892. // Nothing to send
  893. if ( !count )
  894. return 0;
  895. SoundInfo_t defaultSound;
  896. SoundInfo_t *pDeltaSound = &defaultSound;
  897. msg.set_reliable_sound( false );
  898. float finalTickTime = m_Server->GetFinalTickTime();
  899. for ( i = 0 ; i < count; i++ )
  900. {
  901. SoundInfo_t &sound = m_Sounds[ i ];
  902. sound.WriteDelta( pDeltaSound, msg, finalTickTime );
  903. pDeltaSound = &m_Sounds[ i ];
  904. }
  905. // remove added events from list
  906. if ( sv_sound_discardextraunreliable.GetBool() )
  907. {
  908. if ( m_Sounds.Count() != count )
  909. {
  910. DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , m_Sounds.Count() - count, m_Name );
  911. }
  912. m_Sounds.RemoveAll();
  913. }
  914. else
  915. {
  916. int remove = m_Sounds.Count() - ( count + nMaxSounds );
  917. if ( remove > 0 )
  918. {
  919. DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , remove, m_Name );
  920. count+= remove;
  921. }
  922. if ( count > 0 )
  923. {
  924. m_Sounds.RemoveMultiple( 0, count );
  925. }
  926. }
  927. Assert( m_Sounds.Count() <= nMaxSounds );
  928. return msg.sounds_size();
  929. }
  930. bool CGameClient::CheckConnect( void )
  931. {
  932. // Allow the game dll to reject this client.
  933. char szRejectReason[128];
  934. Q_strncpy( szRejectReason, "Connection rejected by game", sizeof( szRejectReason ) );
  935. if ( !g_pServerPluginHandler->ClientConnect( edict, m_Name, m_NetChannel->GetAddress(), szRejectReason, sizeof( szRejectReason ) ) )
  936. {
  937. // Reject the connection and drop the client.
  938. Disconnect( szRejectReason );
  939. return false;
  940. }
  941. return BaseClass::CheckConnect();
  942. }
  943. void CGameClient::ActivatePlayer( void )
  944. {
  945. BaseClass::ActivatePlayer();
  946. COM_TimestampedLog( "CGameClient::ActivatePlayer -start" );
  947. // call the spawn function
  948. if ( !sv.m_bLoadgame )
  949. {
  950. g_ServerGlobalVariables.curtime = sv.GetTime();
  951. COM_TimestampedLog( "g_pServerPluginHandler->ClientPutInServer" );
  952. g_pServerPluginHandler->ClientPutInServer( edict, m_Name );
  953. }
  954. COM_TimestampedLog( "g_pServerPluginHandler->ClientActive" );
  955. g_pServerPluginHandler->ClientActive( edict, sv.m_bLoadgame );
  956. COM_TimestampedLog( "g_pServerPluginHandler->ClientSettingsChanged" );
  957. g_pServerPluginHandler->ClientSettingsChanged( edict );
  958. COM_TimestampedLog( "GetTestScriptMgr()->CheckPoint" );
  959. GetTestScriptMgr()->CheckPoint( "client_connected" );
  960. // don't send signonstate to client, client will switch to FULL as soon
  961. // as the first full entity update packets has been received
  962. // fire a activate event
  963. IGameEvent *event = g_GameEventManager.CreateEvent( "player_activate" );
  964. if ( event )
  965. {
  966. event->SetInt( "userid", GetUserID() );
  967. g_GameEventManager.FireEvent( event );
  968. }
  969. COM_TimestampedLog( "CGameClient::ActivatePlayer -end" );
  970. // We'll let them have additional snapshots so the remote can connect w/o requiring a second
  971. // non-delta update (since the client gets the uncompressed world, then does a bunch of loading
  972. // which can take 7-10 seconds, then ack's a NET_Tick/delta tick which is way out of date by then
  973. m_flTimeClientBecameFullyConnected = realtime;
  974. }
  975. bool CGameClient::SendSignonData( void )
  976. {
  977. bool bClientHasdifferentTables = false;
  978. if ( sv.m_FullSendTables.IsOverflowed() )
  979. {
  980. Host_Error( "Send Table signon buffer overflowed %i bytes!!!\n", sv.m_FullSendTables.GetNumBytesWritten() );
  981. return false;
  982. }
  983. if ( SendTable_GetCRC() != (CRC32_t)0 )
  984. {
  985. bClientHasdifferentTables = m_nSendtableCRC != SendTable_GetCRC();
  986. }
  987. #ifdef _DEBUG
  988. if ( sv_sendtables.GetInt() == 2 )
  989. {
  990. // force sending class tables, for debugging
  991. bClientHasdifferentTables = true;
  992. }
  993. #endif
  994. // Write the send tables & class infos if needed
  995. if ( bClientHasdifferentTables )
  996. {
  997. if ( sv_sendtables.GetBool() )
  998. {
  999. // send client class table descriptions so it can rebuild tables
  1000. ConDMsg("Client sent different SendTable CRC, sending full tables.\n" );
  1001. m_NetChannel->SendData( sv.m_FullSendTables );
  1002. }
  1003. else
  1004. {
  1005. Disconnect( "Server uses different class tables" );
  1006. return false;
  1007. }
  1008. }
  1009. else
  1010. {
  1011. // use your class infos, CRC is correct
  1012. CSVCMsg_ClassInfo_t classmsg;
  1013. classmsg.set_create_on_client( true );
  1014. m_NetChannel->SendNetMsg( classmsg );
  1015. }
  1016. if ( !BaseClass::SendSignonData() )
  1017. return false;
  1018. m_nSoundSequence = 1; // reset sound sequence numbers after signon block
  1019. return true;
  1020. }
  1021. void CGameClient::SpawnPlayer( void )
  1022. {
  1023. SV_ValidateMinRequiredClients( VALIDATE_SPAWN );
  1024. // run the entrance script
  1025. if ( sv.m_bLoadgame )
  1026. { // loaded games are fully inited already
  1027. // if this is the last client to be connected, unpause
  1028. sv.SetPaused( false );
  1029. }
  1030. else
  1031. {
  1032. // set up the edict
  1033. Assert( serverGameEnts );
  1034. serverGameEnts->FreeContainingEntity( edict );
  1035. InitializeEntityDLLFields( edict );
  1036. }
  1037. // restore default client entity and turn off replay mdoe
  1038. m_nEntityIndex = m_nClientSlot+1;
  1039. m_bIsInReplayMode = false;
  1040. // set view entity
  1041. CSVCMsg_SetView_t setView;
  1042. setView.set_entity_index( m_nEntityIndex );
  1043. SendNetMsg( setView );
  1044. BaseClass::SpawnPlayer();
  1045. }
  1046. CClientFrame *CGameClient::GetDeltaFrame( int nTick )
  1047. {
  1048. Assert ( !IsHLTV() ); // has no ClientFrames
  1049. #if defined( REPLAY_ENABLED )
  1050. Assert ( !IsReplay() ); // has no ClientFrames
  1051. #endif
  1052. if ( m_bIsInReplayMode )
  1053. {
  1054. int followEntity;
  1055. serverGameClients->GetReplayDelay( edict, followEntity );
  1056. Assert( followEntity > 0 );
  1057. CGameClient *pFollowEntity = sv.Client( followEntity-1 );
  1058. if ( pFollowEntity )
  1059. return pFollowEntity->GetClientFrame( nTick );
  1060. }
  1061. return GetClientFrame( nTick );
  1062. }
  1063. void CGameClient::WriteViewAngleUpdate()
  1064. {
  1065. //
  1066. // send the current viewpos offset from the view entity
  1067. //
  1068. // a fixangle might get lost in a dropped packet. Oh well.
  1069. if ( IsFakeClient() && !IsSplitScreenUser() )
  1070. return;
  1071. Assert( serverGameClients );
  1072. CPlayerState *pl = serverGameClients->GetPlayerState( edict );
  1073. Assert( pl || IsSplitScreenUser() );
  1074. if ( !pl )
  1075. return;
  1076. if ( pl->fixangle != FIXANGLE_NONE )
  1077. {
  1078. if ( pl->fixangle == FIXANGLE_RELATIVE )
  1079. {
  1080. CSVCMsg_FixAngle_t fixAngle;
  1081. fixAngle.set_relative( true );
  1082. fixAngle.mutable_angle()->set_x( pl->anglechange.x );
  1083. fixAngle.mutable_angle()->set_y( pl->anglechange.y );
  1084. fixAngle.mutable_angle()->set_z( pl->anglechange.z );
  1085. m_NetChannel->SendNetMsg( fixAngle );
  1086. pl->anglechange.Init(); // clear
  1087. }
  1088. else
  1089. {
  1090. CSVCMsg_FixAngle_t fixAngle;
  1091. fixAngle.set_relative( false );
  1092. fixAngle.mutable_angle()->set_x( pl->v_angle.x );
  1093. fixAngle.mutable_angle()->set_y( pl->v_angle.y );
  1094. fixAngle.mutable_angle()->set_z( pl->v_angle.z );
  1095. m_NetChannel->SendNetMsg( fixAngle );
  1096. }
  1097. pl->fixangle = FIXANGLE_NONE;
  1098. }
  1099. }
  1100. /*
  1101. ===================
  1102. SV_ValidateClientCommand
  1103. Determine if passed in user command is valid.
  1104. ===================
  1105. */
  1106. bool CGameClient::IsEngineClientCommand( const CCommand &args ) const
  1107. {
  1108. if ( args.ArgC() == 0 )
  1109. return false;
  1110. for ( int i = 0; s_clcommands[i] != NULL; ++i )
  1111. {
  1112. if ( !Q_strcasecmp( args[0], s_clcommands[i] ) )
  1113. return true;
  1114. }
  1115. return false;
  1116. }
  1117. bool CGameClient::SendNetMsg( INetMessage &msg, bool bForceReliable, bool bVoice )
  1118. {
  1119. if ( m_bIsHLTV )
  1120. {
  1121. if ( CHLTVServer* hltv = GetAnyConnectedHltvServer() )
  1122. {// pass this message to HLTV
  1123. return hltv->SendNetMsg( msg, bForceReliable, bVoice );
  1124. }
  1125. else
  1126. {
  1127. Warning("HLTV client has no HLTV server connected\n");
  1128. return false;
  1129. }
  1130. }
  1131. #if defined( REPLAY_ENABLED )
  1132. if ( m_bIsReplay )
  1133. {
  1134. // pass this message to replay
  1135. return replay->SendNetMsg( msg, bForceReliable, bVoice );
  1136. }
  1137. #endif
  1138. if ( IsHltvReplay() )
  1139. {
  1140. if ( msg.GetType() != svc_VoiceData ) // let the voice messages through
  1141. {
  1142. Assert( !bVoice );
  1143. bool bResult = true;
  1144. if ( msg.GetType() == svc_UserMessage )
  1145. {
  1146. // chat: see UTIL_SayText2Filter(), Say_Host() and "player_say" GameMessage
  1147. CSVCMsg_UserMessage_t &userMessageHeader = ( CSVCMsg_UserMessage_t & )msg;
  1148. // Only send through those user messages that require real-time timeline on the client.
  1149. switch ( userMessageHeader.msg_type() )
  1150. {
  1151. case 22: // CS_UM_RadioText
  1152. case 5: //CS_UM_SayText
  1153. case 6: // CS_UM_SayText2
  1154. case 7: // CS_UM_TextMsg
  1155. case 18: // CS_UM_RawAudio
  1156. // mark this message as real-time and send with that flag, to distinguish it from the replay messages
  1157. userMessageHeader.set_passthrough( 1 );
  1158. bResult = BaseClass::SendNetMsg( msg, bForceReliable, bVoice );
  1159. userMessageHeader.clear_passthrough();
  1160. break;
  1161. }
  1162. }
  1163. return bResult; // just ignore all (other) new messages: we'll take them from hltv later if needed
  1164. }
  1165. Assert( bVoice );
  1166. }
  1167. return BaseClass::SendNetMsg( msg, bForceReliable, bVoice );
  1168. }
  1169. static CUtlStringToken s_HltvUnskippableEvents[] = {
  1170. "round_start",
  1171. "begin_new_match",
  1172. "game_newmap"
  1173. };
  1174. static CUtlStringToken s_HltvQueueableEvents[] = {
  1175. "teamplay_round_start","teamplay_round_end",
  1176. MakeStringToken( "endmatch_cmm_start_reveal_items" ),
  1177. "announce_phase_end",
  1178. "cs_match_end_restart",
  1179. "round_freeze_end"
  1180. };
  1181. static CUtlStringToken s_HltvPassThroughRealtimeEvents[] = {
  1182. "teamplay_broadcast_audio",
  1183. "player_chat",
  1184. "player_say",
  1185. "player_death",
  1186. "round_mvp",
  1187. "round_end"
  1188. };
  1189. bool IsInList( const char *pEventName, const char **ppList, int nListCount )
  1190. {
  1191. for ( int i = 0; i < nListCount; ++i )
  1192. {
  1193. if ( !V_strcmp( pEventName, ppList[ i ] ) )
  1194. {
  1195. return true;
  1196. }
  1197. }
  1198. return false;
  1199. }
  1200. bool IsInList( CUtlStringToken eventName, const CUtlStringToken *pTokenList, int nListCount )
  1201. {
  1202. for ( int i = 0; i < nListCount; ++i )
  1203. {
  1204. if ( eventName == pTokenList[ i ] )
  1205. {
  1206. return true;
  1207. }
  1208. }
  1209. return false;
  1210. }
  1211. void CGameClient::FireGameEvent( IGameEvent *event )
  1212. {
  1213. if ( IsHltvReplay() )
  1214. {
  1215. const char *pEventName = event->GetName(); // please don't fold the string variable, it's useful for debugging
  1216. CUtlStringToken eventName = MakeStringToken( pEventName );
  1217. if ( IsInList( eventName, s_HltvQueueableEvents, ARRAYSIZE( s_HltvQueueableEvents ) ) )
  1218. {
  1219. if ( event->IsReliable() ) // skip this event if it's not reliable
  1220. {
  1221. CSVCMsg_GameEvent_t *pEventMsg = new CSVCMsg_GameEvent_t;
  1222. if ( g_GameEventManager.SerializeEvent( event, pEventMsg ) )
  1223. {
  1224. m_HltvQueuedMessages.AddToTail( pEventMsg );
  1225. }
  1226. else
  1227. {
  1228. delete pEventMsg;
  1229. }
  1230. }
  1231. return;
  1232. }
  1233. else if ( IsInList( eventName, s_HltvUnskippableEvents, ARRAYSIZE( s_HltvUnskippableEvents ) ) )
  1234. {
  1235. Msg( "%s (%d) is skipping replay in progress (%d/%d) due to event %s\n", m_Name, m_UserID, sv.GetTick() - m_nHltvReplayDelay - m_nHltvReplayStartAt, m_nHltvReplayStopAt - m_nHltvReplayStartAt, pEventName );
  1236. StopHltvReplay();
  1237. return BaseClass::FireGameEvent( event );
  1238. }
  1239. else if ( IsInList( eventName, s_HltvPassThroughRealtimeEvents, ARRAYSIZE( s_HltvPassThroughRealtimeEvents ) ) )
  1240. {
  1241. return BaseClass::FireGameEvent( event, true ); // mark the event as real-time pass-through so that on the other end the client knows it's happening in real time, and it's not a replayed-back event
  1242. }
  1243. else
  1244. {
  1245. // For now, ignore game events by default, if they are sent while we're in a replay.
  1246. return;
  1247. }
  1248. }
  1249. return BaseClass::FireGameEvent( event );
  1250. }
  1251. bool CGameClient::ExecuteStringCommand( const char *pCommandString )
  1252. {
  1253. // first let the baseclass handle it
  1254. if ( BaseClass::ExecuteStringCommand( pCommandString ) )
  1255. return true;
  1256. // Determine whether the command is appropriate
  1257. CCommand args;
  1258. if ( !args.Tokenize( pCommandString, kCommandSrcNetClient ) )
  1259. return false;
  1260. if ( args.ArgC() == 0 )
  1261. return false;
  1262. // Disallow all string commands from client
  1263. // Special case for 0 here, as we don't kick in this case.
  1264. int cmdQuota = sv_quota_stringcmdspersecond.GetInt();
  1265. if ( cmdQuota == 0 )
  1266. return false;
  1267. // Client is about to execute a string command, check if we need to reset quota
  1268. if ( realtime - m_flLastClientCommandQuotaStart >= 1.0 )
  1269. {
  1270. // reset quota
  1271. m_flLastClientCommandQuotaStart = realtime;
  1272. m_numClientCommandsInQuota = 0;
  1273. }
  1274. ++ m_numClientCommandsInQuota;
  1275. if ( m_numClientCommandsInQuota > cmdQuota )
  1276. {
  1277. // Disconnect player for Denial-of-service attack
  1278. // REI: Remove this define when we unify trunk/staging (trunk uses enum reasons for disconnection)
  1279. // REI: See network_connection.proto for where this is supposed to come from
  1280. #define NETWORK_DISCONNECT_SERVER_DOS "#GameUI_Disconnect_TooManyCommands"
  1281. Disconnect( NETWORK_DISCONNECT_SERVER_DOS );
  1282. return false;
  1283. }
  1284. if ( IsEngineClientCommand( args ) )
  1285. {
  1286. Cmd_ExecuteCommand( CBUF_SERVER, args, m_nClientSlot );
  1287. return true;
  1288. }
  1289. // FIXME: This logic seem strange; why can't we just go through Cmd_ExecuteCommand? We should check for
  1290. // permission (cheat, sponly, gamedll since we are coming from client code) there, right?
  1291. const ConCommandBase *pCommand = g_pCVar->FindCommandBase( args[ 0 ] );
  1292. if ( pCommand && pCommand->IsCommand() && pCommand->IsFlagSet( FCVAR_GAMEDLL ) )
  1293. {
  1294. // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on
  1295. // NOTE: Don't bother with rpt stuff; commands that matter there shouldn't have FCVAR_GAMEDLL set
  1296. if ( pCommand->IsFlagSet( FCVAR_CHEAT ) )
  1297. {
  1298. if ( sv.IsMultiplayer() && !CanCheat() )
  1299. return false;
  1300. }
  1301. else if ( !sv_allow_legacy_cmd_execution_from_client.GetBool() && !pCommand->IsFlagSet( FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS )
  1302. #if !defined DEDICATED
  1303. && ( sv.IsDedicated() || m_nClientSlot != GetBaseLocalClient().m_nPlayerSlot )
  1304. #endif
  1305. )
  1306. {
  1307. #if DEVELOPMENT_ONLY
  1308. Warning( "WARNING: Client sent concommand %s which is not flagged as executable, ignoring\n" );
  1309. #endif
  1310. return false;
  1311. }
  1312. if ( pCommand->IsFlagSet( FCVAR_SPONLY ) )
  1313. {
  1314. if ( sv.IsMultiplayer() )
  1315. {
  1316. return false;
  1317. }
  1318. }
  1319. // REI 7/25/2016:
  1320. // I added this here; this state is normally set by Cmd_ExecuteCommand when executing
  1321. // a kCmdSrcNetClient command.
  1322. //
  1323. // Is there a reason this code path goes directly to Cmd_Dispatch instead
  1324. // of the usual path through Cmd_ExecuteCommand?
  1325. cmd_clientslot = m_nClientSlot;
  1326. g_pServerPluginHandler->SetCommandClient( m_nClientSlot );
  1327. Cmd_Dispatch( pCommand, args );
  1328. }
  1329. else
  1330. {
  1331. g_pServerPluginHandler->ClientCommand( edict, args ); // TODO pass client id and string
  1332. }
  1333. return true;
  1334. }
  1335. extern ConVar sv_multiplayer_maxsounds;
  1336. bool CGameClient::SendSnapshot( CClientFrame * pFrame )
  1337. {
  1338. if ( IsHltvReplay() )
  1339. {
  1340. return SendHltvReplaySnapshot( pFrame );
  1341. }
  1342. if ( m_bIsHLTV )
  1343. {
  1344. SNPROF( "SendSnapshot - HLTV" );
  1345. CHLTVServer *hltv = GetAnyConnectedHltvServer();
  1346. // pack sounds to one message
  1347. if ( m_Sounds.Count() > 0 )
  1348. {
  1349. CSVCMsg_Sounds_t sounds;
  1350. sounds.SetReliable( false );
  1351. FillSoundsMessage( sounds, m_Server->IsMultiplayer() ? sv_multiplayer_maxsounds.GetInt() : 255 );
  1352. hltv->SendNetMsg( sounds );
  1353. }
  1354. int maxEnts = tv_transmitall.GetBool()?255:64;
  1355. CSVCMsg_TempEntities_t tempentsmsg;
  1356. hltv->WriteTempEntities( this, pFrame->GetSnapshot(), m_pLastSnapshot.GetObject(), tempentsmsg, maxEnts );
  1357. if ( tempentsmsg.num_entries() )
  1358. {
  1359. tempentsmsg.WriteToBuffer( *hltv->GetBuffer( HLTV_BUFFER_TEMPENTS ) );
  1360. }
  1361. // add snapshot to HLTV server frame list
  1362. hltv->AddNewDeltaFrame( pFrame );
  1363. // remember this snapshot
  1364. m_pLastSnapshot = pFrame->GetSnapshot();
  1365. Assert( !GetHltvReplayDelay() ); // hltv master client shouldn't be in killer replay mode
  1366. // fake acknowledgement, remove ClientFrame reference immediately
  1367. UpdateAcknowledgedFramecount( pFrame->tick_count );
  1368. return true;
  1369. }
  1370. #if defined( REPLAY_ENABLED )
  1371. if ( m_bIsReplay )
  1372. {
  1373. SNPROF( "SendSnapshot - Replay" );
  1374. char *buf = (char *)_alloca( NET_MAX_PAYLOAD );
  1375. // pack sounds to one message
  1376. if ( m_Sounds.Count() > 0 )
  1377. {
  1378. CSVCMsg_Sounds_t sounds;
  1379. sounds.SetReliable( false );
  1380. FillSoundsMessage( sounds, m_Server->IsMultiplayer() ? sv_multiplayer_maxsounds.GetInt() : 255 );
  1381. replay->SendNetMsg( sounds );
  1382. }
  1383. int maxEnts = replay_transmitall.GetBool()?255:64;
  1384. replay->WriteTempEntities( this, pFrame->GetSnapshot(), m_pLastSnapshot.GetObject(), *replay->GetBuffer( REPLAY_BUFFER_TEMPENTS ), maxEnts );
  1385. // add snapshot to Replay server frame list
  1386. replay->AddNewFrame( pFrame );
  1387. // remember this snapshot
  1388. m_pLastSnapshot = pFrame->GetSnapshot();
  1389. // fake acknowledgement, remove ClientFrame reference immediately
  1390. UpdateAcknowledgedFramecount( pFrame->tick_count );
  1391. return true;
  1392. }
  1393. #endif
  1394. // update client viewangles update
  1395. WriteViewAngleUpdate();
  1396. bool bRet;
  1397. bRet = BaseClass::SendSnapshot( pFrame );
  1398. if ( bRet )
  1399. {
  1400. // Send messages that were queued up during replay - they need fully created entities, which is why I'm sending them after SendSnapshot
  1401. for ( INetMessage * pMessage : m_HltvQueuedMessages )
  1402. {
  1403. if ( m_NetChannel )
  1404. {
  1405. if ( !m_NetChannel->SendNetMsg( *pMessage, true ) ) // we only queue reliable messages
  1406. break;
  1407. }
  1408. }
  1409. m_HltvQueuedMessages.PurgeAndDeleteElements();
  1410. //////////////////////////////////////////////////////////////////////////
  1411. if ( IsFakeClient() )
  1412. {
  1413. Assert( !GetHltvReplayDelay() ); // fake clients should not be in "killer replay" mode
  1414. // fake acknowledgement, remove ClientFrame reference immediately
  1415. UpdateAcknowledgedFramecount( pFrame->tick_count );
  1416. }
  1417. }
  1418. return bRet;
  1419. }
  1420. //ConVar replay_hltv_voice( "replay_hltv_voice", "0", FCVAR_RELEASE );
  1421. bool CGameClient::SendHltvReplaySnapshot( CClientFrame * pFrame )
  1422. {
  1423. Assert( IsHltvReplay() );
  1424. VPROF_BUDGET( "CGameClient::SendHltvReplaySnapshot", "HLTV" );
  1425. byte buf[ NET_MAX_PAYLOAD ];
  1426. bf_write msg( "CGameClient::SendHltvReplaySnapshot", buf, sizeof( buf ) );
  1427. // if we send a full snapshot (no delta-compression) before, wait until client
  1428. // received and acknowledge that update. don't spam client with full updates
  1429. if ( m_pLastSnapshot == pFrame->GetSnapshot() )
  1430. {
  1431. // never send the same snapshot twice
  1432. m_NetChannel->Transmit();
  1433. return false;
  1434. }
  1435. if ( m_nForceWaitForTick > 0 )
  1436. {
  1437. // just continue transmitting reliable data
  1438. Assert( !m_bFakePlayer ); // Should never happen
  1439. m_NetChannel->Transmit();
  1440. return false;
  1441. }
  1442. CClientFrame *pDeltaFrame = m_pHltvReplayServer->GetDeltaFrame( m_nDeltaTick ); // NULL if delta_tick is not found
  1443. CHLTVFrame *pLastFrame = ( CHLTVFrame* )m_pHltvReplayServer->GetDeltaFrame( m_nHltvLastSendTick );
  1444. if ( pLastFrame )
  1445. {
  1446. // start first frame after last send
  1447. pLastFrame = ( CHLTVFrame* )pLastFrame->m_pNext;
  1448. }
  1449. // add all reliable messages between ]lastframe,currentframe]
  1450. // add all tempent & sound messages between ]lastframe,currentframe]
  1451. while ( pLastFrame && pLastFrame->tick_count <= pFrame->tick_count )
  1452. {
  1453. m_NetChannel->SendData( pLastFrame->m_Messages[ HLTV_BUFFER_RELIABLE ], true );
  1454. if ( pDeltaFrame )
  1455. {
  1456. // if we send entities delta compressed, also send unreliable data
  1457. m_NetChannel->SendData( pLastFrame->m_Messages[ HLTV_BUFFER_UNRELIABLE ], false );
  1458. // we skip the voice messages here because we don't want to hear the 10-second-delayed voice.
  1459. // we might want to send the real-time voice though
  1460. // if ( replay_hltv_voice.GetBool() )
  1461. // {
  1462. // int nVoiceBits = pLastFrame->m_Messages[ HLTV_BUFFER_VOICE ].m_nDataBits;
  1463. // if ( nVoiceBits > 0 )
  1464. // {
  1465. // //Msg( "replaying voice: %d bits\n", nVoiceBits );
  1466. // m_NetChannel->SendData( pLastFrame->m_Messages[ HLTV_BUFFER_VOICE ], false ); // we separate voice, even though it's simply more unreliable data, because we don't send it in replay
  1467. // }
  1468. // }
  1469. }
  1470. pLastFrame = ( CHLTVFrame* )pLastFrame->m_pNext;
  1471. }
  1472. // now create client snapshot packet
  1473. // send tick time
  1474. CNETMsg_Tick_t tickmsg( pFrame->tick_count, host_frameendtime_computationduration, host_frametime_stddeviation, host_framestarttime_stddeviation );
  1475. tickmsg.set_hltv_replay_flags( 1 );
  1476. tickmsg.WriteToBuffer( msg );
  1477. // Update shared client/server string tables. Must be done before sending entities
  1478. m_Server->m_StringTables->WriteUpdateMessage( NULL, GetMaxAckTickCount(), msg );
  1479. // TODO delta cache whole snapshots, not just packet entities. then use net_Align
  1480. // send entity update, delta compressed if deltaFrame != NULL
  1481. {
  1482. CSVCMsg_PacketEntities_t packetmsg;
  1483. m_pHltvReplayServer->WriteDeltaEntities( this, pFrame, pDeltaFrame, packetmsg );
  1484. packetmsg.WriteToBuffer( msg );
  1485. }
  1486. // write message to packet and check for overflow
  1487. if ( msg.IsOverflowed() )
  1488. {
  1489. if ( !pDeltaFrame )
  1490. {
  1491. // if this is a reliable snapshot, drop the client
  1492. //Disconnect( NETWORK_DISCONNECT_SNAPSHOTOVERFLOW );
  1493. return false;
  1494. }
  1495. else
  1496. {
  1497. // unreliable snapshots may be dropped
  1498. ConMsg( "WARNING: msg overflowed for %s\n", m_Name );
  1499. msg.Reset();
  1500. }
  1501. }
  1502. // remember this snapshot
  1503. m_pLastSnapshot = pFrame->GetSnapshot();
  1504. m_nHltvLastSendTick = pFrame->tick_count;
  1505. // Don't send the datagram to fakeplayers
  1506. if ( m_bFakePlayer )
  1507. {
  1508. m_nDeltaTick = pFrame->tick_count;
  1509. return true;
  1510. }
  1511. bool bSendOK;
  1512. // is this is a full entity update (no delta) ?
  1513. if ( !pDeltaFrame )
  1514. {
  1515. if ( replay_debug.GetInt() > 10 )
  1516. Msg( "HLTV send full frame %d: %d bytes\n", pFrame->tick_count, ( msg.m_iCurBit + 7 ) / 8 );
  1517. // transmit snapshot as reliable data chunk
  1518. bSendOK = m_NetChannel->SendData( msg );
  1519. bSendOK = bSendOK && m_NetChannel->Transmit();
  1520. // remember this tickcount we send the reliable snapshot
  1521. // so we can continue sending other updates if this has been acknowledged
  1522. m_nForceWaitForTick = pFrame->tick_count;
  1523. }
  1524. else
  1525. {
  1526. if ( replay_debug.GetInt() > 10 )
  1527. Msg( "HLTV send %d-delta of frame %d: %d bytes\n", pFrame->tick_count - pDeltaFrame->tick_count, pFrame->tick_count, ( msg.m_iCurBit + 7 ) / 8 );
  1528. // just send it as unreliable snapshot
  1529. bSendOK = m_NetChannel->SendDatagram( &msg ) > 0;
  1530. }
  1531. if ( !bSendOK )
  1532. {
  1533. Disconnect( "Snapshot error" );
  1534. return false;
  1535. }
  1536. return true;
  1537. }
  1538. bool CGameClient::CanStartHltvReplay()
  1539. {
  1540. CActiveHltvServerIterator hltv;
  1541. if ( hltv && !IsFakeClient() )
  1542. {
  1543. int nOldestHltvTick = hltv->GetOldestTick();
  1544. return ( nOldestHltvTick > 0 ); // we should have some ticks in history to proceed successfully
  1545. }
  1546. return false;
  1547. }
  1548. void CGameClient::ResetReplayRequestTime()
  1549. {
  1550. m_flHltvLastReplayRequestTime = -spec_replay_message_time.GetFloat();
  1551. }
  1552. bool CGameClient::StartHltvReplay( const HltvReplayParams_t &params )
  1553. {
  1554. m_HltvReplayStats.nStartRequests++;
  1555. CActiveHltvServerIterator hltv;
  1556. if ( hltv && !IsFakeClient() )
  1557. {
  1558. if ( !params.m_bAbortCurrentReplay && m_nHltvReplayDelay )
  1559. {
  1560. // we're already in replay, do not abort it to start a new one
  1561. DevMsg( "Hltv Replay failure: already in replay\n" );
  1562. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_ALREADY_IN_REPLAY ]++;
  1563. return false;
  1564. }
  1565. int nServerTick = sv.GetTick();
  1566. float flRealTime = Plat_FloatTime();
  1567. if ( fabsf( flRealTime - m_flHltvLastReplayRequestTime ) <= spec_replay_rate_limit.GetFloat() )
  1568. {
  1569. DevMsg( "Hltv Replay failure: requests are rate limited to no more than 1 per %g seconds\n", spec_replay_rate_limit.GetFloat() );
  1570. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_TOO_FREQUENT ]++;
  1571. return false;
  1572. }
  1573. int nOldestHltvTick = hltv->GetOldestTick();
  1574. if ( nOldestHltvTick > 0 ) // we should have some ticks in history to proceed successfully
  1575. {
  1576. // GetMaxAckTickCount() cannot be older than signon tick, and I don't know how much logic quietly relies on it, so to make it easier on myself I just won't allow reaching further into the past than the signon time, at least for the first iteration of replay
  1577. int nOldestClientTick = Max( m_nSignonTick, nOldestHltvTick );
  1578. float flTickInterval = sv.GetTickInterval();
  1579. int nDesiredReplayDelay = params.m_flDelay / flTickInterval, nNewReplayDelay = nDesiredReplayDelay;
  1580. int nNewReplayStopAt = nServerTick + params.m_flStopAt / flTickInterval;
  1581. Assert( hltv->m_CurrentFrame->tick_count >= hltv->m_nFirstTick ); // first known tick should have happened at or before the time of the current recorded HLTV frame
  1582. if ( nServerTick - nNewReplayDelay < nOldestClientTick )
  1583. {
  1584. nNewReplayDelay = nServerTick - nOldestClientTick;
  1585. }
  1586. if ( nNewReplayDelay <= 0 || nNewReplayStopAt <= nServerTick - nNewReplayDelay )
  1587. {
  1588. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_NO_FRAME ]++;
  1589. nNewReplayDelay = nNewReplayStopAt = 0;
  1590. m_pCurrentFrame = NULL;
  1591. }
  1592. else
  1593. {
  1594. m_pCurrentFrame = hltv->ExpandAndGetClientFrame( nServerTick - nNewReplayDelay, false );
  1595. if ( m_pCurrentFrame )
  1596. {
  1597. nNewReplayDelay = nServerTick - m_pCurrentFrame->tick_count;
  1598. }
  1599. else
  1600. {
  1601. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_NO_FRAME2 ]++;
  1602. nNewReplayDelay = nNewReplayStopAt = 0;
  1603. }
  1604. }
  1605. if ( !m_pCurrentFrame || abs( nNewReplayDelay - nDesiredReplayDelay ) > 64 )
  1606. {
  1607. DevMsg( "Hltv replay delay %u cannot match the requested delay %u\n", nNewReplayDelay, nDesiredReplayDelay );
  1608. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_CANNOT_MATCH_DELAY ]++;
  1609. nNewReplayDelay = nNewReplayStopAt = 0; // couldn't find anything decently approaching the desired delay in history
  1610. }
  1611. // now commit all the changes if needed
  1612. m_nHltvReplayStopAt = nNewReplayStopAt;
  1613. m_nHltvReplayStartAt = nServerTick;
  1614. if ( nNewReplayDelay != m_nHltvReplayDelay )
  1615. {
  1616. CSVCMsg_HltvReplay_t msg;
  1617. msg.set_delay( nNewReplayDelay );
  1618. msg.set_primary_target( params.m_nPrimaryTargetEntIndex );
  1619. msg.set_replay_stop_at( nNewReplayStopAt );
  1620. msg.set_replay_start_at( m_nHltvReplayStartAt );
  1621. if ( params.m_flSlowdownRate > 1.0f / 16.0f && params.m_flSlowdownBeginAt + 0.125f < params.m_flSlowdownEndAt )
  1622. {
  1623. m_flHltvReplaySlowdownRate = params.m_flSlowdownRate;
  1624. m_nHltvReplaySlowdownBeginAt = Max<int>( nServerTick - nNewReplayDelay, nServerTick + params.m_flSlowdownBeginAt / flTickInterval );
  1625. m_nHltvReplaySlowdownEndAt = Max<int>( m_nHltvReplaySlowdownBeginAt, nServerTick + params.m_flSlowdownEndAt / flTickInterval );
  1626. msg.set_replay_slowdown_rate( m_flHltvReplaySlowdownRate );
  1627. msg.set_replay_slowdown_begin( m_nHltvReplaySlowdownBeginAt );
  1628. msg.set_replay_slowdown_end( m_nHltvReplaySlowdownEndAt );
  1629. }
  1630. else
  1631. {
  1632. m_flHltvReplaySlowdownRate = 1.0f;
  1633. m_nHltvReplaySlowdownBeginAt = 0;
  1634. m_nHltvReplaySlowdownEndAt = 0;
  1635. }
  1636. SendNetMsg( msg, true );
  1637. //if( nNewReplayDelay) ExecuteStringCommand( "spectate" );
  1638. if ( replay_debug.GetBool() )
  1639. Msg( "Start HLMV Replay at %d, delay %d, until %d\n", nServerTick, nNewReplayDelay, nNewReplayStopAt );
  1640. m_nHltvReplayDelay = nNewReplayDelay;
  1641. m_pHltvReplayServer = hltv;
  1642. m_nDeltaTick = -1;
  1643. if ( m_nStringTableAckTick > nServerTick - nNewReplayDelay )
  1644. m_nStringTableAckTick = 0; // need to reset the stringtables, as they were updated in the future relative to the delayed stream
  1645. m_pLastSnapshot = NULL;
  1646. m_nHltvLastSendTick = 0;
  1647. FreeBaselines();
  1648. // all these data become invalid once we start sending HLTV packets from the past
  1649. m_PackInfo.Reset();
  1650. m_PrevPackInfo.Reset();
  1651. m_pCurrentFrame = NULL;
  1652. DeleteClientFrames( -1 ); // Should we clean up all the frames? Seems logical, as we'll never need them
  1653. m_flHltvLastReplayRequestTime = flRealTime;
  1654. m_HltvReplayStats.nSuccessfulStarts++;
  1655. return true;
  1656. }
  1657. }
  1658. else
  1659. {
  1660. DevMsg( "Hltv Replay failure: HLTV frame is not ready\n" );
  1661. m_HltvReplayStats.nFailedReplays[ HltvReplayStats_t::FAILURE_FRAME_NOT_READY ]++;
  1662. }
  1663. }
  1664. return false;
  1665. }
  1666. CBaseClient *CGameClient::GetPropCullClient()
  1667. {
  1668. return GetHltvReplayDelay() ? m_pHltvReplayServer->m_MasterClient : this;
  1669. }
  1670. static char s_HltvReplayBuffers[ 8 ][ 256 ];
  1671. uint s_nLastHltvReplayBuffer = 0;
  1672. const char *HltvReplayStats_t::AsString()const
  1673. {
  1674. if ( !nSuccessfulStarts && !nStartRequests && !nFullReplays && !nUserCancels && !nStopRequests )
  1675. {
  1676. return "";
  1677. }
  1678. s_nLastHltvReplayBuffer++;
  1679. if ( s_nLastHltvReplayBuffer >= ARRAYSIZE( s_HltvReplayBuffers ) )
  1680. s_nLastHltvReplayBuffer = 0;
  1681. char *pBuffer = s_HltvReplayBuffers[ s_nLastHltvReplayBuffer ];
  1682. CUtlString fails = ":";
  1683. int nTotalFailures = 0;
  1684. for ( int i = 0; i < NUM_FAILURES; ++i )
  1685. {
  1686. if ( i )
  1687. fails += ",";
  1688. if ( nFailedReplays[ i ] )
  1689. {
  1690. fails.Append( CFmtStr( "%u", nFailedReplays[ i ] ) );
  1691. nTotalFailures += nFailedReplays[ i ];
  1692. }
  1693. }
  1694. if ( nNetAbortReplays )
  1695. {
  1696. fails.Append( CFmtStr( "[%u!]", nNetAbortReplays ) );
  1697. nTotalFailures += nNetAbortReplays;
  1698. }
  1699. if ( !nTotalFailures )
  1700. fails = "";
  1701. V_snprintf( pBuffer, sizeof( s_HltvReplayBuffers[ s_nLastHltvReplayBuffer ] ), "%u/%u started, %u full, %u cancels / %u stops, %u fails%s", nSuccessfulStarts, nStartRequests, nFullReplays, nUserCancels, nStopRequests, nTotalFailures, fails.Get() );
  1702. return pBuffer;
  1703. }
  1704. void CGameClient::StepHltvReplayStatus( int nServerTick )
  1705. {
  1706. if ( IsHltvReplay() )
  1707. {
  1708. if ( m_nHltvLastSendTick >= m_nHltvReplayStopAt )
  1709. {
  1710. m_HltvReplayStats.nFullReplays++;
  1711. StopHltvReplay();
  1712. }
  1713. else if ( m_nForceWaitForTick > 0 )
  1714. {
  1715. if ( !m_pCurrentFrame || m_pCurrentFrame->tick_count >= m_nHltvReplayStopAt )
  1716. {
  1717. // client doesn't respond or there's no current frame -both indicating some problem. And we're past the time alotted for replay - we should just abort.
  1718. Msg( "Client %d (eidx %d, user id %d) %s - aborting wait for ack for tick %d, stopping replay at %d>=%d\n", m_nClientSlot, m_nEntityIndex, m_UserID, m_Name, m_nForceWaitForTick, m_pCurrentFrame ? m_pCurrentFrame->tick_count : 0, m_nHltvReplayStopAt );
  1719. m_HltvReplayStats.nNetAbortReplays++;
  1720. StopHltvReplay();
  1721. }
  1722. }
  1723. }
  1724. }
  1725. void CGameClient::StopHltvReplay()
  1726. {
  1727. if ( IsHltvReplay() )
  1728. {
  1729. m_HltvReplayStats.nStopRequests++;
  1730. if ( m_nHltvLastSendTick < m_nHltvReplayStopAt )
  1731. m_HltvReplayStats.nAbortStopRequests++;
  1732. m_nHltvReplayStopAt = 0;
  1733. m_nHltvReplayDelay = 0;
  1734. m_nDeltaTick = -1;
  1735. m_nForceWaitForTick = -1;
  1736. m_pLastSnapshot = NULL; // it doesn't matter what last snapshot we sent; we need to send a full frame update
  1737. m_nHltvLastSendTick = 0;
  1738. FreeBaselines();
  1739. m_pCurrentFrame = NULL;
  1740. DeleteClientFrames( -1 ); // Should we clean up all the frames? Seems logical, as we'll never need them
  1741. Assert( CountClientFrames() == 0 ); // we shouldn't have used the client frame manager to send HLTV stream to client
  1742. }
  1743. // just in case, send the end-of-hltv replay message even if we are not in replay
  1744. {
  1745. CSVCMsg_HltvReplay_t msg;
  1746. SendNetMsg( msg, true );
  1747. }
  1748. }
  1749. //-----------------------------------------------------------------------------
  1750. // This function contains all the logic to determine if we should send a datagram
  1751. // to a particular client
  1752. //-----------------------------------------------------------------------------
  1753. bool CGameClient::ShouldSendMessages( void )
  1754. {
  1755. if ( m_bIsHLTV )
  1756. {
  1757. // calc snapshot interval
  1758. if ( CHLTVServer *hltv = GetAnyConnectedHltvServer() )
  1759. {
  1760. int nSnapshotInterval = 1.0f / ( m_Server->GetTickInterval() * hltv->GetSnapshotRate() );
  1761. // I am the HLTV client, record every nSnapshotInterval tick
  1762. return ( sv.m_nTickCount >= ( hltv->m_nLastTick + nSnapshotInterval ) );
  1763. }
  1764. else
  1765. {
  1766. return false; // something is wrong, it'll assert in GetAnyConnectedHltvServer()..
  1767. }
  1768. }
  1769. #if defined( REPLAY_ENABLED )
  1770. if ( m_bIsReplay )
  1771. {
  1772. // calc snapshot interval
  1773. int nSnapshotInterval = 1.0f / ( m_Server->GetTickInterval() * replay_snapshotrate.GetFloat() );
  1774. // I am the Replay client, record every nSnapshotInterval tick
  1775. return ( sv.m_nTickCount >= (replay->m_nLastTick + nSnapshotInterval) );
  1776. }
  1777. #endif
  1778. // If sv_stressbots is true, then treat a bot more like a regular client and do deltas and such for it.
  1779. if( !sv_replaybots.GetBool() && IsFakeClient() )
  1780. {
  1781. if ( !sv_stressbots.GetBool() )
  1782. return false;
  1783. }
  1784. return BaseClass::ShouldSendMessages();
  1785. }
  1786. void CGameClient::FileReceived( const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  1787. {
  1788. //check if file is one of our requested custom files
  1789. for ( int i=0; i<MAX_CUSTOM_FILES; i++ )
  1790. {
  1791. if ( m_nCustomFiles[i].reqID == transferID )
  1792. {
  1793. m_nFilesDownloaded++;
  1794. // broadcast update to other clients so they start downlaoding this file
  1795. m_Server->UserInfoChanged( m_nClientSlot );
  1796. return;
  1797. }
  1798. }
  1799. Msg( "CGameClient::FileReceived: %s not wanted.\n", fileName );
  1800. }
  1801. void CGameClient::FileRequested(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  1802. {
  1803. DevMsg( "File '%s' requested from client %s.\n", fileName, m_NetChannel->GetAddress() );
  1804. if ( sv_allowdownload.GetBool() )
  1805. {
  1806. m_NetChannel->SendFile( fileName, transferID, bIsReplayDemoFile );
  1807. }
  1808. else
  1809. {
  1810. m_NetChannel->DenyFile( fileName, transferID, bIsReplayDemoFile );
  1811. }
  1812. }
  1813. void CGameClient::FileDenied(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  1814. {
  1815. ConMsg( "Downloading file '%s' from client %s failed.\n", fileName, GetClientName() );
  1816. }
  1817. void CGameClient::FileSent(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  1818. {
  1819. ConMsg( "Sent file '%s' to client %s.\n", fileName, GetClientName() );
  1820. }
  1821. void CGameClient::PacketStart(int incoming_sequence, int outgoing_acknowledged)
  1822. {
  1823. for ( int i = 1; i < host_state.max_splitscreen_players; ++i )
  1824. {
  1825. if ( !m_SplitScreenUsers[ i ] )
  1826. continue;
  1827. m_SplitScreenUsers[ i ]->PacketStart( incoming_sequence, outgoing_acknowledged );
  1828. }
  1829. // make sure m_LastMovementTick != sv.tickcount
  1830. m_LastMovementTick = ( sv.m_nTickCount - 1 );
  1831. host_client = this;
  1832. // During connection, only respond if client sends a packet
  1833. m_bReceivedPacket = true;
  1834. }
  1835. void CGameClient::PacketEnd()
  1836. {
  1837. // Fix up clock in case prediction/etc. code reset it.
  1838. g_ServerGlobalVariables.frametime = host_state.interval_per_tick;
  1839. }
  1840. void CGameClient::ConnectionClosing(const char *reason)
  1841. {
  1842. SV_RedirectEnd();
  1843. Disconnect( (reason!=NULL)?reason:"Connection closing" );
  1844. }
  1845. void CGameClient::ConnectionCrashed(const char *reason)
  1846. {
  1847. if ( m_Name[0] && IsConnected() )
  1848. {
  1849. SV_RedirectEnd();
  1850. Disconnect( (reason!=NULL)?reason:"Connection lost" );
  1851. }
  1852. }
  1853. CClientFrame *CGameClient::GetSendFrame()
  1854. {
  1855. CClientFrame *pFrame = m_pCurrentFrame;
  1856. // just return if replay is disabled
  1857. if ( sv_maxreplay.GetFloat() <= 0 || IsHltvReplay() )
  1858. return pFrame;
  1859. int followEntity;
  1860. int delayTicks = serverGameClients->GetReplayDelay( edict, followEntity );
  1861. bool isInReplayMode = ( delayTicks > 0 );
  1862. if ( isInReplayMode != m_bIsInReplayMode )
  1863. {
  1864. // force a full update when modes are switched
  1865. m_nDeltaTick = -1;
  1866. m_bIsInReplayMode = isInReplayMode;
  1867. if ( isInReplayMode )
  1868. {
  1869. m_nEntityIndex = followEntity;
  1870. }
  1871. else
  1872. {
  1873. m_nEntityIndex = m_nClientSlot+1;
  1874. }
  1875. }
  1876. Assert( (m_nClientSlot+1 == m_nEntityIndex) || isInReplayMode );
  1877. if ( isInReplayMode )
  1878. {
  1879. CGameClient *pFollowPlayer = sv.Client( followEntity-1 );
  1880. if ( !pFollowPlayer )
  1881. return NULL;
  1882. pFrame = pFollowPlayer->GetClientFrame( sv.GetTick() - delayTicks, false );
  1883. if ( !pFrame )
  1884. return NULL;
  1885. if ( m_pLastSnapshot == pFrame->GetSnapshot() )
  1886. return NULL;
  1887. }
  1888. return pFrame;
  1889. }
  1890. bool CGameClient::IgnoreTempEntity( CEventInfo *event )
  1891. {
  1892. // in replay mode replay all temp entities
  1893. if ( m_bIsInReplayMode )
  1894. return false;
  1895. return BaseClass::IgnoreTempEntity( event );
  1896. }
  1897. const CCheckTransmitInfo* CGameClient::GetPrevPackInfo()
  1898. {
  1899. Assert( !IsHltvReplay() ); // we don't maintain this data during Hltv-fed replay
  1900. return &m_PrevPackInfo;
  1901. }
  1902. // This code is useful for verifying that the networking of soundinfo_t stuff isn't borked.
  1903. #if 0
  1904. #include "vstdlib/random.h"
  1905. class CTestSoundInfoNetworking
  1906. {
  1907. public:
  1908. CTestSoundInfoNetworking();
  1909. void RunTest();
  1910. private:
  1911. void CreateRandomSounds( int nCount );
  1912. void CreateRandomSound( SoundInfo_t &si );
  1913. void Compare( const SoundInfo_t &s1, const SoundInfo_t &s2 );
  1914. CUtlVector< SoundInfo_t > m_Sounds;
  1915. CUtlVector< SoundInfo_t > m_Received;
  1916. };
  1917. static CTestSoundInfoNetworking g_SoundTest;
  1918. CON_COMMAND( st, "sound test" )
  1919. {
  1920. int nCount = 1;
  1921. if ( args.ArgC() >= 2 )
  1922. {
  1923. nCount = clamp( Q_atoi( args.Arg( 1 ) ), 1, 100000 );
  1924. }
  1925. for ( int i = 0 ; i < nCount; ++i )
  1926. {
  1927. if ( !( i % 100 ) && i > 0 )
  1928. {
  1929. Msg( "Running test %d %f %% done\n",
  1930. i, 100.0f * (float)i/(float)nCount );
  1931. }
  1932. g_SoundTest.RunTest();
  1933. }
  1934. }
  1935. CTestSoundInfoNetworking::CTestSoundInfoNetworking()
  1936. {
  1937. }
  1938. void CTestSoundInfoNetworking::CreateRandomSound( SoundInfo_t &si )
  1939. {
  1940. int entindex = RandomInt( 0, MAX_EDICTS - 1 );
  1941. int channel = RandomInt( 0, 7 );
  1942. int soundnum = RandomInt( 0, MAX_SOUNDS - 1 );
  1943. Vector org = RandomVector( -16383, 16383 );
  1944. Vector dir = RandomVector( -1.0f, 1.0f );
  1945. float flVolume = RandomFloat( 0.1f, 1.0f );
  1946. bool bLooping = RandomInt( 0, 100 ) < 5;
  1947. int nPitch = RandomInt( 0, 100 ) < 5 ? RandomInt( 95, 105 ) : 100;
  1948. Vector lo = RandomInt( 0, 100 ) < 5 ? RandomVector( -16383, 16383 ) : org;
  1949. int speaker = RandomInt( 0, 100 ) < 2 ? RandomInt( 0, MAX_EDICTS - 1 ) : -1;
  1950. soundlevel_t level = soundlevel_t(RandomInt( 70, 150 ));
  1951. si.Set( entindex, channel, "foo.wav", org, dir, flVolume, level, bLooping, nPitch, lo, speaker );
  1952. si.nFlags = ( 1 << RandomInt( 0, 6 ) );
  1953. si.nSoundNum = soundnum;
  1954. si.bIsSentence = RandomInt( 0, 1 );
  1955. si.bIsAmbient = RandomInt( 0, 1 );
  1956. si.fDelay = RandomInt( 0, 100 ) < 2 ? RandomFloat( -0.1, 0.1f ) : 0.0f;
  1957. }
  1958. void CTestSoundInfoNetworking::CreateRandomSounds( int nCount )
  1959. {
  1960. m_Sounds.Purge();
  1961. m_Sounds.EnsureCount( nCount );
  1962. for ( int i = 0; i < nCount; ++i )
  1963. {
  1964. SoundInfo_t &si = m_Sounds[ i ];
  1965. CreateRandomSound( si );
  1966. }
  1967. }
  1968. void CTestSoundInfoNetworking::RunTest()
  1969. {
  1970. int m_nSoundSequence = 0;
  1971. CreateRandomSounds( 512 );
  1972. SoundInfo_t defaultSound; defaultSound.SetDefault();
  1973. SoundInfo_t *pDeltaSound = &defaultSound;
  1974. CSVCMsg_Sounds_t msg;
  1975. msg.set_reliable_sound( false );
  1976. msg.SetReliable( false );
  1977. for ( int i = 0 ; i < m_Sounds.Count(); i++ )
  1978. {
  1979. SoundInfo_t &sound = m_Sounds[ i ];
  1980. sound.WriteDelta( pDeltaSound, msg.m_DataOut );
  1981. pDeltaSound = &m_Sounds[ i ];
  1982. }
  1983. // Now read them out
  1984. defaultSound.SetDefault();
  1985. pDeltaSound = &defaultSound;
  1986. msg.m_DataIn.StartReading( buf, msg.m_DataOut.GetNumBytesWritten(), 0, msg.m_DataOut.GetNumBitsWritten() );
  1987. SoundInfo_t sound;
  1988. for ( int i=0; i<msg.m_nNumSounds; i++ )
  1989. {
  1990. sound.ReadDelta( pDeltaSound, msg.m_DataIn );
  1991. pDeltaSound = &sound; // copy delta values
  1992. if ( msg.m_bReliableSound )
  1993. {
  1994. // client is incrementing the reliable sequence numbers itself
  1995. m_nSoundSequence = ( m_nSoundSequence + 1 ) & SOUND_SEQNUMBER_MASK;
  1996. Assert ( sound.nSequenceNumber == 0 );
  1997. sound.nSequenceNumber = m_nSoundSequence;
  1998. }
  1999. // Add no ambient sounds to sorted queue, will be processed after packet has been completly parsed
  2000. // CL_AddSound( sound );
  2001. m_Received.AddToTail( sound );
  2002. }
  2003. // Now validate them
  2004. for ( int i = 0 ; i < msg.m_nNumSounds; ++i )
  2005. {
  2006. SoundInfo_t &server = m_Sounds[ i ];
  2007. SoundInfo_t &client = m_Received[ i ];
  2008. Compare( server, client );
  2009. }
  2010. m_Sounds.Purge();
  2011. m_Received.Purge();
  2012. }
  2013. void CTestSoundInfoNetworking::Compare( const SoundInfo_t &s1, const SoundInfo_t &s2 )
  2014. {
  2015. bool bSndStop = s2.nFlags == SND_STOP;
  2016. if ( !bSndStop && s1.nSequenceNumber != s2.nSequenceNumber )
  2017. {
  2018. Msg( "seq number mismatch %d %d\n", s1.nSequenceNumber, s2.nSequenceNumber );
  2019. }
  2020. if ( s1.nEntityIndex != s2.nEntityIndex )
  2021. {
  2022. Msg( "ent mismatch %d %d\n", s1.nEntityIndex, s2.nEntityIndex );
  2023. }
  2024. if ( s1.nChannel != s2.nChannel )
  2025. {
  2026. Msg( "channel mismatch %d %d\n", s1.nChannel, s2.nChannel );
  2027. }
  2028. Vector d;
  2029. d = s1.vOrigin - s2.vOrigin;
  2030. if ( !bSndStop && d.Length() > 32.0f )
  2031. {
  2032. Msg( "origin mismatch [%f] (%f %f %f) != (%f %f %f)\n", d.Length(), s1.vOrigin.x, s1.vOrigin.y, s1.vOrigin.z, s2.vOrigin.x, s2.vOrigin.y, s2.vOrigin.z );
  2033. }
  2034. // Vector vDirection;
  2035. float delta = fabs( s1.fVolume - s2.fVolume );
  2036. if ( !bSndStop && delta > 1.0f )
  2037. {
  2038. Msg( "vol mismatch %f %f\n", s1.fVolume, s2.fVolume );
  2039. }
  2040. if ( !bSndStop && s1.Soundlevel != s2.Soundlevel )
  2041. {
  2042. Msg( "sndlvl mismatch %d %d\n", s1.Soundlevel, s2.Soundlevel );
  2043. }
  2044. // bLooping;
  2045. if ( s1.bIsSentence != s2.bIsSentence )
  2046. {
  2047. Msg( "sentence mismatch %d %d\n", s1.bIsSentence ? 1 : 0, s2.bIsSentence ? 1 : 0 );
  2048. }
  2049. if ( s1.bIsAmbient != s2.bIsAmbient )
  2050. {
  2051. Msg( "ambient mismatch %d %d\n", s1.bIsAmbient ? 1 : 0, s2.bIsAmbient ? 1 : 0 );
  2052. }
  2053. if ( !bSndStop && s1.nPitch != s2.nPitch )
  2054. {
  2055. Msg( "pitch mismatch %d %d\n", s1.nPitch, s2.nPitch );
  2056. }
  2057. // Vector vListenerOrigin;
  2058. if ( s1.nFlags != s2.nFlags )
  2059. {
  2060. Msg( "flags mismatch %d %d\n", s1.nFlags, s2.nFlags );
  2061. }
  2062. if ( s1.nSoundNum != s2.nSoundNum )
  2063. {
  2064. Msg( "soundnum mismatch %d %d\n", s1.nSoundNum, s2.nSoundNum );
  2065. }
  2066. delta = fabs( s1.fDelay - s2.fDelay );
  2067. if ( !bSndStop && delta > 0.020f )
  2068. {
  2069. Msg( "delay mismatch %f %f\n", s1.fDelay, s2.fDelay );
  2070. }
  2071. if ( !bSndStop && s1.nSpeakerEntity != s2.nSpeakerEntity )
  2072. {
  2073. Msg( "speakerentity mismatch %d %d\n", s1.nSpeakerEntity, s2.nSpeakerEntity );
  2074. }
  2075. }
  2076. #endif