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