diff --git a/configs/core.cfg b/configs/core.cfg index bafb12b9..8969cdff 100644 --- a/configs/core.cfg +++ b/configs/core.cfg @@ -88,4 +88,18 @@ * "off" - Translate using default server's language */ "AllowClLanguageVar" "On" + + /** + * Enables or Disables SourceMod's automatic gamedata updating. + * + * The default value is "no". A value of "yes" will block the Auto Updater. + */ + "DisableAutoUpdate" "no" + + /** + * Enables or disables automatic restarting of the server after a successful update. + * + * The default value is "no". A value of "yes" will let the server automatically restart. + */ + "ForceRestartAfterUpdate" "no" } diff --git a/core/GameConfigs.cpp b/core/GameConfigs.cpp index 777b20e1..78d9b894 100644 --- a/core/GameConfigs.cpp +++ b/core/GameConfigs.cpp @@ -42,6 +42,7 @@ #include "LibrarySys.h" #include "HandleSys.h" #include "sm_crc32.h" +#include "GameDataFetcher.h" #if defined PLATFORM_LINUX #include @@ -630,6 +631,13 @@ void GameConfigManager::OnSourceModAllShutdown() bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength) { + /* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */ + if (g_blockGameDataLoad) + { + UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash"); + return false; + } + CGameConfig *pConfig; if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig)) diff --git a/core/GameDataFetcher.cpp b/core/GameDataFetcher.cpp new file mode 100644 index 00000000..e26ae7fa --- /dev/null +++ b/core/GameDataFetcher.cpp @@ -0,0 +1,682 @@ +/** +* 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 . +* +* 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 . +* +* Version: $Id$ +*/ + +#include "GameDataFetcher.h" +#include "bitbuf.h" + +#ifdef PLATFORM_WINDOWS +#include +#else +#include +#include +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#define closesocket close +#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" + +#define QUERY_MAX_LENGTH 1024 + +BuildMD5ableBuffer g_MD5Builder; +FetcherThread g_FetchThread; + +FILE *logfile = NULL; + +bool g_disableGameDataUpdate = false; +bool g_restartAfterUpdate = false; + +void FetcherThread::RunThread( IThreadHandle *pHandle ) +{ + 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"); + + g_Logger.LogMessage("Starting experimental gamedata update fetcher... please report problems to bugs.alliedmods.net"); + + 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); + + g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday); + + logfile = fopen(log_path, "a"); + + if (!logfile) + { + g_Logger.LogError("Failed to create GameData log file"); + return; + } + + //Create a blank lock file + FILE *fp = fopen(lock_path, "w"); + if (fp) + { + fclose(fp); + } + + /* Create a new socket for this connection */ + int socketDescriptor = ConnectSocket(); + + if (socketDescriptor == INVALID_SOCKET) + { + return; + } + + char query[QUERY_MAX_LENGTH]; + + /* Check for updated gamedata files */ + int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH); + + if (len == 0) + { + g_Logger.LogToOpenFile(logfile, "Query Writing failed"); + return; + } + + if (g_disableGameDataUpdate) + { + g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true"); + return; + } + + int sent = NonBlockingSend(socketDescriptor, query, len); + + g_Logger.LogToOpenFile(logfile, "Sent Query!"); + + if (sent == 0) + { + g_Logger.LogToOpenFile(logfile, "Failed to send data"); + closesocket(socketDescriptor); + return; + } + + ProcessGameDataQuery(socketDescriptor); + + /* And we're done! */ + closesocket(socketDescriptor); + + fclose(logfile); + + // Delete our lock + unlink(lock_path); +} + +void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel ) +{ + //delete this; +} + +int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen ) +{ + 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); + if (len >= 4 + && strcmp(&name[len-4], ".txt") == 0) + { + char file[PLATFORM_MAX_PATH]; + + g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name); + + SMCStates states; + if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay) + { + unsigned char *md5 = g_MD5Builder.GetMD5(); + if (md5 != NULL) + { + (uint8_t)buffer[10]++; //Increment the file counter + Writer.WriteBytes(md5, 16); + + g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String()); + + FileData *data = new FileData(); + data->filename = new SourceHook::String(file); + memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33); + filenames.push_back(data); + } + else + { + g_Logger.LogToOpenFile(logfile, "%s no md5?", file); + } + } + else + { + g_Logger.LogToOpenFile(logfile, "%s failed!", file); + } + } + } + + dir->NextEntry(); + } + + return Writer.GetNumBytesWritten(); +} + +int FetcherThread::ConnectSocket() +{ + struct protoent *ptrp; + ptrp = getprotobyname("tcp"); + +#ifdef WIN32 + WSADATA wsaData; + WSAStartup(0x0101, &wsaData); +#endif + + if (ptrp == NULL) + { + g_Logger.LogToOpenFile(logfile, "Failed to find TCP"); + return INVALID_SOCKET; + } + + int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto); + + if (socketDescriptor == INVALID_SOCKET) + { + //bugger aye? + g_Logger.LogToOpenFile(logfile, "Failed to create a new socket"); + closesocket(socketDescriptor); + return INVALID_SOCKET; + } + + struct hostent *he; + struct sockaddr_in local_addr; + + local_addr.sin_family = AF_INET; + local_addr.sin_port = htons((u_short)6500); + + he = gethostbyname("hayate.alliedmods.net"); + + if (!he) + { + if ((local_addr.sin_addr.s_addr = inet_addr("hayate.alliedmods.net")) == INADDR_NONE) + { + g_Logger.LogToOpenFile(logfile, "Couldnt locate address"); + 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) + { + g_Logger.LogToOpenFile(logfile, "Couldn't connect"); + closesocket(socketDescriptor); + return INVALID_SOCKET; + } + + return socketDescriptor; +} + +void FetcherThread::ProcessGameDataQuery(int socketDescriptor) +{ + char buffer[50]; + + g_Logger.LogToOpenFile(logfile, "Waiting for reply!"); + + //Read in the header bytes + int returnLen = NonBlockingRecv(socketDescriptor, buffer, 12); + + g_Logger.LogToOpenFile(logfile, "Recv Completed"); + + if (returnLen == 0) + { + g_Logger.LogToOpenFile(logfile, ",but it failed."); + /* Timeout or fail? */ + return; + } + + g_Logger.LogToOpenFile(logfile, "Received Header!"); + + bf_read Reader = bf_read("GameDataQuery", buffer, 12); + + if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G') + { + g_Logger.LogToOpenFile(logfile, "Invalid Query to handle"); + return; + } + + UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte(); + + short build[4] = {0,0,0,0}; + + build[0] = Reader.ReadShort(); + build[1] = Reader.ReadShort(); + build[2] = Reader.ReadShort(); + build[3] = Reader.ReadShort(); + + g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]); + + HandleUpdateStatus(updateStatus, build); + + int changedFiles = Reader.ReadByte(); + + g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles); + + for (int i=0; iCreateMem(tempLen+1, &memPtr); + + //Read the contents of our file into the memtable + returnLen = NonBlockingRecv(socketDescriptor, (char *)memPtr, tempLen); + + g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen); + + if (returnLen == 0) + { + /* Timeout or fail? */ + return; + } + + ((unsigned char *)memPtr)[tempLen] = '\0'; + + FileData *data = filenames.at(index); + const char* filename; + if (data != NULL) + { + filename = data->filename->c_str(); + + FILE *fp = fopen(filename, "w"); + + if (fp) + { + fprintf(fp, (const char *)memPtr); + fclose(fp); + } + } + else + { + filename = ""; + } + + memtable->Reset(); + + g_Logger.LogToOpenFile(logfile, "Updated File %s", filename); + } + + g_Logger.LogToOpenFile(logfile, "File Downloads Completed!"); + + bool needsRestart = false; + + if (changedFiles > 0) + { + needsRestart = true; + g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect"); + } + + //Read changed file count + returnLen = NonBlockingRecv(socketDescriptor, buffer, 1); + + if (returnLen == 0) + { + /* Timeout or fail? */ + g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count"); + return; + } + + Reader.StartReading(buffer, 1); + + changedFiles = Reader.ReadByte(); + + if (changedFiles == 0) + { + g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done"); + return; + } + + char *changedFileIndexes = new char[changedFiles]; + + g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles); + + returnLen = NonBlockingRecv(socketDescriptor, changedFileIndexes, changedFiles); + + if (returnLen == 0) + { + /* Timeout or fail? */ + g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list"); + return; + } + + Reader.StartReading(changedFileIndexes, changedFiles); + + for (int i=0; ifilename->c_str(); + } + else + { + pathname = ""; + } + + g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname); + + g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName); + } + + delete [] changedFileIndexes; + + if (needsRestart && g_restartAfterUpdate) + { + g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!"); + engine->ServerCommand("quit\n"); + } +} + +int FetcherThread::NonBlockingRecv( int socketDescriptor, char *buffer, int len ) +{ + fd_set fds; + struct timeval tv; + + /* Create a 10 Second Timeout */ + tv.tv_sec = 10; + tv.tv_usec = 0; + + /* Add our socket to a socket set */ + FD_ZERO(&fds); + FD_SET(socketDescriptor, &fds); + + /* Wait max of 10 seconds for recv to become available */ + select(socketDescriptor+1, &fds, NULL, NULL, &tv); + + int bytesReceived = 0; + + /* 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)) + { + bytesReceived = recv(socketDescriptor, buffer, len, 0); + } + + if (bytesReceived == 0 || bytesReceived == -1) + { + return 0; + } + + if (bytesReceived < len) + { + return bytesReceived + NonBlockingRecv(socketDescriptor, buffer+bytesReceived, len-bytesReceived); + } + + return bytesReceived; +} + +int FetcherThread::NonBlockingSend( int socketDescriptor, char *buffer, int len ) +{ + fd_set fds; + struct timeval tv; + + tv.tv_sec = 10; + tv.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(socketDescriptor, &fds); + + select(socketDescriptor+1, NULL, &fds, NULL, &tv); + + int sentBytes = 0; + + if (FD_ISSET(socketDescriptor, &fds)) + { + sentBytes = send(socketDescriptor, buffer, len, 0); + } + + if (sentBytes == 0 || sentBytes == -1) + { + return 0; + } + + if (sentBytes < len) + { + return sentBytes + NonBlockingSend(socketDescriptor, buffer+sentBytes, len-sentBytes); + } + + return sentBytes; +} + +void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] ) +{ + switch (status) + { + case Update_Unknown: + case Update_Current: + { + break; + } + + case Update_NewBuild: + { + g_Logger.LogMessage("SourceMod Update: A new SVN build is available from sourcemod.net"); + g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]); + break; + } + + case Update_MinorAvailable: + { + g_Logger.LogMessage("SourceMod Update: An incremental minor 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_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: + { + g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update"); + g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]); + break; + } + } +} + +bool g_blockGameDataLoad = false; + +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; + g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams); + } + + ConfigResult OnSourceModConfigChanged(const char *key, + const char *value, + ConfigSource source, + char *error, + size_t maxlength) + { + if (strcmp(key, "DisableAutoUpdate") == 0) + { + if (strcmp(value, "yes") == 0) + { + g_disableGameDataUpdate = true; + return ConfigResult_Accept; + } + else if (strcmp(value, "no") == 0) + { + g_disableGameDataUpdate = false; + return ConfigResult_Accept; + } + + return ConfigResult_Reject; + } + + if (strcmp(key, "ForceRestartAfterUpdate") == 0) + { + if (strcmp(value, "yes") == 0) + { + g_restartAfterUpdate = true; + return ConfigResult_Accept; + } + else if (strcmp(value, "no") == 0) + { + g_restartAfterUpdate = false; + return ConfigResult_Accept; + } + + return ConfigResult_Reject; + } + + return ConfigResult_Ignore; + } +} g_InitFetch; + +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 \n"); + return; + } + + const char *file = args.Arg(1); + if (!file || file[0] == '\0') + { + g_SMAPI->ConPrint("Usage: sm_gamedata_md5 \n"); + return; + } + + SourceHook::CVector::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"); +} diff --git a/core/GameDataFetcher.h b/core/GameDataFetcher.h new file mode 100644 index 00000000..2d2b3424 --- /dev/null +++ b/core/GameDataFetcher.h @@ -0,0 +1,185 @@ +/** +* 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 . +* +* 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 . +* +* Version: $Id$ +*/ + +#ifndef _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_ +#define _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_ + +#include "sourcemod.h" +#include "TextParsers.h" +#include "IThreader.h" +#include "Logger.h" +#include "LibrarySys.h" +#include "ThreadSupport.h" +#include "md5.h" +#include "sm_memtable.h" + +enum UpdateStatus +{ + Update_Unknown = 0, /* Version wasn't recognised or version querying is unsupported */ + Update_Current = 1, /* Server is running latest version */ + Update_NewBuild = 2, /* Server is on a svn release and a newer version is available */ + Update_MinorAvailable = 3, /* Server is on a release and a minor release has superceeded it */ + Update_MajorAvailable = 4, /* Server is on a release and a major release has superceeded it */ + Update_CriticalAvailable = 5, /* A critical update has been released (security fixes etc) */ +}; + +class BuildMD5ableBuffer : public ITextListener_SMC +{ +public: + + BuildMD5ableBuffer() + { + stringTable = new BaseStringTable(2048); + md5[0] = 0; + md5String[0] = 0; + } + + ~BuildMD5ableBuffer() + { + delete stringTable; + } + + void ReadSMC_ParseStart() + { + checksum = MD5(); + } + + SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) + { + stringTable->AddString(key); + stringTable->AddString(value); + + return SMCResult_Continue; + } + + SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name) + { + stringTable->AddString(name); + + return SMCResult_Continue; + } + + void 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(); + + checksum.hex_digest(md5String); + checksum.raw_digest(md5); + + stringTable->Reset(); + } + + unsigned char * GetMD5() + { + return md5; + } + + unsigned char * GetMD5String() + { + return (unsigned char *)&md5String[0]; + } + +private: + MD5 checksum; + unsigned char md5[16]; + char md5String[33]; + BaseStringTable *stringTable; +}; + +struct FileData +{ + SourceHook::String *filename; + unsigned char checksum[33]; +}; + + +class FetcherThread : public IThread +{ +public: + FetcherThread() + { + //filenames = new BaseStringTable(200); + memtable = new BaseMemTable(4096); + } + + ~FetcherThread() + { + //delete filenames; + SourceHook::CVector::iterator iter = filenames.begin(); + + FileData *curData; + + while (iter != filenames.end()) + { + curData = (*iter); + delete curData->filename; + delete curData; + iter = filenames.erase(iter); + } + } + + void RunThread(IThreadHandle *pHandle); + void OnTerminate(IThreadHandle *pHandle, bool cancel); + +private: + int BuildGameDataQuery(char *buffer, int maxlen); + void ProcessGameDataQuery(int SocketDescriptor); + + /* These names are a lie. It's really NoMoreBlockingThan10Seconds */ + int NonBlockingRecv(int socketDescriptor, char *buffer, int len); + int NonBlockingSend(int socketDescriptor, char *buffer, int len); + + int ConnectSocket(); + + void HandleUpdateStatus(UpdateStatus status, short version[4]); + +public: + SourceHook::CVector filenames; +private: + BaseMemTable *memtable; +}; + +extern BuildMD5ableBuffer g_MD5Builder; +extern bool g_blockGameDataLoad; + +#endif // _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_ diff --git a/core/Makefile b/core/Makefile index 2323c1d1..ef2e9d9e 100644 --- a/core/Makefile +++ b/core/Makefile @@ -19,7 +19,7 @@ OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreC sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \ MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \ frame_hooks.cpp concmd_cleaner.cpp Profiler.cpp PhraseCollection.cpp NextMap.cpp \ - NativeOwner.cpp + NativeOwner.cpp GameDataFetcher.cpp md5.cpp OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \ smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \ smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp \ diff --git a/core/md5.cpp b/core/md5.cpp new file mode 100644 index 00000000..a5c899ef --- /dev/null +++ b/core/md5.cpp @@ -0,0 +1,485 @@ +// MD5.CC - source code for the C++/object oriented translation and +// modification of MD5. + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + + */ + +#include "md5.h" + +#include +#include + +// MD5 simple initialization method + +MD5::MD5(){ + + init(); + +} + + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. + +void MD5::update (uint1 *input, uint4 input_length) { + + uint4 input_index, buffer_index; + uint4 buffer_space; // how much space is left in buffer + + if (finalized){ // so we can't update! + /*cerr << "MD5::update: Can't update a finalized digest!" << endl;*/ + return; + } + + // Compute number of bytes mod 64 + buffer_index = (unsigned int)((count[0] >> 3) & 0x3F); + + // Update number of bits + if ( (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) ) + count[1]++; + + count[1] += ((uint4)input_length >> 29); + + + buffer_space = 64 - buffer_index; // how much space is left in buffer + + // Transform as many times as possible. + if (input_length >= buffer_space) { // ie. we have enough to fill the buffer + // fill the rest of the buffer and transform + memcpy (buffer + buffer_index, input, buffer_space); + transform (buffer); + + // now, transform each 64-byte piece of the input, bypassing the buffer + for (input_index = buffer_space; input_index + 63 < input_length; + input_index += 64) + transform (input+input_index); + + buffer_index = 0; // so we can buffer remaining + } + else + input_index=0; // so we can buffer the whole input + + + // and here we do the buffering: + memcpy(buffer+buffer_index, input+input_index, input_length-input_index); +} + + + +// MD5 update for files. +// Like above, except that it works on files (and uses above as a primitive.) + +void MD5::update(FILE *file){ + + unsigned char buffer[1024]; + int len; + + while ((len=fread(buffer, 1, 1024, file))) + update(buffer, len); + + fclose (file); + +} + + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. + + +void MD5::finalize (){ + + unsigned char bits[8]; + unsigned int index, padLen; + static uint1 PADDING[64]={ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (finalized){ + /* cerr << "MD5::finalize: Already finalized this digest!" << endl;*/ + return; + } + + // Save number of bits + encode (bits, count, 8); + + // Pad out to 56 mod 64. + index = (uint4) ((count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + update (PADDING, padLen); + + // Append length (before padding) + update (bits, 8); + + // Store state in digest + encode (digest, state, 16); + + // Zeroize sensitive information + memset (buffer, 0, sizeof(*buffer)); + + finalized=1; + +} + + + + +MD5::MD5(FILE *file){ + + init(); // must be called be all constructors + update(file); + finalize (); +} + +unsigned char *MD5::raw_digest(){ + + uint1 *s = new uint1[16]; + + if (!finalized){ +/* cerr << "MD5::raw_digest: Can't get digest if you haven't "<< + "finalized the digest!" <> 8) & 0xff); + output[j+2] = (uint1) ((input[i] >> 16) & 0xff); + output[j+3] = (uint1) ((input[i] >> 24) & 0xff); + } +} + + + + +// Decodes input (unsigned char) into output (UINT4). Assumes len is +// a multiple of 4. +void MD5::decode (uint4 *output, uint1 *input, uint4 len){ + + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + + + + + +// Note: Replace "for loop" with standard memcpy if possible. +void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){ + + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + + + +// Note: Replace "for loop" with standard memset if possible. +void MD5::memset (uint1 *output, uint1 value, uint4 len){ + + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = value; +} + + + +// ROTATE_LEFT rotates x left n bits. + +inline unsigned int MD5::rotate_left (uint4 x, uint4 n){ + return (x << n) | (x >> (32-n)) ; +} + + + + +// F, G, H and I are basic MD5 functions. + +inline unsigned int MD5::F (uint4 x, uint4 y, uint4 z){ + return (x & y) | (~x & z); +} + +inline unsigned int MD5::G (uint4 x, uint4 y, uint4 z){ + return (x & z) | (y & ~z); +} + +inline unsigned int MD5::H (uint4 x, uint4 y, uint4 z){ + return x ^ y ^ z; +} + +inline unsigned int MD5::I (uint4 x, uint4 y, uint4 z){ + return y ^ (x | ~z); +} + + + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. + + +inline void MD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += F(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += G(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += H(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} + +inline void MD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac){ + a += I(b, c, d) + x + ac; + a = rotate_left (a, s) +b; +} diff --git a/core/md5.h b/core/md5.h new file mode 100644 index 00000000..3f6979d9 --- /dev/null +++ b/core/md5.h @@ -0,0 +1,106 @@ +// MD5.CC - source code for the C++/object oriented translation and +// modification of MD5. + +// Translation and modification (c) 1995 by Mordechai T. Abzug + +// This translation/ modification is provided "as is," without express or +// implied warranty of any kind. + +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially +// copied from the disclaimer below). + +/* based on: + + MD5.H - header file for MD5C.C + MDDRIVER.C - test driver for MD2, MD4 and MD5 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#include +//#include +//#include + +class MD5 { + +public: +// methods for controlled operation: + MD5 (); // simple initializer + void update (unsigned char *input, unsigned int input_length); + void update (FILE *file); + void finalize (); + +// constructors for special circumstances. All these constructors finalize +// the MD5 context. + MD5 (unsigned char *string); // digest string, finalize + MD5 (FILE *file); // digest file, close, finalize + +// methods to acquire finalized result + unsigned char *raw_digest (); // digest as a 16-byte binary array + unsigned char *raw_digest(unsigned char buffer[16]); + char * hex_digest (); // digest as a 33-byte ascii-hex string + char * hex_digest (char buffer[33]); //same as above, passing buffer + + + +private: + +// first, some types: + typedef unsigned int uint4; // assumes integer is 4 words long + typedef unsigned short int uint2; // assumes short integer is 2 words long + typedef unsigned char uint1; // assumes char is 1 word long + +// next, the private data: + uint4 state[4]; + uint4 count[2]; // number of *bits*, mod 2^64 + uint1 buffer[64]; // input buffer + uint1 digest[16]; + uint1 finalized; + +// last, the private methods, mostly static: + void init (); // called by all constructors + void transform (uint1 *buffer); // does the real update work. Note + // that length is implied to be 64. + + static void encode (uint1 *dest, uint4 *src, uint4 length); + static void decode (uint4 *dest, uint1 *src, uint4 length); + static void memcpy (uint1 *dest, uint1 *src, uint4 length); + static void memset (uint1 *start, uint1 val, uint4 length); + + static inline uint4 rotate_left (uint4 x, uint4 n); + static inline uint4 F (uint4 x, uint4 y, uint4 z); + static inline uint4 G (uint4 x, uint4 y, uint4 z); + static inline uint4 H (uint4 x, uint4 y, uint4 z); + static inline uint4 I (uint4 x, uint4 y, uint4 z); + static inline void FF (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void GG (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void HH (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + static inline void II (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, + uint4 s, uint4 ac); + +}; diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 962fafd9..0ff71916 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -869,6 +869,10 @@ RelativePath="..\GameConfigs.cpp" > + + @@ -877,6 +881,10 @@ RelativePath="..\Logger.cpp" > + + @@ -1043,6 +1051,10 @@ RelativePath="..\GameConfigs.h" > + + @@ -1051,6 +1063,10 @@ RelativePath="..\Logger.h" > + + diff --git a/core/sm_memtable.h b/core/sm_memtable.h index caf8cf87..2db4c4e6 100644 --- a/core/sm_memtable.h +++ b/core/sm_memtable.h @@ -63,6 +63,11 @@ public: return size; } + inline unsigned int GetActualMemUsed() + { + return tail; + } + private: unsigned char *membase; unsigned int size; diff --git a/core/systems/ForwardSys.cpp b/core/systems/ForwardSys.cpp index d83b1040..a3e31fe0 100644 --- a/core/systems/ForwardSys.cpp +++ b/core/systems/ForwardSys.cpp @@ -186,6 +186,11 @@ void CForwardManager::ReleaseForward(IForward *forward) void CForwardManager::ForwardFree(CForward *fwd) { + if (fwd == NULL) + { + return; + } + m_FreeForwards.push(fwd); m_managed.remove(fwd); }