|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "quakedef.h"
#include "sv_client.h"
#include "logofile_shared.h"
#include "server.h"
#include "filetransfermgr.h"
#include "filesystem_engine.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
ConVar sv_logo_rate( "sv_logo_rate", "1024", 0, "How fast (bytes per second) the server sends logo files to clients." );
class CPendingFile { public: CRC32_t m_nLogoFileCRC; bool m_bWaitingForServerToGetFile; };
class CPerClientLogoInfo { public: CPerClientLogoInfo() { m_bLogoFileCRCValid = false; m_bSendFileInProgress = false; }
// This client's logo info.
bool m_bLogoFileCRCValid; int m_nLogoFileCRC;
// Are we sending this client a file right now?
bool m_bSendFileInProgress;
// Files that this client has requested but we aren't able to send yet.
CUtlVector<CPendingFile> m_PendingFiles; };
class CServerFileTransferMgr : public CFileTransferMgr { public: virtual bool SendChunk( INetChannel *pDest, const void *pData, int len ) { SVC_LogoFileData fileData; fileData.m_Data.CopyArray( (const char*)pData, len ); return pDest->SendNetMsg( fileData, true ); }
virtual void OnSendCancelled( FileTransferID_t id ) { }
CGameClient* GetClientByNetChannel( INetChannel *pChan ) { for ( int i=0; i < sv.clients.Count(); i++ ) { CGameClient *pClient = sv.Client( i ); if ( pClient && pClient->GetNetChannel() == pChan ) return pClient; } return NULL; } virtual void OnFinishedSending( INetChannel *pDest, const void *pUserData, int userDataLen, FileTransferID_t id ) { // Start sending the next file to this guy.
CGameClient *pClient = GetClientByNetChannel( pDest ); if ( pClient ) { pClient->m_pLogoInfo->m_bSendFileInProgress = false; UpdatePendingFiles(); } else { Warning( "OnFinishedSending: can't get CGameClient from INetChannel.\n" ); } }
virtual void OnFileReceived( INetChannel *pChan, const void *pUserData, int userDataLength, const char *pFileData, int fileLength ) { // Ok, now the server has received a file the client sent. First, validate the VTF.
if ( !LogoFile_IsValidVTFFile( pFileData, fileLength ) ) { Warning( "CServerFileTransferMgr::OnFileReceived: received an invalid logo file from a client.\n" ); return; }
if ( userDataLength < sizeof( CRC32_t ) ) { Warning( "CServerFileTransferMgr::OnFileReceived: invalid userDataLength (%d).\n", userDataLength ); }
CRC32_t crcValue = *((CRC32_t*)pUserData);
// Save this file in our cache.
if ( SaveCRCFileToCache( crcValue, pFileData, fileLength ) ) { // Start transfers to any clients that we can now.
MarkPendingFilesWithCRC( crcValue ); UpdatePendingFiles(); } }
// If any clients are waiting on this file, mark them so they know they can be sent the file now.
void MarkPendingFilesWithCRC( CRC32_t crcValue ) { for ( int i=0; i < sv.clients.Count(); i++ ) { CGameClient *pClient = sv.Client( i ); if ( !pClient || !pClient->m_pLogoInfo ) continue; for ( int i=0; i < pClient->m_pLogoInfo->m_PendingFiles.Count(); i++ ) { CPendingFile *pFile = &pClient->m_pLogoInfo->m_PendingFiles[i]; if ( pFile->m_nLogoFileCRC == crcValue ) pFile->m_bWaitingForServerToGetFile = false; } } }
bool SaveCRCFileToCache( CRC32_t crcValue, const void *pFileData, int fileLength ) { CLogoFilename logohex( crcValue, true ); FileHandle_t hFile = g_pFileSystem->Open( logohex.m_Filename, "wb" ); if ( hFile == FILESYSTEM_INVALID_HANDLE ) { Warning( "SaveCRCFileToCache: couldn't open '%s' for writing.\n", logohex.m_Filename ); return false; } else { int writeRet = g_pFileSystem->Write( pFileData, fileLength, hFile ); g_pFileSystem->Close( hFile );
// If we couldn't write it, then delete it.
if ( writeRet == fileLength ) { return true; } else { Warning( "SaveCRCFileToCache: couldn't write data (%d should be %d).\n", writeRet, fileLength ); return false; } } }
void UpdatePendingFiles() { CUtlVector<char> fileData; CRC32_t lastCRC = 0;
// Find clients who want to receive this file.
for ( int i=0; i < sv.clients.Count(); i++ ) { CGameClient *pClient = sv.Client( i ); if ( !pClient || !pClient->m_pLogoInfo ) continue; // Are we already sending the client a file?
if ( pClient->m_pLogoInfo->m_bSendFileInProgress ) continue;
for ( int iFile=0; iFile < pClient->m_pLogoInfo->m_PendingFiles.Count(); iFile++ ) { CPendingFile *pFile = &pClient->m_pLogoInfo->m_PendingFiles[iFile];
// If we still have to wait for the server to get this file, then stop.
if ( pFile->m_bWaitingForServerToGetFile ) continue;
pClient->m_pLogoInfo->m_PendingFiles.Remove( iFile );
// Load the file, if we haven't already.
if ( fileData.Count() == 0 || lastCRC != pFile->m_nLogoFileCRC ) { // Remember the last CRC so we don't have to reopen the file if
// this one is going to a bunch of clients in a row.
lastCRC = pFile->m_nLogoFileCRC; if ( !LogoFile_ReadFile( pFile->m_nLogoFileCRC, fileData ) ) break; }
StartSending( pClient->GetNetChannel(), &lastCRC, sizeof( lastCRC ), fileData.Base(), fileData.Count(), sv_logo_rate.GetInt() );
pClient->m_pLogoInfo->m_bSendFileInProgress = true; break; } } } }; CServerFileTransferMgr g_ServerFileTransferMgr;
bool SV_LogoFile_HasLogoFile( CRC32_t crcValue ) { CLogoFilename logohex( crcValue, true ); return g_pFileSystem->FileExists( logohex.m_Filename ); }
PROCESS_MSG_SERVER( CLC_LogoFileData ) { g_ServerFileTransferMgr.HandleReceivedData( m_Client->GetNetChannel(), m_Data.Base(), m_Data.Count() ); return true; } };
PROCESS_MSG_SERVER( CLC_LogoFileRequest ) { // The client is requesting that we send it a specific logo file.
int index = m_Client->m_pLogoInfo->m_PendingFiles.AddToTail(); CPendingFile &file = m_Client->m_pLogoInfo->m_PendingFiles[index]; file.m_nLogoFileCRC = m_nLogoFileCRC; file.m_bWaitingForServerToGetFile = SV_LogoFile_HasLogoFile( file.m_nLogoFileCRC );
// Start sending it if it's time..
g_ServerFileTransferMgr.UpdatePendingFiles(); return true; } };
CPerClientLogoInfo* SV_LogoFile_CreatePerClientLogoInfo() { CPerClientLogoInfo *pInfo = new CPerClientLogoInfo; pInfo->m_bLogoFileCRCValid = false; return pInfo; }
void SV_LogoFile_DeletePerClientLogoInfo( CPerClientLogoInfo *pInfo ) { delete pInfo; }
void SV_LogoFile_HandleClientDisconnect( CGameClient *pClient ) { g_ServerFileTransferMgr.HandleClientDisconnect( pClient->GetNetChannel() ); }
void SV_LogoFile_NewConnection( INetChannel *chan, CGameClient *pGameClient ) { REGISTER_MSG_SERVER( CLC_LogoFileRequest ); }
bool SV_LogoFile_IsDownloadingLogoFile( CRC32_t crcValue ) { for ( int i=g_ServerFileTransferMgr.FirstIncoming(); i != g_ServerFileTransferMgr.InvalidIncoming(); i=g_ServerFileTransferMgr.NextIncoming( i ) ) { const void *pData; int dataLen; g_ServerFileTransferMgr.GetIncomingUserData( i, pData, dataLen );
CRC32_t *pTestValue = (CRC32_t*)pData; if ( *pTestValue == crcValue ) return true; } return false; }
void SV_LogoFile_OnConnect( CGameClient *pSenderClient, bool bValid, CRC32_t crcValue ) { pSenderClient->m_pLogoInfo->m_bLogoFileCRCValid = bValid; pSenderClient->m_pLogoInfo->m_nLogoFileCRC = crcValue;
if ( bValid ) { // Does the server need this file? If so, request it.
if ( !SV_LogoFile_HasLogoFile( crcValue ) && !SV_LogoFile_IsDownloadingLogoFile( crcValue ) ) { SVC_LogoFileRequest fileRequest; fileRequest.m_nLogoFileCRC = crcValue; if ( !pSenderClient->SendNetMsg( fileRequest, true ) ) { Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" ); return; } }
// Tell all clients (except the sending client) about this logo.
SVC_LogoFileCRC logoNotify; logoNotify.m_nLogoFileCRC = crcValue;
for ( int i=0; i < sv.clients.Count(); i++ ) { CGameClient *pClient = sv.Client( i ); if ( !pClient || pClient == pSenderClient ) continue; bool bReliable = true; if ( !pClient->SendNetMsg( logoNotify, bReliable ) ) { Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" ); return; } } }
// Also, tell this client about all other client CRCs so it can aks for the one it needs.
for ( int i=0; i < sv.clients.Count(); i++ ) { CGameClient *pClient = sv.Client( i ); if ( !pClient || pClient == pSenderClient || !pClient->m_pLogoInfo->m_bLogoFileCRCValid ) continue;
SVC_LogoFileCRC logoNotify; logoNotify.m_nLogoFileCRC = pClient->m_pLogoInfo->m_nLogoFileCRC;
bool bReliable = true; if ( !pSenderClient->SendNetMsg( logoNotify, bReliable ) ) { Host_Error( "SV_LogoFile_OnConnect: Reliable broadcast message would overflow client" ); return; } } }
|