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.

349 lines
8.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "quakedef.h"
  7. #include "sv_client.h"
  8. #include "logofile_shared.h"
  9. #include "server.h"
  10. #include "filetransfermgr.h"
  11. #include "filesystem_engine.h"
  12. ConVar sv_logo_rate( "sv_logo_rate", "1024", 0, "How fast (bytes per second) the server sends logo files to clients." );
  13. class CPendingFile
  14. {
  15. public:
  16. CRC32_t m_nLogoFileCRC;
  17. bool m_bWaitingForServerToGetFile;
  18. };
  19. class CPerClientLogoInfo
  20. {
  21. public:
  22. CPerClientLogoInfo()
  23. {
  24. m_bLogoFileCRCValid = false;
  25. m_bSendFileInProgress = false;
  26. }
  27. // This client's logo info.
  28. bool m_bLogoFileCRCValid;
  29. int m_nLogoFileCRC;
  30. // Are we sending this client a file right now?
  31. bool m_bSendFileInProgress;
  32. // Files that this client has requested but we aren't able to send yet.
  33. CUtlVector<CPendingFile> m_PendingFiles;
  34. };
  35. class CServerFileTransferMgr : public CFileTransferMgr
  36. {
  37. public:
  38. virtual bool SendChunk( INetChannel *pDest, const void *pData, int len )
  39. {
  40. SVC_LogoFileData fileData;
  41. fileData.m_Data.CopyArray( (const char*)pData, len );
  42. return pDest->SendNetMsg( fileData, true );
  43. }
  44. virtual void OnSendCancelled( FileTransferID_t id )
  45. {
  46. }
  47. CGameClient* GetClientByNetChannel( INetChannel *pChan )
  48. {
  49. for ( int i=0; i < sv.clients.Count(); i++ )
  50. {
  51. CGameClient *pClient = sv.Client( i );
  52. if ( pClient && pClient->GetNetChannel() == pChan )
  53. return pClient;
  54. }
  55. return NULL;
  56. }
  57. virtual void OnFinishedSending(
  58. INetChannel *pDest,
  59. const void *pUserData,
  60. int userDataLen,
  61. FileTransferID_t id )
  62. {
  63. // Start sending the next file to this guy.
  64. CGameClient *pClient = GetClientByNetChannel( pDest );
  65. if ( pClient )
  66. {
  67. pClient->m_pLogoInfo->m_bSendFileInProgress = false;
  68. UpdatePendingFiles();
  69. }
  70. else
  71. {
  72. Warning( "OnFinishedSending: can't get CGameClient from INetChannel.\n" );
  73. }
  74. }
  75. virtual void OnFileReceived(
  76. INetChannel *pChan,
  77. const void *pUserData,
  78. int userDataLength,
  79. const char *pFileData,
  80. int fileLength )
  81. {
  82. // Ok, now the server has received a file the client sent. First, validate the VTF.
  83. if ( !LogoFile_IsValidVTFFile( pFileData, fileLength ) )
  84. {
  85. Warning( "CServerFileTransferMgr::OnFileReceived: received an invalid logo file from a client.\n" );
  86. return;
  87. }
  88. if ( userDataLength < sizeof( CRC32_t ) )
  89. {
  90. Warning( "CServerFileTransferMgr::OnFileReceived: invalid userDataLength (%d).\n", userDataLength );
  91. }
  92. CRC32_t crcValue = *((CRC32_t*)pUserData);
  93. // Save this file in our cache.
  94. if ( SaveCRCFileToCache( crcValue, pFileData, fileLength ) )
  95. {
  96. // Start transfers to any clients that we can now.
  97. MarkPendingFilesWithCRC( crcValue );
  98. UpdatePendingFiles();
  99. }
  100. }
  101. // If any clients are waiting on this file, mark them so they know they can be sent the file now.
  102. void MarkPendingFilesWithCRC( CRC32_t crcValue )
  103. {
  104. for ( int i=0; i < sv.clients.Count(); i++ )
  105. {
  106. CGameClient *pClient = sv.Client( i );
  107. if ( !pClient || !pClient->m_pLogoInfo )
  108. continue;
  109. for ( int i=0; i < pClient->m_pLogoInfo->m_PendingFiles.Count(); i++ )
  110. {
  111. CPendingFile *pFile = &pClient->m_pLogoInfo->m_PendingFiles[i];
  112. if ( pFile->m_nLogoFileCRC == crcValue )
  113. pFile->m_bWaitingForServerToGetFile = false;
  114. }
  115. }
  116. }
  117. bool SaveCRCFileToCache( CRC32_t crcValue, const void *pFileData, int fileLength )
  118. {
  119. CLogoFilename logohex( crcValue, true );
  120. FileHandle_t hFile = g_pFileSystem->Open( logohex.m_Filename, "wb" );
  121. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  122. {
  123. Warning( "SaveCRCFileToCache: couldn't open '%s' for writing.\n", logohex.m_Filename );
  124. return false;
  125. }
  126. else
  127. {
  128. int writeRet = g_pFileSystem->Write( pFileData, fileLength, hFile );
  129. g_pFileSystem->Close( hFile );
  130. // If we couldn't write it, then delete it.
  131. if ( writeRet == fileLength )
  132. {
  133. return true;
  134. }
  135. else
  136. {
  137. Warning( "SaveCRCFileToCache: couldn't write data (%d should be %d).\n", writeRet, fileLength );
  138. return false;
  139. }
  140. }
  141. }
  142. void UpdatePendingFiles()
  143. {
  144. CUtlVector<char> fileData;
  145. CRC32_t lastCRC = 0;
  146. // Find clients who want to receive this file.
  147. for ( int i=0; i < sv.clients.Count(); i++ )
  148. {
  149. CGameClient *pClient = sv.Client( i );
  150. if ( !pClient || !pClient->m_pLogoInfo )
  151. continue;
  152. // Are we already sending the client a file?
  153. if ( pClient->m_pLogoInfo->m_bSendFileInProgress )
  154. continue;
  155. for ( int iFile=0; iFile < pClient->m_pLogoInfo->m_PendingFiles.Count(); iFile++ )
  156. {
  157. CPendingFile *pFile = &pClient->m_pLogoInfo->m_PendingFiles[iFile];
  158. // If we still have to wait for the server to get this file, then stop.
  159. if ( pFile->m_bWaitingForServerToGetFile )
  160. continue;
  161. pClient->m_pLogoInfo->m_PendingFiles.Remove( iFile );
  162. // Load the file, if we haven't already.
  163. if ( fileData.Count() == 0 || lastCRC != pFile->m_nLogoFileCRC )
  164. {
  165. // Remember the last CRC so we don't have to reopen the file if
  166. // this one is going to a bunch of clients in a row.
  167. lastCRC = pFile->m_nLogoFileCRC;
  168. if ( !LogoFile_ReadFile( pFile->m_nLogoFileCRC, fileData ) )
  169. break;
  170. }
  171. StartSending(
  172. pClient->GetNetChannel(),
  173. &lastCRC,
  174. sizeof( lastCRC ),
  175. fileData.Base(),
  176. fileData.Count(),
  177. sv_logo_rate.GetInt()
  178. );
  179. pClient->m_pLogoInfo->m_bSendFileInProgress = true;
  180. break;
  181. }
  182. }
  183. }
  184. };
  185. CServerFileTransferMgr g_ServerFileTransferMgr;
  186. bool SV_LogoFile_HasLogoFile( CRC32_t crcValue )
  187. {
  188. CLogoFilename logohex( crcValue, true );
  189. return g_pFileSystem->FileExists( logohex.m_Filename );
  190. }
  191. PROCESS_MSG_SERVER( CLC_LogoFileData )
  192. {
  193. g_ServerFileTransferMgr.HandleReceivedData( m_Client->GetNetChannel(), m_Data.Base(), m_Data.Count() );
  194. return true;
  195. } };
  196. PROCESS_MSG_SERVER( CLC_LogoFileRequest )
  197. {
  198. // The client is requesting that we send it a specific logo file.
  199. int index = m_Client->m_pLogoInfo->m_PendingFiles.AddToTail();
  200. CPendingFile &file = m_Client->m_pLogoInfo->m_PendingFiles[index];
  201. file.m_nLogoFileCRC = m_nLogoFileCRC;
  202. file.m_bWaitingForServerToGetFile = SV_LogoFile_HasLogoFile( file.m_nLogoFileCRC );
  203. // Start sending it if it's time..
  204. g_ServerFileTransferMgr.UpdatePendingFiles();
  205. return true;
  206. } };
  207. CPerClientLogoInfo* SV_LogoFile_CreatePerClientLogoInfo()
  208. {
  209. CPerClientLogoInfo *pInfo = new CPerClientLogoInfo;
  210. pInfo->m_bLogoFileCRCValid = false;
  211. return pInfo;
  212. }
  213. void SV_LogoFile_DeletePerClientLogoInfo( CPerClientLogoInfo *pInfo )
  214. {
  215. delete pInfo;
  216. }
  217. void SV_LogoFile_HandleClientDisconnect( CGameClient *pClient )
  218. {
  219. g_ServerFileTransferMgr.HandleClientDisconnect( pClient->GetNetChannel() );
  220. }
  221. void SV_LogoFile_NewConnection( INetChannel *chan, CGameClient *pGameClient )
  222. {
  223. REGISTER_MSG_SERVER( CLC_LogoFileRequest );
  224. }
  225. bool SV_LogoFile_IsDownloadingLogoFile( CRC32_t crcValue )
  226. {
  227. for ( int i=g_ServerFileTransferMgr.FirstIncoming(); i != g_ServerFileTransferMgr.InvalidIncoming(); i=g_ServerFileTransferMgr.NextIncoming( i ) )
  228. {
  229. const void *pData;
  230. int dataLen;
  231. g_ServerFileTransferMgr.GetIncomingUserData( i, pData, dataLen );
  232. CRC32_t *pTestValue = (CRC32_t*)pData;
  233. if ( *pTestValue == crcValue )
  234. return true;
  235. }
  236. return false;
  237. }
  238. void SV_LogoFile_OnConnect( CGameClient *pSenderClient, bool bValid, CRC32_t crcValue )
  239. {
  240. pSenderClient->m_pLogoInfo->m_bLogoFileCRCValid = bValid;
  241. pSenderClient->m_pLogoInfo->m_nLogoFileCRC = crcValue;
  242. if ( bValid )
  243. {
  244. // Does the server need this file? If so, request it.
  245. if ( !SV_LogoFile_HasLogoFile( crcValue ) && !SV_LogoFile_IsDownloadingLogoFile( crcValue ) )
  246. {
  247. SVC_LogoFileRequest fileRequest;
  248. fileRequest.m_nLogoFileCRC = crcValue;
  249. if ( !pSenderClient->SendNetMsg( fileRequest, true ) )
  250. {
  251. Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" );
  252. return;
  253. }
  254. }
  255. // Tell all clients (except the sending client) about this logo.
  256. SVC_LogoFileCRC logoNotify;
  257. logoNotify.m_nLogoFileCRC = crcValue;
  258. for ( int i=0; i < sv.clients.Count(); i++ )
  259. {
  260. CGameClient *pClient = sv.Client( i );
  261. if ( !pClient || pClient == pSenderClient )
  262. continue;
  263. bool bReliable = true;
  264. if ( !pClient->SendNetMsg( logoNotify, bReliable ) )
  265. {
  266. Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" );
  267. return;
  268. }
  269. }
  270. }
  271. // Also, tell this client about all other client CRCs so it can aks for the one it needs.
  272. for ( int i=0; i < sv.clients.Count(); i++ )
  273. {
  274. CGameClient *pClient = sv.Client( i );
  275. if ( !pClient || pClient == pSenderClient || !pClient->m_pLogoInfo->m_bLogoFileCRCValid )
  276. continue;
  277. SVC_LogoFileCRC logoNotify;
  278. logoNotify.m_nLogoFileCRC = pClient->m_pLogoInfo->m_nLogoFileCRC;
  279. bool bReliable = true;
  280. if ( !pSenderClient->SendNetMsg( logoNotify, bReliable ) )
  281. {
  282. Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" );
  283. return;
  284. }
  285. }
  286. }