core/GameDataFetcher.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 06 04:49:55 2008 +0000 (2008-07-06)
branchrefac-jit
changeset 2168 c24a6fa957db
permissions -rw-r--r--
jit refactoring branch
dvander@2168
     1
/**
dvander@2168
     2
* vim: set ts=4 :
dvander@2168
     3
* =============================================================================
dvander@2168
     4
* SourceMod
dvander@2168
     5
* Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2168
     6
* =============================================================================
dvander@2168
     7
*
dvander@2168
     8
* This program is free software; you can redistribute it and/or modify it under
dvander@2168
     9
* the terms of the GNU General Public License, version 3.0, as published by the
dvander@2168
    10
* Free Software Foundation.
dvander@2168
    11
* 
dvander@2168
    12
* This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2168
    13
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2168
    14
* FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2168
    15
* details.
dvander@2168
    16
*
dvander@2168
    17
* You should have received a copy of the GNU General Public License along with
dvander@2168
    18
* this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2168
    19
*
dvander@2168
    20
* As a special exception, AlliedModders LLC gives you permission to link the
dvander@2168
    21
* code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2168
    22
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2168
    23
* by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2168
    24
* all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2168
    25
* this exception to all derivative works.  AlliedModders LLC defines further
dvander@2168
    26
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2168
    27
* or <http://www.sourcemod.net/license.php>.
dvander@2168
    28
*
dvander@2168
    29
* Version: $Id$
dvander@2168
    30
*/
dvander@2168
    31
dvander@2168
    32
#include "GameDataFetcher.h"
dvander@2168
    33
#include "bitbuf.h"
dvander@2168
    34
dvander@2168
    35
#ifdef PLATFORM_WINDOWS
dvander@2168
    36
#include <winsock2.h>
dvander@2168
    37
#else
dvander@2168
    38
#include <unistd.h>
dvander@2168
    39
#include <sys/types.h>
dvander@2168
    40
#include <sys/socket.h>
dvander@2168
    41
#include <netinet/in.h>
dvander@2168
    42
#include <netdb.h>
dvander@2168
    43
#include <arpa/inet.h>
dvander@2168
    44
dvander@2168
    45
#define INVALID_SOCKET -1
dvander@2168
    46
#define closesocket close
dvander@2168
    47
#endif
dvander@2168
    48
dvander@2168
    49
#include "sh_vector.h"
dvander@2168
    50
#include "sh_string.h"
dvander@2168
    51
#include "sm_version.h"
dvander@2168
    52
dvander@2168
    53
#ifdef ORANGEBOX_BUILD
dvander@2168
    54
#include "convar_sm_ob.h"
dvander@2168
    55
#else
dvander@2168
    56
#include "convar_sm.h"
dvander@2168
    57
#endif
dvander@2168
    58
dvander@2168
    59
#include "sourcemm_api.h"
dvander@2168
    60
#include "time.h"
dvander@2168
    61
#include "TimerSys.h"
dvander@2168
    62
#include "compat_wrappers.h"
dvander@2168
    63
dvander@2168
    64
#define QUERY_MAX_LENGTH 1024
dvander@2168
    65
dvander@2168
    66
BuildMD5ableBuffer g_MD5Builder;
dvander@2168
    67
FetcherThread g_FetchThread;
dvander@2168
    68
dvander@2168
    69
FILE *logfile = NULL;
dvander@2168
    70
dvander@2168
    71
bool g_disableGameDataUpdate = false;
dvander@2168
    72
bool g_restartAfterUpdate = false;
dvander@2168
    73
dvander@2168
    74
void FetcherThread::RunThread( IThreadHandle *pHandle )
dvander@2168
    75
{
dvander@2168
    76
	char lock_path[PLATFORM_MAX_PATH];
dvander@2168
    77
	g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
dvander@2168
    78
	g_LibSys.CreateFolder(lock_path);
dvander@2168
    79
dvander@2168
    80
	g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
dvander@2168
    81
dvander@2168
    82
	g_Logger.LogMessage("Starting experimental gamedata update fetcher... please report problems to bugs.alliedmods.net");
dvander@2168
    83
dvander@2168
    84
	char log_path[PLATFORM_MAX_PATH];
dvander@2168
    85
	g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata");
dvander@2168
    86
dvander@2168
    87
	g_LibSys.CreateFolder(log_path);
dvander@2168
    88
dvander@2168
    89
	time_t t;
dvander@2168
    90
	GetAdjustedTime(&t);
dvander@2168
    91
	tm *curtime = localtime(&t);
dvander@2168
    92
dvander@2168
    93
	g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday);
dvander@2168
    94
dvander@2168
    95
	logfile = fopen(log_path, "a");
dvander@2168
    96
dvander@2168
    97
	if (!logfile)
dvander@2168
    98
	{
dvander@2168
    99
		g_Logger.LogError("Failed to create GameData log file");
dvander@2168
   100
		return;
dvander@2168
   101
	}
dvander@2168
   102
dvander@2168
   103
	//Create a blank lock file
dvander@2168
   104
	FILE *fp = fopen(lock_path, "w");
dvander@2168
   105
	if (fp)
dvander@2168
   106
	{
dvander@2168
   107
		fclose(fp);
dvander@2168
   108
	}
dvander@2168
   109
dvander@2168
   110
	/* Create a new socket for this connection */
dvander@2168
   111
	int socketDescriptor = ConnectSocket();
dvander@2168
   112
dvander@2168
   113
	if (socketDescriptor == INVALID_SOCKET)
dvander@2168
   114
	{
dvander@2168
   115
		fclose(logfile);
dvander@2168
   116
		unlink(lock_path);
dvander@2168
   117
		return;
dvander@2168
   118
	}
dvander@2168
   119
dvander@2168
   120
	char query[QUERY_MAX_LENGTH];
dvander@2168
   121
dvander@2168
   122
	/* Check for updated gamedata files */
dvander@2168
   123
	int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH);
dvander@2168
   124
dvander@2168
   125
	if (len == 0)
dvander@2168
   126
	{
dvander@2168
   127
		g_Logger.LogToOpenFile(logfile, "Query Writing failed");
dvander@2168
   128
dvander@2168
   129
		closesocket(socketDescriptor);
dvander@2168
   130
		fclose(logfile);
dvander@2168
   131
		unlink(lock_path);
dvander@2168
   132
		return;
dvander@2168
   133
	}
dvander@2168
   134
dvander@2168
   135
	if (g_disableGameDataUpdate)
dvander@2168
   136
	{
dvander@2168
   137
		g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
dvander@2168
   138
dvander@2168
   139
		closesocket(socketDescriptor);
dvander@2168
   140
		fclose(logfile);
dvander@2168
   141
		unlink(lock_path);
dvander@2168
   142
		return;
dvander@2168
   143
	}
dvander@2168
   144
dvander@2168
   145
	int sent = NonBlockingSend(socketDescriptor, query, len);
dvander@2168
   146
dvander@2168
   147
	g_Logger.LogToOpenFile(logfile, "Sent Query!");
dvander@2168
   148
dvander@2168
   149
	if (sent == 0)
dvander@2168
   150
	{
dvander@2168
   151
		g_Logger.LogToOpenFile(logfile, "Failed to send data");
dvander@2168
   152
dvander@2168
   153
		closesocket(socketDescriptor);
dvander@2168
   154
		fclose(logfile);
dvander@2168
   155
		unlink(lock_path);
dvander@2168
   156
		return;
dvander@2168
   157
	}
dvander@2168
   158
dvander@2168
   159
	ProcessGameDataQuery(socketDescriptor);
dvander@2168
   160
dvander@2168
   161
	/* And we're done! */
dvander@2168
   162
	closesocket(socketDescriptor);
dvander@2168
   163
	fclose(logfile);
dvander@2168
   164
	unlink(lock_path);
dvander@2168
   165
}
dvander@2168
   166
dvander@2168
   167
void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
dvander@2168
   168
{
dvander@2168
   169
	//delete this;
dvander@2168
   170
}
dvander@2168
   171
dvander@2168
   172
int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen )
dvander@2168
   173
{
dvander@2168
   174
	char gamedata_path[PLATFORM_MAX_PATH];
dvander@2168
   175
	g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
dvander@2168
   176
dvander@2168
   177
	IDirectory *dir = g_LibSys.OpenDirectory(gamedata_path);
dvander@2168
   178
dvander@2168
   179
	if (dir == NULL)
dvander@2168
   180
	{
dvander@2168
   181
		return 0;
dvander@2168
   182
	}
dvander@2168
   183
dvander@2168
   184
	bf_write Writer = bf_write("GameDataQuery", buffer, maxlen);
dvander@2168
   185
dvander@2168
   186
	Writer.WriteByte('A'); //Generic Header char
dvander@2168
   187
	Writer.WriteByte('G'); //G for gamedata query, or green, like my hat.
dvander@2168
   188
dvander@2168
   189
	short build[4] = { SVN_FILE_VERSION };
dvander@2168
   190
dvander@2168
   191
	Writer.WriteBytes(&build[0], 8);
dvander@2168
   192
dvander@2168
   193
	Writer.WriteByte(0); // Initialize the file counter - Index 10
dvander@2168
   194
dvander@2168
   195
	while (dir->MoreFiles())
dvander@2168
   196
	{
dvander@2168
   197
		if (dir->IsEntryFile())
dvander@2168
   198
		{
dvander@2168
   199
			const char *name = dir->GetEntryName();
dvander@2168
   200
			size_t len = strlen(name);
dvander@2168
   201
			if (len >= 4
dvander@2168
   202
				&& strcmp(&name[len-4], ".txt") == 0)
dvander@2168
   203
			{
dvander@2168
   204
				char file[PLATFORM_MAX_PATH];
dvander@2168
   205
dvander@2168
   206
				g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
dvander@2168
   207
dvander@2168
   208
				SMCStates states;
dvander@2168
   209
				if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay)
dvander@2168
   210
				{
dvander@2168
   211
					unsigned char *md5 = g_MD5Builder.GetMD5();
dvander@2168
   212
					if (md5 != NULL)
dvander@2168
   213
					{
dvander@2168
   214
						(uint8_t)buffer[10]++; //Increment the file counter
dvander@2168
   215
						Writer.WriteBytes(md5, 16);
dvander@2168
   216
dvander@2168
   217
						g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String());
dvander@2168
   218
dvander@2168
   219
						FileData *data = new FileData();
dvander@2168
   220
						data->filename = new SourceHook::String(file);
dvander@2168
   221
						memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
dvander@2168
   222
						filenames.push_back(data);
dvander@2168
   223
					}
dvander@2168
   224
					else
dvander@2168
   225
					{
dvander@2168
   226
						g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
dvander@2168
   227
					}
dvander@2168
   228
				}
dvander@2168
   229
				else
dvander@2168
   230
				{
dvander@2168
   231
					g_Logger.LogToOpenFile(logfile, "%s failed!", file);
dvander@2168
   232
				}
dvander@2168
   233
			}
dvander@2168
   234
		}
dvander@2168
   235
dvander@2168
   236
		dir->NextEntry();
dvander@2168
   237
	}
dvander@2168
   238
dvander@2168
   239
	return Writer.GetNumBytesWritten();
dvander@2168
   240
}
dvander@2168
   241
dvander@2168
   242
int FetcherThread::ConnectSocket()
dvander@2168
   243
{
dvander@2168
   244
	struct protoent *ptrp;
dvander@2168
   245
	ptrp = getprotobyname("tcp");
dvander@2168
   246
dvander@2168
   247
#ifdef WIN32
dvander@2168
   248
	WSADATA wsaData;
dvander@2168
   249
	WSAStartup(0x0101, &wsaData);
dvander@2168
   250
#endif
dvander@2168
   251
dvander@2168
   252
	if (ptrp == NULL) 
dvander@2168
   253
	{
dvander@2168
   254
		g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
dvander@2168
   255
		return INVALID_SOCKET;
dvander@2168
   256
	}
dvander@2168
   257
dvander@2168
   258
	int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto);
dvander@2168
   259
dvander@2168
   260
	if (socketDescriptor == INVALID_SOCKET)
dvander@2168
   261
	{
dvander@2168
   262
		//bugger aye?
dvander@2168
   263
		g_Logger.LogToOpenFile(logfile, "Failed to create a new socket");
dvander@2168
   264
		closesocket(socketDescriptor);
dvander@2168
   265
		return INVALID_SOCKET;
dvander@2168
   266
	}
dvander@2168
   267
dvander@2168
   268
	struct hostent *he;
dvander@2168
   269
	struct sockaddr_in local_addr;
dvander@2168
   270
dvander@2168
   271
	local_addr.sin_family = AF_INET;
dvander@2168
   272
	local_addr.sin_port = htons((u_short)6500);
dvander@2168
   273
dvander@2168
   274
	he = gethostbyname("hayate.alliedmods.net");
dvander@2168
   275
dvander@2168
   276
	if (!he)
dvander@2168
   277
	{
dvander@2168
   278
		if ((local_addr.sin_addr.s_addr = inet_addr("hayate.alliedmods.net")) == INADDR_NONE)
dvander@2168
   279
		{
dvander@2168
   280
			g_Logger.LogToOpenFile(logfile, "Couldnt locate address");
dvander@2168
   281
			closesocket(socketDescriptor);
dvander@2168
   282
			return INVALID_SOCKET;
dvander@2168
   283
		}
dvander@2168
   284
	} 
dvander@2168
   285
	else 
dvander@2168
   286
	{
dvander@2168
   287
		memcpy(&local_addr.sin_addr, (struct in_addr *)he->h_addr, he->h_length);
dvander@2168
   288
	}
dvander@2168
   289
dvander@2168
   290
	if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
dvander@2168
   291
	{
dvander@2168
   292
		g_Logger.LogToOpenFile(logfile, "Couldn't connect");
dvander@2168
   293
		closesocket(socketDescriptor);
dvander@2168
   294
		return INVALID_SOCKET;
dvander@2168
   295
	}
dvander@2168
   296
dvander@2168
   297
	return socketDescriptor;
dvander@2168
   298
}
dvander@2168
   299
dvander@2168
   300
void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
dvander@2168
   301
{
dvander@2168
   302
	char buffer[50];
dvander@2168
   303
dvander@2168
   304
	g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
dvander@2168
   305
dvander@2168
   306
	//Read in the header bytes
dvander@2168
   307
	int returnLen = NonBlockingRecv(socketDescriptor, buffer, 12);
dvander@2168
   308
dvander@2168
   309
	g_Logger.LogToOpenFile(logfile, "Recv Completed");
dvander@2168
   310
dvander@2168
   311
	if (returnLen == 0)
dvander@2168
   312
	{
dvander@2168
   313
		g_Logger.LogToOpenFile(logfile, ",but it failed.");
dvander@2168
   314
		/* Timeout or fail? */
dvander@2168
   315
		return;
dvander@2168
   316
	}
dvander@2168
   317
dvander@2168
   318
	g_Logger.LogToOpenFile(logfile, "Received Header!");
dvander@2168
   319
dvander@2168
   320
	bf_read Reader = bf_read("GameDataQuery", buffer, 12);
dvander@2168
   321
dvander@2168
   322
	if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
dvander@2168
   323
	{
dvander@2168
   324
		g_Logger.LogToOpenFile(logfile, "Invalid Query to handle");
dvander@2168
   325
		return;
dvander@2168
   326
	}
dvander@2168
   327
dvander@2168
   328
	UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
dvander@2168
   329
dvander@2168
   330
	short build[4] = {0,0,0,0};
dvander@2168
   331
dvander@2168
   332
	build[0] = Reader.ReadShort();
dvander@2168
   333
	build[1] = Reader.ReadShort();
dvander@2168
   334
	build[2] = Reader.ReadShort();
dvander@2168
   335
	build[3] = Reader.ReadShort();
dvander@2168
   336
dvander@2168
   337
	g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
dvander@2168
   338
dvander@2168
   339
	HandleUpdateStatus(updateStatus, build);
dvander@2168
   340
dvander@2168
   341
	int changedFiles = Reader.ReadByte();
dvander@2168
   342
dvander@2168
   343
	g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
dvander@2168
   344
dvander@2168
   345
	for (int i=0; i<changedFiles; i++)
dvander@2168
   346
	{
dvander@2168
   347
		//Read in the file index and byte count
dvander@2168
   348
		returnLen = NonBlockingRecv(socketDescriptor, buffer, 5);
dvander@2168
   349
dvander@2168
   350
		if (returnLen == 0)
dvander@2168
   351
		{
dvander@2168
   352
			/* Timeout or fail? */
dvander@2168
   353
			return;
dvander@2168
   354
		}
dvander@2168
   355
dvander@2168
   356
		Reader.StartReading(buffer, 5);
dvander@2168
   357
dvander@2168
   358
		int index = Reader.ReadByte();
dvander@2168
   359
		int tempLen = Reader.ReadUBitLong(32);
dvander@2168
   360
dvander@2168
   361
		g_Logger.LogToOpenFile(logfile, "File index %i and length %i", index, tempLen);
dvander@2168
   362
dvander@2168
   363
		void *memPtr;
dvander@2168
   364
		memtable->CreateMem(tempLen+1, &memPtr);
dvander@2168
   365
dvander@2168
   366
		//Read the contents of our file into the memtable
dvander@2168
   367
		returnLen = NonBlockingRecv(socketDescriptor, (char *)memPtr, tempLen);
dvander@2168
   368
dvander@2168
   369
		g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
dvander@2168
   370
dvander@2168
   371
		if (returnLen == 0)
dvander@2168
   372
		{
dvander@2168
   373
			/* Timeout or fail? */
dvander@2168
   374
			return;
dvander@2168
   375
		}
dvander@2168
   376
dvander@2168
   377
		((unsigned char *)memPtr)[tempLen] = '\0';
dvander@2168
   378
dvander@2168
   379
		FileData *data = filenames.at(index);
dvander@2168
   380
		const char* filename;
dvander@2168
   381
		if (data != NULL)
dvander@2168
   382
		{
dvander@2168
   383
			filename = data->filename->c_str();
dvander@2168
   384
dvander@2168
   385
			FILE *fp = fopen(filename, "w");
dvander@2168
   386
dvander@2168
   387
			if (fp)
dvander@2168
   388
			{
dvander@2168
   389
				fprintf(fp, (const char *)memPtr);
dvander@2168
   390
				fclose(fp);
dvander@2168
   391
			}
dvander@2168
   392
		}
dvander@2168
   393
		else
dvander@2168
   394
		{
dvander@2168
   395
			filename = "";
dvander@2168
   396
		}
dvander@2168
   397
dvander@2168
   398
		memtable->Reset();
dvander@2168
   399
dvander@2168
   400
		g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
dvander@2168
   401
	}
dvander@2168
   402
dvander@2168
   403
	g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
dvander@2168
   404
dvander@2168
   405
	bool needsRestart = false;
dvander@2168
   406
dvander@2168
   407
	if (changedFiles > 0)
dvander@2168
   408
	{
dvander@2168
   409
		needsRestart = true;
dvander@2168
   410
		g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect");
dvander@2168
   411
	}
dvander@2168
   412
dvander@2168
   413
	//Read changed file count
dvander@2168
   414
	returnLen = NonBlockingRecv(socketDescriptor, buffer, 1);
dvander@2168
   415
dvander@2168
   416
	if (returnLen == 0)
dvander@2168
   417
	{
dvander@2168
   418
		/* Timeout or fail? */
dvander@2168
   419
		g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
dvander@2168
   420
		return;
dvander@2168
   421
	}
dvander@2168
   422
dvander@2168
   423
	Reader.StartReading(buffer, 1);
dvander@2168
   424
dvander@2168
   425
	changedFiles = Reader.ReadByte();
dvander@2168
   426
dvander@2168
   427
	if (changedFiles == 0)
dvander@2168
   428
	{
dvander@2168
   429
		g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
dvander@2168
   430
		return;
dvander@2168
   431
	}
dvander@2168
   432
dvander@2168
   433
	char *changedFileIndexes = new char[changedFiles];
dvander@2168
   434
	
dvander@2168
   435
	g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
dvander@2168
   436
	
dvander@2168
   437
	returnLen = NonBlockingRecv(socketDescriptor, changedFileIndexes, changedFiles);
dvander@2168
   438
dvander@2168
   439
	if (returnLen == 0)
dvander@2168
   440
	{
dvander@2168
   441
		/* Timeout or fail? */
dvander@2168
   442
		g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
dvander@2168
   443
		return;
dvander@2168
   444
	}
dvander@2168
   445
dvander@2168
   446
	Reader.StartReading(changedFileIndexes, changedFiles);
dvander@2168
   447
dvander@2168
   448
	for (int i=0; i<changedFiles; i++)
dvander@2168
   449
	{
dvander@2168
   450
		int index = Reader.ReadByte();
dvander@2168
   451
		char fileName[30];
dvander@2168
   452
dvander@2168
   453
		FileData *data = filenames.at(index);
dvander@2168
   454
		const char* pathname;
dvander@2168
   455
		if (data != NULL)
dvander@2168
   456
		{
dvander@2168
   457
			pathname = data->filename->c_str();
dvander@2168
   458
		}
dvander@2168
   459
		else
dvander@2168
   460
		{
dvander@2168
   461
			pathname = "";
dvander@2168
   462
		}
dvander@2168
   463
dvander@2168
   464
		g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
dvander@2168
   465
dvander@2168
   466
		g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
dvander@2168
   467
	}
dvander@2168
   468
dvander@2168
   469
	delete [] changedFileIndexes;
dvander@2168
   470
dvander@2168
   471
	if (needsRestart && g_restartAfterUpdate)
dvander@2168
   472
	{
dvander@2168
   473
		g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
dvander@2168
   474
		engine->ServerCommand("quit\n");
dvander@2168
   475
	}
dvander@2168
   476
}
dvander@2168
   477
dvander@2168
   478
int FetcherThread::NonBlockingRecv( int socketDescriptor, char *buffer, int len )
dvander@2168
   479
{
dvander@2168
   480
	fd_set fds;
dvander@2168
   481
	struct timeval tv;
dvander@2168
   482
dvander@2168
   483
	/* Create a 10 Second Timeout */
dvander@2168
   484
	tv.tv_sec = 10;
dvander@2168
   485
	tv.tv_usec = 0;
dvander@2168
   486
dvander@2168
   487
	/* Add our socket to a socket set */
dvander@2168
   488
	FD_ZERO(&fds);
dvander@2168
   489
	FD_SET(socketDescriptor, &fds);
dvander@2168
   490
dvander@2168
   491
	/* Wait max of 10 seconds for recv to become available */
dvander@2168
   492
	select(socketDescriptor+1, &fds, NULL, NULL, &tv);
dvander@2168
   493
dvander@2168
   494
	int bytesReceived = 0;
dvander@2168
   495
dvander@2168
   496
	/* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
dvander@2168
   497
	if (FD_ISSET(socketDescriptor, &fds))
dvander@2168
   498
	{
dvander@2168
   499
		bytesReceived = recv(socketDescriptor, buffer, len, 0);
dvander@2168
   500
	}
dvander@2168
   501
dvander@2168
   502
	if (bytesReceived == 0 || bytesReceived == -1)
dvander@2168
   503
	{
dvander@2168
   504
		return 0;
dvander@2168
   505
	}
dvander@2168
   506
dvander@2168
   507
	if (bytesReceived < len)
dvander@2168
   508
	{
dvander@2168
   509
		return bytesReceived + NonBlockingRecv(socketDescriptor, buffer+bytesReceived, len-bytesReceived);
dvander@2168
   510
	}
dvander@2168
   511
dvander@2168
   512
	return bytesReceived;	
dvander@2168
   513
}
dvander@2168
   514
dvander@2168
   515
int FetcherThread::NonBlockingSend( int socketDescriptor, char *buffer, int len )
dvander@2168
   516
{
dvander@2168
   517
	fd_set fds;
dvander@2168
   518
	struct timeval tv;
dvander@2168
   519
dvander@2168
   520
	tv.tv_sec = 10;
dvander@2168
   521
	tv.tv_usec = 0;
dvander@2168
   522
dvander@2168
   523
	FD_ZERO(&fds);
dvander@2168
   524
	FD_SET(socketDescriptor, &fds);
dvander@2168
   525
dvander@2168
   526
	select(socketDescriptor+1, NULL, &fds, NULL, &tv);
dvander@2168
   527
dvander@2168
   528
	int sentBytes = 0;
dvander@2168
   529
dvander@2168
   530
	if (FD_ISSET(socketDescriptor, &fds))
dvander@2168
   531
	{
dvander@2168
   532
		sentBytes = send(socketDescriptor, buffer, len, 0);
dvander@2168
   533
	}
dvander@2168
   534
dvander@2168
   535
	if (sentBytes == 0 || sentBytes == -1)
dvander@2168
   536
	{
dvander@2168
   537
		return 0;
dvander@2168
   538
	}
dvander@2168
   539
dvander@2168
   540
	if (sentBytes < len)
dvander@2168
   541
	{
dvander@2168
   542
		return sentBytes + NonBlockingSend(socketDescriptor, buffer+sentBytes, len-sentBytes);
dvander@2168
   543
	}
dvander@2168
   544
dvander@2168
   545
	return sentBytes;
dvander@2168
   546
}
dvander@2168
   547
dvander@2168
   548
void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] )
dvander@2168
   549
{
dvander@2168
   550
	switch (status)
dvander@2168
   551
	{
dvander@2168
   552
		case Update_Unknown:
dvander@2168
   553
		case Update_Current:
dvander@2168
   554
		{
dvander@2168
   555
			break;
dvander@2168
   556
		}
dvander@2168
   557
dvander@2168
   558
		case Update_NewBuild:
dvander@2168
   559
		{
dvander@2168
   560
			g_Logger.LogMessage("SourceMod Update: A new SVN build is available from sourcemod.net");
dvander@2168
   561
			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]);
dvander@2168
   562
			break;
dvander@2168
   563
		}
dvander@2168
   564
dvander@2168
   565
		case Update_MinorAvailable:
dvander@2168
   566
		{
dvander@2168
   567
			g_Logger.LogMessage("SourceMod Update: An incremental minor release of SourceMod is now available from sourcemod.net");
dvander@2168
   568
			g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
dvander@2168
   569
			break;
dvander@2168
   570
		}
dvander@2168
   571
dvander@2168
   572
		case Update_MajorAvailable:
dvander@2168
   573
		{
dvander@2168
   574
			g_Logger.LogMessage("SourceMod Update: An major release of SourceMod is now available from sourcemod.net");
dvander@2168
   575
			g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
dvander@2168
   576
			break;
dvander@2168
   577
		}
dvander@2168
   578
dvander@2168
   579
		case Update_CriticalAvailable:
dvander@2168
   580
		{
dvander@2168
   581
			g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update");
dvander@2168
   582
			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]);
dvander@2168
   583
			break;
dvander@2168
   584
		}
dvander@2168
   585
	}
dvander@2168
   586
}
dvander@2168
   587
dvander@2168
   588
bool g_blockGameDataLoad = false;
dvander@2168
   589
dvander@2168
   590
class InitFetch : public SMGlobalClass
dvander@2168
   591
{
dvander@2168
   592
public:
dvander@2168
   593
	void OnSourceModAllInitialized_Post()
dvander@2168
   594
	{
dvander@2168
   595
		char lock_path[PLATFORM_MAX_PATH];
dvander@2168
   596
		g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
dvander@2168
   597
dvander@2168
   598
		if (g_LibSys.IsPathFile(lock_path) && g_LibSys.PathExists(lock_path))
dvander@2168
   599
		{
dvander@2168
   600
			g_Logger.LogError("sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading");
dvander@2168
   601
			g_Logger.LogError("If this error persists delete the file manually");
dvander@2168
   602
			g_blockGameDataLoad = true;
dvander@2168
   603
		}
dvander@2168
   604
dvander@2168
   605
		ThreadParams fetchThreadParams = ThreadParams();
dvander@2168
   606
		fetchThreadParams.prio = ThreadPrio_Low;
dvander@2168
   607
		g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
dvander@2168
   608
	}
dvander@2168
   609
dvander@2168
   610
	ConfigResult OnSourceModConfigChanged(const char *key, 
dvander@2168
   611
		const char *value, 
dvander@2168
   612
		ConfigSource source,
dvander@2168
   613
		char *error, 
dvander@2168
   614
		size_t maxlength)
dvander@2168
   615
	{
dvander@2168
   616
		if (strcmp(key, "DisableAutoUpdate") == 0)
dvander@2168
   617
		{
dvander@2168
   618
			if (strcmp(value, "yes") == 0)
dvander@2168
   619
			{
dvander@2168
   620
				g_disableGameDataUpdate = true;
dvander@2168
   621
				return ConfigResult_Accept;
dvander@2168
   622
			}
dvander@2168
   623
			else if (strcmp(value, "no") == 0)
dvander@2168
   624
			{
dvander@2168
   625
				g_disableGameDataUpdate = false;
dvander@2168
   626
				return ConfigResult_Accept;
dvander@2168
   627
			}
dvander@2168
   628
dvander@2168
   629
			return ConfigResult_Reject;
dvander@2168
   630
		}
dvander@2168
   631
dvander@2168
   632
		if (strcmp(key, "ForceRestartAfterUpdate") == 0)
dvander@2168
   633
		{
dvander@2168
   634
			if (strcmp(value, "yes") == 0)
dvander@2168
   635
			{
dvander@2168
   636
				g_restartAfterUpdate = true;
dvander@2168
   637
				return ConfigResult_Accept;
dvander@2168
   638
			}
dvander@2168
   639
			else if (strcmp(value, "no") == 0)
dvander@2168
   640
			{
dvander@2168
   641
				g_restartAfterUpdate = false;
dvander@2168
   642
				return ConfigResult_Accept;
dvander@2168
   643
			}
dvander@2168
   644
dvander@2168
   645
			return ConfigResult_Reject;
dvander@2168
   646
		}
dvander@2168
   647
dvander@2168
   648
		return ConfigResult_Ignore;
dvander@2168
   649
	}
dvander@2168
   650
} g_InitFetch;
dvander@2168
   651
dvander@2168
   652
CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
dvander@2168
   653
{
dvander@2168
   654
#if !defined ORANGEBOX_BUILD
dvander@2168
   655
	CCommand args;
dvander@2168
   656
#endif
dvander@2168
   657
dvander@2168
   658
	if (args.ArgC() < 2)
dvander@2168
   659
	{
dvander@2168
   660
		g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
dvander@2168
   661
		return;
dvander@2168
   662
	}
dvander@2168
   663
dvander@2168
   664
	const char *file = args.Arg(1);
dvander@2168
   665
	if (!file || file[0] == '\0')
dvander@2168
   666
	{
dvander@2168
   667
		g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
dvander@2168
   668
		return;
dvander@2168
   669
	}
dvander@2168
   670
dvander@2168
   671
	SourceHook::CVector<FileData *>::iterator iter = g_FetchThread.filenames.begin();
dvander@2168
   672
dvander@2168
   673
	FileData *curData;
dvander@2168
   674
dvander@2168
   675
	while (iter != g_FetchThread.filenames.end())
dvander@2168
   676
	{
dvander@2168
   677
		curData = (*iter);
dvander@2168
   678
dvander@2168
   679
		char fileName[30];
dvander@2168
   680
dvander@2168
   681
		g_LibSys.GetFileFromPath(fileName, sizeof(fileName), curData->filename->c_str());
dvander@2168
   682
dvander@2168
   683
		if (strcmpi(fileName, file) == 0)
dvander@2168
   684
		{
dvander@2168
   685
			g_SMAPI->ConPrintf("MD5 Sum: %s\n", curData->checksum);
dvander@2168
   686
			return;
dvander@2168
   687
		}
dvander@2168
   688
dvander@2168
   689
		iter++;
dvander@2168
   690
	}
dvander@2168
   691
dvander@2168
   692
	g_SMAPI->ConPrint("File not found!\n");
dvander@2168
   693
}