Pre-emptive removal of gamedata updater pending rewrite.
This commit is contained in:
		
							parent
							
								
									d2ce50f989
								
							
						
					
					
						commit
						ccc59ecdaf
					
				| @ -104,16 +104,6 @@ | ||||
| 	 */ | ||||
| 	"ForceRestartAfterUpdate"	"no" | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sets the server to connect to for auotmatic gamedata updates. | ||||
| 	 */ | ||||
| 	"AutoUpdateServer"			"smupdate.alliedmods.net" | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sets the port to connect to on the AutoUpdateServer server | ||||
| 	 */ | ||||
| 	"AutoUpdatePort"			"6500" | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Whether to show debug spew.   | ||||
| 	 * Currently this will log details about the gamedata updating process. | ||||
|  | ||||
| @ -42,7 +42,6 @@ | ||||
| #include "LibrarySys.h" | ||||
| #include "HandleSys.h" | ||||
| #include "sm_crc32.h" | ||||
| #include "GameDataFetcher.h" | ||||
| 
 | ||||
| #if defined PLATFORM_LINUX | ||||
| #include <dlfcn.h> | ||||
| @ -680,12 +679,14 @@ void GameConfigManager::OnSourceModAllShutdown() | ||||
| 
 | ||||
| bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength) | ||||
| { | ||||
| #if 0 | ||||
| 	/* 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; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	CGameConfig *pConfig; | ||||
| 
 | ||||
|  | ||||
| @ -1,911 +0,0 @@ | ||||
| /**
 | ||||
|  * 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$ | ||||
|  */ | ||||
| 
 | ||||
| #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> | ||||
| 
 | ||||
| #define INVALID_SOCKET		-1 | ||||
| #define closesocket 		close | ||||
| #define WSAGetLastError()	errno | ||||
| #endif | ||||
| 
 | ||||
| #include "sh_vector.h" | ||||
| #include "sh_string.h" | ||||
| #include "sm_version.h" | ||||
| 
 | ||||
| #if SOURCE_ENGINE == SE_LEFT4DEAD | ||||
| #include "convar_sm_l4d.h" | ||||
| #elif SOURCE_ENGINE == SE_ORANGEBOX | ||||
| #include "convar_sm_ob.h" | ||||
| #else | ||||
| #include "convar_sm.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "sourcemm_api.h" | ||||
| #include "time.h" | ||||
| #include "TimerSys.h" | ||||
| #include "compat_wrappers.h" | ||||
| #include "sm_stringutil.h" | ||||
| #include "md5.h" | ||||
| #include "frame_hooks.h" | ||||
| 
 | ||||
| #define QUERY_MAX_LENGTH 1024 | ||||
| 
 | ||||
| static BuildMD5ableBuffer g_MD5Builder; | ||||
| static FetcherThread g_FetchThread; | ||||
| 
 | ||||
| static FILE *logfile = NULL; | ||||
| 
 | ||||
| bool g_disableGameDataUpdate = false; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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. | ||||
|  */ | ||||
| bool g_restartAfterUpdate = false; | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| 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"); | ||||
| 
 | ||||
| 	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%04d%02d%02d.log", | ||||
| 						  curtime->tm_year + 1900, | ||||
| 						  curtime->tm_mon + 1, | ||||
| 						  curtime->tm_mday); | ||||
| 
 | ||||
| 	logfile = fopen(log_path, "at"); | ||||
| 
 | ||||
| 	if (!logfile) | ||||
| 	{ | ||||
| 		/* :( */ | ||||
| 		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) | ||||
| 	{ | ||||
| 		g_Logger.LogToFileOnly(logfile, "Could not build gamedata query!"); | ||||
| 		fclose(logfile); | ||||
| 		unlink(lock_path); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We check this late so we have the MD5 sums available.  This may change in the future. */ | ||||
| 	if (g_disableGameDataUpdate) | ||||
| 	{ | ||||
| 		g_Logger.LogToFileOnly(logfile, "Skipping gamedata fetcher (DisableAutoUpdate set)"); | ||||
| 		fclose(logfile); | ||||
| 		unlink(lock_path); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* 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); | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "Sent gamedata query"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	if (sent == 0) | ||||
| 	{ | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "Failed to send gamedata query data to remote host"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 		closesocket(socketDescriptor); | ||||
| 		fclose(logfile); | ||||
| 		unlink(lock_path); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	ProcessGameDataQuery(socketDescriptor); | ||||
| 
 | ||||
| 	/* And we're done! */ | ||||
| 	closesocket(socketDescriptor); | ||||
| 	fclose(logfile); | ||||
| 	unlink(lock_path); | ||||
| } | ||||
| 
 | ||||
| void FetcherThread::OnTerminate(IThreadHandle *pHandle, bool cancel) | ||||
| { | ||||
| 	g_blockGameDataLoad = false; | ||||
| 
 | ||||
| 	if (cancel) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (wasSuccess) | ||||
| 	{ | ||||
| 		HandleUpdateStatus(updateStatus, build); | ||||
| 
 | ||||
| 		if (needsRestart) | ||||
| 		{ | ||||
| 			if (g_restartAfterUpdate) | ||||
| 			{ | ||||
| 				if (was_level_started) | ||||
| 				{ | ||||
| 					ForceRestart(); | ||||
| 				} | ||||
| 			} | ||||
| 			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."); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| 			{ | ||||
| 				MD5 md5; | ||||
| 				SMCError err; | ||||
| 				SMCStates states; | ||||
| 				unsigned char raw[16]; | ||||
| 				char file[PLATFORM_MAX_PATH]; | ||||
| 
 | ||||
| 				g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name); | ||||
| 
 | ||||
| 				g_MD5Builder.checksum = &md5; | ||||
| 				if ((err = g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states)) == SMCError_Okay) | ||||
| 				{ | ||||
| 					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 | ||||
| 					g_Logger.LogToFileOnly(logfile, "Parsed file: %s as %s", file, data->checksum); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| IF_DEBUG_SPEW | ||||
| 					const char *error = g_TextParser.GetSMCErrorString(err); | ||||
| 					g_Logger.LogToFileOnly(logfile, "Parsing of file %s failed: %s", file, error); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		dir->NextEntry(); | ||||
| 	} | ||||
| 
 | ||||
| 	return Writer.GetNumBytesWritten(); | ||||
| } | ||||
| 
 | ||||
| int FetcherThread::ConnectSocket() | ||||
| { | ||||
| #if defined PLATFORM_WINDOWS | ||||
| 	WSADATA wsaData; | ||||
| 	WSAStartup(0x0101, &wsaData); | ||||
| #endif | ||||
| 
 | ||||
| 	struct protoent *ptrp; | ||||
| 	 | ||||
| 	if ((ptrp = getprotobyname("tcp")) == NULL)  | ||||
| 	{ | ||||
| 		g_Logger.LogToFileOnly(logfile, "Error: Failed to find TCP protocol"); | ||||
| 		return INVALID_SOCKET; | ||||
| 	} | ||||
| 
 | ||||
| 	int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto); | ||||
| 
 | ||||
| 	if (socketDescriptor == INVALID_SOCKET) | ||||
| 	{ | ||||
| 		char error[255]; | ||||
| 		g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); | ||||
| 		g_Logger.LogToFileOnly(logfile, "Error: Failed to create socket: %s", error); | ||||
| 		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)g_serverPort); | ||||
| 
 | ||||
| 	he = gethostbyname(g_serverAddress); | ||||
| 
 | ||||
| 	if (!he) | ||||
| 	{ | ||||
| 		if ((local_addr.sin_addr.s_addr = inet_addr(g_serverAddress)) == INADDR_NONE) | ||||
| 		{ | ||||
| 			g_Logger.LogToFileOnly(logfile, "Couldn't locate address: %s", g_serverAddress); | ||||
| 			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) | ||||
| 	{ | ||||
| 		char error[255]; | ||||
| 		g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); | ||||
| 		g_Logger.LogToFileOnly(logfile, "Couldn't connect to %s: %s", g_serverAddress, error); | ||||
| 		closesocket(socketDescriptor); | ||||
| 		return INVALID_SOCKET; | ||||
| 	} | ||||
| 
 | ||||
| 	return socketDescriptor; | ||||
| } | ||||
| 
 | ||||
| void FetcherThread::ProcessGameDataQuery(int socketDescriptor) | ||||
| { | ||||
| 	char buffer[50]; | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "Waiting for reply!"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	//Read in the header bytes
 | ||||
| 	int returnLen = RecvData(socketDescriptor, buffer, 12); | ||||
| 
 | ||||
| 
 | ||||
| 	if (returnLen == 0) | ||||
| 	{ | ||||
| 		char error[255]; | ||||
| 		g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); | ||||
| 		g_Logger.LogToFileOnly(logfile, "Did not receive reply: %s", error); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "Received Header!"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	bf_read Reader = bf_read("GameDataQuery", buffer, 12); | ||||
| 
 | ||||
| 	if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G') | ||||
| 	{ | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "Unknown Query Response"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	updateStatus = (UpdateStatus)Reader.ReadByte(); | ||||
| 
 | ||||
| 	build[0] = Reader.ReadShort(); | ||||
| 	build[1] = Reader.ReadShort(); | ||||
| 	build[2] = Reader.ReadShort(); | ||||
| 	build[3] = Reader.ReadShort(); | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, | ||||
| 						   "Update Status: %i - Latest %i.%i.%i.%i", | ||||
| 						   updateStatus, | ||||
| 						   build[0], | ||||
| 						   build[1], | ||||
| 						   build[2], | ||||
| 						   build[3]); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	int changedFiles = Reader.ReadByte(); | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "Files to download: %i", changedFiles); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	for (int i=0; i<changedFiles; i++) | ||||
| 	{ | ||||
| 		//Read in the file index and byte count
 | ||||
| 		returnLen = RecvData(socketDescriptor, buffer, 5); | ||||
| 
 | ||||
| 		if (returnLen == 0) | ||||
| 		{ | ||||
| 			/* Timeout or fail? */ | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		Reader.StartReading(buffer, 5); | ||||
| 
 | ||||
| 		int index = Reader.ReadByte(); | ||||
| 		int tempLen = Reader.ReadUBitLong(32); | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "File index %i and length %i", index, tempLen); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 		void *memPtr; | ||||
| 		memtable->CreateMem(tempLen+1, &memPtr); | ||||
| 
 | ||||
| 		//Read the contents of our file into the memtable
 | ||||
| 		returnLen = RecvData(socketDescriptor, (char *)memPtr, tempLen); | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "Received %i bytes", returnLen); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 		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, "%s", (const char *)memPtr); | ||||
| 				fclose(fp); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				g_Logger.LogToFileOnly(logfile, "Failed to open file \"%s\" for writing", filename); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			filename = ""; | ||||
| 		} | ||||
| 
 | ||||
| 		memtable->Reset(); | ||||
| 
 | ||||
| 		g_Logger.LogToFileOnly(logfile, "Updated file: %s", filename); | ||||
| 	} | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "File Downloads Completed!"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	needsRestart = false; | ||||
| 
 | ||||
| 	if (changedFiles > 0) | ||||
| 	{ | ||||
| 		needsRestart = true; | ||||
| 	} | ||||
| 
 | ||||
| 	//Read changed file count
 | ||||
| 	returnLen = RecvData(socketDescriptor, buffer, 1); | ||||
| 
 | ||||
| 	if (returnLen == 0) | ||||
| 	{ | ||||
| 		char error[255]; | ||||
| 		g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); | ||||
| 		g_Logger.LogToFileOnly(logfile, "Did not receive count reply: %s", error); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	Reader.StartReading(buffer, 1); | ||||
| 
 | ||||
| 	changedFiles = Reader.ReadByte(); | ||||
| 
 | ||||
| 	if (changedFiles < 1) | ||||
| 	{ | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "No unknown files. We're all done"); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	char *changedFileIndexes = new char[changedFiles]; | ||||
| 
 | ||||
| IF_DEBUG_SPEW | ||||
| 	g_Logger.LogToFileOnly(logfile, "%i files were unknown", changedFiles); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 
 | ||||
| 	returnLen = RecvData(socketDescriptor, changedFileIndexes, changedFiles); | ||||
| 
 | ||||
| 	if (returnLen == 0) | ||||
| 	{ | ||||
| 		char error[255]; | ||||
| 		g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); | ||||
| 		g_Logger.LogToFileOnly(logfile, "Did not receive list reply: %s", error); | ||||
| 		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); | ||||
| IF_DEBUG_SPEW | ||||
| 		g_Logger.LogToFileOnly(logfile, "Unknown File %i : %s", index, fileName); | ||||
| ENDIF_DEBUG_SPEW | ||||
| 	} | ||||
| 
 | ||||
| 	delete [] changedFileIndexes; | ||||
| 
 | ||||
| 	wasSuccess = true; | ||||
| } | ||||
| 
 | ||||
| int FetcherThread::RecvData(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; | ||||
| 
 | ||||
| 	int bytesReceivedTotal = 0; | ||||
| 
 | ||||
| 	while (bytesReceivedTotal < len) | ||||
| 	{ | ||||
| 		/* 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+bytesReceivedTotal, len-bytesReceivedTotal, 0); | ||||
| 		} | ||||
| 
 | ||||
| 		if (bytesReceived == 0 || bytesReceived == -1) | ||||
| 		{ | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		bytesReceivedTotal += bytesReceived; | ||||
| 	} | ||||
| 
 | ||||
| 	return bytesReceivedTotal;	 | ||||
| } | ||||
| 
 | ||||
| int FetcherThread::SendData(int socketDescriptor, char *buffer, int len) | ||||
| { | ||||
| 	fd_set fds; | ||||
| 	struct timeval tv; | ||||
| 
 | ||||
| 	tv.tv_sec = 10; | ||||
| 	tv.tv_usec = 0; | ||||
| 
 | ||||
| 	int sentBytesTotal = 0; | ||||
| 
 | ||||
| 	while (sentBytesTotal < len) | ||||
| 	{ | ||||
| 		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+sentBytesTotal, len-sentBytesTotal, 0); | ||||
| 		} | ||||
| 
 | ||||
| 		if (sentBytes == 0 || sentBytes == -1) | ||||
| 		{ | ||||
| 			return 0; | ||||
| 		}		 | ||||
| 
 | ||||
| 		sentBytesTotal += sentBytes; | ||||
| 	} | ||||
| 
 | ||||
| 	return sentBytesTotal; | ||||
| } | ||||
| 
 | ||||
| void FetcherThread::HandleUpdateStatus(UpdateStatus status, short version[4]) | ||||
| { | ||||
| 	switch (status) | ||||
| 	{ | ||||
| 		case Update_Unknown: | ||||
| 		case Update_Current: | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case Update_NewBuild: | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case Update_MinorAvailable: | ||||
| 		{ | ||||
| 			g_Logger.LogMessage("SourceMod Update: A new 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 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]); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool g_blockGameDataLoad = false; | ||||
| static IThreadHandle *fetch_thread_hndl; | ||||
| 
 | ||||
| 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; | ||||
| 		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; | ||||
| 	} | ||||
| 
 | ||||
| 	ConfigResult OnSourceModConfigChanged(const char *key,  | ||||
| 		const char *value,  | ||||
| 		ConfigSource source, | ||||
| 		char *error,  | ||||
| 		size_t maxlength) | ||||
| 	{ | ||||
| 		if (strcasecmp(key, "DisableAutoUpdate") == 0) | ||||
| 		{ | ||||
| 			if (strcasecmp(value, "yes") == 0) | ||||
| 			{ | ||||
| 				g_disableGameDataUpdate = true; | ||||
| 				return ConfigResult_Accept; | ||||
| 			} | ||||
| 			else if (strcasecmp(value, "no") == 0) | ||||
| 			{ | ||||
| 				g_disableGameDataUpdate = false; | ||||
| 				return ConfigResult_Accept; | ||||
| 			} | ||||
| 
 | ||||
| 			return ConfigResult_Reject; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcasecmp(key, "ForceRestartAfterUpdate") == 0) | ||||
| 		{ | ||||
| 			if (strcasecmp(value, "yes") == 0) | ||||
| 			{ | ||||
| 				g_restartAfterUpdate = true; | ||||
| 				return ConfigResult_Accept; | ||||
| 			} | ||||
| 			else if (strcasecmp(value, "no") == 0) | ||||
| 			{ | ||||
| 				g_restartAfterUpdate = false; | ||||
| 				return ConfigResult_Accept; | ||||
| 			} | ||||
| 
 | ||||
| 			return ConfigResult_Reject; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcasecmp(key, "AutoUpdateServer") == 0) | ||||
| 		{ | ||||
| 			UTIL_Format(g_serverAddress, sizeof(g_serverAddress), "%s", value); | ||||
| 
 | ||||
| 			return ConfigResult_Accept; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcasecmp(key, "AutoUpdatePort") == 0) | ||||
| 		{ | ||||
| 			int port = atoi(value); | ||||
| 
 | ||||
| 			if (!port) | ||||
| 			{ | ||||
| 				return ConfigResult_Reject; | ||||
| 			} | ||||
| 
 | ||||
| 			g_serverPort = port; | ||||
| 
 | ||||
| 			return ConfigResult_Accept; | ||||
| 		} | ||||
| 
 | ||||
| 		return ConfigResult_Ignore; | ||||
| 	} | ||||
| } g_InitFetch; | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file") | ||||
| { | ||||
| #if SOURCE_ENGINE == SE_EPISODEONE | ||||
| 	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"); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 	wasSuccess = false; | ||||
| 	needsRestart = false; | ||||
| } | ||||
| 
 | ||||
| @ -1,106 +0,0 @@ | ||||
| /**
 | ||||
|  * 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$ | ||||
|  */ | ||||
| 
 | ||||
| #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 "sm_memtable.h" | ||||
| #include <sh_string.h> | ||||
| #include <sh_list.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 MD5; | ||||
| 
 | ||||
| class BuildMD5ableBuffer : public ITextListener_SMC | ||||
| { | ||||
| public: | ||||
| 	BuildMD5ableBuffer(); | ||||
| 	~BuildMD5ableBuffer(); | ||||
| 	void ReadSMC_ParseStart(); | ||||
| 	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); | ||||
| 	SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name); | ||||
| 	void ReadSMC_ParseEnd(bool halted, bool failed); | ||||
| public: | ||||
| 	MD5 *checksum; | ||||
| private: | ||||
| 	BaseStringTable *stringTable; | ||||
| }; | ||||
| 
 | ||||
| struct FileData | ||||
| { | ||||
| 	SourceHook::String *filename; | ||||
| 	char checksum[33]; | ||||
| }; | ||||
| 
 | ||||
| class FetcherThread : public IThread | ||||
| { | ||||
| public: | ||||
| 	FetcherThread(); | ||||
| 	~FetcherThread(); | ||||
| public: | ||||
| 	void RunThread(IThreadHandle *pHandle); | ||||
| 	void OnTerminate(IThreadHandle *pHandle, bool cancel); | ||||
| private: | ||||
| 	int BuildGameDataQuery(char *buffer, int maxlen); | ||||
| 	void ProcessGameDataQuery(int SocketDescriptor); | ||||
| 	int RecvData(int socketDescriptor, char *buffer, int len); | ||||
| 	int SendData(int socketDescriptor, char *buffer, int len); | ||||
| 	int ConnectSocket(); | ||||
| 	void HandleUpdateStatus(UpdateStatus status, short version[4]); | ||||
| public: | ||||
| 	SourceHook::CVector<FileData *> filenames; | ||||
| 	bool needsRestart; | ||||
| 	bool wasSuccess; | ||||
| private: | ||||
| 	UpdateStatus updateStatus; | ||||
| 	BaseMemTable *memtable; | ||||
| 	short build[4]; | ||||
| }; | ||||
| 
 | ||||
| extern bool g_blockGameDataLoad; | ||||
| 
 | ||||
| #endif // _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
 | ||||
| 
 | ||||
| @ -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 GameDataFetcher.cpp md5.cpp | ||||
| 	NativeOwner.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 \
 | ||||
|  | ||||
| @ -1141,10 +1141,6 @@ | ||||
| 				RelativePath="..\GameConfigs.cpp" | ||||
| 				> | ||||
| 			</File> | ||||
| 			<File | ||||
| 				RelativePath="..\GameDataFetcher.cpp" | ||||
| 				> | ||||
| 			</File> | ||||
| 			<File | ||||
| 				RelativePath="..\HalfLife2.cpp" | ||||
| 				> | ||||
| @ -1351,10 +1347,6 @@ | ||||
| 				RelativePath="..\GameConfigs.h" | ||||
| 				> | ||||
| 			</File> | ||||
| 			<File | ||||
| 				RelativePath="..\GameDataFetcher.h" | ||||
| 				> | ||||
| 			</File> | ||||
| 			<File | ||||
| 				RelativePath="..\HalfLife2.h" | ||||
| 				> | ||||
|  | ||||
| @ -1,76 +0,0 @@ | ||||
| # (C)2004-2008 SourceMod Development Team
 | ||||
| # Makefile written by David "BAILOPAN" Anderson
 | ||||
| 
 | ||||
| 
 | ||||
| #####################################
 | ||||
| ### EDIT BELOW FOR OTHER PROJECTS ###
 | ||||
| #####################################
 | ||||
| 
 | ||||
| OBJECTS = smud.cpp smud_connections.cpp smud_threads.cpp | ||||
| 
 | ||||
| ##############################################
 | ||||
| ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
 | ||||
| ##############################################
 | ||||
| 
 | ||||
| C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing | ||||
| C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3 | ||||
| C_GCC4_FLAGS = -fvisibility=hidden | ||||
| CPP_GCC4_FLAGS = -fvisibility-inlines-hidden | ||||
| CPP = gcc | ||||
| 
 | ||||
| BINARY = smupdated | ||||
| 
 | ||||
| LINK += -lpthread -static-libgcc | ||||
| 	 | ||||
| INCLUDE += -I. | ||||
| 
 | ||||
| CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
 | ||||
| 	-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror \
 | ||||
| 	-Wno-uninitialized -mfpmath=sse -msse -DHAVE_STDINT_H -DSM_DEFAULT_THREADER -m32 | ||||
| CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti | ||||
| 
 | ||||
| ################################################
 | ||||
| ### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
 | ||||
| ################################################
 | ||||
| 
 | ||||
| ifeq "$(DEBUG)" "true" | ||||
| 	BIN_DIR = Debug | ||||
| 	CFLAGS += $(C_DEBUG_FLAGS) | ||||
| else | ||||
| 	BIN_DIR = Release | ||||
| 	CFLAGS += $(C_OPT_FLAGS) | ||||
| endif | ||||
| 
 | ||||
| GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) | ||||
| ifeq "$(GCC_VERSION)" "4" | ||||
| 	CFLAGS += $(C_GCC4_FLAGS) | ||||
| 	CPPFLAGS += $(CPP_GCC4_FLAGS) | ||||
| endif | ||||
| 
 | ||||
| OBJ_LINUX := $(OBJECTS:%vm_engine.cpp=$(BIN_DIR)/%vm_engine.o) | ||||
| OBJ_LINUX := $(OBJ_LINUX:%.cpp=$(BIN_DIR)/%.o) | ||||
| OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o) | ||||
| 
 | ||||
| $(BIN_DIR)/%vm_engine.o: %vm_engine.cpp | ||||
| 	$(CPP) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< | ||||
| 
 | ||||
| $(BIN_DIR)/%.o: %.cpp | ||||
| 	$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< | ||||
| 
 | ||||
| $(BIN_DIR)/%.o: %.c | ||||
| 	$(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< | ||||
| 
 | ||||
| all: | ||||
| 	mkdir -p $(BIN_DIR) | ||||
| 	$(MAKE) -f Makefile sourcemod | ||||
| 
 | ||||
| sourcemod: $(OBJ_LINUX) | ||||
| 	$(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -lstdc++ -o $(BIN_DIR)/$(BINARY) | ||||
| 
 | ||||
| debug: | ||||
| 	$(MAKE) -f Makefile all DEBUG=true | ||||
| 
 | ||||
| default: all | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf $(BIN_DIR)/$(BINARY) | ||||
| @ -1,157 +0,0 @@ | ||||
| #include <errno.h> | ||||
| #include "smud.h" | ||||
| #include "smud_threads.h" | ||||
| 
 | ||||
| #define LISTEN_PORT 6500 | ||||
| #define LISTEN_QUEUE_LENGTH 6 | ||||
| 
 | ||||
| char fileNames[NUM_FILES][30] = { | ||||
| 	"core.games.txt", | ||||
| 	"sdktools.games.txt", | ||||
| 	"sdktools.games.ep2.txt", | ||||
| 	"sdktools.games.l4d.txt", | ||||
| 	"sm-cstrike.games.txt", | ||||
| 	"sm-tf2.games.txt", | ||||
| }; | ||||
| 
 | ||||
| void *fileLocations[NUM_FILES]; | ||||
| int fileLength[NUM_FILES]; | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	ThreadPool *pool; | ||||
| 	struct protoent *pProtocol; | ||||
| 	struct sockaddr_in serverAddress; | ||||
| 	struct sockaddr_in clientAddress; | ||||
| 	int serverSocket; | ||||
| 	int clientSocket; | ||||
| 	int addressLen; | ||||
| 	int opts; | ||||
| 	int file; | ||||
| 	char filename[100]; | ||||
| 	struct stat sbuf; | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Loading Gamedata files into memory\n"); | ||||
| #endif | ||||
| 
 | ||||
| 	for (int i=0; i<NUM_FILES; i++) | ||||
| 	{ | ||||
| 		snprintf(filename, sizeof(filename), "./md5/%s", fileNames[i]); | ||||
| 		file = open(filename, O_RDWR); | ||||
| 
 | ||||
| 		if (!file) | ||||
| 		{ | ||||
| 			fprintf(stderr, "Could not find file: %s", filename); | ||||
| 			return 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (stat(filename, &sbuf) == -1) | ||||
| 		{ | ||||
| 			fprintf(stderr, "Could not stat file: %s (error: %s)", filename, strerror(errno)); | ||||
| 			return 1; | ||||
| 		} | ||||
| 
 | ||||
| 		if ((fileLocations[i] = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, file, 0)) == MAP_FAILED) | ||||
| 		{ | ||||
| 			fprintf(stderr, "Could not mmap file: %s (error: %s)", filename, strerror(errno)); | ||||
| 			return 1; | ||||
| 		} | ||||
| 
 | ||||
| 		fileLength[i] = sbuf.st_size; | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 		fprintf(stdout, "Initialised file of %s of length %i\n", fileNames[i], fileLength[i]); | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Initializing Thread Pool\n"); | ||||
| #endif | ||||
| 
 | ||||
| 	pool = new ThreadPool(); | ||||
| 	 | ||||
| 	if (!pool->Start()) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not initialize thread pool!\n"); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	printf("Create Server Socket\n"); | ||||
| #endif | ||||
| 
 | ||||
| 	memset(&serverAddress, 0, sizeof(serverAddress)); | ||||
| 	serverAddress.sin_family = AF_INET; | ||||
| 	serverAddress.sin_addr.s_addr = INADDR_ANY; | ||||
| 	serverAddress.sin_port = htons(LISTEN_PORT); | ||||
| 
 | ||||
| 	pProtocol = getprotobyname("tcp"); | ||||
| 
 | ||||
| 	if (pProtocol == NULL) | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not get tcp proto: %s", strerror(errno)); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	serverSocket = socket(AF_INET, SOCK_STREAM, pProtocol->p_proto); | ||||
| 
 | ||||
| 	if (serverSocket < 0)  | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not open socket: %s", strerror(errno)); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	opts = 1; | ||||
| 	setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opts, sizeof(opts)); | ||||
| 
 | ||||
| 	if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)  | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not bind socket: %s", strerror(errno)); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (listen(serverSocket, LISTEN_QUEUE_LENGTH) < 0)  | ||||
| 	{ | ||||
| 		fprintf(stderr, "Could not listen on socket: %s", strerror(errno)); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	fprintf(stdout, "Server has started.\n"); | ||||
| 
 | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		addressLen = sizeof(clientAddress); | ||||
| 
 | ||||
| 		clientSocket = accept(serverSocket, | ||||
| 							  (struct sockaddr *)&clientAddress, | ||||
| 							  (socklen_t *)&addressLen); | ||||
| 		if (clientSocket < 0) | ||||
| 		{ | ||||
| 			fprintf(stderr, "Could not accept client: %s", strerror(errno)); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		opts = fcntl(clientSocket, F_GETFL, 0); | ||||
| 		if (fcntl(clientSocket, F_SETFL, opts|O_NONBLOCK) < 0) | ||||
| 		{ | ||||
| 			fprintf(stderr, "Could not non-block client: %s", strerror(errno)); | ||||
| 			closesocket(clientSocket); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 		fprintf(stdout, | ||||
| 				"Accepting connection from client (sock %d, ip %s)", | ||||
| 				clientSocket, | ||||
| 				inet_ntoa(clientAddress.sin_addr)); | ||||
| #endif | ||||
| 
 | ||||
| 		pool->AddConnection(clientSocket); | ||||
| 	} | ||||
| 
 | ||||
| 	delete pool; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -1,25 +0,0 @@ | ||||
| #ifndef _INCLUDE_SMUD_MAIN_H_ | ||||
| #define _INCLUDE_SMUD_MAIN_H_ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/stat.h> | ||||
| #include <pthread.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <netdb.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #define closesocket close | ||||
| 
 | ||||
| #define NUM_FILES 6 | ||||
| 
 | ||||
| extern char fileNames[NUM_FILES][30]; | ||||
| extern void *fileLocations[NUM_FILES]; | ||||
| extern int fileLength[NUM_FILES]; | ||||
| 
 | ||||
| #endif //_INCLUDE_SMUD_MAIN_H_
 | ||||
| @ -1,541 +0,0 @@ | ||||
| #include <assert.h> | ||||
| #include "smud_connections.h" | ||||
| #include "smud.h" | ||||
| 
 | ||||
| ConnectionPool::ConnectionPool() | ||||
| { | ||||
| 	pthread_mutex_init(&m_AddLock, NULL); | ||||
| 	m_timeOut = 1000; | ||||
| } | ||||
| 
 | ||||
| ConnectionPool::~ConnectionPool() | ||||
| { | ||||
| 	pthread_mutex_destroy(&m_AddLock); | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::AddConnection( int fd ) | ||||
| { | ||||
| 	smud_connection *connection = new smud_connection(fd); | ||||
| 
 | ||||
| 	pthread_mutex_lock(&m_AddLock); | ||||
| 	m_AddQueue.push_back(connection); | ||||
| 	pthread_mutex_unlock(&m_AddLock); | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::Process( bool *terminate ) | ||||
| { | ||||
| 	struct timespec ts_wait; | ||||
| 
 | ||||
| 	ts_wait.tv_sec = 0; | ||||
| 	ts_wait.tv_nsec = 50000000; /* 50ms */ | ||||
| 
 | ||||
| 	std::list<smud_connection *>::iterator iter; | ||||
| 	smud_connection *con = NULL; | ||||
| 
 | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		if (*terminate) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		iter = m_Links.begin(); | ||||
| 
 | ||||
| 		QueryResult result = QueryResult_Continue; | ||||
| 		int pollReturn = 0; | ||||
| 
 | ||||
| 		/* Add all connections that want processing to the sets */ | ||||
| 		while (iter != m_Links.end()) | ||||
| 		{ | ||||
| 			con = *iter; | ||||
| 
 | ||||
| 			pollReturn = poll(&(con->pollData), 1, 0); | ||||
| 			assert(pollReturn <= 1); | ||||
| 
 | ||||
| 			if (pollReturn == -1) | ||||
| 			{ | ||||
| 				//Something went badly wrong or the connection closed.
 | ||||
| 				result = QueryResult_Complete; | ||||
| 			} | ||||
| 			else if (pollReturn == 1) | ||||
| 			{ | ||||
| 				//Poll returns the number of sockets available (which can only ever be 1)
 | ||||
| 				result = ProcessConnection(con); | ||||
| 			} | ||||
| 
 | ||||
| 			if (result == QueryResult_Complete) | ||||
| 			{ | ||||
| 				iter = m_Links.erase(iter); | ||||
| #if defined DEBUG | ||||
| 				fprintf(stdout, "Closing socket %d\n", con->fd); | ||||
| #endif | ||||
| 				closesocket(con->fd); | ||||
| 				delete con; | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			iter++; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Add new items to process */ | ||||
| 
 | ||||
| 		iter = m_Links.end(); | ||||
| 		pthread_mutex_lock(&m_AddLock); | ||||
| 		m_Links.splice(iter, m_AddQueue); | ||||
| 		pthread_mutex_unlock(&m_AddLock); | ||||
| 
 | ||||
| 		nanosleep(&ts_wait, NULL); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QueryResult ConnectionPool::ProcessConnection( smud_connection *con ) | ||||
| { | ||||
| 	switch (con->state) | ||||
| 	{ | ||||
| 		case ConnectionState_ReadQueryHeader: | ||||
| 		{ | ||||
| 			ReadQueryHeader(con); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case ConnectionState_ReadQueryData: | ||||
| 		{ | ||||
| 			ReadQueryContent(con); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case ConnectionState_ReplyQuery: | ||||
| 		{ | ||||
| 			ReplyQuery(con); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case ConnectionState_SendingFiles: | ||||
| 		{ | ||||
| 			SendFile(con); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case ConnectionState_SendUnknownList: | ||||
| 		{ | ||||
| 			SendUnknownList(con); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		case ConnectionState_Complete: | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (con->state == ConnectionState_Complete) | ||||
| 	{ | ||||
| 		printf("Ending connection because it marked itself as finished\n"); | ||||
| 		return QueryResult_Complete; | ||||
| 	} | ||||
| 
 | ||||
| 	if (con->start + m_timeOut < time(NULL)) | ||||
| 	{ | ||||
| 		printf("Ending connection because it has passed maximum allowed time\n"); | ||||
| 		return QueryResult_Complete; | ||||
| 	} | ||||
| 
 | ||||
| 	return QueryResult_Continue; | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::ReadQueryHeader( smud_connection *con ) | ||||
| { | ||||
| 	if (con->buffer == NULL) | ||||
| 	{ | ||||
| 		con->buffer = new char[QUERY_HEADER_SIZE]; | ||||
| 		con->writtenCount = 0; | ||||
| 	} | ||||
| 	 | ||||
| 	int bytesReceived = 0; | ||||
| 
 | ||||
| 	bytesReceived = recv(con->fd, con->buffer+con->writtenCount, QUERY_HEADER_SIZE-con->writtenCount, 0); | ||||
| 
 | ||||
| 	if (bytesReceived == -1) | ||||
| 	{ | ||||
| 		if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 		{ | ||||
| 			con->state = ConnectionState_Complete; | ||||
| 			delete [] con->buffer; | ||||
| 			con->buffer = NULL; | ||||
| 			con->writtenCount = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->writtenCount += bytesReceived; | ||||
| 
 | ||||
| 	assert(con->writtenCount <= QUERY_HEADER_SIZE); | ||||
| 
 | ||||
| 	if (con->writtenCount < QUERY_HEADER_SIZE) | ||||
| 	{ | ||||
| 		/* Don't change the connection status, so next cycle we will come back to here and continue receiving data */ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	if (con->buffer[0] != 'A' || con->buffer[1] != 'G') | ||||
| 	{ | ||||
| 		con->state = ConnectionState_Complete; | ||||
| 		delete [] con->buffer; | ||||
| 		con->buffer = NULL; | ||||
| 		con->writtenCount = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	//Ignore the next 8 bytes for the moment. Versioning data is currently unused
 | ||||
| 	// uint16[4] - source version major/minor/something/rev
 | ||||
| 
 | ||||
| 	con->sentSums = con->buffer[10]; | ||||
| 
 | ||||
| 	con->state = ConnectionState_ReadQueryData; | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Query Header Read Complete, %i md5's expected\n", con->sentSums); | ||||
| #endif | ||||
| 
 | ||||
| 	delete [] con->buffer; | ||||
| 	con->buffer = NULL; | ||||
| 	con->writtenCount = 0; | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::ReplyQuery(smud_connection *con) | ||||
| { | ||||
| 	char data[12]; | ||||
| 	data[0] = 'A'; | ||||
| 	data[1] = 'G'; | ||||
| 
 | ||||
| 	data[2] = (char)Update_Unknown; //unused versioning crap
 | ||||
| 	*(short *)&data[3] = 1; | ||||
| 	*(short *)&data[5] = 0; | ||||
| 	*(short *)&data[7] = 0; | ||||
| 	*(short *)&data[9] = 3; | ||||
| 
 | ||||
| 	data[11] = (char)con->sendCount; | ||||
| 
 | ||||
| 	int bytesSent = send(con->fd, data+con->writtenCount, sizeof(data)-con->writtenCount, 0); | ||||
| 	 | ||||
| 	if (bytesSent == -1) | ||||
| 	{ | ||||
| 		if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 		{ | ||||
| 			con->state = ConnectionState_Complete; | ||||
| 			con->writtenCount = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->writtenCount += bytesSent; | ||||
| 
 | ||||
| 	assert(con->writtenCount <= 12); | ||||
| 
 | ||||
| 	if (con->writtenCount < 12) | ||||
| 	{ | ||||
| 		/** Still more data needs to be sent - Return so we come back here next cycle */ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	con->state = ConnectionState_SendingFiles; | ||||
| 	con->writtenCount = 0; | ||||
| #if defined DEBUG | ||||
| 	printf("Query Reply Header Complete\n"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::ReadQueryContent( smud_connection *con ) | ||||
| { | ||||
| 	if (con->buffer == NULL) | ||||
| 	{ | ||||
| 		con->buffer = new char[QUERY_CONTENT_SIZE*con->sentSums]; | ||||
| 		con->writtenCount = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	int bytesReceived = 0; | ||||
| 
 | ||||
| 	bytesReceived = recv(con->fd, con->buffer+con->writtenCount, (QUERY_CONTENT_SIZE*con->sentSums)-con->writtenCount, 0); | ||||
| 
 | ||||
| 	if (bytesReceived == -1) | ||||
| 	{ | ||||
| 		if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 		{ | ||||
| 			con->state = ConnectionState_Complete; | ||||
| 			delete [] con->buffer; | ||||
| 			con->buffer = NULL; | ||||
| 			con->writtenCount = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->writtenCount += bytesReceived; | ||||
| 
 | ||||
| 	assert(con->writtenCount <= (QUERY_CONTENT_SIZE*con->sentSums)); | ||||
| 
 | ||||
| 	if (con->writtenCount < (QUERY_CONTENT_SIZE*con->sentSums)) | ||||
| 	{ | ||||
| 		/* Don't change the connection status, so next cycle we will come back to here and continue receiving data */ | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->shouldSend = new MD5Status[con->sentSums](); | ||||
| 	con->fileLocation = new int[con->sentSums](); | ||||
| 	con->headerSent = new bool[con->sentSums](); | ||||
| 
 | ||||
| 	for (int i=0; i<con->sentSums; i++) | ||||
| 	{ | ||||
| 		con->fileLocation[i] = -1; | ||||
| 		con->shouldSend[i] = GetMD5UpdateStatus(con->buffer + (QUERY_CONTENT_SIZE*i), con, i); | ||||
| 		 | ||||
| 		if (con->shouldSend[i] == MD5Status_NeedsUpdate) | ||||
| 		{ | ||||
| #if defined DEBUG | ||||
| 			fprintf(stdout, "File %i needs updating\n", i); | ||||
| #endif | ||||
| 			con->sendCount++; | ||||
| 			con->headerSent[i] = false; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (con->shouldSend[i] == MD5Status_Unknown) | ||||
| 		{ | ||||
| #if defined DEBUG | ||||
| 			fprintf(stdout, "File %i is unknown\n", i); | ||||
| #endif | ||||
| 			con->unknownCount++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	con->state = ConnectionState_ReplyQuery; | ||||
| 	con->pollData.events = POLLOUT; | ||||
| 	delete [] con->buffer; | ||||
| 	con->buffer = NULL; | ||||
| 	con->writtenCount = 0; | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Query Data Read Complete\n"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| MD5Status ConnectionPool::GetMD5UpdateStatus( const char *md5 , smud_connection *con, int fileNum) | ||||
| { | ||||
| 	//Try find a file with this name in some directory.
 | ||||
| 	char path[100] = "./md5/"; | ||||
| 	char temp[4]; | ||||
| 	char md5String[33] = ""; | ||||
| 
 | ||||
| 	for (int i=0; i<16; i++) | ||||
| 	{ | ||||
| 		snprintf(temp, sizeof(temp), "%02x", (unsigned char)md5[i]); | ||||
| 		strcat(md5String, temp); | ||||
| 	} | ||||
| 
 | ||||
| 	strcat(path, md5String); | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "checking for file \"%s\"\n", path); | ||||
| #endif | ||||
| 	 | ||||
| 	FILE *file = fopen(path, "r"); | ||||
| 
 | ||||
| 	if (file == NULL) | ||||
| 	{ | ||||
| 		printf("Couldn't find file!\n"); | ||||
| 		return MD5Status_Unknown; | ||||
| 	} | ||||
| 
 | ||||
| 	char latestMD5[33]; | ||||
| 	fgets(latestMD5, 33, file); | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Latest md5 is: %s\n", latestMD5); | ||||
| #endif | ||||
| 
 | ||||
| 	if (strcmp(latestMD5, md5String) == 0) | ||||
| 	{ | ||||
| 		fclose(file); | ||||
| 		return MD5Status_Current; | ||||
| 	} | ||||
| 
 | ||||
| 	char filename[100]; | ||||
| 	filename[0] = '\n'; | ||||
| 
 | ||||
| 	while (filename[0] == '\n') | ||||
| 	{ | ||||
| 		fgets(filename, sizeof(filename), file); | ||||
| 	} | ||||
| 
 | ||||
| 	if (filename[strlen(filename)-1] == '\n') | ||||
| 	{	 | ||||
| 		filename[strlen(filename)-1] = '\0'; | ||||
| 	} | ||||
| 
 | ||||
| 	fclose(file); | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Filename is %s\n", filename); | ||||
| #endif | ||||
| 
 | ||||
| 	//We now need to match this filename with one of our mmap'd files in memory and store it until send gets called.
 | ||||
| 	for (int i=0; i<NUM_FILES; i++) | ||||
| 	{ | ||||
| 		if (strcmp(fileNames[i], filename) == 0) | ||||
| 		{ | ||||
| 			con->fileLocation[fileNum] = i; | ||||
| #if defined DEBUG | ||||
| 			fprintf(stdout, "File %i mapped to local file %i\n", fileNum, i); | ||||
| #endif | ||||
| 			return MD5Status_NeedsUpdate; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return MD5Status_Unknown; | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::SendFile( smud_connection *con ) | ||||
| { | ||||
| 	//Find the next file to send.
 | ||||
| 	while (con->writtenCount == 0 && | ||||
| 			con->currentFile < con->sentSums && | ||||
| 			con->shouldSend[con->currentFile] != MD5Status_NeedsUpdate) | ||||
| 	{ | ||||
| 		con->currentFile++; | ||||
| 	} | ||||
| 
 | ||||
| 	//All files have been sent.
 | ||||
| 	if (con->currentFile >= con->sentSums) | ||||
| 	{ | ||||
| #if defined DEBUG | ||||
| 		fprintf(stdout, "All files sent!\n"); | ||||
| #endif | ||||
| 		con->state = ConnectionState_SendUnknownList; | ||||
| 		con->writtenCount = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	void *file = fileLocations[con->fileLocation[con->currentFile]]; | ||||
| 	int filelength = fileLength[con->fileLocation[con->currentFile]]; | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Sending file of length %i\n", filelength); | ||||
| 	fprintf(stdout, | ||||
| 			"Current file index is: %i, maps to file index: %i\n", | ||||
| 			con->currentFile, | ||||
| 			con->fileLocation[con->currentFile]); | ||||
| #endif | ||||
| 
 | ||||
| 	int sentBytes = 0; | ||||
| 
 | ||||
| 	if (!con->headerSent[con->currentFile]) | ||||
| 	{ | ||||
| 		char buffer[5]; | ||||
| 		buffer[0] = con->currentFile; | ||||
| 		*((int *)&buffer[1]) = filelength; | ||||
| 
 | ||||
| 		sentBytes = send(con->fd, buffer+con->writtenCount, 5-con->writtenCount, 0); | ||||
| 
 | ||||
| 		if (sentBytes == -1) | ||||
| 		{ | ||||
| 			if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 			{ | ||||
| 				con->state = ConnectionState_Complete; | ||||
| 				con->writtenCount = 0; | ||||
| 			} | ||||
| 
 | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		con->writtenCount += sentBytes; | ||||
| 	 | ||||
| 		assert(con->writtenCount <= 5); | ||||
| 
 | ||||
| 		if (con->writtenCount < 5) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		con->headerSent[con->currentFile] = true; | ||||
| 		con->writtenCount = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	sentBytes = send(con->fd, (unsigned char *)file+con->writtenCount, filelength-con->writtenCount, 0); | ||||
| 
 | ||||
| 	if (sentBytes == -1) | ||||
| 	{ | ||||
| 		if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 		{ | ||||
| 			con->state = ConnectionState_Complete; | ||||
| 			con->writtenCount = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->writtenCount += sentBytes; | ||||
| 
 | ||||
| 	assert(con->writtenCount <= filelength); | ||||
| 
 | ||||
| 	if (con->writtenCount < filelength) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->currentFile++; | ||||
| 	con->writtenCount = 0; | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Sent a file!: %s\n", fileNames[con->fileLocation[con->currentFile-1]]); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void ConnectionPool::SendUnknownList( smud_connection *con ) | ||||
| { | ||||
| 	int size = con->unknownCount+1; | ||||
| 	char *packet = new char[size](); | ||||
| 
 | ||||
| 	packet[0] = con->unknownCount; | ||||
| 
 | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "%i Files are unknown\n", con->unknownCount); | ||||
| #endif | ||||
| 
 | ||||
| 	int i=1; | ||||
| 
 | ||||
| 	for (int j=0; j<con->sentSums; j++) | ||||
| 	{ | ||||
| 		if (con->shouldSend[j] == MD5Status_Unknown) | ||||
| 		{ | ||||
| 			packet[i] = j; | ||||
| 			i++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	int sentBytes = send(con->fd, packet+con->writtenCount, size-con->writtenCount, 0); | ||||
| 	 | ||||
| 	if (sentBytes == -1) | ||||
| 	{ | ||||
| 		if (errno != EAGAIN && errno != EWOULDBLOCK) | ||||
| 		{ | ||||
| 			con->state = ConnectionState_Complete; | ||||
| 			con->writtenCount = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->writtenCount += sentBytes; | ||||
| 
 | ||||
| 	assert(con->writtenCount <= size); | ||||
| 
 | ||||
| 	if (con->writtenCount < size) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	con->state = ConnectionState_Complete; | ||||
| 	con->writtenCount = 0; | ||||
| #if defined DEBUG | ||||
| 	fprintf(stdout, "Unknowns Sent\n"); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| @ -1,116 +0,0 @@ | ||||
| #ifndef _INCLUDE_SMUD_CONNECTION_H_ | ||||
| #define _INCLUDE_SMUD_CONNECTION_H_ | ||||
| 
 | ||||
| #include "smud.h" | ||||
| #include <list> | ||||
| #include "poll.h" | ||||
| 
 | ||||
| #define QUERY_HEADER_SIZE 11 | ||||
| #define QUERY_CONTENT_SIZE 16 | ||||
| 
 | ||||
| enum ConnectionState | ||||
| { | ||||
| 	ConnectionState_ReadQueryHeader, | ||||
| 	ConnectionState_ReadQueryData, | ||||
| 	ConnectionState_ReplyQuery, | ||||
| 	ConnectionState_SendingFiles, | ||||
| 	ConnectionState_SendUnknownList, | ||||
| 	ConnectionState_Complete, | ||||
| }; | ||||
| 
 | ||||
| enum QueryResult | ||||
| { | ||||
| 	QueryResult_Continue, | ||||
| 	QueryResult_Complete, | ||||
| }; | ||||
| 
 | ||||
| enum MD5Status | ||||
| { | ||||
| 	MD5Status_Unknown, | ||||
| 	MD5Status_Current, | ||||
| 	MD5Status_NeedsUpdate, | ||||
| }; | ||||
| 
 | ||||
| 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) */ | ||||
| }; | ||||
| 
 | ||||
| struct smud_connection | ||||
| { | ||||
| 	smud_connection(int fd) | ||||
| 	{ | ||||
| 		shouldSend = NULL; | ||||
| 		fileLocation = NULL; | ||||
| 		headerSent = NULL; | ||||
| 		sentSums = 0; | ||||
| 		sendCount = 0; | ||||
| 		currentFile = 0; | ||||
| 		unknownCount = 0; | ||||
| 		pollData.events = POLLIN; | ||||
| 		start = time(NULL); | ||||
| 		state = ConnectionState_ReadQueryHeader; | ||||
| 		this->fd = fd; | ||||
| 		pollData.fd = fd; | ||||
| 		buffer = NULL; | ||||
| 		writtenCount = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	~smud_connection() | ||||
| 	{ | ||||
| 		if (shouldSend != NULL) | ||||
| 			delete [] shouldSend; | ||||
| 		if (fileLocation != NULL) | ||||
| 			delete [] fileLocation; | ||||
| 		if (headerSent != NULL) | ||||
| 			delete [] headerSent; | ||||
| 	} | ||||
| 
 | ||||
| 	int fd;							/** Socket file descriptor */ | ||||
| 	time_t start;					/** The time this connection was received (for timeouts) */ | ||||
| 	ConnectionState state;			/** How far through processing the connection we are */ | ||||
| 	uint8_t sentSums;				/** Number of MD5 Sums sent from the client */ | ||||
| 	MD5Status *shouldSend;			/** Arrays of statuses for each sum */ | ||||
| 	int *fileLocation;				/** Array of indexes into the global file list for each sum (only valid if shouldSend[i] == MD5Status_NeedsUpdate) */ | ||||
| 	bool *headerSent;				/** Has the header been sent yet for each sum? Header == file index and size */ | ||||
| 	int sendCount;					/** Number of files that need to be sent */ | ||||
| 	int unknownCount;				/** Number of files that were unknown */ | ||||
| 	int currentFile;				/** Current file being sent (index into the above 3 arrays) */ | ||||
| 	pollfd pollData;				/** Data to be passed into poll() */ | ||||
| 	char *buffer;					/** Temporary storage buffer to hold data until all of it is available */ | ||||
| 	int writtenCount;				/** Number of bytes written into the storage buffer */ | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class ConnectionPool | ||||
| { | ||||
| public: | ||||
| 	ConnectionPool(); | ||||
| 	~ConnectionPool(); | ||||
| public: | ||||
| 	void AddConnection(int fd); | ||||
| 	void Process(bool *terminate); | ||||
| private: | ||||
| 	QueryResult ProcessConnection(smud_connection *con); | ||||
| 	void ReadQueryHeader(smud_connection *con); | ||||
| 	void ReadQueryContent(smud_connection *con); | ||||
| 	void ReplyQuery(smud_connection *con); | ||||
| 	void SendFile(smud_connection *con); | ||||
| 	void SendUnknownList(smud_connection *con); | ||||
| 
 | ||||
| 	MD5Status GetMD5UpdateStatus(const char *md5, smud_connection *con, int fileNum); | ||||
| private: | ||||
| 	std::list<smud_connection *> m_Links; | ||||
| 	std::list<smud_connection *> m_AddQueue; | ||||
| 	pthread_mutex_t m_AddLock; | ||||
| 	time_t m_timeOut; | ||||
| }; | ||||
| 
 | ||||
| #endif //_INCLUDE_SMUD_CONNECTION_H_
 | ||||
| 
 | ||||
| @ -1,86 +0,0 @@ | ||||
| #include "smud_threads.h" | ||||
| 
 | ||||
| ThreadPool::ThreadPool()  | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ThreadPool::~ThreadPool() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| bool ThreadPool::Start() | ||||
| { | ||||
| 	m_pWorker = new ThreadWorker(); | ||||
| 	 | ||||
| 	if (!m_pWorker->Start()) | ||||
| 	{ | ||||
| 		delete m_pWorker; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void ThreadPool::Stop() | ||||
| { | ||||
| 	m_pWorker->CancelAndWait(); | ||||
| 	delete m_pWorker; | ||||
| } | ||||
| 
 | ||||
| void ThreadPool::AddConnection(int fd) | ||||
| { | ||||
| 	m_pWorker->AddConnection(fd); | ||||
| } | ||||
| 
 | ||||
| ThreadWorker::ThreadWorker() : m_bShouldCancel(false) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ThreadWorker::~ThreadWorker() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| bool ThreadWorker::Start() | ||||
| { | ||||
| 	m_pPool = new ConnectionPool(); | ||||
| 	pthread_mutex_init(&m_NotifyLock, NULL); | ||||
| 	pthread_cond_init(&m_Notify, NULL); | ||||
| 
 | ||||
| 	if (pthread_create(&m_Thread, NULL, ThreadCallback, this) != 0) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void ThreadWorker::CancelAndWait() | ||||
| { | ||||
| 	m_bShouldCancel = true; | ||||
| 
 | ||||
| 	pthread_join(m_Thread, NULL); | ||||
| 
 | ||||
| 	pthread_cond_destroy(&m_Notify); | ||||
| 	pthread_mutex_destroy(&m_NotifyLock); | ||||
| 
 | ||||
| 	delete m_pPool; | ||||
| } | ||||
| 
 | ||||
| void ThreadWorker::AddConnection( int fd ) | ||||
| { | ||||
| 	m_pPool->AddConnection(fd); | ||||
| } | ||||
| 
 | ||||
| void ThreadWorker::Process() | ||||
| { | ||||
| 	m_pPool->Process(&m_bShouldCancel); | ||||
| } | ||||
| 
 | ||||
| void *ThreadCallback(void *data) | ||||
| { | ||||
| 	((ThreadWorker *)data)->Process(); | ||||
| 
 | ||||
| 	pthread_exit(NULL); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1,41 +0,0 @@ | ||||
| #ifndef _INCLUDE_SMUD_H_ | ||||
| #define _INCLUDE_SMUD_H_ | ||||
| 
 | ||||
| #include "smud.h" | ||||
| #include "smud_connections.h" | ||||
| 
 | ||||
| void *ThreadCallback(void *data); | ||||
| 
 | ||||
| class ThreadWorker | ||||
| { | ||||
| public: | ||||
| 	ThreadWorker(); | ||||
| 	~ThreadWorker(); | ||||
| public: | ||||
| 	bool Start(); | ||||
| 	void CancelAndWait(); | ||||
| 	void AddConnection(int fd); | ||||
| 	void Process(); | ||||
| private: | ||||
| 	ConnectionPool *m_pPool; | ||||
| 	pthread_t m_Thread; | ||||
| 	pthread_mutex_t m_NotifyLock; | ||||
| 	pthread_cond_t m_Notify; | ||||
| 	bool m_bShouldCancel; | ||||
| }; | ||||
| 
 | ||||
| class ThreadPool | ||||
| { | ||||
| public: | ||||
| 	ThreadPool(); | ||||
| 	~ThreadPool(); | ||||
| public: | ||||
| 	void AddConnection(int fd); | ||||
| 	bool Start(); | ||||
| 	void Stop(); | ||||
| private: | ||||
| 	ThreadWorker *m_pWorker; | ||||
| }; | ||||
| 
 | ||||
| #endif //_INCLUDE_SMUD_H_
 | ||||
| 
 | ||||
| @ -1,30 +0,0 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| FILENAME="" | ||||
| SUMSFILE="" | ||||
| 
 | ||||
| if [ -n "$1" ] | ||||
| then | ||||
| 	FILENAME="$1.txt" | ||||
| 	SUMSFILE="$1.sums" | ||||
| else | ||||
| 	echo "Need to specify a gamedata filename" | ||||
| 	exit -1 | ||||
| fi | ||||
| 
 | ||||
| 
 | ||||
| if [ -s $FILENAME ] | ||||
| then | ||||
| 	#run ./gamedatamd5 on this file and pipe output+filename into $1.sums | ||||
| 	MD5=`./gamedatamd5 $FILENAME` | ||||
| 	#need to stop here if gamedatamd5 failed. (returns -1 and prints to stderr) | ||||
| 	echo "$MD5" > "$SUMSFILE" | ||||
| 	echo "$FILENAME" >> "$SUMSFILE" | ||||
| 	ln -s "$SUMSFILE" "$MD5" | ||||
| 
 | ||||
| 	exit 0 | ||||
| fi | ||||
| 
 | ||||
| echo "File $FILENAME not found!" | ||||
| 
 | ||||
| exit -1 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user