1.1 --- a/core/GameDataFetcher.cpp Sun Dec 28 03:15:15 2008 -0500
1.2 +++ b/core/GameDataFetcher.cpp Sun Dec 28 03:23:45 2008 -0500
1.3 @@ -1,33 +1,33 @@
1.4 /**
1.5 -* vim: set ts=4 :
1.6 -* =============================================================================
1.7 -* SourceMod
1.8 -* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 -* =============================================================================
1.10 -*
1.11 -* This program is free software; you can redistribute it and/or modify it under
1.12 -* the terms of the GNU General Public License, version 3.0, as published by the
1.13 -* Free Software Foundation.
1.14 -*
1.15 -* This program is distributed in the hope that it will be useful, but WITHOUT
1.16 -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 -* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 -* details.
1.19 -*
1.20 -* You should have received a copy of the GNU General Public License along with
1.21 -* this program. If not, see <http://www.gnu.org/licenses/>.
1.22 -*
1.23 -* As a special exception, AlliedModders LLC gives you permission to link the
1.24 -* code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 -* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 -* by the Valve Corporation. You must obey the GNU General Public License in
1.27 -* all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 -* this exception to all derivative works. AlliedModders LLC defines further
1.29 -* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 -* or <http://www.sourcemod.net/license.php>.
1.31 -*
1.32 -* Version: $Id$
1.33 -*/
1.34 + * vim: set ts=4 :
1.35 + * =============================================================================
1.36 + * SourceMod
1.37 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.38 + * =============================================================================
1.39 + *
1.40 + * This program is free software; you can redistribute it and/or modify it under
1.41 + * the terms of the GNU General Public License, version 3.0, as published by the
1.42 + * Free Software Foundation.
1.43 + *
1.44 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.45 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.46 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.47 + * details.
1.48 + *
1.49 + * You should have received a copy of the GNU General Public License along with
1.50 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.51 + *
1.52 + * As a special exception, AlliedModders LLC gives you permission to link the
1.53 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.54 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.55 + * by the Valve Corporation. You must obey the GNU General Public License in
1.56 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.57 + * this exception to all derivative works. AlliedModders LLC defines further
1.58 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.59 + * or <http://www.sourcemod.net/license.php>.
1.60 + *
1.61 + * Version: $Id$
1.62 + */
1.63
1.64 #include "GameDataFetcher.h"
1.65 #include "bitbuf.h"
1.66 @@ -42,8 +42,9 @@
1.67 #include <netdb.h>
1.68 #include <arpa/inet.h>
1.69
1.70 -#define INVALID_SOCKET -1
1.71 -#define closesocket close
1.72 +#define INVALID_SOCKET -1
1.73 +#define closesocket close
1.74 +#define WSAGetLastError() errno
1.75 #endif
1.76
1.77 #include "sh_vector.h"
1.78 @@ -63,21 +64,52 @@
1.79 #include "TimerSys.h"
1.80 #include "compat_wrappers.h"
1.81 #include "sm_stringutil.h"
1.82 +#include "md5.h"
1.83 +#include "frame_hooks.h"
1.84
1.85 #define QUERY_MAX_LENGTH 1024
1.86
1.87 -BuildMD5ableBuffer g_MD5Builder;
1.88 -FetcherThread g_FetchThread;
1.89 +static BuildMD5ableBuffer g_MD5Builder;
1.90 +static FetcherThread g_FetchThread;
1.91
1.92 -FILE *logfile = NULL;
1.93 +static FILE *logfile = NULL;
1.94
1.95 bool g_disableGameDataUpdate = false;
1.96 +
1.97 +/**
1.98 + * Note on this. If we issue a reload and changelevel, my srcds.exe will emit
1.99 + * Assertion Failed: !m_bServiceStarted
1.100 + * on quit. This seems like a non-issue, because before we just terminated the
1.101 + * server anyway. If anyone notices and files a bug, we can look into it further.
1.102 + */
1.103 bool g_restartAfterUpdate = false;
1.104
1.105 -int g_serverPort = 6500;
1.106 -char g_serverAddress[100] = "hayate.alliedmods.net";
1.107 +static bool was_level_started = false;
1.108 +static int g_serverPort = 6500;
1.109 +static char g_serverAddress[100] = "smupdate.alliedmods.net";
1.110
1.111 -void FetcherThread::RunThread( IThreadHandle *pHandle )
1.112 +static void _ForceRestart(void *data)
1.113 +{
1.114 + char cmd[300];
1.115 + g_Logger.LogMessage("Automatically restarting SourceMod after a successful gamedata update.");
1.116 + UTIL_Format(cmd, sizeof(cmd), "meta unload %d\n", g_PLID);
1.117 + engine->ServerCommand(cmd);
1.118 + UTIL_Format(cmd, sizeof(cmd), "changelevel \"%s\"\n", STRING(gpGlobals->mapname));
1.119 + engine->ServerCommand(cmd);
1.120 + UTIL_Format(cmd, sizeof(cmd), "echo SourceMod restarted after gamedata update.\n");
1.121 + engine->ServerCommand(cmd);
1.122 +}
1.123 +
1.124 +static void ForceRestart()
1.125 +{
1.126 + FrameAction action;
1.127 +
1.128 + action.action = _ForceRestart;
1.129 + action.data = NULL;
1.130 + AddFrameAction(action);
1.131 +}
1.132 +
1.133 +void FetcherThread::RunThread(IThreadHandle *pHandle)
1.134 {
1.135 char lock_path[PLATFORM_MAX_PATH];
1.136 g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
1.137 @@ -85,8 +117,6 @@
1.138
1.139 g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
1.140
1.141 - g_Logger.LogMessage("Starting Gamedata update fetcher... please report problems to bugs.alliedmods.net");
1.142 -
1.143 char log_path[PLATFORM_MAX_PATH];
1.144 g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata");
1.145
1.146 @@ -102,7 +132,7 @@
1.147
1.148 if (!logfile)
1.149 {
1.150 - g_Logger.LogError("Failed to create GameData log file");
1.151 + /* :( */
1.152 return;
1.153 }
1.154
1.155 @@ -120,19 +150,16 @@
1.156
1.157 if (len == 0)
1.158 {
1.159 - g_Logger.LogToOpenFile(logfile, "Query Writing failed");
1.160 -
1.161 + g_Logger.LogToFileOnly(logfile, "Could not build gamedata query!");
1.162 fclose(logfile);
1.163 unlink(lock_path);
1.164 return;
1.165 }
1.166
1.167 + /* We check this late so we have the MD5 sums available. This may change in the future. */
1.168 if (g_disableGameDataUpdate)
1.169 {
1.170 -#ifdef DEBUG
1.171 - g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
1.172 -#endif
1.173 -
1.174 + g_Logger.LogToFileOnly(logfile, "Skipping gamedata fetcher (DisableAutoUpdate set)");
1.175 fclose(logfile);
1.176 unlink(lock_path);
1.177 return;
1.178 @@ -150,13 +177,15 @@
1.179
1.180 int sent = SendData(socketDescriptor, query, len);
1.181
1.182 -#ifdef DEBUG
1.183 - g_Logger.LogToOpenFile(logfile, "Sent Query!");
1.184 -#endif
1.185 +IF_DEBUG_SPEW
1.186 + g_Logger.LogToFileOnly(logfile, "Sent gamedata query");
1.187 +ENDIF_DEBUG_SPEW
1.188
1.189 if (sent == 0)
1.190 {
1.191 - g_Logger.LogToOpenFile(logfile, "Failed to send gamedata query data to remote host");
1.192 +IF_DEBUG_SPEW
1.193 + g_Logger.LogToFileOnly(logfile, "Failed to send gamedata query data to remote host");
1.194 +ENDIF_DEBUG_SPEW
1.195
1.196 closesocket(socketDescriptor);
1.197 fclose(logfile);
1.198 @@ -172,12 +201,41 @@
1.199 unlink(lock_path);
1.200 }
1.201
1.202 -void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
1.203 +void FetcherThread::OnTerminate(IThreadHandle *pHandle, bool cancel)
1.204 {
1.205 g_blockGameDataLoad = false;
1.206 +
1.207 + if (cancel)
1.208 + {
1.209 + return;
1.210 + }
1.211 +
1.212 + if (wasSuccess)
1.213 + {
1.214 + HandleUpdateStatus(updateStatus, build);
1.215 +
1.216 + if (needsRestart)
1.217 + {
1.218 + if (g_restartAfterUpdate)
1.219 + {
1.220 + if (was_level_started)
1.221 + {
1.222 + ForceRestart();
1.223 + }
1.224 + }
1.225 + else
1.226 + {
1.227 + g_Logger.LogMessage("Your gamedata files have been updated, please restart your server.");
1.228 + }
1.229 + }
1.230 + }
1.231 + else if (!g_disableGameDataUpdate)
1.232 + {
1.233 + g_Logger.LogError("An error occurred in the gamedata fetcher, see your gamedata log files for more information.");
1.234 + }
1.235 }
1.236
1.237 -int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen )
1.238 +int FetcherThread::BuildGameDataQuery(char *buffer, int maxlen)
1.239 {
1.240 char gamedata_path[PLATFORM_MAX_PATH];
1.241 g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
1.242 @@ -206,45 +264,42 @@
1.243 {
1.244 const char *name = dir->GetEntryName();
1.245 size_t len = strlen(name);
1.246 - if (len >= 4
1.247 - && strcmp(&name[len-4], ".txt") == 0)
1.248 + if (len >= 4 && strcmp(&name[len-4], ".txt") == 0)
1.249 {
1.250 + MD5 md5;
1.251 + SMCError err;
1.252 + SMCStates states;
1.253 + unsigned char raw[16];
1.254 char file[PLATFORM_MAX_PATH];
1.255
1.256 g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
1.257
1.258 - SMCStates states;
1.259 - if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay)
1.260 + g_MD5Builder.checksum = &md5;
1.261 + if ((err = g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states)) == SMCError_Okay)
1.262 {
1.263 - unsigned char *md5 = g_MD5Builder.GetMD5();
1.264 - if (md5 != NULL)
1.265 - {
1.266 - (uint8_t)buffer[10]++; //Increment the file counter
1.267 - Writer.WriteBytes(md5, 16);
1.268 -
1.269 - g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String());
1.270 + md5.raw_digest(raw);
1.271 + (uint8_t)buffer[10]++; //Increment the file counter
1.272 + Writer.WriteBytes(raw, 16);
1.273
1.274 - FileData *data = new FileData();
1.275 - data->filename = new SourceHook::String(file);
1.276 - memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
1.277 - filenames.push_back(data);
1.278 - }
1.279 - else
1.280 - {
1.281 -#ifdef DEBUG
1.282 - g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
1.283 -#endif
1.284 - }
1.285 + FileData *data = new FileData();
1.286 + data->filename = new SourceHook::String(file);
1.287 + md5.hex_digest(data->checksum);
1.288 + filenames.push_back(data);
1.289 +
1.290 +IF_DEBUG_SPEW
1.291 + g_Logger.LogToFileOnly(logfile, "Parsed file: %s as %s", file, data->checksum);
1.292 +ENDIF_DEBUG_SPEW
1.293 +
1.294 }
1.295 else
1.296 {
1.297 -#ifdef DEBUG
1.298 - g_Logger.LogToOpenFile(logfile, "%s failed!", file);
1.299 -#endif
1.300 +IF_DEBUG_SPEW
1.301 + const char *error = g_TextParser.GetSMCErrorString(err);
1.302 + g_Logger.LogToFileOnly(logfile, "Parsing of file %s failed: %s", file, error);
1.303 +ENDIF_DEBUG_SPEW
1.304 }
1.305 }
1.306 }
1.307 -
1.308 dir->NextEntry();
1.309 }
1.310
1.311 @@ -253,17 +308,16 @@
1.312
1.313 int FetcherThread::ConnectSocket()
1.314 {
1.315 - struct protoent *ptrp;
1.316 - ptrp = getprotobyname("tcp");
1.317 -
1.318 -#ifdef WIN32
1.319 +#if defined PLATFORM_WINDOWS
1.320 WSADATA wsaData;
1.321 WSAStartup(0x0101, &wsaData);
1.322 #endif
1.323
1.324 - if (ptrp == NULL)
1.325 + struct protoent *ptrp;
1.326 +
1.327 + if ((ptrp = getprotobyname("tcp")) == NULL)
1.328 {
1.329 - g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
1.330 + g_Logger.LogToFileOnly(logfile, "Error: Failed to find TCP protocol");
1.331 return INVALID_SOCKET;
1.332 }
1.333
1.334 @@ -271,12 +325,9 @@
1.335
1.336 if (socketDescriptor == INVALID_SOCKET)
1.337 {
1.338 - //bugger aye?
1.339 -#ifdef WIN32
1.340 - g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", WSAGetLastError());
1.341 -#else
1.342 - g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", errno);
1.343 -#endif
1.344 + char error[255];
1.345 + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
1.346 + g_Logger.LogToFileOnly(logfile, "Error: Failed to create socket: %s", error);
1.347 closesocket(socketDescriptor);
1.348 return INVALID_SOCKET;
1.349 }
1.350 @@ -293,7 +344,7 @@
1.351 {
1.352 if ((local_addr.sin_addr.s_addr = inet_addr(g_serverAddress)) == INADDR_NONE)
1.353 {
1.354 - g_Logger.LogToOpenFile(logfile, "Couldn't locate address");
1.355 + g_Logger.LogToFileOnly(logfile, "Couldn't locate address: %s", g_serverAddress);
1.356 closesocket(socketDescriptor);
1.357 return INVALID_SOCKET;
1.358 }
1.359 @@ -305,11 +356,9 @@
1.360
1.361 if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
1.362 {
1.363 -#ifdef WIN32
1.364 - g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", WSAGetLastError());
1.365 -#else
1.366 - g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", errno);
1.367 -#endif
1.368 + char error[255];
1.369 + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
1.370 + g_Logger.LogToFileOnly(logfile, "Couldn't connect to %s: %s", g_serverAddress, error);
1.371 closesocket(socketDescriptor);
1.372 return INVALID_SOCKET;
1.373 }
1.374 @@ -320,58 +369,59 @@
1.375 void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
1.376 {
1.377 char buffer[50];
1.378 -#ifdef DEBUG
1.379 - g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
1.380 -#endif
1.381 +
1.382 +IF_DEBUG_SPEW
1.383 + g_Logger.LogToFileOnly(logfile, "Waiting for reply!");
1.384 +ENDIF_DEBUG_SPEW
1.385
1.386 //Read in the header bytes
1.387 int returnLen = RecvData(socketDescriptor, buffer, 12);
1.388
1.389 -#ifdef DEBUG
1.390 - g_Logger.LogToOpenFile(logfile, "Recv Completed");
1.391 -#endif
1.392
1.393 if (returnLen == 0)
1.394 {
1.395 -#ifdef DEBUG
1.396 - g_Logger.LogToOpenFile(logfile, ",but it failed.");
1.397 -#endif
1.398 - /* Timeout or fail? */
1.399 + char error[255];
1.400 + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
1.401 + g_Logger.LogToFileOnly(logfile, "Did not receive reply: %s", error);
1.402 return;
1.403 }
1.404
1.405 -#ifdef DEBUG
1.406 - g_Logger.LogToOpenFile(logfile, "Received Header!");
1.407 -#endif
1.408 +IF_DEBUG_SPEW
1.409 + g_Logger.LogToFileOnly(logfile, "Received Header!");
1.410 +ENDIF_DEBUG_SPEW
1.411
1.412 bf_read Reader = bf_read("GameDataQuery", buffer, 12);
1.413
1.414 if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
1.415 {
1.416 - g_Logger.LogToOpenFile(logfile, "Unknown Query Response");
1.417 +IF_DEBUG_SPEW
1.418 + g_Logger.LogToFileOnly(logfile, "Unknown Query Response");
1.419 +ENDIF_DEBUG_SPEW
1.420 return;
1.421 }
1.422
1.423 - UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
1.424 -
1.425 - short build[4] = {0,0,0,0};
1.426 + updateStatus = (UpdateStatus)Reader.ReadByte();
1.427
1.428 build[0] = Reader.ReadShort();
1.429 build[1] = Reader.ReadShort();
1.430 build[2] = Reader.ReadShort();
1.431 build[3] = Reader.ReadShort();
1.432
1.433 -#ifdef DEBUG
1.434 - g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
1.435 -#endif
1.436 -
1.437 - HandleUpdateStatus(updateStatus, build);
1.438 +IF_DEBUG_SPEW
1.439 + g_Logger.LogToFileOnly(logfile,
1.440 + "Update Status: %i - Latest %i.%i.%i.%i",
1.441 + updateStatus,
1.442 + build[0],
1.443 + build[1],
1.444 + build[2],
1.445 + build[3]);
1.446 +ENDIF_DEBUG_SPEW
1.447
1.448 int changedFiles = Reader.ReadByte();
1.449
1.450 -#ifdef DEBUG
1.451 - g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
1.452 -#endif
1.453 +IF_DEBUG_SPEW
1.454 + g_Logger.LogToFileOnly(logfile, "Files to download: %i", changedFiles);
1.455 +ENDIF_DEBUG_SPEW
1.456
1.457 for (int i=0; i<changedFiles; i++)
1.458 {
1.459 @@ -389,9 +439,9 @@
1.460 int index = Reader.ReadByte();
1.461 int tempLen = Reader.ReadUBitLong(32);
1.462
1.463 -#ifdef DEBUG
1.464 - g_Logger.LogToOpenFile(logfile, "File index %i and length %i", index, tempLen);
1.465 -#endif
1.466 +IF_DEBUG_SPEW
1.467 + g_Logger.LogToFileOnly(logfile, "File index %i and length %i", index, tempLen);
1.468 +ENDIF_DEBUG_SPEW
1.469
1.470 void *memPtr;
1.471 memtable->CreateMem(tempLen+1, &memPtr);
1.472 @@ -399,9 +449,9 @@
1.473 //Read the contents of our file into the memtable
1.474 returnLen = RecvData(socketDescriptor, (char *)memPtr, tempLen);
1.475
1.476 -#ifdef DEBUG
1.477 - g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
1.478 -#endif
1.479 +IF_DEBUG_SPEW
1.480 + g_Logger.LogToFileOnly(logfile, "Received %i bytes", returnLen);
1.481 +ENDIF_DEBUG_SPEW
1.482
1.483 if (returnLen == 0)
1.484 {
1.485 @@ -412,7 +462,7 @@
1.486 ((unsigned char *)memPtr)[tempLen] = '\0';
1.487
1.488 FileData *data = filenames.at(index);
1.489 - const char* filename;
1.490 + const char *filename;
1.491 if (data != NULL)
1.492 {
1.493 filename = data->filename->c_str();
1.494 @@ -421,12 +471,12 @@
1.495
1.496 if (fp)
1.497 {
1.498 - fprintf(fp, (const char *)memPtr);
1.499 + fprintf(fp, "%s", (const char *)memPtr);
1.500 fclose(fp);
1.501 }
1.502 else
1.503 {
1.504 - g_Logger.LogToOpenFile(logfile, "Failed to open file \"%s\"", filename);
1.505 + g_Logger.LogToFileOnly(logfile, "Failed to open file \"%s\" for writing", filename);
1.506 }
1.507 }
1.508 else
1.509 @@ -436,21 +486,18 @@
1.510
1.511 memtable->Reset();
1.512
1.513 -#ifdef DEBUG
1.514 - g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
1.515 -#endif
1.516 + g_Logger.LogToFileOnly(logfile, "Updated file: %s", filename);
1.517 }
1.518
1.519 -#ifdef DEBUG
1.520 - g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
1.521 -#endif
1.522 +IF_DEBUG_SPEW
1.523 + g_Logger.LogToFileOnly(logfile, "File Downloads Completed!");
1.524 +ENDIF_DEBUG_SPEW
1.525
1.526 - bool needsRestart = false;
1.527 + needsRestart = false;
1.528
1.529 if (changedFiles > 0)
1.530 {
1.531 needsRestart = true;
1.532 - g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect");
1.533 }
1.534
1.535 //Read changed file count
1.536 @@ -458,8 +505,9 @@
1.537
1.538 if (returnLen == 0)
1.539 {
1.540 - /* Timeout or fail? */
1.541 - g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
1.542 + char error[255];
1.543 + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
1.544 + g_Logger.LogToFileOnly(logfile, "Did not receive count reply: %s", error);
1.545 return;
1.546 }
1.547
1.548 @@ -467,28 +515,27 @@
1.549
1.550 changedFiles = Reader.ReadByte();
1.551
1.552 - if (changedFiles == 0)
1.553 + if (changedFiles < 1)
1.554 {
1.555 -#ifdef DEBUG
1.556 - g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
1.557 -#endif
1.558 +IF_DEBUG_SPEW
1.559 + g_Logger.LogToFileOnly(logfile, "No unknown files. We're all done");
1.560 +ENDIF_DEBUG_SPEW
1.561 return;
1.562 }
1.563
1.564 char *changedFileIndexes = new char[changedFiles];
1.565
1.566 -#ifdef DEBUG
1.567 - g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
1.568 -#endif
1.569 +IF_DEBUG_SPEW
1.570 + g_Logger.LogToFileOnly(logfile, "%i files were unknown", changedFiles);
1.571 +ENDIF_DEBUG_SPEW
1.572
1.573 returnLen = RecvData(socketDescriptor, changedFileIndexes, changedFiles);
1.574
1.575 if (returnLen == 0)
1.576 {
1.577 - /* Timeout or fail? */
1.578 -#ifdef DEBUG
1.579 - g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
1.580 -#endif
1.581 + char error[255];
1.582 + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error));
1.583 + g_Logger.LogToFileOnly(logfile, "Did not receive list reply: %s", error);
1.584 return;
1.585 }
1.586
1.587 @@ -511,21 +558,17 @@
1.588 }
1.589
1.590 g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
1.591 -#ifdef DEBUG
1.592 - g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
1.593 -#endif
1.594 +IF_DEBUG_SPEW
1.595 + g_Logger.LogToFileOnly(logfile, "Unknown File %i : %s", index, fileName);
1.596 +ENDIF_DEBUG_SPEW
1.597 }
1.598
1.599 delete [] changedFileIndexes;
1.600
1.601 - if (needsRestart && g_restartAfterUpdate)
1.602 - {
1.603 - g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
1.604 - engine->ServerCommand("quit\n");
1.605 - }
1.606 + wasSuccess = true;
1.607 }
1.608
1.609 -int FetcherThread::RecvData( int socketDescriptor, char *buffer, int len )
1.610 +int FetcherThread::RecvData(int socketDescriptor, char *buffer, int len)
1.611 {
1.612 fd_set fds;
1.613 struct timeval tv;
1.614 @@ -564,7 +607,7 @@
1.615 return bytesReceivedTotal;
1.616 }
1.617
1.618 -int FetcherThread::SendData( int socketDescriptor, char *buffer, int len )
1.619 +int FetcherThread::SendData(int socketDescriptor, char *buffer, int len)
1.620 {
1.621 fd_set fds;
1.622 struct timeval tv;
1.623 @@ -599,7 +642,7 @@
1.624 return sentBytesTotal;
1.625 }
1.626
1.627 -void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] )
1.628 +void FetcherThread::HandleUpdateStatus(UpdateStatus status, short version[4])
1.629 {
1.630 switch (status)
1.631 {
1.632 @@ -611,14 +654,12 @@
1.633
1.634 case Update_NewBuild:
1.635 {
1.636 - g_Logger.LogMessage("SourceMod Update: A new Mercurial build is available from sourcemod.net");
1.637 - 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]);
1.638 break;
1.639 }
1.640
1.641 case Update_MinorAvailable:
1.642 {
1.643 - g_Logger.LogMessage("SourceMod Update: An incremental minor release of SourceMod is now available from sourcemod.net");
1.644 + g_Logger.LogMessage("SourceMod Update: A new release of SourceMod is now available from sourcemod.net");
1.645 g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
1.646 break;
1.647 }
1.648 @@ -632,14 +673,15 @@
1.649
1.650 case Update_CriticalAvailable:
1.651 {
1.652 - g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update");
1.653 - 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]);
1.654 + g_Logger.LogError("SourceMod Update: A critical SourceMod release is available from sourcemod.net. It is strongly recommended that you update!");
1.655 + g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
1.656 break;
1.657 }
1.658 }
1.659 }
1.660
1.661 bool g_blockGameDataLoad = false;
1.662 +static IThreadHandle *fetch_thread_hndl;
1.663
1.664 class InitFetch : public SMGlobalClass
1.665 {
1.666 @@ -658,7 +700,30 @@
1.667
1.668 ThreadParams fetchThreadParams = ThreadParams();
1.669 fetchThreadParams.prio = ThreadPrio_Low;
1.670 - g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
1.671 + fetch_thread_hndl = g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
1.672 + }
1.673 +
1.674 + void OnSourceModShutdown()
1.675 + {
1.676 + fetch_thread_hndl->WaitForThread();
1.677 + fetch_thread_hndl->DestroyThis();
1.678 + }
1.679 +
1.680 + void OnSourceModLevelActivated()
1.681 + {
1.682 + was_level_started = true;
1.683 +
1.684 + if (g_restartAfterUpdate &&
1.685 + g_FetchThread.wasSuccess &&
1.686 + g_FetchThread.needsRestart)
1.687 + {
1.688 + ForceRestart();
1.689 + }
1.690 + }
1.691 +
1.692 + void OnSourceModLevelEnd()
1.693 + {
1.694 + was_level_started = false;
1.695 }
1.696
1.697 ConfigResult OnSourceModConfigChanged(const char *key,
1.698 @@ -667,14 +732,14 @@
1.699 char *error,
1.700 size_t maxlength)
1.701 {
1.702 - if (strcmp(key, "DisableAutoUpdate") == 0)
1.703 + if (strcasecmp(key, "DisableAutoUpdate") == 0)
1.704 {
1.705 - if (strcmp(value, "yes") == 0)
1.706 + if (strcasecmp(value, "yes") == 0)
1.707 {
1.708 g_disableGameDataUpdate = true;
1.709 return ConfigResult_Accept;
1.710 }
1.711 - else if (strcmp(value, "no") == 0)
1.712 + else if (strcasecmp(value, "no") == 0)
1.713 {
1.714 g_disableGameDataUpdate = false;
1.715 return ConfigResult_Accept;
1.716 @@ -683,14 +748,14 @@
1.717 return ConfigResult_Reject;
1.718 }
1.719
1.720 - if (strcmp(key, "ForceRestartAfterUpdate") == 0)
1.721 + if (strcasecmp(key, "ForceRestartAfterUpdate") == 0)
1.722 {
1.723 - if (strcmp(value, "yes") == 0)
1.724 + if (strcasecmp(value, "yes") == 0)
1.725 {
1.726 g_restartAfterUpdate = true;
1.727 return ConfigResult_Accept;
1.728 }
1.729 - else if (strcmp(value, "no") == 0)
1.730 + else if (strcasecmp(value, "no") == 0)
1.731 {
1.732 g_restartAfterUpdate = false;
1.733 return ConfigResult_Accept;
1.734 @@ -699,14 +764,14 @@
1.735 return ConfigResult_Reject;
1.736 }
1.737
1.738 - if (strcmp(key, "AutoUpdateServer") == 0)
1.739 + if (strcasecmp(key, "AutoUpdateServer") == 0)
1.740 {
1.741 UTIL_Format(g_serverAddress, sizeof(g_serverAddress), "%s", value);
1.742
1.743 return ConfigResult_Accept;
1.744 }
1.745
1.746 - if (strcmp(key, "AutoUpdatePort") == 0)
1.747 + if (strcasecmp(key, "AutoUpdatePort") == 0)
1.748 {
1.749 int port = atoi(value);
1.750
1.751 @@ -724,6 +789,55 @@
1.752 }
1.753 } g_InitFetch;
1.754
1.755 +BuildMD5ableBuffer::BuildMD5ableBuffer()
1.756 +{
1.757 + stringTable = new BaseStringTable(2048);
1.758 +}
1.759 +
1.760 +BuildMD5ableBuffer::~BuildMD5ableBuffer()
1.761 +{
1.762 + delete stringTable;
1.763 +}
1.764 +
1.765 +void BuildMD5ableBuffer::ReadSMC_ParseStart()
1.766 +{
1.767 + stringTable->Reset();
1.768 +}
1.769 +
1.770 +SMCResult BuildMD5ableBuffer::ReadSMC_KeyValue(const SMCStates *states,
1.771 + const char *key,
1.772 + const char *value)
1.773 +{
1.774 + stringTable->AddString(key);
1.775 + stringTable->AddString(value);
1.776 +
1.777 + return SMCResult_Continue;
1.778 +}
1.779 +
1.780 +SMCResult BuildMD5ableBuffer::ReadSMC_NewSection(const SMCStates *states, const char *name)
1.781 +{
1.782 + stringTable->AddString(name);
1.783 +
1.784 + return SMCResult_Continue;
1.785 +}
1.786 +
1.787 +void BuildMD5ableBuffer::ReadSMC_ParseEnd(bool halted, bool failed)
1.788 +{
1.789 + if (halted || failed)
1.790 + {
1.791 + return;
1.792 + }
1.793 +
1.794 + void *data = stringTable->GetMemTable()->GetAddress(0);
1.795 +
1.796 + if (data != NULL)
1.797 + {
1.798 + checksum->update((unsigned char *)data, stringTable->GetMemTable()->GetActualMemUsed());
1.799 + }
1.800 +
1.801 + checksum->finalize();
1.802 +}
1.803 +
1.804 CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
1.805 {
1.806 #if SOURCE_ENGINE == SE_EPISODEONE
1.807 @@ -766,3 +880,26 @@
1.808
1.809 g_SMAPI->ConPrint("File not found!\n");
1.810 }
1.811 +
1.812 +FetcherThread::~FetcherThread()
1.813 +{
1.814 + SourceHook::CVector<FileData *>::iterator iter = filenames.begin();
1.815 +
1.816 + FileData *curData;
1.817 +
1.818 + while (iter != filenames.end())
1.819 + {
1.820 + curData = (*iter);
1.821 + delete curData->filename;
1.822 + delete curData;
1.823 + iter = filenames.erase(iter);
1.824 + }
1.825 +}
1.826 +
1.827 +FetcherThread::FetcherThread()
1.828 +{
1.829 + memtable = new BaseMemTable(4096);
1.830 + wasSuccess = false;
1.831 + needsRestart = false;
1.832 +}
1.833 +