Team Fortress 2 Source Code as on 22/4/2020
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.

1606 lines
44 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include <winsock2.h>
  7. #include "vmpi_filesystem_internal.h"
  8. #include "zlib.h"
  9. #include "vstdlib/random.h"
  10. #define MINIMUM_SLEEP_MS 1
  11. // NOTE: This number comes from measurements on our network to find out how fast
  12. // we can broadcast without the network freaking out.
  13. //
  14. // This number can be changed on the command line with the -mpi_FileTransmitRate parameter.
  15. int MULTICAST_TRANSMIT_RATE = (1024*1000); // N megs per second
  16. // Defines when we'll stop transmitting a file to a client.
  17. // (After we've transmitted the file to the client N times and we haven't heard an ack back for M seconds).
  18. #define MIN_FILE_CYCLE_COUNT 5
  19. #define CLIENT_FILE_ACK_TIMEOUT 20
  20. // ------------------------------------------------------------------------------------------------------------------------ //
  21. // Global helpers.
  22. // ------------------------------------------------------------------------------------------------------------------------ //
  23. static void SendMulticastIP( const CIPAddr *pAddr )
  24. {
  25. unsigned char packetID[2] = { VMPI_PACKETID_FILESYSTEM, VMPI_FSPACKETID_MULTICAST_ADDR };
  26. VMPI_Send2Chunks(
  27. packetID, sizeof( packetID ),
  28. pAddr, sizeof( *pAddr ),
  29. VMPI_PERSISTENT );
  30. }
  31. static bool IsOpeningForWriteAccess( const char *pOptions )
  32. {
  33. return strchr( pOptions, 'w' ) || strchr( pOptions, 'a' ) || strchr( pOptions, '+' );
  34. }
  35. // This does a fast zlib compression of the source data into the 'out' buffer.
  36. static bool ZLibCompress( const void *pData, int len, CUtlVector<char> &out )
  37. {
  38. if ( len == 0 )
  39. {
  40. out.Purge();
  41. return true;
  42. }
  43. int outStartLen = len;
  44. RETRY:;
  45. // Prepare the compression stream.
  46. z_stream zs;
  47. memset( &zs, 0, sizeof( zs ) );
  48. if ( deflateInit( &zs, 1 ) != Z_OK )
  49. return false;
  50. // Now compress it into the output buffer.
  51. out.SetSize( outStartLen );
  52. zs.next_in = (unsigned char*)pData;
  53. zs.avail_in = len;
  54. zs.next_out = (unsigned char*)out.Base();
  55. zs.avail_out = out.Count();
  56. int ret = deflate( &zs, Z_FINISH );
  57. deflateEnd( &zs );
  58. if ( ret == Z_STREAM_END )
  59. {
  60. // Get rid of whatever was left over.
  61. out.RemoveMultiple( zs.total_out, out.Count() - zs.total_out );
  62. return true;
  63. }
  64. else if ( ret == Z_OK )
  65. {
  66. // Need more space in the output buffer.
  67. outStartLen += 1024 * 128;
  68. goto RETRY;
  69. }
  70. else
  71. {
  72. return false;
  73. }
  74. }
  75. // ------------------------------------------------------------------------------------------------------------------------ //
  76. // CVMPIFile_PassThru
  77. // ------------------------------------------------------------------------------------------------------------------------ //
  78. class CVMPIFile_PassThru : public IVMPIFile
  79. {
  80. public:
  81. void Init( IBaseFileSystem *pPassThru, FileHandle_t fp )
  82. {
  83. m_pPassThru = pPassThru;
  84. m_fp = fp;
  85. }
  86. virtual void Close()
  87. {
  88. m_pPassThru->Close( m_fp );
  89. delete this;
  90. }
  91. virtual void Seek( int pos, FileSystemSeek_t seekType )
  92. {
  93. m_pPassThru->Seek( m_fp, pos, seekType );
  94. }
  95. virtual unsigned int Tell()
  96. {
  97. return m_pPassThru->Tell( m_fp );
  98. }
  99. virtual unsigned int Size()
  100. {
  101. return m_pPassThru->Size( m_fp );
  102. }
  103. virtual void Flush()
  104. {
  105. m_pPassThru->Flush( m_fp );
  106. }
  107. virtual int Read( void* pOutput, int size )
  108. {
  109. return m_pPassThru->Read( pOutput, size, m_fp );
  110. }
  111. virtual int Write( void const* pInput, int size )
  112. {
  113. return m_pPassThru->Write( pInput, size, m_fp );
  114. }
  115. private:
  116. IBaseFileSystem *m_pPassThru;
  117. FileHandle_t m_fp;
  118. };
  119. // ---------------------------------------------------------------------------------------------------- //
  120. // CTransmitRateMgr coordinates with any other currently-running VMPI jobs, and they all will cut
  121. // down their transmission rate to stay within MULTICAST_TRANSMIT_RATE.
  122. // ---------------------------------------------------------------------------------------------------- //
  123. #define TRANSMITRATEMGR_BROADCAST_INVERVAL (1.0 / 3.0) // How many times per second we broadcast our presence.
  124. #define TRANSMITRATEMGR_EXPIRE_TIME 0.7 // How long it'll go before deciding a guy is not transmitting anymore.
  125. static char s_cTransmitRateMgrPacket[] = {2,6,-3,2,1,-66};
  126. class CTransmitRateMgr
  127. {
  128. public:
  129. CTransmitRateMgr();
  130. void ReadPackets();
  131. void BroadcastPresence();
  132. double GetMicrosecondsPerByte() const;
  133. private:
  134. class CMachineRecord
  135. {
  136. public:
  137. unsigned long m_UniqueID;
  138. float m_flLastTime;
  139. };
  140. CUtlVector<CMachineRecord> m_MachineRecords;
  141. unsigned long m_UniqueID;
  142. float m_flLastBroadcastTime;
  143. double m_nMicrosecondsPerByte;
  144. ISocket *m_pSocket;
  145. };
  146. CTransmitRateMgr::CTransmitRateMgr()
  147. {
  148. m_nMicrosecondsPerByte = 1000000.0 / (double)MULTICAST_TRANSMIT_RATE;
  149. m_flLastBroadcastTime = 0;
  150. // Build a (hopefully) unique ID.
  151. m_UniqueID = (unsigned long)this;
  152. CCycleCount cnt;
  153. cnt.Sample();
  154. m_UniqueID += cnt.GetMicroseconds();
  155. Sleep( 1 );
  156. m_UniqueID += cnt.GetMicroseconds();
  157. m_pSocket = CreateIPSocket();
  158. if ( m_pSocket )
  159. {
  160. m_pSocket->BindToAny( VMPI_MASTER_FILESYSTEM_BROADCAST_PORT );
  161. }
  162. }
  163. void CTransmitRateMgr::ReadPackets()
  164. {
  165. if ( !m_pSocket )
  166. return;
  167. float flCurTime = Plat_FloatTime();
  168. // First, update/add records.
  169. while ( 1 )
  170. {
  171. char data[512];
  172. CIPAddr ipFrom;
  173. int len = m_pSocket->RecvFrom( data, sizeof( data ), &ipFrom );
  174. if ( len == -1 )
  175. break;
  176. if ( len == sizeof( s_cTransmitRateMgrPacket ) + sizeof( unsigned long ) &&
  177. memcmp( data, s_cTransmitRateMgrPacket, sizeof( s_cTransmitRateMgrPacket ) ) == 0 )
  178. {
  179. unsigned long id = *((unsigned long*)&data[sizeof(s_cTransmitRateMgrPacket)]);
  180. if ( id == m_UniqueID )
  181. continue;
  182. int i;
  183. for ( i=0; i < m_MachineRecords.Count(); i++ )
  184. {
  185. if ( m_MachineRecords[i].m_UniqueID == id )
  186. {
  187. m_MachineRecords[i].m_flLastTime = flCurTime;
  188. break;
  189. }
  190. }
  191. if ( i == m_MachineRecords.Count() )
  192. {
  193. int index = m_MachineRecords.AddToTail();
  194. m_MachineRecords[index].m_UniqueID = id;
  195. m_MachineRecords[index].m_flLastTime = flCurTime;
  196. }
  197. }
  198. }
  199. // Now, expire any old records.
  200. for ( int i=0; i < m_MachineRecords.Count(); i++ )
  201. {
  202. if ( (flCurTime - m_MachineRecords[i].m_flLastTime) > TRANSMITRATEMGR_EXPIRE_TIME )
  203. {
  204. m_MachineRecords.Remove( i );
  205. --i;
  206. }
  207. }
  208. // Recalculate our transmit rate (assuming we're receiving our own broadcast packets).
  209. m_nMicrosecondsPerByte = 1000000.0 / (double)(MULTICAST_TRANSMIT_RATE / (m_MachineRecords.Count() + 1));
  210. }
  211. void CTransmitRateMgr::BroadcastPresence()
  212. {
  213. if ( !m_pSocket )
  214. return;
  215. float flCurTime = Plat_FloatTime();
  216. if ( (flCurTime - m_flLastBroadcastTime) < TRANSMITRATEMGR_BROADCAST_INVERVAL )
  217. return;
  218. m_flLastBroadcastTime = flCurTime;
  219. char cData[sizeof( s_cTransmitRateMgrPacket ) + sizeof( unsigned long )];
  220. memcpy( cData, s_cTransmitRateMgrPacket, sizeof( s_cTransmitRateMgrPacket ) );
  221. *((unsigned long*)&cData[ sizeof( s_cTransmitRateMgrPacket ) ] ) = m_UniqueID;
  222. m_pSocket->Broadcast( cData, sizeof( cData ), VMPI_MASTER_FILESYSTEM_BROADCAST_PORT );
  223. }
  224. inline double CTransmitRateMgr::GetMicrosecondsPerByte() const
  225. {
  226. return m_nMicrosecondsPerByte;
  227. }
  228. // ---------------------------------------------------------------------------------------------------- //
  229. // CRateLimiter manages waiting for small periods of time between packets so the rate is
  230. // whatever we want it to be.
  231. //
  232. // It also will give up some CPU time to other processes every 50 milliseconds.
  233. // ---------------------------------------------------------------------------------------------------- //
  234. class CRateLimiter
  235. {
  236. public:
  237. CRateLimiter();
  238. void GiveUpTimeSlice();
  239. void NoteExcessTimeTaken( unsigned long excessTimeInMicroseconds );
  240. public:
  241. DWORD m_SleepIntervalMS; // Give up a timeslice every N milliseconds.
  242. // Since we sleep once in a while, we time how long the sleep took and we beef
  243. // up the transmit rate until we've accounted for the time lost during the sleep.
  244. DWORD m_AccumulatedSleepMicroseconds;
  245. // When was the last time we gave up a little bit of CPU to other programs.
  246. CCycleCount m_LastSleepTime;
  247. };
  248. CRateLimiter::CRateLimiter()
  249. {
  250. m_SleepIntervalMS = 50;
  251. m_AccumulatedSleepMicroseconds = 0;
  252. m_LastSleepTime.Sample();
  253. }
  254. void CRateLimiter::GiveUpTimeSlice()
  255. {
  256. // Sleep again?
  257. CCycleCount currentTime, dtSinceLastSleep;
  258. currentTime.Sample();
  259. CCycleCount::Sub( currentTime, m_LastSleepTime, dtSinceLastSleep );
  260. if ( dtSinceLastSleep.GetMilliseconds() >= m_SleepIntervalMS )
  261. {
  262. CFastTimer sleepTimer;
  263. sleepTimer.Start();
  264. Sleep( 10 );
  265. sleepTimer.End();
  266. m_AccumulatedSleepMicroseconds += sleepTimer.GetDuration().GetMicroseconds();
  267. m_LastSleepTime.Sample();
  268. }
  269. }
  270. void CRateLimiter::NoteExcessTimeTaken( unsigned long excessTimeInMicroseconds )
  271. {
  272. // Note: we give up time slices above.
  273. if ( excessTimeInMicroseconds > m_AccumulatedSleepMicroseconds )
  274. {
  275. excessTimeInMicroseconds -= m_AccumulatedSleepMicroseconds;
  276. m_AccumulatedSleepMicroseconds = 0;
  277. CCycleCount startCount;
  278. startCount.Sample();
  279. while ( 1 )
  280. {
  281. CCycleCount curCount, diff;
  282. curCount.Sample();
  283. CCycleCount::Sub( curCount, startCount, diff );
  284. if ( diff.GetMicroseconds() >= excessTimeInMicroseconds )
  285. break;
  286. }
  287. }
  288. else
  289. {
  290. m_AccumulatedSleepMicroseconds -= excessTimeInMicroseconds;
  291. excessTimeInMicroseconds = 0;
  292. }
  293. }
  294. // ------------------------------------------------------------------------------------------------------------------------ //
  295. // CMasterMulticastThread.
  296. // ------------------------------------------------------------------------------------------------------------------------ //
  297. class CMasterMulticastThread
  298. {
  299. public:
  300. CMasterMulticastThread();
  301. ~CMasterMulticastThread();
  302. // This creates the socket and starts the thread (initially in an idle state since it doesn't
  303. // know of any files anyone wants).
  304. bool Init( IBaseFileSystem *pPassThru, unsigned short localPort, const CIPAddr *pAddr, int maxFileSystemMemoryUsage );
  305. void Term();
  306. // Returns -1 if there is an error.
  307. int FindOrAddFile( const char *pFilename, const char *pPathID );
  308. const CUtlVector<char>& GetFileData( int iFile ) const;
  309. // When a client requests a files, this is called to tell the thread to start
  310. // adding chunks from the specified file into the queue it's multicasting.
  311. //
  312. // Returns -1 if the file isn't there. Otherwise, it returns the file ID
  313. // that will be sent along with the file's chunks in the multicast packets.
  314. int AddFileRequest( const char *pFilename, const char *pPathID, int clientID, bool *bZeroLength );
  315. // As each client receives multicasted chunks, they ack them so the master can
  316. // stop transmitting any chunks it knows nobody wants.
  317. void OnChunkReceived( int fileID, int clientID, int iChunk );
  318. void OnFileReceived( int fileID, int clientID );
  319. // Call this if a client disconnects so it can stop sending anything this client wants.
  320. void OnClientDisconnect( int clientID, bool bGrabCriticalSection=true );
  321. void CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength );
  322. private:
  323. class CChunkInfo
  324. {
  325. public:
  326. unsigned short m_iChunk;
  327. unsigned short m_RefCount; // How many clients want this chunk.
  328. unsigned short m_iActiveChunksIndex; // Index into m_ActiveChunks.
  329. };
  330. // This stores a client's reference to a file so it knows which pieces of the file the client needs.
  331. class CClientFileInfo
  332. {
  333. public:
  334. bool NeedsChunk( int i ) const { return (m_ChunksToSend[i>>3] & (1 << (i&7))) != 0; }
  335. public:
  336. int m_ClientID;
  337. CUtlVector<unsigned char> m_ChunksToSend; // One bit for each chunk that this client still wants.
  338. int m_nChunksLeft;
  339. // TCP transmission only.
  340. int m_TCP_LastChunkAcked;
  341. int m_TCP_LastChunkSent;
  342. float m_flTransmitStartTime;
  343. float m_flLastAckTime; // Last time we heard an ack back from this client about this file.
  344. // If this goes for too long, then we assume that the client is
  345. // in a screwed state, and we stop sending the file to him.
  346. int m_nTimesFileCycled; // How many times has the master multicast thread cycled over this file?
  347. // We won't kick the client until we've cycled over the file a few times
  348. // after the client asked for it.
  349. };
  350. class CMulticastFile
  351. {
  352. public:
  353. ~CMulticastFile()
  354. {
  355. m_Clients.PurgeAndDeleteElements();
  356. }
  357. const char* GetFilename() { return m_Filename.Base(); }
  358. const char* GetPathID() { return m_PathID.Base(); }
  359. public:
  360. int m_nCycles; // How many times has the multicast thread visited this file?
  361. // This is sent along with every packet. If a client gets a chunk and doesn't have that file's
  362. // info, the client will receive that file too.
  363. CUtlVector<char> m_Filename;
  364. CUtlVector<char> m_PathID;
  365. CMulticastFileInfo m_Info;
  366. // This is stored so the app can read out the uncompressed data.
  367. CUtlVector<char> m_UncompressedData;
  368. // zlib-compressed file data
  369. CUtlVector<char> m_Data;
  370. // This gets set to false if we run over our memory limit and start caching file data out.
  371. // Then it'll reload the data if a client requests the file.
  372. bool m_bDataLoaded;
  373. // m_Chunks holds the chunks by index.
  374. // m_ActiveChunks holds them sorted by whether they're active or not.
  375. //
  376. // Each chunk has a refcount. While the refcount is > 0, the chunk is in the first
  377. // half of m_ActiveChunks. When the refcount gets to 0, the chunk is moved to the end of
  378. // m_ActiveChunks. That way, we can iterate through the chunks that need to be sent and
  379. // stop iterating the first time we hit one with a refcount of 0.
  380. CUtlVector<CChunkInfo> m_Chunks;
  381. CUtlLinkedList<CChunkInfo*,int> m_ActiveChunks;
  382. // This tells which clients want pieces of this file.
  383. CUtlLinkedList<CClientFileInfo*,int> m_Clients;
  384. };
  385. private:
  386. static DWORD WINAPI StaticMulticastThread( LPVOID pParameter );
  387. DWORD MulticastThread();
  388. bool CheckClientTimeouts();
  389. bool Thread_SendFileChunk_Multicast( int *pnBytesSent );
  390. void Thread_SeekToNextActiveChunk();
  391. // In TCP mode, we send new chunks as they are acked.
  392. void TCP_SendNextChunk( CMulticastFile *pFile, CClientFileInfo *pClient );
  393. void EnsureMemoryLimit( CMulticastFile *pIgnore );
  394. // Called after pFile->m_UncompressedData has been setup. This compresses the data, prepares the header,
  395. // copies the filename, and adds it into the queue for the multicast thread.
  396. int FinishFileSetup( CMulticastFile *pFile, const char *pFilename, const char *pPathID, bool bFileAlreadyExisted );
  397. void IncrementChunkRefCount( CMasterMulticastThread::CMulticastFile *pFile, int iChunk );
  398. void DecrementChunkRefCount( int iFile, int iChunk );
  399. int FindFile( const char *pFilename, const char *pPathID );
  400. bool FindWarningSuppression( const char *pFilename );
  401. void AddWarningSuppression( const char *pFilename );
  402. private:
  403. CUtlLinkedList<CMulticastFile*,int> m_Files;
  404. unsigned long m_nCurMemoryUsage; // Total of all the file data we have loaded.
  405. unsigned long m_nMaxMemoryUsage; // 0 means that there is no limit.
  406. // This tracks how many chunks we have that want to be sent.
  407. int m_nTotalActiveChunks;
  408. SOCKET m_Socket;
  409. sockaddr_in m_MulticastAddr;
  410. HANDLE m_hMainThread;
  411. IBaseFileSystem *m_pPassThru;
  412. HANDLE m_hThread;
  413. CRITICAL_SECTION m_CS;
  414. // Events used to communicate with our thread.
  415. HANDLE m_hTermEvent;
  416. // The thread walks through this as it spews chunks of data.
  417. volatile int m_iCurFile; // Index into m_Files.
  418. volatile int m_iCurActiveChunk; // Current index into CMulticastFile::m_ActiveChunks.
  419. CUtlLinkedList<char*,int> m_WarningSuppressions;
  420. };
  421. CMasterMulticastThread::CMasterMulticastThread()
  422. {
  423. m_hThread = m_hMainThread = NULL;
  424. m_Socket = INVALID_SOCKET;
  425. m_nTotalActiveChunks = 0;
  426. m_iCurFile = m_iCurActiveChunk = -1;
  427. m_pPassThru = NULL;
  428. m_hTermEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  429. InitializeCriticalSection( &m_CS );
  430. m_nCurMemoryUsage = m_nMaxMemoryUsage = 0;
  431. }
  432. CMasterMulticastThread::~CMasterMulticastThread()
  433. {
  434. Term();
  435. CloseHandle( m_hTermEvent );
  436. DeleteCriticalSection( &m_CS );
  437. }
  438. bool CMasterMulticastThread::Init( IBaseFileSystem *pPassThru, unsigned short localPort, const CIPAddr *pAddr, int maxMemoryUsage )
  439. {
  440. Term();
  441. m_nMaxMemoryUsage = maxMemoryUsage;
  442. Assert( m_nCurMemoryUsage == 0 );
  443. m_nCurMemoryUsage = 0;
  444. if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_TCP )
  445. {
  446. // No need for an extra socket in this mode.
  447. m_Socket = INVALID_SOCKET;
  448. }
  449. else
  450. {
  451. // First, create our socket.
  452. m_Socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
  453. if ( m_Socket == INVALID_SOCKET )
  454. {
  455. Warning( "CMasterMulticastThread::Init - socket() failed\n" );
  456. return false;
  457. }
  458. // Bind to INADDR_ANY.
  459. CIPAddr localAddr( 0, 0, 0, 0, localPort );
  460. sockaddr_in addr;
  461. IPAddrToSockAddr( &localAddr, &addr );
  462. int status = bind( m_Socket, (sockaddr*)&addr, sizeof(addr) );
  463. if ( status != 0 )
  464. {
  465. Term();
  466. Warning( "CMasterMulticastThread::Init - bind( %d.%d.%d.%d:%d ) failed\n", EXPAND_ADDR( *pAddr ) );
  467. return false;
  468. }
  469. if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_BROADCAST )
  470. {
  471. // Set up for broadcast
  472. BOOL bBroadcast = TRUE;
  473. if ( setsockopt( m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, bBroadcast ) == SOCKET_ERROR )
  474. {
  475. Term();
  476. Warning( "CMasterMulticastThread::Init - setsockopt() failed to set broadcast mode\n" );
  477. return false;
  478. }
  479. }
  480. // Remember the address we want to send to.
  481. IPAddrToSockAddr( pAddr, &m_MulticastAddr );
  482. // Now create our thread.
  483. DWORD dwThreadID = 0;
  484. m_hThread = CreateThread( NULL, 0, &CMasterMulticastThread::StaticMulticastThread, this, 0, &dwThreadID );
  485. if ( !m_hThread )
  486. {
  487. Term();
  488. Warning( "CMasterMulticastThread::Init - CreateThread failed\n" );
  489. return false;
  490. }
  491. SetThreadPriority( m_hThread, THREAD_PRIORITY_LOWEST );
  492. }
  493. // For debug mode to verify that we don't try to open files while in another thread.
  494. m_hMainThread = GetCurrentThread();
  495. m_pPassThru = pPassThru;
  496. return true;
  497. }
  498. void CMasterMulticastThread::Term()
  499. {
  500. // Stop the thread if it is running.
  501. if ( m_hThread )
  502. {
  503. SetEvent( m_hTermEvent );
  504. WaitForSingleObject( m_hThread, INFINITE );
  505. CloseHandle( m_hThread );
  506. m_hThread = NULL;
  507. }
  508. // Close the socket.
  509. if ( m_Socket != INVALID_SOCKET )
  510. {
  511. closesocket( m_Socket );
  512. m_Socket = INVALID_SOCKET;
  513. }
  514. // Free up other data.
  515. m_Files.PurgeAndDeleteElements();
  516. m_nCurMemoryUsage = m_nMaxMemoryUsage = 0;
  517. }
  518. void CMasterMulticastThread::TCP_SendNextChunk( CMulticastFile *pFile, CClientFileInfo *pClient )
  519. {
  520. // No more chunks to send?
  521. if ( (pClient->m_TCP_LastChunkSent+1) >= pFile->m_Info.m_nChunks )
  522. return;
  523. // Figure out what data we'd be sending.
  524. int iChunkToSend = pClient->m_TCP_LastChunkSent + 1;
  525. int iStartByte = iChunkToSend * TCP_CHUNK_PAYLOAD_SIZE;
  526. int iEndByte = min( iStartByte + TCP_CHUNK_PAYLOAD_SIZE, pFile->m_Data.Count() );
  527. // If the start point is past the end, then we're done sending the file to this client.
  528. if ( iStartByte >= pFile->m_Data.Count() )
  529. return;
  530. // Record that we sent this data.
  531. pClient->m_TCP_LastChunkSent = iChunkToSend;
  532. // Assemble the packet.
  533. unsigned char cPacket[2] = { VMPI_PACKETID_FILESYSTEM, VMPI_FSPACKETID_FILE_CHUNK };
  534. const void *chunks[5] =
  535. {
  536. cPacket,
  537. &pFile->m_Info,
  538. &iChunkToSend,
  539. pFile->GetFilename(),
  540. &pFile->m_Data[iStartByte]
  541. };
  542. int chunkLengths[5] =
  543. {
  544. sizeof( cPacket ),
  545. sizeof( pFile->m_Info ),
  546. sizeof( m_iCurActiveChunk ),
  547. strlen( pFile->GetFilename() ) + 1,
  548. iEndByte - iStartByte
  549. };
  550. VMPI_SendChunks( chunks, chunkLengths, 5, pClient->m_ClientID );
  551. }
  552. int CMasterMulticastThread::AddFileRequest( const char *pFilename, const char *pPathID, int clientID, bool *bZeroLength )
  553. {
  554. // Firstly, do we already have this file?
  555. int iFile = FindOrAddFile( pFilename, pPathID );
  556. if ( iFile == -1 )
  557. return -1;
  558. CMulticastFile *pFile = m_Files[iFile];
  559. // Now that we have a file setup, merge in this client's info.
  560. EnterCriticalSection( &m_CS );
  561. CClientFileInfo *pClient = new CClientFileInfo;
  562. pClient->m_TCP_LastChunkAcked = -1;
  563. pClient->m_TCP_LastChunkSent = -1;
  564. pClient->m_ClientID = clientID;
  565. pClient->m_flLastAckTime = Plat_FloatTime();
  566. pClient->m_flTransmitStartTime = pClient->m_flLastAckTime;
  567. pClient->m_nTimesFileCycled = 0;
  568. pClient->m_nChunksLeft = pFile->m_Info.m_nChunks;
  569. pClient->m_ChunksToSend.SetSize( PAD_NUMBER( pFile->m_Info.m_nChunks, 8 ) / 8 );
  570. memset( pClient->m_ChunksToSend.Base(), 0xFF, pClient->m_ChunksToSend.Count() );
  571. pFile->m_Clients.AddToTail( pClient );
  572. for ( int i=0; i < pFile->m_Chunks.Count(); i++ )
  573. {
  574. IncrementChunkRefCount( pFile, i );
  575. }
  576. // In TCP mode, let's get the sliding window started..
  577. if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_TCP )
  578. {
  579. for ( int iDepth=0; iDepth < TCP_CHUNK_QUEUE_LEN; iDepth++ )
  580. TCP_SendNextChunk( pFile, pClient );
  581. }
  582. LeaveCriticalSection( &m_CS );
  583. *bZeroLength = (pFile->m_Info.m_UncompressedSize == 0);
  584. return iFile;
  585. }
  586. void CMasterMulticastThread::OnChunkReceived( int fileID, int clientID, int iChunk )
  587. {
  588. if ( !m_Files.IsValidIndex( fileID ) )
  589. {
  590. Warning( "CMasterMulticastThread::OnChunkReceived: invalid file (%d) from client %d\n", fileID, clientID );
  591. return;
  592. }
  593. CMulticastFile *pFile = m_Files[fileID];
  594. CClientFileInfo *pClient = NULL;
  595. FOR_EACH_LL( pFile->m_Clients, iClient )
  596. {
  597. if ( pFile->m_Clients[iClient]->m_ClientID == clientID )
  598. {
  599. pClient = pFile->m_Clients[iClient];
  600. break;
  601. }
  602. }
  603. if ( !pClient )
  604. {
  605. // This will spam sometimes if a worker stops responding and we timeout on it,
  606. // but then it comes back alive and starts responding. So let's ignore its packets silently.
  607. //Warning( "CMasterMulticastThread::OnChunkReceived: invalid client ID (%d) for file %s\n", clientID, pFile->GetFilename() );
  608. return;
  609. }
  610. if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_TCP )
  611. {
  612. // Send the next chunk, if there is one.
  613. EnterCriticalSection( &m_CS );
  614. TCP_SendNextChunk( pFile, pClient );
  615. LeaveCriticalSection( &m_CS );
  616. }
  617. else
  618. {
  619. if ( !pFile->m_Chunks.IsValidIndex( iChunk ) )
  620. {
  621. Warning( "CMasterMulticastThread::OnChunkReceived: invalid chunk index (%d) for file %s\n", iChunk, pFile->GetFilename() );
  622. return;
  623. }
  624. // Mark that this client doesn't need this chunk anymore.
  625. pClient->m_ChunksToSend[iChunk >> 3] &= ~(1 << (iChunk & 7));
  626. pClient->m_nChunksLeft--;
  627. pClient->m_flLastAckTime = Plat_FloatTime();
  628. if ( pClient->m_nChunksLeft == 0 && g_iVMPIVerboseLevel >= 2 )
  629. Warning( "Client %d got file %s\n", clientID, pFile->GetFilename() );
  630. EnterCriticalSection( &m_CS );
  631. DecrementChunkRefCount( fileID, iChunk );
  632. LeaveCriticalSection( &m_CS );
  633. }
  634. }
  635. void CMasterMulticastThread::OnFileReceived( int fileID, int clientID )
  636. {
  637. if ( !m_Files.IsValidIndex( fileID ) )
  638. {
  639. Warning( "CMasterMulticastThread::OnChunkReceived: invalid file (%d) from client %d\n", fileID, clientID );
  640. return;
  641. }
  642. CMulticastFile *pFile = m_Files[fileID];
  643. for ( int i=0; i < pFile->m_Info.m_nChunks; i++ )
  644. OnChunkReceived( fileID, clientID, i );
  645. }
  646. void CMasterMulticastThread::OnClientDisconnect( int clientID, bool bGrabCriticalSection )
  647. {
  648. if ( bGrabCriticalSection )
  649. EnterCriticalSection( &m_CS );
  650. // Remove all references from this client.
  651. FOR_EACH_LL( m_Files, iFile )
  652. {
  653. CMulticastFile *pFile = m_Files[iFile];
  654. FOR_EACH_LL( pFile->m_Clients, iClient )
  655. {
  656. CClientFileInfo *pClient = pFile->m_Clients[iClient];
  657. if ( pClient->m_ClientID != clientID )
  658. continue;
  659. // Ok, this is our man. Decrement the refcount of any chunks this client wanted.
  660. for ( int iChunk=0; iChunk < pFile->m_Info.m_nChunks; iChunk++ )
  661. {
  662. if ( pClient->NeedsChunk( iChunk ) )
  663. {
  664. DecrementChunkRefCount( iFile, iChunk );
  665. }
  666. }
  667. delete pClient;
  668. pFile->m_Clients.Remove( iClient );
  669. break;
  670. }
  671. }
  672. if ( bGrabCriticalSection )
  673. LeaveCriticalSection( &m_CS );
  674. }
  675. void CMasterMulticastThread::CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength )
  676. {
  677. const char *pPathID = VMPI_VIRTUAL_FILES_PATH_ID;
  678. int iFile = FindFile( pFilename, pPathID );
  679. if ( iFile != -1 )
  680. Error( "CMasterMulticastThread::CreateVirtualFile( %s ) - file already exists!", pFilename );
  681. CMulticastFile *pFile = new CMulticastFile;
  682. pFile->m_UncompressedData.CopyArray( (const char*)pData, fileLength );
  683. FinishFileSetup( pFile, pFilename, pPathID, false );
  684. }
  685. DWORD WINAPI CMasterMulticastThread::StaticMulticastThread( LPVOID pParameter )
  686. {
  687. return ((CMasterMulticastThread*)pParameter)->MulticastThread();
  688. }
  689. bool CMasterMulticastThread::CheckClientTimeouts()
  690. {
  691. bool bRet = false;
  692. CMulticastFile *pFile = m_Files[m_iCurFile];
  693. float flCurTime = Plat_FloatTime();
  694. int iNext;
  695. for( int iCur=pFile->m_Clients.Head(); iCur != pFile->m_Clients.InvalidIndex(); iCur=iNext )
  696. {
  697. iNext = pFile->m_Clients.Next( iCur );
  698. CClientFileInfo *pInfo = pFile->m_Clients[iCur];
  699. // If the client has already fully received this file, don't bother timing out on it.
  700. if ( pInfo->m_nChunksLeft == 0 )
  701. continue;
  702. ++pInfo->m_nTimesFileCycled;
  703. if ( pInfo->m_nTimesFileCycled >= MIN_FILE_CYCLE_COUNT && (flCurTime - pInfo->m_flLastAckTime) > CLIENT_FILE_ACK_TIMEOUT )
  704. {
  705. // For debug output, get the most recent time we heard any ack from this client at all.
  706. float flMostRecentTime = pInfo->m_flLastAckTime;
  707. FOR_EACH_LL( m_Files, iTestFile )
  708. {
  709. CMulticastFile *pTestFile = m_Files[iTestFile];
  710. FOR_EACH_LL( pTestFile->m_Clients, iTestClient )
  711. {
  712. if ( pTestFile->m_Clients[iTestClient]->m_ClientID == pInfo->m_ClientID )
  713. {
  714. flMostRecentTime = max( flMostRecentTime, pTestFile->m_Clients[iTestClient]->m_flLastAckTime );
  715. }
  716. }
  717. }
  718. Warning( "\nClient %s timed out on file %s (latest: %.2f / cur: %.2f).\n",
  719. VMPI_GetMachineName( pInfo->m_ClientID ), pFile->GetFilename(), flMostRecentTime, flCurTime );
  720. OnClientDisconnect( pInfo->m_ClientID, false );
  721. bRet = true; // yes, we booted a client.
  722. }
  723. }
  724. return bRet;
  725. }
  726. inline bool CMasterMulticastThread::Thread_SendFileChunk_Multicast( int *pnBytesSent )
  727. {
  728. // Send the next chunk (file, size, time, chunk data).
  729. CMulticastFile *pFile = m_Files[m_iCurFile];
  730. int iStartByte = m_iCurActiveChunk * MULTICAST_CHUNK_PAYLOAD_SIZE;
  731. int iEndByte = min( iStartByte + MULTICAST_CHUNK_PAYLOAD_SIZE, pFile->m_Data.Count() );
  732. WSABUF bufs[4];
  733. bufs[0].buf = (char*)&pFile->m_Info;
  734. bufs[0].len = sizeof( pFile->m_Info );
  735. bufs[1].buf = (char*)&m_iCurActiveChunk;
  736. bufs[1].len = sizeof( m_iCurActiveChunk );
  737. bufs[2].buf = (char*)pFile->GetFilename();
  738. bufs[2].len = strlen( pFile->GetFilename() ) + 1;
  739. bufs[3].buf = &pFile->m_Data[iStartByte];
  740. bufs[3].len = iEndByte - iStartByte;
  741. DWORD nBytesSent = 0;
  742. DWORD nWantedBytes = ( bufs[0].len + bufs[1].len + bufs[2].len + bufs[3].len );
  743. bool bSuccess;
  744. if ( m_MulticastAddr.sin_addr.S_un.S_un_b.s_b1 == 127 &&
  745. m_MulticastAddr.sin_addr.S_un.S_un_b.s_b2 == 0 &&
  746. m_MulticastAddr.sin_addr.S_un.S_un_b.s_b3 == 0 &&
  747. m_MulticastAddr.sin_addr.S_un.S_un_b.s_b4 == 1 )
  748. {
  749. // For some mysterious reason, WSASendTo only sends the first buffer
  750. // if we're sending to 127.0.0.1 (ie: in local mode).
  751. char allData[1024*8];
  752. if ( nWantedBytes > sizeof( allData ) )
  753. Error( "nWantedBytes > sizeof( allData )" );
  754. memcpy( &allData[0], bufs[0].buf, bufs[0].len );
  755. memcpy( &allData[bufs[0].len], bufs[1].buf, bufs[1].len );
  756. memcpy( &allData[bufs[0].len+bufs[1].len], bufs[2].buf, bufs[2].len );
  757. memcpy( &allData[bufs[0].len+bufs[1].len+bufs[2].len], bufs[3].buf, bufs[3].len );
  758. int ret = sendto( m_Socket, allData, nWantedBytes, 0, (sockaddr*)&m_MulticastAddr, sizeof( m_MulticastAddr ) );
  759. bSuccess = (ret == (int)nWantedBytes);
  760. }
  761. else
  762. {
  763. WSASendTo(
  764. m_Socket,
  765. bufs,
  766. ARRAYSIZE( bufs ),
  767. &nBytesSent,
  768. 0,
  769. (sockaddr*)&m_MulticastAddr,
  770. sizeof( m_MulticastAddr ),
  771. NULL,
  772. NULL );
  773. bSuccess = (nBytesSent == nWantedBytes);
  774. }
  775. // Handle errors.. let it get a few errors, then quit.
  776. if ( bSuccess )
  777. {
  778. *pnBytesSent = (int)nBytesSent;
  779. }
  780. else
  781. {
  782. static int nWarnings = 0;
  783. ++nWarnings;
  784. if ( nWarnings < 10 )
  785. {
  786. Warning( "\nMulticastThread: WSASendTo with %d bytes sent %d bytes.\n", nWantedBytes, nBytesSent );
  787. char *lpMsgBuf;
  788. if ( FormatMessage(
  789. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  790. FORMAT_MESSAGE_FROM_SYSTEM |
  791. FORMAT_MESSAGE_IGNORE_INSERTS,
  792. NULL,
  793. GetLastError(),
  794. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  795. (char*)&lpMsgBuf,
  796. 0,
  797. NULL
  798. ) )
  799. {
  800. Warning( "%s", lpMsgBuf );
  801. LocalFree( lpMsgBuf );
  802. }
  803. }
  804. else if ( nWarnings == 10 )
  805. {
  806. Warning( "\nThis machine's ability to multicast may be broken. Please reboot and try again.\n" );
  807. }
  808. }
  809. return bSuccess;
  810. }
  811. void CMasterMulticastThread::Thread_SeekToNextActiveChunk()
  812. {
  813. // Make sure we're on a valid chunk.
  814. if ( m_iCurFile == -1 )
  815. {
  816. Assert( m_Files.Count() > 0 );
  817. m_iCurFile = m_Files.Head();
  818. m_iCurActiveChunk = m_Files[m_iCurFile]->m_ActiveChunks.Head();
  819. }
  820. while ( 1 )
  821. {
  822. if ( m_iCurActiveChunk == m_Files[m_iCurFile]->m_ActiveChunks.InvalidIndex() ||
  823. m_Files[m_iCurFile]->m_ActiveChunks[m_iCurActiveChunk]->m_RefCount == 0 )
  824. {
  825. // Now check for client timeouts.
  826. // (This is kicking clients unjustly for some reason.. need to debug).
  827. if ( CheckClientTimeouts() && m_nTotalActiveChunks == 0 )
  828. break;
  829. // Finished with that file. Send the next one.
  830. m_iCurFile = m_Files.Next( m_iCurFile );
  831. if ( m_iCurFile == m_Files.InvalidIndex() )
  832. m_iCurFile = m_Files.Head();
  833. m_iCurActiveChunk = m_Files[m_iCurFile]->m_ActiveChunks.Head();
  834. }
  835. if ( m_iCurActiveChunk != m_Files[m_iCurFile]->m_ActiveChunks.InvalidIndex() )
  836. {
  837. // Only break if we're on an active chunk.
  838. if ( m_Files[m_iCurFile]->m_ActiveChunks[m_iCurActiveChunk]->m_RefCount != 0 )
  839. {
  840. break;
  841. }
  842. m_iCurActiveChunk = m_Files[m_iCurFile]->m_ActiveChunks.Next( m_iCurActiveChunk );
  843. }
  844. }
  845. }
  846. DWORD CMasterMulticastThread::MulticastThread()
  847. {
  848. CTransmitRateMgr transmitRateMgr;
  849. CRateLimiter rateLimiter;
  850. DWORD msToWait = 0; // Only temporarily used if we don't have any data to send.
  851. while ( WaitForSingleObject( m_hTermEvent, msToWait ) != WAIT_OBJECT_0 )
  852. {
  853. rateLimiter.GiveUpTimeSlice();
  854. msToWait = 0;
  855. EnterCriticalSection( &m_CS );
  856. transmitRateMgr.ReadPackets();
  857. // If we have nothing to send then kick back for a while.
  858. if ( m_nTotalActiveChunks == 0 )
  859. {
  860. LeaveCriticalSection( &m_CS );
  861. msToWait = 50;
  862. continue;
  863. }
  864. // Ok, now we're active, so send out our presence to other CTransmitRateMgrs on the network.
  865. transmitRateMgr.BroadcastPresence();
  866. // We're going to time how long this chunk took to send.
  867. CFastTimer timer;
  868. timer.Start();
  869. Thread_SeekToNextActiveChunk();
  870. // We have to do this check a second time here because the CheckClientTimeouts() call may have
  871. // booted our last client. If we don't check it here, we might be transmitting
  872. // something we don't want to transmit. Also, if we don't break out of the loop above,
  873. // it can prevent the process from ever exiting because it'll never exit that while() loop.
  874. if ( m_nTotalActiveChunks == 0 )
  875. {
  876. LeaveCriticalSection( &m_CS );
  877. msToWait = 50;
  878. continue;
  879. }
  880. int nBytesSent = 0;
  881. bool bSuccess;
  882. bSuccess = Thread_SendFileChunk_Multicast( &nBytesSent );
  883. g_nMulticastBytesSent += (int)nBytesSent;
  884. // Move to the next chunk.
  885. m_iCurActiveChunk = m_Files[m_iCurFile]->m_ActiveChunks.Next( m_iCurActiveChunk );
  886. LeaveCriticalSection( &m_CS );
  887. // Measure how long it took to send this.
  888. timer.End();
  889. unsigned long timeTaken = timer.GetDuration().GetMicroseconds();
  890. // Measure how long it should have taken.
  891. unsigned long estimatedPacketHeaderSize = 32;
  892. unsigned long optimalTimeTaken = (unsigned long)( transmitRateMgr.GetMicrosecondsPerByte() * (nBytesSent + estimatedPacketHeaderSize) );
  893. // If we went faster than we should have, then wait for the difference in time.
  894. if ( timeTaken < optimalTimeTaken )
  895. {
  896. rateLimiter.NoteExcessTimeTaken( optimalTimeTaken - timeTaken );
  897. }
  898. }
  899. return 0;
  900. }
  901. void CMasterMulticastThread::IncrementChunkRefCount( CMasterMulticastThread::CMulticastFile *pFile, int iChunk )
  902. {
  903. CChunkInfo *pChunk = &pFile->m_Chunks[iChunk];
  904. if ( pChunk->m_RefCount == 0 )
  905. {
  906. ++m_nTotalActiveChunks;
  907. // Move the chunk to the head of the list since it is now active.
  908. pFile->m_ActiveChunks.Remove( pChunk->m_iActiveChunksIndex );
  909. pChunk->m_iActiveChunksIndex = pFile->m_ActiveChunks.AddToHead( pChunk );
  910. }
  911. pChunk->m_RefCount++;
  912. }
  913. void CMasterMulticastThread::DecrementChunkRefCount( int iFile, int iChunk )
  914. {
  915. CMulticastFile *pFile = m_Files[iFile];
  916. CChunkInfo *pChunk = &pFile->m_Chunks[iChunk];
  917. if ( pChunk->m_RefCount == 0 )
  918. {
  919. Error( "CMasterMulticastThread::DecrementChunkRefCount - refcount already zero!\n" );
  920. }
  921. pChunk->m_RefCount--;
  922. if ( pChunk->m_RefCount == 0 )
  923. {
  924. --m_nTotalActiveChunks;
  925. // If this is the current chunk the thread is reading on, seek up to the next chunk so
  926. // the thread doesn't spin off into the next file and skip its current file's contents.
  927. if ( iFile == m_iCurFile && pChunk->m_iActiveChunksIndex == m_iCurActiveChunk )
  928. {
  929. m_iCurActiveChunk = pFile->m_ActiveChunks.Next( pChunk->m_iActiveChunksIndex );
  930. }
  931. // Move the chunk to the end of the list since it is now inactive.
  932. pFile->m_ActiveChunks.Remove( pChunk->m_iActiveChunksIndex );
  933. pChunk->m_iActiveChunksIndex = pFile->m_ActiveChunks.AddToTail( pChunk );
  934. }
  935. }
  936. int CMasterMulticastThread::FindFile( const char *pName, const char *pPathID )
  937. {
  938. FOR_EACH_LL( m_Files, i )
  939. {
  940. CMulticastFile *pFile = m_Files[i];
  941. if ( stricmp( pFile->GetFilename(), pName ) == 0 && stricmp( pFile->GetPathID(), pPathID ) == 0 )
  942. return i;
  943. }
  944. return -1;
  945. }
  946. bool CMasterMulticastThread::FindWarningSuppression( const char *pFilename )
  947. {
  948. FOR_EACH_LL( m_WarningSuppressions, i )
  949. {
  950. if ( Q_stricmp( m_WarningSuppressions[i], pFilename ) == 0 )
  951. return true;
  952. }
  953. return false;
  954. }
  955. void CMasterMulticastThread::AddWarningSuppression( const char *pFilename )
  956. {
  957. char *pBlah = new char[ strlen( pFilename ) + 1 ];
  958. strcpy( pBlah, pFilename );
  959. m_WarningSuppressions.AddToTail( pBlah );
  960. }
  961. int CMasterMulticastThread::FindOrAddFile( const char *pFilename, const char *pPathID )
  962. {
  963. CMulticastFile *pFile = NULL;
  964. bool bFileAlreadyExisted = false;
  965. // See if we've already opened this file.
  966. int iFile = FindFile( pFilename, pPathID );
  967. if ( iFile != -1 )
  968. {
  969. pFile = m_Files[iFile];
  970. if ( pFile->m_bDataLoaded )
  971. {
  972. return iFile;
  973. }
  974. else
  975. {
  976. // Ok, we have the file entry, but its data has been freed, so we need to reload it.
  977. EnterCriticalSection( &m_CS );
  978. bFileAlreadyExisted = true;
  979. }
  980. }
  981. // Can't open a file outside our main thread, because we have to talk to the filesystem
  982. // and the filesystem doesn't support that.
  983. Assert( GetCurrentThread() == m_hMainThread );
  984. // When the worker originally asked for the path ID, they could pass NULL and it would come through as "".
  985. // Now set it back to null for the filesystem we're passing the call to.
  986. FileHandle_t fp = m_pPassThru->Open( pFilename, "rb", pPathID[0] == 0 ? NULL : pPathID );
  987. if ( !fp )
  988. {
  989. if ( bFileAlreadyExisted )
  990. LeaveCriticalSection( &m_CS );
  991. return -1;
  992. }
  993. if ( !bFileAlreadyExisted )
  994. pFile = new CMulticastFile;
  995. pFile->m_UncompressedData.SetSize( m_pPassThru->Size( fp ) );
  996. m_pPassThru->Read( pFile->m_UncompressedData.Base(), pFile->m_UncompressedData.Count(), fp );
  997. m_pPassThru->Close( fp );
  998. int iRet = FinishFileSetup( pFile, pFilename, pPathID, bFileAlreadyExisted );
  999. if ( bFileAlreadyExisted )
  1000. LeaveCriticalSection( &m_CS );
  1001. return iRet;
  1002. }
  1003. int CMasterMulticastThread::FinishFileSetup( CMulticastFile *pFile, const char *pFilename, const char *pPathID, bool bFileAlreadyExisted )
  1004. {
  1005. // Compress the file's contents.
  1006. if ( !ZLibCompress( pFile->m_UncompressedData.Base(), pFile->m_UncompressedData.Count(), pFile->m_Data ) )
  1007. {
  1008. delete pFile;
  1009. return -1;
  1010. }
  1011. pFile->m_bDataLoaded = true;
  1012. int chunkSize = VMPI_GetChunkPayloadSize();
  1013. // Get this file in the queue.
  1014. if ( !bFileAlreadyExisted )
  1015. {
  1016. pFile->m_Filename.SetSize( strlen( pFilename ) + 1 );
  1017. strcpy( pFile->m_Filename.Base(), pFilename );
  1018. pFile->m_PathID.SetSize( strlen( pPathID ) + 1 );
  1019. strcpy( pFile->m_PathID.Base(), pPathID );
  1020. pFile->m_nCycles = 0;
  1021. pFile->m_Info.m_CompressedSize = pFile->m_Data.Count();
  1022. pFile->m_Info.m_UncompressedSize = pFile->m_UncompressedData.Count();
  1023. pFile->m_Info.m_nChunks = PAD_NUMBER( pFile->m_Info.m_CompressedSize, chunkSize ) / chunkSize;
  1024. // Initialize the chunks.
  1025. pFile->m_Chunks.SetSize( pFile->m_Info.m_nChunks );
  1026. for ( int i=0; i < pFile->m_Chunks.Count(); i++ )
  1027. {
  1028. CChunkInfo *pChunk = &pFile->m_Chunks[i];
  1029. pChunk->m_iChunk = (unsigned short)i;
  1030. pChunk->m_RefCount = 0;
  1031. pChunk->m_iActiveChunksIndex = pFile->m_ActiveChunks.AddToTail( pChunk );
  1032. }
  1033. EnterCriticalSection( &m_CS );
  1034. }
  1035. // Boot some other file out of memory if we're out of space.
  1036. m_nCurMemoryUsage += ( pFile->m_Info.m_CompressedSize + pFile->m_Info.m_UncompressedSize );
  1037. EnsureMemoryLimit( pFile );
  1038. if ( !bFileAlreadyExisted )
  1039. {
  1040. pFile->m_Info.m_FileID = m_Files.AddToTail( pFile );
  1041. LeaveCriticalSection( &m_CS );
  1042. }
  1043. return pFile->m_Info.m_FileID;
  1044. }
  1045. void CMasterMulticastThread::EnsureMemoryLimit( CMulticastFile *pIgnore )
  1046. {
  1047. if ( m_nMaxMemoryUsage != 0 && m_nCurMemoryUsage > m_nMaxMemoryUsage )
  1048. {
  1049. // Free all the files that we can.
  1050. FOR_EACH_LL( m_Files, iFile )
  1051. {
  1052. CMulticastFile *pFile = m_Files[iFile];
  1053. if ( pFile == pIgnore || !pFile->m_bDataLoaded )
  1054. continue;
  1055. if ( pFile->m_ActiveChunks.Count() == 0 )
  1056. {
  1057. m_nCurMemoryUsage -= ( pFile->m_Info.m_CompressedSize + pFile->m_Info.m_UncompressedSize );
  1058. pFile->m_Data.Purge();
  1059. pFile->m_UncompressedData.Purge();
  1060. pFile->m_bDataLoaded = false;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. const CUtlVector<char>& CMasterMulticastThread::GetFileData( int iFile ) const
  1066. {
  1067. return m_Files[iFile]->m_UncompressedData;
  1068. }
  1069. // ------------------------------------------------------------------------------------------------------------------------ //
  1070. // CMasterVMPIFileSystem implementation.
  1071. // ------------------------------------------------------------------------------------------------------------------------ //
  1072. class CMasterVMPIFileSystem : public CBaseVMPIFileSystem
  1073. {
  1074. public:
  1075. CMasterVMPIFileSystem();
  1076. virtual ~CMasterVMPIFileSystem();
  1077. bool Init( int maxMemoryUsage, IFileSystem *pPassThru );
  1078. virtual void Term();
  1079. virtual FileHandle_t Open( const char *pFilename, const char *pOptions, const char *pathID );
  1080. virtual bool HandleFileSystemPacket( MessageBuffer *pBuf, int iSource, int iPacketID );
  1081. virtual void CreateVirtualFile( const char *pFilename, const void *pData, int fileLength );
  1082. virtual CSysModule *LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly );
  1083. virtual void UnloadModule( CSysModule *pModule );
  1084. private:
  1085. static void OnClientDisconnect( int procID, const char *pReason );
  1086. private:
  1087. CMasterMulticastThread m_MasterThread;
  1088. IFileSystem *m_pMasterVMPIFileSystemPassThru;
  1089. static CMasterVMPIFileSystem *s_pMasterVMPIFileSystem;
  1090. };
  1091. CMasterVMPIFileSystem *CMasterVMPIFileSystem::s_pMasterVMPIFileSystem = NULL;
  1092. CBaseVMPIFileSystem* CreateMasterVMPIFileSystem( int maxMemoryUsage, IFileSystem *pPassThru )
  1093. {
  1094. CMasterVMPIFileSystem *pRet = new CMasterVMPIFileSystem;
  1095. g_pBaseVMPIFileSystem = pRet;
  1096. if ( pRet->Init( maxMemoryUsage, pPassThru ) )
  1097. {
  1098. return pRet;
  1099. }
  1100. else
  1101. {
  1102. delete pRet;
  1103. g_pBaseVMPIFileSystem = NULL;
  1104. return NULL;
  1105. }
  1106. }
  1107. CMasterVMPIFileSystem::CMasterVMPIFileSystem()
  1108. {
  1109. Assert( !s_pMasterVMPIFileSystem );
  1110. s_pMasterVMPIFileSystem = this;
  1111. }
  1112. CMasterVMPIFileSystem::~CMasterVMPIFileSystem()
  1113. {
  1114. Assert( s_pMasterVMPIFileSystem == this );
  1115. s_pMasterVMPIFileSystem = NULL;
  1116. }
  1117. bool CMasterVMPIFileSystem::Init( int maxMemoryUsage, IFileSystem *pPassThru )
  1118. {
  1119. // Only init the BASE filesystem passthru. Leave the IFileSystem passthru using NULL so it'll crash
  1120. // immediately if they try to use a function we don't support.
  1121. InitPassThru( pPassThru, true );
  1122. m_pMasterVMPIFileSystemPassThru = pPassThru;
  1123. // Pick a random IP in the multicast range (224.0.0.2 to 239.255.255.255);
  1124. CCycleCount cnt;
  1125. cnt.Sample();
  1126. RandomSeed( (int)cnt.GetMicroseconds() );
  1127. int localPort = 23412; // This can be anything.
  1128. unsigned short port = RandomInt( 22000, 25000 );
  1129. if ( VMPI_GetRunMode() == VMPI_RUN_NETWORKED )
  1130. {
  1131. if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_MULTICAST )
  1132. {
  1133. m_MulticastIP.port = port;
  1134. m_MulticastIP.ip[0] = (unsigned char)RandomInt( 225, 238 );
  1135. m_MulticastIP.ip[1] = (unsigned char)RandomInt( 0, 255 );
  1136. m_MulticastIP.ip[2] = (unsigned char)RandomInt( 0, 255 );
  1137. m_MulticastIP.ip[3] = (unsigned char)RandomInt( 3, 255 );
  1138. }
  1139. else if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_BROADCAST )
  1140. {
  1141. m_MulticastIP.Init( 0xFF, 0xFF, 0xFF, 0xFF, port );
  1142. }
  1143. }
  1144. else
  1145. {
  1146. // Doesn't matter.. we don't use the multicast IP in TCP mode.
  1147. m_MulticastIP.Init( 0, 0, 0, 0, 0 );
  1148. }
  1149. if ( !m_MasterThread.Init( pPassThru, localPort, &m_MulticastIP, maxMemoryUsage ) )
  1150. return false;
  1151. // Send out the multicast addr to all the clients.
  1152. SendMulticastIP( &m_MulticastIP );
  1153. // Make sure we're notified when a client disconnects so we can unlink them from the
  1154. // multicast thread's structures.
  1155. VMPI_AddDisconnectHandler( &CMasterVMPIFileSystem::OnClientDisconnect );
  1156. return true;
  1157. }
  1158. void CMasterVMPIFileSystem::Term()
  1159. {
  1160. m_MasterThread.Term();
  1161. }
  1162. FileHandle_t CMasterVMPIFileSystem::Open( const char *pFilename, const char *pOptions, const char *pPathID )
  1163. {
  1164. Assert( g_bUseMPI );
  1165. if ( g_bDisableFileAccess )
  1166. Error( "Open( %s, %s ) - file access has been disabled.", pFilename, pOptions );
  1167. // Use a stdio file if they want to write to it.
  1168. bool bWriteAccess = IsOpeningForWriteAccess( pOptions );
  1169. if ( bWriteAccess )
  1170. {
  1171. FileHandle_t fp = m_pBaseFileSystemPassThru->Open( pFilename, pOptions, pPathID );
  1172. if ( fp == FILESYSTEM_INVALID_HANDLE )
  1173. return FILESYSTEM_INVALID_HANDLE;
  1174. CVMPIFile_PassThru *pFile = new CVMPIFile_PassThru;
  1175. pFile->Init( m_pBaseFileSystemPassThru, fp );
  1176. return (FileHandle_t)pFile;
  1177. }
  1178. // Internally, we require path IDs to be non-null. We'll convert it back to null whenever we make filesystem calls though.
  1179. if ( !pPathID )
  1180. pPathID = "";
  1181. // Have our multicast thread load all the data so it's there when workers want it.
  1182. int iFile = m_MasterThread.FindOrAddFile( pFilename, pPathID );
  1183. if ( iFile == -1 )
  1184. return FILESYSTEM_INVALID_HANDLE;
  1185. const CUtlVector<char> &data = m_MasterThread.GetFileData( iFile );
  1186. CVMPIFile_Memory *pFile = new CVMPIFile_Memory;
  1187. pFile->Init( data.Base(), data.Count(), strchr( pOptions, 't' ) ? 't' : 'b' );
  1188. return (FileHandle_t)pFile;
  1189. }
  1190. void CMasterVMPIFileSystem::OnClientDisconnect( int procID, const char *pReason )
  1191. {
  1192. s_pMasterVMPIFileSystem->m_MasterThread.OnClientDisconnect( procID );
  1193. }
  1194. void CMasterVMPIFileSystem::CreateVirtualFile( const char *pFilename, const void *pData, int fileLength )
  1195. {
  1196. m_MasterThread.CreateVirtualFile( pFilename, pData, fileLength );
  1197. }
  1198. bool CMasterVMPIFileSystem::HandleFileSystemPacket( MessageBuffer *pBuf, int iSource, int iPacketID )
  1199. {
  1200. // Handle this packet.
  1201. int subPacketID = pBuf->data[1];
  1202. switch( subPacketID )
  1203. {
  1204. case VMPI_FSPACKETID_FILE_REQUEST:
  1205. {
  1206. int requestID = *((int*)&pBuf->data[2]);
  1207. const char *pFilename = (const char*)&pBuf->data[6];
  1208. const char *pPathID = (const char*)pFilename + strlen( pFilename ) + 1;
  1209. if ( g_iVMPIVerboseLevel >= 2 )
  1210. Msg( "Client %d requested '%s'\n", iSource, pFilename );
  1211. bool bZeroLength;
  1212. int fileID = m_MasterThread.AddFileRequest( pFilename, pPathID, iSource, &bZeroLength );
  1213. // Send back the file ID.
  1214. unsigned char cPacket[2] = { VMPI_PACKETID_FILESYSTEM, VMPI_FSPACKETID_FILE_RESPONSE };
  1215. void *pChunks[4] = { cPacket, &requestID, &fileID, &bZeroLength };
  1216. int chunkLen[4] = { sizeof( cPacket ), sizeof( requestID ), sizeof( fileID ), sizeof( bZeroLength ) };
  1217. VMPI_SendChunks( pChunks, chunkLen, ARRAYSIZE( pChunks ), iSource );
  1218. }
  1219. return true;
  1220. case VMPI_FSPACKETID_CHUNK_RECEIVED:
  1221. {
  1222. unsigned short *pFileID = (unsigned short*)&pBuf->data[2];
  1223. unsigned short *pChunkID = pFileID+1;
  1224. int nChunks = (pBuf->getLen() - 2) / 4;
  1225. for ( int i=0; i < nChunks; i++ )
  1226. {
  1227. m_MasterThread.OnChunkReceived( *pFileID, iSource, *pChunkID );
  1228. pFileID += 2;
  1229. pChunkID += 2;
  1230. }
  1231. }
  1232. return true;
  1233. case VMPI_FSPACKETID_FILE_RECEIVED:
  1234. {
  1235. unsigned short *pFileID = (unsigned short*)&pBuf->data[2];
  1236. m_MasterThread.OnFileReceived( *pFileID, iSource );
  1237. }
  1238. return true;
  1239. default:
  1240. return false;
  1241. }
  1242. }
  1243. CSysModule* CMasterVMPIFileSystem::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
  1244. {
  1245. return m_pMasterVMPIFileSystemPassThru->LoadModule( pFileName, pPathID, bValidatedDllOnly );
  1246. }
  1247. void CMasterVMPIFileSystem::UnloadModule( CSysModule *pModule )
  1248. {
  1249. m_pMasterVMPIFileSystemPassThru->UnloadModule( pModule );
  1250. }