pred@2142: /**
pred@2142: * vim: set ts=4 :
pred@2142: * =============================================================================
pred@2142: * SourceMod
pred@2142: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
pred@2142: * =============================================================================
pred@2142: *
pred@2142: * This program is free software; you can redistribute it and/or modify it under
pred@2142: * the terms of the GNU General Public License, version 3.0, as published by the
pred@2142: * Free Software Foundation.
pred@2142: *
pred@2142: * This program is distributed in the hope that it will be useful, but WITHOUT
pred@2142: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@2142: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
pred@2142: * details.
pred@2142: *
pred@2142: * You should have received a copy of the GNU General Public License along with
pred@2142: * this program. If not, see .
pred@2142: *
pred@2142: * As a special exception, AlliedModders LLC gives you permission to link the
pred@2142: * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@2142: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@2142: * by the Valve Corporation. You must obey the GNU General Public License in
pred@2142: * all respects for all other code used. Additionally, AlliedModders LLC grants
pred@2142: * this exception to all derivative works. AlliedModders LLC defines further
pred@2142: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@2142: * or .
pred@2142: *
pred@2142: * Version: $Id$
pred@2142: */
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:
pred@2142: #define INVALID_SOCKET -1
pred@2142: #define closesocket close
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:
ds@2400: #if SOURCE_ENGINE == SE_LEFT4DEAD
ds@2400: #include "convar_sm_l4d.h"
ds@2400: #elif SOURCE_ENGINE == SE_ORANGEBOX
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"
pred@2142:
pred@2142: #define QUERY_MAX_LENGTH 1024
pred@2142:
pred@2142: BuildMD5ableBuffer g_MD5Builder;
pred@2142: FetcherThread g_FetchThread;
pred@2142:
pred@2142: FILE *logfile = NULL;
pred@2142:
pred@2142: bool g_disableGameDataUpdate = false;
pred@2142: bool g_restartAfterUpdate = false;
pred@2142:
pred@2247: int g_serverPort = 6500;
pred@2247: char g_serverAddress[100] = "hayate.alliedmods.net";
pred@2247:
pred@2142: 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@2367: g_Logger.LogMessage("Starting Gamedata update fetcher... please report problems to bugs.alliedmods.net");
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:
pred@2142: g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday);
pred@2142:
pred@2142: logfile = fopen(log_path, "a");
pred@2142:
pred@2142: if (!logfile)
pred@2142: {
pred@2142: g_Logger.LogError("Failed to create GameData log file");
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: {
pred@2367: g_Logger.LogToOpenFile(logfile, "Query Writing failed");
pred@2367:
pred@2367: fclose(logfile);
pred@2367: unlink(lock_path);
pred@2367: return;
pred@2367: }
pred@2367:
pred@2367: if (g_disableGameDataUpdate)
pred@2367: {
pred@2367: #ifdef DEBUG
pred@2367: g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
pred@2367: #endif
pred@2367:
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:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Sent Query!");
pred@2367: #endif
pred@2142:
pred@2142: if (sent == 0)
pred@2142: {
pred@2367: g_Logger.LogToOpenFile(logfile, "Failed to send gamedata query data to remote host");
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:
pred@2142: void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
pred@2142: {
pred@2186: g_blockGameDataLoad = false;
pred@2142: }
pred@2142:
pred@2142: 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);
pred@2142: if (len >= 4
pred@2142: && strcmp(&name[len-4], ".txt") == 0)
pred@2142: {
pred@2142: char file[PLATFORM_MAX_PATH];
pred@2142:
pred@2142: g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
pred@2142:
pred@2142: SMCStates states;
pred@2142: if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay)
pred@2142: {
pred@2142: unsigned char *md5 = g_MD5Builder.GetMD5();
pred@2142: if (md5 != NULL)
pred@2142: {
pred@2142: (uint8_t)buffer[10]++; //Increment the file counter
pred@2142: Writer.WriteBytes(md5, 16);
pred@2142:
pred@2142: g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String());
pred@2142:
pred@2142: FileData *data = new FileData();
pred@2142: data->filename = new SourceHook::String(file);
pred@2142: memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
pred@2142: filenames.push_back(data);
pred@2142: }
pred@2142: else
pred@2142: {
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
pred@2367: #endif
pred@2142: }
pred@2142: }
pred@2142: else
pred@2142: {
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "%s failed!", file);
pred@2367: #endif
pred@2142: }
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: {
pred@2142: struct protoent *ptrp;
pred@2142: ptrp = getprotobyname("tcp");
pred@2142:
pred@2142: #ifdef WIN32
pred@2142: WSADATA wsaData;
pred@2142: WSAStartup(0x0101, &wsaData);
pred@2142: #endif
pred@2142:
pred@2142: if (ptrp == NULL)
pred@2142: {
pred@2142: g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
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: {
pred@2142: //bugger aye?
pred@2367: #ifdef WIN32
pred@2367: g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", WSAGetLastError());
pred@2367: #else
pred@2367: g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", errno);
pred@2367: #endif
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: {
pred@2367: g_Logger.LogToOpenFile(logfile, "Couldn't locate address");
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: {
pred@2367: #ifdef WIN32
pred@2367: g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", WSAGetLastError());
pred@2367: #else
pred@2367: g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", errno);
pred@2367: #endif
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];
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
pred@2367: #endif
pred@2142:
pred@2142: //Read in the header bytes
pred@2367: int returnLen = RecvData(socketDescriptor, buffer, 12);
pred@2142:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Recv Completed");
pred@2367: #endif
pred@2142:
pred@2142: if (returnLen == 0)
pred@2142: {
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, ",but it failed.");
pred@2367: #endif
pred@2142: /* Timeout or fail? */
pred@2142: return;
pred@2142: }
pred@2142:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Received Header!");
pred@2367: #endif
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: {
pred@2367: g_Logger.LogToOpenFile(logfile, "Unknown Query Response");
pred@2142: return;
pred@2142: }
pred@2142:
pred@2142: UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
pred@2142:
pred@2142: short build[4] = {0,0,0,0};
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:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
pred@2367: #endif
pred@2142:
pred@2142: HandleUpdateStatus(updateStatus, build);
pred@2142:
pred@2142: int changedFiles = Reader.ReadByte();
pred@2142:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
pred@2367: #endif
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:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
pred@2367: #endif
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);
pred@2142: 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: {
pred@2142: fprintf(fp, (const char *)memPtr);
pred@2142: fclose(fp);
pred@2142: }
pred@2367: else
pred@2367: {
pred@2367: g_Logger.LogToOpenFile(logfile, "Failed to open file \"%s\"", filename);
pred@2367: }
pred@2142: }
pred@2142: else
pred@2142: {
pred@2142: filename = "";
pred@2142: }
pred@2142:
pred@2142: memtable->Reset();
pred@2142:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
pred@2367: #endif
pred@2142: }
pred@2142:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
pred@2367: #endif
pred@2142:
pred@2142: bool needsRestart = false;
pred@2142:
pred@2142: if (changedFiles > 0)
pred@2142: {
pred@2142: needsRestart = true;
pred@2142: g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect");
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: {
pred@2142: /* Timeout or fail? */
pred@2142: g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
pred@2142: return;
pred@2142: }
pred@2142:
pred@2142: Reader.StartReading(buffer, 1);
pred@2142:
pred@2142: changedFiles = Reader.ReadByte();
pred@2142:
pred@2142: if (changedFiles == 0)
pred@2142: {
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
pred@2367: #endif
pred@2142: return;
pred@2142: }
pred@2142:
pred@2142: char *changedFileIndexes = new char[changedFiles];
pred@2367:
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
pred@2367: #endif
pred@2367:
pred@2367: returnLen = RecvData(socketDescriptor, changedFileIndexes, changedFiles);
pred@2142:
pred@2142: if (returnLen == 0)
pred@2142: {
pred@2142: /* Timeout or fail? */
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
pred@2367: #endif
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);
pred@2367: #ifdef DEBUG
pred@2142: g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
pred@2367: #endif
pred@2142: }
pred@2142:
pred@2142: delete [] changedFileIndexes;
pred@2142:
pred@2142: if (needsRestart && g_restartAfterUpdate)
pred@2142: {
pred@2142: g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
pred@2142: engine->ServerCommand("quit\n");
pred@2142: }
pred@2142: }
pred@2142:
pred@2367: 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:
pred@2367: 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:
pred@2142: 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@2367: g_Logger.LogMessage("SourceMod Update: A new Mercurial build is available from sourcemod.net");
pred@2142: 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]);
pred@2142: break;
pred@2142: }
pred@2142:
pred@2142: case Update_MinorAvailable:
pred@2142: {
pred@2142: g_Logger.LogMessage("SourceMod Update: An incremental minor 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: {
pred@2142: g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update");
pred@2142: 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]);
pred@2142: break;
pred@2142: }
pred@2142: }
pred@2142: }
pred@2142:
pred@2142: bool g_blockGameDataLoad = false;
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;
pred@2142: g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
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: {
pred@2142: if (strcmp(key, "DisableAutoUpdate") == 0)
pred@2142: {
pred@2142: if (strcmp(value, "yes") == 0)
pred@2142: {
pred@2142: g_disableGameDataUpdate = true;
pred@2142: return ConfigResult_Accept;
pred@2142: }
pred@2142: else if (strcmp(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:
pred@2142: if (strcmp(key, "ForceRestartAfterUpdate") == 0)
pred@2142: {
pred@2142: if (strcmp(value, "yes") == 0)
pred@2142: {
pred@2142: g_restartAfterUpdate = true;
pred@2142: return ConfigResult_Accept;
pred@2142: }
pred@2142: else if (strcmp(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:
pred@2247: if (strcmp(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:
pred@2247: if (strcmp(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:
pred@2142: CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
pred@2142: {
ds@2400: #if SOURCE_ENGINE == SE_EPISODEONE
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: }