2008-07-01 11:48:17 +02:00
/**
2008-12-28 07:02:05 +01:00
* vim : set ts = 4 :
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* SourceMod
* Copyright ( C ) 2004 - 2008 AlliedModders LLC . All rights reserved .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* This program is free software ; you can redistribute it and / or modify it under
* the terms of the GNU General Public License , version 3.0 , as published by the
* Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*
* As a special exception , AlliedModders LLC gives you permission to link the
* code of this program ( as well as its derivative works ) to " Half-Life 2, " the
* " Source Engine, " the " SourcePawn JIT, " and any Game MODs that run on software
* by the Valve Corporation . You must obey the GNU General Public License in
* all respects for all other code used . Additionally , AlliedModders LLC grants
* this exception to all derivative works . AlliedModders LLC defines further
* exceptions , found in LICENSE . txt ( as of this writing , version JULY - 31 - 2007 ) ,
* or < http : //www.sourcemod.net/license.php>.
*
* Version : $ Id $
*/
2008-07-01 11:48:17 +02:00
# include "GameDataFetcher.h"
# include "bitbuf.h"
# ifdef PLATFORM_WINDOWS
# include <winsock2.h>
# else
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
2008-12-27 05:26:21 +01:00
# define INVALID_SOCKET -1
# define closesocket close
# define WSAGetLastError() errno
2008-07-01 11:48:17 +02:00
# endif
# include "sh_vector.h"
# include "sh_string.h"
# include "sm_version.h"
# ifdef ORANGEBOX_BUILD
# include "convar_sm_ob.h"
# else
# include "convar_sm.h"
# endif
# include "sourcemm_api.h"
# include "time.h"
# include "TimerSys.h"
2008-07-03 08:26:28 +02:00
# include "compat_wrappers.h"
2008-09-02 08:29:50 +02:00
# include "sm_stringutil.h"
2008-12-27 05:26:21 +01:00
# include "md5.h"
2008-12-28 07:02:05 +01:00
# include "frame_hooks.h"
2008-07-01 11:48:17 +02:00
# define QUERY_MAX_LENGTH 1024
2008-12-28 07:02:05 +01:00
static BuildMD5ableBuffer g_MD5Builder ;
static FetcherThread g_FetchThread ;
2008-07-01 11:48:17 +02:00
2008-12-28 07:02:05 +01:00
static FILE * logfile = NULL ;
2008-07-01 11:48:17 +02:00
bool g_disableGameDataUpdate = false ;
2008-12-28 07:02:05 +01:00
/**
* Note on this . If we issue a reload and changelevel , my srcds . exe will emit
* Assertion Failed : ! m_bServiceStarted
* on quit . This seems like a non - issue , because before we just terminated the
* server anyway . If anyone notices and files a bug , we can look into it further .
*/
2008-07-01 11:48:17 +02:00
bool g_restartAfterUpdate = false ;
2008-12-28 07:02:05 +01:00
static bool was_level_started = false ;
static int g_serverPort = 6500 ;
static char g_serverAddress [ 100 ] = " smupdate.alliedmods.net " ;
static void _ForceRestart ( void * data )
{
char cmd [ 300 ] ;
g_Logger . LogMessage ( " Automatically restarting SourceMod after a successful gamedata update. " ) ;
UTIL_Format ( cmd , sizeof ( cmd ) , " meta unload %d \n " , g_PLID ) ;
engine - > ServerCommand ( cmd ) ;
UTIL_Format ( cmd , sizeof ( cmd ) , " changelevel \" %s \" \n " , STRING ( gpGlobals - > mapname ) ) ;
engine - > ServerCommand ( cmd ) ;
UTIL_Format ( cmd , sizeof ( cmd ) , " echo SourceMod restarted after gamedata update. \n " ) ;
engine - > ServerCommand ( cmd ) ;
}
static void ForceRestart ( )
{
FrameAction action ;
action . action = _ForceRestart ;
action . data = NULL ;
AddFrameAction ( action ) ;
}
2008-09-02 08:29:50 +02:00
2008-12-27 05:26:21 +01:00
void FetcherThread : : RunThread ( IThreadHandle * pHandle )
2008-07-01 11:48:17 +02:00
{
char lock_path [ PLATFORM_MAX_PATH ] ;
g_SourceMod . BuildPath ( Path_SM , lock_path , sizeof ( lock_path ) , " data/temp " ) ;
g_LibSys . CreateFolder ( lock_path ) ;
g_SourceMod . BuildPath ( Path_SM , lock_path , sizeof ( lock_path ) , " data/temp/gamedata.lock " ) ;
char log_path [ PLATFORM_MAX_PATH ] ;
g_SourceMod . BuildPath ( Path_SM , log_path , sizeof ( log_path ) , " logs/gamedata " ) ;
g_LibSys . CreateFolder ( log_path ) ;
time_t t ;
GetAdjustedTime ( & t ) ;
tm * curtime = localtime ( & t ) ;
2008-12-29 00:54:36 +01:00
g_SourceMod . BuildPath ( Path_SM ,
log_path ,
sizeof ( log_path ) ,
" logs/gamedata/L%04d%02d%02d.log " ,
curtime - > tm_year + 1900 ,
curtime - > tm_mon + 1 ,
curtime - > tm_mday ) ;
logfile = fopen ( log_path , " at " ) ;
2008-07-01 11:48:17 +02:00
if ( ! logfile )
{
2008-12-28 01:50:13 +01:00
/* :( */
2008-07-01 11:48:17 +02:00
return ;
}
//Create a blank lock file
FILE * fp = fopen ( lock_path , " w " ) ;
if ( fp )
{
fclose ( fp ) ;
}
char query [ QUERY_MAX_LENGTH ] ;
/* Check for updated gamedata files */
int len = BuildGameDataQuery ( query , QUERY_MAX_LENGTH ) ;
if ( len = = 0 )
{
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Could not build gamedata query! " ) ;
2008-07-02 04:57:08 +02:00
fclose ( logfile ) ;
unlink ( lock_path ) ;
2008-07-01 11:48:17 +02:00
return ;
}
2008-12-28 01:50:13 +01:00
/* We check this late so we have the MD5 sums available. This may change in the future. */
2008-07-01 11:48:17 +02:00
if ( g_disableGameDataUpdate )
{
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Skipping gamedata fetcher (DisableAutoUpdate set) " ) ;
2008-07-02 04:57:08 +02:00
fclose ( logfile ) ;
unlink ( lock_path ) ;
2008-07-01 11:48:17 +02:00
return ;
}
2008-10-19 06:27:54 +02:00
/* Create a new socket for this connection */
int socketDescriptor = ConnectSocket ( ) ;
if ( socketDescriptor = = INVALID_SOCKET )
{
fclose ( logfile ) ;
unlink ( lock_path ) ;
return ;
}
int sent = SendData ( socketDescriptor , query , len ) ;
2008-07-01 11:48:17 +02:00
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Sent gamedata query " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
if ( sent = = 0 )
{
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Failed to send gamedata query data to remote host " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-02 04:57:08 +02:00
2008-07-01 11:48:17 +02:00
closesocket ( socketDescriptor ) ;
2008-07-02 04:57:08 +02:00
fclose ( logfile ) ;
unlink ( lock_path ) ;
2008-07-01 11:48:17 +02:00
return ;
}
ProcessGameDataQuery ( socketDescriptor ) ;
/* And we're done! */
closesocket ( socketDescriptor ) ;
fclose ( logfile ) ;
unlink ( lock_path ) ;
}
2008-12-27 05:26:21 +01:00
void FetcherThread : : OnTerminate ( IThreadHandle * pHandle , bool cancel )
2008-07-01 11:48:17 +02:00
{
2008-07-10 03:26:02 +02:00
g_blockGameDataLoad = false ;
2008-12-28 01:50:13 +01:00
2008-12-28 07:02:05 +01:00
if ( cancel )
{
return ;
}
2008-12-28 01:50:13 +01:00
if ( wasSuccess )
{
HandleUpdateStatus ( updateStatus , build ) ;
if ( needsRestart )
{
if ( g_restartAfterUpdate )
{
2008-12-28 07:02:05 +01:00
if ( was_level_started )
{
ForceRestart ( ) ;
}
2008-12-28 01:50:13 +01:00
}
else
{
g_Logger . LogMessage ( " Your gamedata files have been updated, please restart your server. " ) ;
}
}
}
else if ( ! g_disableGameDataUpdate )
{
g_Logger . LogError ( " An error occurred in the gamedata fetcher, see your gamedata log files for more information. " ) ;
}
2008-07-01 11:48:17 +02:00
}
2008-12-27 05:26:21 +01:00
int FetcherThread : : BuildGameDataQuery ( char * buffer , int maxlen )
2008-07-01 11:48:17 +02:00
{
char gamedata_path [ PLATFORM_MAX_PATH ] ;
g_SourceMod . BuildPath ( Path_SM , gamedata_path , sizeof ( gamedata_path ) , " gamedata " ) ;
IDirectory * dir = g_LibSys . OpenDirectory ( gamedata_path ) ;
if ( dir = = NULL )
{
return 0 ;
}
bf_write Writer = bf_write ( " GameDataQuery " , buffer , maxlen ) ;
Writer . WriteByte ( ' A ' ) ; //Generic Header char
Writer . WriteByte ( ' G ' ) ; //G for gamedata query, or green, like my hat.
short build [ 4 ] = { SVN_FILE_VERSION } ;
Writer . WriteBytes ( & build [ 0 ] , 8 ) ;
Writer . WriteByte ( 0 ) ; // Initialize the file counter - Index 10
while ( dir - > MoreFiles ( ) )
{
if ( dir - > IsEntryFile ( ) )
{
const char * name = dir - > GetEntryName ( ) ;
size_t len = strlen ( name ) ;
2008-12-27 05:26:21 +01:00
if ( len > = 4 & & strcmp ( & name [ len - 4 ] , " .txt " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
2008-12-27 05:26:21 +01:00
MD5 md5 ;
SMCError err ;
SMCStates states ;
unsigned char raw [ 16 ] ;
2008-07-01 11:48:17 +02:00
char file [ PLATFORM_MAX_PATH ] ;
g_LibSys . PathFormat ( file , sizeof ( file ) , " %s/%s " , gamedata_path , name ) ;
2008-12-27 05:26:21 +01:00
g_MD5Builder . checksum = & md5 ;
if ( ( err = g_TextParser . ParseFile_SMC ( file , & g_MD5Builder , & states ) ) = = SMCError_Okay )
2008-07-01 11:48:17 +02:00
{
2008-12-27 05:26:21 +01:00
md5 . raw_digest ( raw ) ;
( uint8_t ) buffer [ 10 ] + + ; //Increment the file counter
Writer . WriteBytes ( raw , 16 ) ;
FileData * data = new FileData ( ) ;
data - > filename = new SourceHook : : String ( file ) ;
md5 . hex_digest ( data - > checksum ) ;
filenames . push_back ( data ) ;
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Parsed file: %s as %s " , file , data - > checksum ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
}
else
{
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
const char * error = g_TextParser . GetSMCErrorString ( err ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Parsing of file %s failed: %s " , file , error ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
}
}
}
dir - > NextEntry ( ) ;
}
return Writer . GetNumBytesWritten ( ) ;
}
int FetcherThread : : ConnectSocket ( )
{
2008-12-27 05:26:21 +01:00
# if defined PLATFORM_WINDOWS
2008-07-01 11:48:17 +02:00
WSADATA wsaData ;
WSAStartup ( 0x0101 , & wsaData ) ;
# endif
2008-12-27 05:26:21 +01:00
struct protoent * ptrp ;
if ( ( ptrp = getprotobyname ( " tcp " ) ) = = NULL )
2008-07-01 11:48:17 +02:00
{
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Error: Failed to find TCP protocol " ) ;
2008-07-01 11:48:17 +02:00
return INVALID_SOCKET ;
}
int socketDescriptor = socket ( AF_INET , SOCK_STREAM , ptrp - > p_proto ) ;
if ( socketDescriptor = = INVALID_SOCKET )
{
2008-12-27 05:26:21 +01:00
char error [ 255 ] ;
g_LibSys . GetPlatformErrorEx ( WSAGetLastError ( ) , error , sizeof ( error ) ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Error: Failed to create socket: %s " , error ) ;
2008-07-01 11:48:17 +02:00
closesocket ( socketDescriptor ) ;
return INVALID_SOCKET ;
}
struct hostent * he ;
struct sockaddr_in local_addr ;
local_addr . sin_family = AF_INET ;
2008-09-02 08:29:50 +02:00
local_addr . sin_port = htons ( ( u_short ) g_serverPort ) ;
2008-07-01 11:48:17 +02:00
2008-09-02 08:29:50 +02:00
he = gethostbyname ( g_serverAddress ) ;
2008-07-01 11:48:17 +02:00
if ( ! he )
{
2008-09-02 08:29:50 +02:00
if ( ( local_addr . sin_addr . s_addr = inet_addr ( g_serverAddress ) ) = = INADDR_NONE )
2008-07-01 11:48:17 +02:00
{
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Couldn't locate address: %s " , g_serverAddress ) ;
2008-07-01 11:48:17 +02:00
closesocket ( socketDescriptor ) ;
return INVALID_SOCKET ;
}
}
else
{
memcpy ( & local_addr . sin_addr , ( struct in_addr * ) he - > h_addr , he - > h_length ) ;
}
if ( connect ( socketDescriptor , ( struct sockaddr * ) & local_addr , sizeof ( local_addr ) ) < 0 )
{
2008-12-27 05:26:21 +01:00
char error [ 255 ] ;
g_LibSys . GetPlatformErrorEx ( WSAGetLastError ( ) , error , sizeof ( error ) ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Couldn't connect to %s: %s " , g_serverAddress , error ) ;
2008-07-01 11:48:17 +02:00
closesocket ( socketDescriptor ) ;
return INVALID_SOCKET ;
}
return socketDescriptor ;
}
void FetcherThread : : ProcessGameDataQuery ( int socketDescriptor )
{
char buffer [ 50 ] ;
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Waiting for reply! " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
//Read in the header bytes
2008-10-19 06:27:54 +02:00
int returnLen = RecvData ( socketDescriptor , buffer , 12 ) ;
2008-07-01 11:48:17 +02:00
if ( returnLen = = 0 )
{
2008-12-27 05:26:21 +01:00
char error [ 255 ] ;
g_LibSys . GetPlatformErrorEx ( WSAGetLastError ( ) , error , sizeof ( error ) ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Did not receive reply: %s " , error ) ;
2008-07-01 11:48:17 +02:00
return ;
}
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Received Header! " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
bf_read Reader = bf_read ( " GameDataQuery " , buffer , 12 ) ;
if ( Reader . ReadByte ( ) ! = ' A ' | | Reader . ReadByte ( ) ! = ' G ' )
{
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Unknown Query Response " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
return ;
}
2008-12-28 01:50:13 +01:00
updateStatus = ( UpdateStatus ) Reader . ReadByte ( ) ;
2008-07-01 11:48:17 +02:00
build [ 0 ] = Reader . ReadShort ( ) ;
build [ 1 ] = Reader . ReadShort ( ) ;
build [ 2 ] = Reader . ReadShort ( ) ;
build [ 3 ] = Reader . ReadShort ( ) ;
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile ,
2008-12-27 05:26:21 +01:00
" Update Status: %i - Latest %i.%i.%i.%i " ,
updateStatus ,
build [ 0 ] ,
build [ 1 ] ,
build [ 2 ] ,
build [ 3 ] ) ;
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
int changedFiles = Reader . ReadByte ( ) ;
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Files to download: %i " , changedFiles ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
for ( int i = 0 ; i < changedFiles ; i + + )
{
//Read in the file index and byte count
2008-10-19 06:27:54 +02:00
returnLen = RecvData ( socketDescriptor , buffer , 5 ) ;
2008-07-01 11:48:17 +02:00
if ( returnLen = = 0 )
{
/* Timeout or fail? */
return ;
}
Reader . StartReading ( buffer , 5 ) ;
int index = Reader . ReadByte ( ) ;
int tempLen = Reader . ReadUBitLong ( 32 ) ;
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " File index %i and length %i " , index , tempLen ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
void * memPtr ;
memtable - > CreateMem ( tempLen + 1 , & memPtr ) ;
//Read the contents of our file into the memtable
2008-10-19 06:27:54 +02:00
returnLen = RecvData ( socketDescriptor , ( char * ) memPtr , tempLen ) ;
2008-07-01 11:48:17 +02:00
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Received %i bytes " , returnLen ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
if ( returnLen = = 0 )
{
/* Timeout or fail? */
return ;
}
( ( unsigned char * ) memPtr ) [ tempLen ] = ' \0 ' ;
FileData * data = filenames . at ( index ) ;
2008-12-27 05:26:21 +01:00
const char * filename ;
2008-07-01 11:48:17 +02:00
if ( data ! = NULL )
{
filename = data - > filename - > c_str ( ) ;
FILE * fp = fopen ( filename , " w " ) ;
if ( fp )
{
2008-12-27 05:26:21 +01:00
fprintf ( fp , " %s " , ( const char * ) memPtr ) ;
2008-07-01 11:48:17 +02:00
fclose ( fp ) ;
}
2008-10-19 06:27:54 +02:00
else
{
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Failed to open file \" %s \" for writing " , filename ) ;
2008-10-19 06:27:54 +02:00
}
2008-07-01 11:48:17 +02:00
}
else
{
filename = " " ;
}
memtable - > Reset ( ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Updated file: %s " , filename ) ;
2008-07-01 11:48:17 +02:00
}
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " File Downloads Completed! " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
2008-12-28 01:50:13 +01:00
needsRestart = false ;
2008-07-01 11:48:17 +02:00
if ( changedFiles > 0 )
{
needsRestart = true ;
}
//Read changed file count
2008-10-19 06:27:54 +02:00
returnLen = RecvData ( socketDescriptor , buffer , 1 ) ;
2008-07-01 11:48:17 +02:00
if ( returnLen = = 0 )
{
2008-12-27 05:26:21 +01:00
char error [ 255 ] ;
g_LibSys . GetPlatformErrorEx ( WSAGetLastError ( ) , error , sizeof ( error ) ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Did not receive count reply: %s " , error ) ;
2008-07-01 11:48:17 +02:00
return ;
}
Reader . StartReading ( buffer , 1 ) ;
changedFiles = Reader . ReadByte ( ) ;
2008-12-27 05:26:21 +01:00
if ( changedFiles < 1 )
2008-07-01 11:48:17 +02:00
{
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " No unknown files. We're all done " ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
return ;
}
char * changedFileIndexes = new char [ changedFiles ] ;
2008-10-19 06:27:54 +02:00
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " %i files were unknown " , changedFiles ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-10-19 06:27:54 +02:00
returnLen = RecvData ( socketDescriptor , changedFileIndexes , changedFiles ) ;
2008-07-01 11:48:17 +02:00
if ( returnLen = = 0 )
{
2008-12-27 05:26:21 +01:00
char error [ 255 ] ;
g_LibSys . GetPlatformErrorEx ( WSAGetLastError ( ) , error , sizeof ( error ) ) ;
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Did not receive list reply: %s " , error ) ;
2008-07-01 11:48:17 +02:00
return ;
}
Reader . StartReading ( changedFileIndexes , changedFiles ) ;
for ( int i = 0 ; i < changedFiles ; i + + )
{
int index = Reader . ReadByte ( ) ;
char fileName [ 30 ] ;
FileData * data = filenames . at ( index ) ;
const char * pathname ;
if ( data ! = NULL )
{
pathname = data - > filename - > c_str ( ) ;
}
else
{
pathname = " " ;
}
g_LibSys . GetFileFromPath ( fileName , sizeof ( fileName ) , pathname ) ;
2008-12-27 05:26:21 +01:00
IF_DEBUG_SPEW
2008-12-28 01:50:13 +01:00
g_Logger . LogToFileOnly ( logfile , " Unknown File %i : %s " , index , fileName ) ;
2008-12-27 05:26:21 +01:00
ENDIF_DEBUG_SPEW
2008-07-01 11:48:17 +02:00
}
delete [ ] changedFileIndexes ;
2008-12-28 01:50:13 +01:00
wasSuccess = true ;
2008-07-01 11:48:17 +02:00
}
2008-12-27 05:26:21 +01:00
int FetcherThread : : RecvData ( int socketDescriptor , char * buffer , int len )
2008-07-01 11:48:17 +02:00
{
fd_set fds ;
struct timeval tv ;
/* Create a 10 Second Timeout */
tv . tv_sec = 10 ;
tv . tv_usec = 0 ;
2008-10-19 06:27:54 +02:00
int bytesReceivedTotal = 0 ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
while ( bytesReceivedTotal < len )
{
/* Add our socket to a socket set */
FD_ZERO ( & fds ) ;
FD_SET ( socketDescriptor , & fds ) ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
/* Wait max of 10 seconds for recv to become available */
select ( socketDescriptor + 1 , & fds , NULL , NULL , & tv ) ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
int bytesReceived = 0 ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
/* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
if ( FD_ISSET ( socketDescriptor , & fds ) )
{
2008-10-20 07:42:22 +02:00
bytesReceived = recv ( socketDescriptor , buffer + bytesReceivedTotal , len - bytesReceivedTotal , 0 ) ;
2008-10-19 06:27:54 +02:00
}
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
if ( bytesReceived = = 0 | | bytesReceived = = - 1 )
{
return 0 ;
}
bytesReceivedTotal + = bytesReceived ;
2008-07-01 11:48:17 +02:00
}
2008-10-19 06:27:54 +02:00
return bytesReceivedTotal ;
2008-07-01 11:48:17 +02:00
}
2008-12-27 05:26:21 +01:00
int FetcherThread : : SendData ( int socketDescriptor , char * buffer , int len )
2008-07-01 11:48:17 +02:00
{
fd_set fds ;
struct timeval tv ;
tv . tv_sec = 10 ;
tv . tv_usec = 0 ;
2008-10-19 06:27:54 +02:00
int sentBytesTotal = 0 ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
while ( sentBytesTotal < len )
{
FD_ZERO ( & fds ) ;
FD_SET ( socketDescriptor , & fds ) ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
select ( socketDescriptor + 1 , NULL , & fds , NULL , & tv ) ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
int sentBytes = 0 ;
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
if ( FD_ISSET ( socketDescriptor , & fds ) )
{
2008-10-20 07:42:22 +02:00
sentBytes = send ( socketDescriptor , buffer + sentBytesTotal , len - sentBytesTotal , 0 ) ;
2008-10-19 06:27:54 +02:00
}
2008-07-01 11:48:17 +02:00
2008-10-19 06:27:54 +02:00
if ( sentBytes = = 0 | | sentBytes = = - 1 )
{
return 0 ;
}
sentBytesTotal + = sentBytes ;
2008-07-01 11:48:17 +02:00
}
2008-10-19 06:27:54 +02:00
return sentBytesTotal ;
2008-07-01 11:48:17 +02:00
}
2008-12-27 05:26:21 +01:00
void FetcherThread : : HandleUpdateStatus ( UpdateStatus status , short version [ 4 ] )
2008-07-01 11:48:17 +02:00
{
switch ( status )
{
case Update_Unknown :
case Update_Current :
{
break ;
}
case Update_NewBuild :
{
break ;
}
case Update_MinorAvailable :
{
2008-12-27 05:26:21 +01:00
g_Logger . LogMessage ( " SourceMod Update: A new release of SourceMod is now available from sourcemod.net " ) ;
2008-07-01 11:48:17 +02:00
g_Logger . LogMessage ( " Current Version: %i.%i.%i Available: %i.%i.%i " , version [ 0 ] , version [ 1 ] , version [ 2 ] , version [ 0 ] , version [ 1 ] , version [ 2 ] ) ;
break ;
}
case Update_MajorAvailable :
{
g_Logger . LogMessage ( " SourceMod Update: An major release of SourceMod is now available from sourcemod.net " ) ;
g_Logger . LogMessage ( " Current Version: %i.%i.%i Available: %i.%i.%i " , version [ 0 ] , version [ 1 ] , version [ 2 ] , version [ 0 ] , version [ 1 ] , version [ 2 ] ) ;
break ;
}
case Update_CriticalAvailable :
{
2008-12-27 05:26:21 +01:00
g_Logger . LogError ( " SourceMod Update: A critical SourceMod release is available from sourcemod.net. It is strongly recommended that you update! " ) ;
g_Logger . LogMessage ( " Current Version: %i.%i.%i Available: %i.%i.%i " , version [ 0 ] , version [ 1 ] , version [ 2 ] , version [ 0 ] , version [ 1 ] , version [ 2 ] ) ;
2008-07-01 11:48:17 +02:00
break ;
}
}
}
bool g_blockGameDataLoad = false ;
2008-12-28 07:02:05 +01:00
static IThreadHandle * fetch_thread_hndl ;
2008-07-01 11:48:17 +02:00
class InitFetch : public SMGlobalClass
{
public :
void OnSourceModAllInitialized_Post ( )
{
char lock_path [ PLATFORM_MAX_PATH ] ;
g_SourceMod . BuildPath ( Path_SM , lock_path , sizeof ( lock_path ) , " data/temp/gamedata.lock " ) ;
if ( g_LibSys . IsPathFile ( lock_path ) & & g_LibSys . PathExists ( lock_path ) )
{
g_Logger . LogError ( " sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading " ) ;
g_Logger . LogError ( " If this error persists delete the file manually " ) ;
g_blockGameDataLoad = true ;
}
ThreadParams fetchThreadParams = ThreadParams ( ) ;
fetchThreadParams . prio = ThreadPrio_Low ;
2008-12-28 07:02:05 +01:00
fetch_thread_hndl = g_pThreader - > MakeThread ( & g_FetchThread , & fetchThreadParams ) ;
}
void OnSourceModShutdown ( )
{
fetch_thread_hndl - > WaitForThread ( ) ;
fetch_thread_hndl - > DestroyThis ( ) ;
}
void OnSourceModLevelActivated ( )
{
was_level_started = true ;
if ( g_restartAfterUpdate & &
g_FetchThread . wasSuccess & &
g_FetchThread . needsRestart )
{
ForceRestart ( ) ;
}
}
void OnSourceModLevelEnd ( )
{
was_level_started = false ;
2008-07-01 11:48:17 +02:00
}
ConfigResult OnSourceModConfigChanged ( const char * key ,
const char * value ,
ConfigSource source ,
char * error ,
size_t maxlength )
{
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( key , " DisableAutoUpdate " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( value , " yes " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
g_disableGameDataUpdate = true ;
return ConfigResult_Accept ;
}
2008-12-27 05:26:21 +01:00
else if ( strcasecmp ( value , " no " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
g_disableGameDataUpdate = false ;
return ConfigResult_Accept ;
}
return ConfigResult_Reject ;
}
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( key , " ForceRestartAfterUpdate " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( value , " yes " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
g_restartAfterUpdate = true ;
return ConfigResult_Accept ;
}
2008-12-27 05:26:21 +01:00
else if ( strcasecmp ( value , " no " ) = = 0 )
2008-07-01 11:48:17 +02:00
{
g_restartAfterUpdate = false ;
return ConfigResult_Accept ;
}
return ConfigResult_Reject ;
}
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( key , " AutoUpdateServer " ) = = 0 )
2008-09-02 08:29:50 +02:00
{
UTIL_Format ( g_serverAddress , sizeof ( g_serverAddress ) , " %s " , value ) ;
return ConfigResult_Accept ;
}
2008-12-27 05:26:21 +01:00
if ( strcasecmp ( key , " AutoUpdatePort " ) = = 0 )
2008-09-02 08:29:50 +02:00
{
int port = atoi ( value ) ;
if ( ! port )
{
return ConfigResult_Reject ;
}
g_serverPort = port ;
return ConfigResult_Accept ;
}
2008-07-01 11:48:17 +02:00
return ConfigResult_Ignore ;
}
} g_InitFetch ;
2008-12-27 05:26:21 +01:00
BuildMD5ableBuffer : : BuildMD5ableBuffer ( )
{
stringTable = new BaseStringTable ( 2048 ) ;
}
BuildMD5ableBuffer : : ~ BuildMD5ableBuffer ( )
{
delete stringTable ;
}
void BuildMD5ableBuffer : : ReadSMC_ParseStart ( )
{
stringTable - > Reset ( ) ;
}
SMCResult BuildMD5ableBuffer : : ReadSMC_KeyValue ( const SMCStates * states ,
const char * key ,
const char * value )
{
stringTable - > AddString ( key ) ;
stringTable - > AddString ( value ) ;
return SMCResult_Continue ;
}
SMCResult BuildMD5ableBuffer : : ReadSMC_NewSection ( const SMCStates * states , const char * name )
{
stringTable - > AddString ( name ) ;
return SMCResult_Continue ;
}
void BuildMD5ableBuffer : : ReadSMC_ParseEnd ( bool halted , bool failed )
{
if ( halted | | failed )
{
return ;
}
void * data = stringTable - > GetMemTable ( ) - > GetAddress ( 0 ) ;
if ( data ! = NULL )
{
checksum - > update ( ( unsigned char * ) data , stringTable - > GetMemTable ( ) - > GetActualMemUsed ( ) ) ;
}
checksum - > finalize ( ) ;
}
2008-07-01 11:48:17 +02:00
CON_COMMAND ( sm_gamedata_md5 , " Checks the MD5 sum for a given gamedata file " )
{
# if !defined ORANGEBOX_BUILD
CCommand args ;
# endif
if ( args . ArgC ( ) < 2 )
{
g_SMAPI - > ConPrint ( " Usage: sm_gamedata_md5 <file> \n " ) ;
return ;
}
const char * file = args . Arg ( 1 ) ;
if ( ! file | | file [ 0 ] = = ' \0 ' )
{
g_SMAPI - > ConPrint ( " Usage: sm_gamedata_md5 <file> \n " ) ;
return ;
}
SourceHook : : CVector < FileData * > : : iterator iter = g_FetchThread . filenames . begin ( ) ;
FileData * curData ;
while ( iter ! = g_FetchThread . filenames . end ( ) )
{
curData = ( * iter ) ;
char fileName [ 30 ] ;
g_LibSys . GetFileFromPath ( fileName , sizeof ( fileName ) , curData - > filename - > c_str ( ) ) ;
if ( strcmpi ( fileName , file ) = = 0 )
{
g_SMAPI - > ConPrintf ( " MD5 Sum: %s \n " , curData - > checksum ) ;
return ;
}
iter + + ;
}
g_SMAPI - > ConPrint ( " File not found! \n " ) ;
}
2008-12-27 05:26:21 +01:00
FetcherThread : : ~ FetcherThread ( )
{
SourceHook : : CVector < FileData * > : : iterator iter = filenames . begin ( ) ;
FileData * curData ;
while ( iter ! = filenames . end ( ) )
{
curData = ( * iter ) ;
delete curData - > filename ;
delete curData ;
iter = filenames . erase ( iter ) ;
}
}
FetcherThread : : FetcherThread ( )
{
memtable = new BaseMemTable ( 4096 ) ;
2008-12-28 01:50:13 +01:00
wasSuccess = false ;
2008-12-28 07:02:05 +01:00
needsRestart = false ;
2008-12-27 05:26:21 +01:00
}