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.

352 lines
8.9 KiB

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