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
349 lines
8.8 KiB
//========= Copyright 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" |
|
|
|
|
|
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; |
|
} |
|
} |
|
} |
|
|
|
|