summaryrefslogtreecommitdiff
path: root/engine/sv_logofile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/sv_logofile.cpp')
-rw-r--r--engine/sv_logofile.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/engine/sv_logofile.cpp b/engine/sv_logofile.cpp
new file mode 100644
index 0000000..962c7b4
--- /dev/null
+++ b/engine/sv_logofile.cpp
@@ -0,0 +1,349 @@
+//========= 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;
+ }
+ }
+}
+