1 // ==============================================================
2 // This file is part of Glest (www.glest.org)
3 //
4 // Copyright (C) 2001-2008 Martiño Figueroa
5 //
6 // You can redistribute this code and/or modify it under
7 // the terms of the GNU General Public License as published
8 // by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version
10 // ==============================================================
11
12 #include "server_interface.h"
13
14 #include <stdexcept>
15
16 #include "window.h"
17 #include "logger.h"
18
19 #include "platform_util.h"
20 #include "conversion.h"
21 #include "config.h"
22 #include "lang.h"
23 #include "util.h"
24 #include "game_util.h"
25 #include "miniftpserver.h"
26 #include "map_preview.h"
27 #include "stats.h"
28 #include <time.h>
29 #include <set>
30 #include <iostream>
31 #include <iterator>
32
33 #include "leak_dumper.h"
34
35 using namespace std;
36 using namespace Shared::Platform;
37 using namespace Shared::Util;
38 using namespace Shared::Map;
39
40 namespace Glest { namespace Game {
41
42 double maxFrameCountLagAllowed = 30;
43 double maxClientLagTimeAllowed = 25;
44 double maxFrameCountLagAllowedEver = 30;
45 double maxClientLagTimeAllowedEver = 25;
46
47 double warnFrameCountLagPercent = 0.50;
48 double LAG_CHECK_GRACE_PERIOD = 15;
49 double LAG_CHECK_INTERVAL_PERIOD = 4;
50
51 const int MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS = 15000;
52 const int MAX_CLIENT_PAUSE_FOR_LAG_COUNT = 3;
53 const int MAX_SLOT_THREAD_WAIT_TIME_MILLISECONDS = 1500;
54 const int MASTERSERVER_HEARTBEAT_GAME_STATUS_SECONDS = 30;
55
56 const int MAX_EMPTY_NETWORK_COMMAND_LIST_BROADCAST_INTERVAL_MILLISECONDS = 4000;
57
ServerInterface(bool publishEnabled,ClientLagCallbackInterface * clientLagCallbackInterface)58 ServerInterface::ServerInterface(bool publishEnabled, ClientLagCallbackInterface *clientLagCallbackInterface) : GameNetworkInterface() {
59 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
60
61 this->clientLagCallbackInterface = clientLagCallbackInterface;
62 this->clientsAutoPausedDueToLag = false;
63
64 allowInGameConnections = false;
65 gameLaunched = false;
66
67 serverSynchAccessor = new Mutex(CODE_AT_LINE);
68 switchSetupRequestsSynchAccessor = new Mutex(CODE_AT_LINE);
69
70 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
71 slotAccessorMutexes[index] = new Mutex(CODE_AT_LINE);
72 }
73 masterServerThreadAccessor = new Mutex(CODE_AT_LINE);
74 textMessageQueueThreadAccessor = new Mutex(CODE_AT_LINE);
75 broadcastMessageQueueThreadAccessor = new Mutex(CODE_AT_LINE);
76 inBroadcastMessageThreadAccessor = new Mutex(CODE_AT_LINE);
77
78 serverSocketAdmin = NULL;
79 nextEventId = 1;
80 gameHasBeenInitiated = false;
81 exitServer = false;
82 gameSettingsUpdateCount = 0;
83 currentFrameCount = 0;
84 gameStartTime = 0;
85 resumeGameStartTime = 0;
86 publishToMasterserverThread = NULL;
87 lastMasterserverHeartbeatTime = 0;
88 needToRepublishToMasterserver = false;
89 ftpServer = NULL;
90 inBroadcastMessage = false;
91 lastGlobalLagCheckTime = 0;
92 masterserverAdminRequestLaunch = false;
93 lastListenerSlotCheckTime = 0;
94
95 // This is an admin port listening only on the localhost intended to
96 // give current connection status info
97 #ifndef __APPLE__
98 try {
99 serverSocketAdmin = new ServerSocket(true);
100 serverSocketAdmin->setBlock(false);
101 serverSocketAdmin->setBindPort(Config::getInstance().getInt("ServerAdminPort", intToStr(GameConstants::serverAdminPort).c_str()));
102 serverSocketAdmin->setBindSpecificAddress(Config::getInstance().getString("ServerAdminBindAddress", "127.0.0.1"));
103 serverSocketAdmin->listen(5);
104 }
105 catch(const std::exception &ex) {
106 char szBuf[8096]="";
107 snprintf(szBuf,8096,"In [%s::%s Line: %d] Warning Server admin port bind/listen error:\n%s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
108 SystemFlags::OutputDebug(SystemFlags::debugError,szBuf);
109
110 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"%s",szBuf);
111 }
112 #endif
113
114 maxFrameCountLagAllowed = Config::getInstance().getInt("MaxFrameCountLagAllowed", intToStr(maxFrameCountLagAllowed).c_str());
115 maxFrameCountLagAllowedEver = Config::getInstance().getInt("MaxFrameCountLagAllowedEver", intToStr(maxFrameCountLagAllowedEver).c_str());
116 maxClientLagTimeAllowedEver = Config::getInstance().getInt("MaxClientLagTimeAllowedEver", intToStr(maxClientLagTimeAllowedEver).c_str());
117 maxClientLagTimeAllowed = Config::getInstance().getInt("MaxClientLagTimeAllowed", intToStr(maxClientLagTimeAllowed).c_str());
118 warnFrameCountLagPercent = Config::getInstance().getFloat("WarnFrameCountLagPercent", doubleToStr(warnFrameCountLagPercent).c_str());
119
120 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] maxFrameCountLagAllowed = %f, maxFrameCountLagAllowedEver = %f, maxClientLagTimeAllowed = %f, maxClientLagTimeAllowedEver = %f\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,maxFrameCountLagAllowed,maxFrameCountLagAllowedEver,maxClientLagTimeAllowed,maxClientLagTimeAllowedEver);
121
122 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
123 slots[index] = NULL;
124 switchSetupRequests[index] = NULL;
125 }
126
127 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
128
129 serverSocket.setBlock(false);
130 serverSocket.setBindPort(Config::getInstance().getInt("PortServer", intToStr(GameConstants::serverPort).c_str()));
131
132 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
133
134 gameStatsThreadAccessor = new Mutex(CODE_AT_LINE);
135 gameStats = NULL;
136
137 Config &config = Config::getInstance();
138 string scenarioDir = "";
139 vector<string> pathList = config.getPathListForType(ptMaps,scenarioDir);
140 vector<string> invalidMapList;
141 vector<string> allMaps = MapPreview::findAllValidMaps(pathList,scenarioDir,false,true,&invalidMapList);
142 if (allMaps.empty()) {
143 //throw megaglest_runtime_error("No maps were found!");
144 printf("No maps were found (srv)!\n");
145 }
146 std::sort(allMaps.begin(),allMaps.end(),compareNonCaseSensitive);
147 vector<string> results;
148 copy(allMaps.begin(), allMaps.end(), std::back_inserter(results));
149 mapFiles = results;
150
151 //player Sorted maps
152 ////////////////////
153 for(unsigned int i = 0; i < GameConstants::maxPlayers+1; ++i) {
154 playerSortedMaps[i].clear();
155 }
156
157 // at index=0 fill in the whole list
158 copy(mapFiles.begin(), mapFiles.end(), std::back_inserter(playerSortedMaps[0]));
159
160 MapInfo mapInfo;
161 // fill playerSortedMaps according to map player count
162 for(int i= 0; i < (int)mapFiles.size(); i++){// fetch info and put map in right list
163 //printf("mapFiles.at(i) %s allMaps.at[i] %s\n",mapFiles[i].c_str(),allMaps.at(i).c_str());
164 MapPreview::loadMapInfo(Config::getMapPath(mapFiles.at(i)), &mapInfo, "MaxPlayers","Size",true) ;
165 playerSortedMaps[mapInfo.players].push_back(mapFiles.at(i));
166 }
167 ///////////////////
168
169 results.clear();
170 findDirs(config.getPathListForType(ptTilesets), results);
171 if (results.empty()) {
172 //throw megaglest_runtime_error("No tile-sets were found!");
173 printf("No tile-sets were found (srv)!");
174 }
175 tilesetFiles = results;
176
177 results.clear();
178 findDirs(config.getPathListForType(ptTechs), results);
179 if(results.empty()) {
180 //throw megaglest_runtime_error("No tech-trees were found!");
181 printf("No tech-trees were found (srv)!\n");
182 }
183 techTreeFiles = results;
184
185 if(Config::getInstance().getBool("EnableFTPServer","true") == true) {
186 std::pair<string,string> mapsPath;
187 vector<string> pathList = Config::getInstance().getPathListForType(ptMaps);
188 if(pathList.empty() == false) {
189 mapsPath.first = pathList[0];
190 if(pathList.size() > 1) {
191 mapsPath.second = pathList[1];
192 }
193 }
194
195 std::pair<string,string> tilesetsPath;
196 vector<string> tilesetsList = Config::getInstance().getPathListForType(ptTilesets);
197 if(tilesetsList.empty() == false) {
198 tilesetsPath.first = tilesetsList[0];
199 if(tilesetsList.size() > 1) {
200 tilesetsPath.second = tilesetsList[1];
201 }
202 }
203
204 std::pair<string,string> techtreesPath;
205 vector<string> techtreesList = Config::getInstance().getPathListForType(ptTechs);
206 if(techtreesList.empty() == false) {
207 techtreesPath.first = techtreesList[0];
208 if(techtreesList.size() > 1) {
209 techtreesPath.second = techtreesList[1];
210 }
211 }
212
213 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
214 int portNumber = Config::getInstance().getInt("FTPServerPort",intToStr(ServerSocket::getFTPServerPort()).c_str());
215 ServerSocket::setFTPServerPort(portNumber);
216 //printf("In [%s::%s] portNumber = %d ServerSocket::getFTPServerPort() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,portNumber,ServerSocket::getFTPServerPort());
217
218 bool allowInternetTilesetFileTransfers = Config::getInstance().getBool("EnableFTPServerInternetTilesetXfer","true");
219 bool allowInternetTechtreeFileTransfers = Config::getInstance().getBool("EnableFTPServerInternetTechtreeXfer","true");
220
221 // Get path to temp files
222 string tempFilePath = "temp/";
223 if(getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) != "") {
224 tempFilePath = getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) + tempFilePath;
225 }
226 else {
227 string userData = config.getString("UserData_Root","");
228 if(userData != "") {
229 endPathWithSlash(userData);
230 }
231 tempFilePath = userData + tempFilePath;
232 }
233 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Temp files path [%s]\n",tempFilePath.c_str());
234
235 ftpServer = new FTPServerThread(mapsPath,tilesetsPath,techtreesPath,
236 publishEnabled,allowInternetTilesetFileTransfers,
237 allowInternetTechtreeFileTransfers,portNumber,GameConstants::maxPlayers,
238 this,tempFilePath);
239 ftpServer->start();
240 }
241
242 if(publishToMasterserverThread == NULL) {
243 if(needToRepublishToMasterserver == true || GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
244 static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
245 publishToMasterserverThread = new SimpleTaskThread(this,0,125);
246 publishToMasterserverThread->setUniqueID(mutexOwnerId);
247 publishToMasterserverThread->start();
248
249 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
250 }
251 }
252
253 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
254 }
255
setPublishEnabled(bool value)256 void ServerInterface::setPublishEnabled(bool value) {
257 if(ftpServer != NULL) {
258 ftpServer->setInternetEnabled(value);
259 }
260 }
261
shutdownMasterserverPublishThread()262 void ServerInterface::shutdownMasterserverPublishThread() {
263 MutexSafeWrapper safeMutex(masterServerThreadAccessor,CODE_AT_LINE);
264
265 if(publishToMasterserverThread != NULL) {
266 time_t elapsed = time(NULL);
267 publishToMasterserverThread->signalQuit();
268 for(;publishToMasterserverThread->canShutdown(false) == false &&
269 difftime((long int)time(NULL),elapsed) <= 15;) {
270 //sleep(150);
271 }
272 if(publishToMasterserverThread->canShutdown(true)) {
273 delete publishToMasterserverThread;
274 publishToMasterserverThread = NULL;
275 }
276 }
277 }
278
~ServerInterface()279 ServerInterface::~ServerInterface() {
280 //printf("===> Destructor for ServerInterface\n");
281 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
282
283 masterController.clearSlaves(true);
284 exitServer = true;
285 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
286 if(slots[index] != NULL) {
287 MutexSafeWrapper safeMutex(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
288 delete slots[index];
289 slots[index] = NULL;
290 }
291
292 if(switchSetupRequests[index] != NULL) {
293 delete switchSetupRequests[index];
294 switchSetupRequests[index] = NULL;
295 }
296 }
297
298 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
299 close();
300 shutdownFTPServer();
301 shutdownMasterserverPublishThread();
302
303 lastMasterserverHeartbeatTime = 0;
304 if(needToRepublishToMasterserver == true) {
305 simpleTask(NULL,NULL);
306 }
307
308 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
309 delete slotAccessorMutexes[index];
310 slotAccessorMutexes[index] = NULL;
311 }
312
313 delete textMessageQueueThreadAccessor;
314 textMessageQueueThreadAccessor = NULL;
315
316 delete broadcastMessageQueueThreadAccessor;
317 broadcastMessageQueueThreadAccessor = NULL;
318
319 delete inBroadcastMessageThreadAccessor;
320 inBroadcastMessageThreadAccessor = NULL;
321
322 delete serverSynchAccessor;
323 serverSynchAccessor = NULL;
324
325 delete masterServerThreadAccessor;
326 masterServerThreadAccessor = NULL;
327
328 delete serverSocketAdmin;
329 serverSocketAdmin = NULL;
330
331 for(int index = 0; index < (int)broadcastMessageQueue.size(); ++index) {
332 pair<NetworkMessage *,int> &item = broadcastMessageQueue[index];
333 if(item.first != NULL) {
334 delete item.first;
335 }
336 item.first = NULL;
337 }
338 broadcastMessageQueue.clear();
339
340 delete switchSetupRequestsSynchAccessor;
341 switchSetupRequestsSynchAccessor = NULL;
342
343 delete gameStatsThreadAccessor;
344 gameStatsThreadAccessor = NULL;
345
346 delete gameStats;
347 gameStats = NULL;
348
349 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
350 }
351
getSwitchSetupRequests()352 SwitchSetupRequest ** ServerInterface::getSwitchSetupRequests() {
353 MutexSafeWrapper safeMutex(switchSetupRequestsSynchAccessor,CODE_AT_LINE);
354 return &switchSetupRequests[0];
355 }
356
getSwitchSetupRequests(int index)357 SwitchSetupRequest * ServerInterface::getSwitchSetupRequests(int index) {
358 MutexSafeWrapper safeMutex(switchSetupRequestsSynchAccessor,CODE_AT_LINE);
359 return switchSetupRequests[index];
360 }
361
setSwitchSetupRequests(int index,SwitchSetupRequest * ptr)362 void ServerInterface::setSwitchSetupRequests(int index,SwitchSetupRequest *ptr) {
363 MutexSafeWrapper safeMutex(switchSetupRequestsSynchAccessor,CODE_AT_LINE);
364 switchSetupRequests[index] = ptr;
365 }
366
isValidClientType(uint32 clientIp)367 int ServerInterface::isValidClientType(uint32 clientIp) {
368 int result = 0;
369 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
370 MutexSafeWrapper safeMutex(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
371 if(slots[index] != NULL) {
372 Socket *socket = slots[index]->getSocket();
373 if(socket != NULL) {
374 uint32 slotIp = socket->getConnectedIPAddress(socket->getIpAddress());
375 if(slotIp == clientIp) {
376 result = 1;
377 break;
378 }
379 }
380 }
381 }
382 return result;
383 }
384
isClientAllowedToGetFile(uint32 clientIp,const char * username,const char * filename)385 int ServerInterface::isClientAllowedToGetFile(uint32 clientIp, const char *username, const char *filename) {
386 int result = 1;
387
388 if( username != NULL &&
389 strlen(username) > 0 &&
390 filename != NULL &&
391 strlen(filename) > 0) {
392 string user = username;
393 string file = filename;
394
395 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d username [%s] file [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,username,filename);
396
397 if(StartsWith(user,"tilesets") == true && EndsWith(file,"7z") == false) {
398 if(Config::getInstance().getBool("DisableFTPServerXferUncompressedTilesets","false") == true) {
399 result = 0;
400 }
401 else {
402 char szIP[100] = "";
403 Ip::Inet_NtoA(clientIp,szIP);
404 string clientIP = szIP;
405 std::vector<std::string> serverList = Socket::getLocalIPAddressList();
406
407 result = 0;
408 for(unsigned int index = 0; index < serverList.size(); ++index) {
409 string serverIP = serverList[index];
410
411 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d clientIP [%s] serverIP [%s] %d / %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientIP.c_str(),serverIP.c_str(),index,serverList.size());
412
413 vector<string> clientTokens;
414 Tokenize(clientIP,clientTokens,".");
415
416 vector<string> serverTokens;
417 Tokenize(serverIP,serverTokens,".");
418
419 if(clientTokens.size() == 4 && serverTokens.size() == 4) {
420 if( clientTokens[0] == serverTokens[0] ||
421 clientTokens[1] == serverTokens[1] ||
422 clientTokens[2] == serverTokens[2]) {
423 result = 1;
424
425 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d clientIP [%s] IS NOT BLOCKED\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientIP.c_str());
426
427 break;
428 }
429 }
430 }
431 }
432 }
433 }
434 return result;
435 }
436
addClientToServerIPAddress(uint32 clientIp,uint32 ServerIp)437 void ServerInterface::addClientToServerIPAddress(uint32 clientIp, uint32 ServerIp) {
438 FTPServerThread::addClientToServerIPAddress(clientIp, ServerIp);
439 }
440
addSlot(int playerIndex)441 void ServerInterface::addSlot(int playerIndex) {
442 //printf("Adding slot for playerIndex = %d, serverSocket.isPortBound() = %d\n",playerIndex,serverSocket.isPortBound());
443 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
444
445 if(playerIndex < 0 || playerIndex >= GameConstants::maxPlayers) {
446 char szBuf[8096]="";
447 snprintf(szBuf,8096,"In [%s::%s %d] playerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,playerIndex);
448 throw megaglest_runtime_error(szBuf);
449 }
450 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
451 if(serverSocketAdmin != NULL && serverSocketAdmin->isSocketValid() == false) {
452 serverSocketAdmin->listen(5);
453 }
454 if(serverSocket.isPortBound() == false) {
455 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
456 serverSocket.bind(serverSocket.getBindPort());
457 }
458
459 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
460
461 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[playerIndex],CODE_AT_LINE_X(playerIndex));
462
463 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
464
465 ConnectionSlot *slot = slots[playerIndex];
466 if(slot != NULL) {
467 slots[playerIndex] = NULL;
468 }
469 slots[playerIndex] = new ConnectionSlot(this, playerIndex);
470
471 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
472
473 safeMutexSlot.ReleaseLock();
474 delete slot;
475
476 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
477
478 safeMutex.ReleaseLock();
479
480 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
481
482 updateListen();
483
484 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
485 }
486
removeSlot(int playerIndex,int lockedSlotIndex)487 void ServerInterface::removeSlot(int playerIndex, int lockedSlotIndex) {
488 //printf("Removing slot for playerIndex = %d, serverSocket.isPortBound() = %d\n",playerIndex,serverSocket.isPortBound());
489
490 if(playerIndex < 0 || playerIndex >= GameConstants::maxPlayers) {
491 char szBuf[8096]="";
492 snprintf(szBuf,8096,"In [%s::%s %d] playerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,playerIndex);
493 throw megaglest_runtime_error(szBuf);
494 }
495
496 Lang &lang= Lang::getInstance();
497 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
498
499 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
500 MutexSafeWrapper safeMutexSlot(NULL,CODE_AT_LINE_X(playerIndex));
501 if(playerIndex != lockedSlotIndex) {
502 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
503 safeMutexSlot.setMutex(slotAccessorMutexes[playerIndex],CODE_AT_LINE_X(playerIndex));
504 }
505
506 vector<string> msgList;
507 ConnectionSlot *slot = slots[playerIndex];
508 bool notifyDisconnect = false;
509 const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
510 if(slot != NULL) {
511 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
512
513 if(slot->getLastReceiveCommandListTime() > 0) {
514 char szBuf[4096] = "";
515
516 for(unsigned int index = 0; index < languageList.size(); ++index) {
517 string msgTemplate = "Player %s, disconnected from the game.";
518 if(lang.hasString("PlayerDisconnected",languageList[index]) == true) {
519 msgTemplate = lang.getString("PlayerDisconnected",languageList[index]);
520 }
521 #ifdef WIN32
522 _snprintf(szBuf,4095,msgTemplate.c_str(),slot->getName().c_str());
523 #else
524 snprintf(szBuf,4095,msgTemplate.c_str(),slot->getName().c_str());
525 #endif
526 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] %s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,szBuf);
527
528 msgList.push_back(szBuf);
529 }
530
531 notifyDisconnect = true;
532 }
533 }
534 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
535
536 slots[playerIndex]= NULL;
537 safeMutexSlot.ReleaseLock();
538 safeMutex.ReleaseLock();
539
540 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
541
542 if(slot != NULL) slot->close();
543 delete slot;
544
545 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
546
547 updateListen();
548
549 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
550
551 if(notifyDisconnect == true) {
552 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
553
554 for(unsigned int index = 0; index < languageList.size(); ++index) {
555 bool localEcho = lang.isLanguageLocal(languageList[index]);
556 queueTextMessage(msgList[index],-1, localEcho, languageList[index]);
557 }
558 }
559 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,playerIndex,lockedSlotIndex);
560 }
561
switchSlot(int fromPlayerIndex,int toPlayerIndex)562 bool ServerInterface::switchSlot(int fromPlayerIndex, int toPlayerIndex) {
563 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
564 bool result = false;
565
566 //printf("#1 Server is switching slots\n");
567
568 if(fromPlayerIndex < 0 || fromPlayerIndex >= GameConstants::maxPlayers) {
569 char szBuf[8096]="";
570 snprintf(szBuf,8096,"In [%s::%s %d] fromPlayerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,fromPlayerIndex);
571 throw megaglest_runtime_error(szBuf);
572 }
573
574 if(toPlayerIndex < 0 || toPlayerIndex >= GameConstants::maxPlayers) {
575 char szBuf[8096]="";
576 snprintf(szBuf,8096,"In [%s::%s %d] toPlayerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,toPlayerIndex);
577 throw megaglest_runtime_error(szBuf);
578 }
579
580 if(fromPlayerIndex == toPlayerIndex) {
581 return false;
582 }
583
584 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
585 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[fromPlayerIndex],CODE_AT_LINE_X(fromPlayerIndex));
586 MutexSafeWrapper safeMutexSlot2(slotAccessorMutexes[toPlayerIndex],CODE_AT_LINE_X(toPlayerIndex));
587
588 //printf("#1a Server is switching slots\n");
589
590 if(slots[toPlayerIndex] != NULL &&
591 slots[toPlayerIndex]->hasValidSocketId() == false) {
592
593 //printf("#2 Server is switching slots\n");
594
595 slots[fromPlayerIndex]->setPlayerIndex(toPlayerIndex);
596 slots[toPlayerIndex]->setPlayerIndex(fromPlayerIndex);
597 ConnectionSlot *tmp = slots[toPlayerIndex];
598 slots[toPlayerIndex] = slots[fromPlayerIndex];
599 slots[fromPlayerIndex] = tmp;
600
601 safeMutex.ReleaseLock();
602
603 PlayerIndexMessage playerIndexMessage(toPlayerIndex);
604 slots[toPlayerIndex]->sendMessage(&playerIndexMessage);
605
606 //slots[fromPlayerIndex]->resetJoinGameInProgressFlags();
607 //slots[toPlayerIndex]->setJoinGameInProgressFlags();
608
609 safeMutexSlot.ReleaseLock();
610 safeMutexSlot2.ReleaseLock();
611 result = true;
612 updateListen();
613 }
614 else {
615 //printf("#3 Server is switching slots aborted, is slot already connected?\n");
616
617 safeMutexSlot.ReleaseLock();
618 safeMutexSlot2.ReleaseLock();
619 safeMutex.ReleaseLock();
620 }
621 //printf("#4 Server is switching slots\n");
622
623 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
624 return result;
625 }
626
getSlotMutex(int playerIndex)627 Mutex *ServerInterface::getSlotMutex(int playerIndex) {
628 return slotAccessorMutexes[playerIndex];
629 }
630
getSlot(int playerIndex,bool lockMutex)631 ConnectionSlot *ServerInterface::getSlot(int playerIndex, bool lockMutex) {
632 if(playerIndex < 0 || playerIndex >= GameConstants::maxPlayers) {
633 char szBuf[8096]="";
634 snprintf(szBuf,8096,"In [%s::%s %d] playerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,playerIndex);
635 throw megaglest_runtime_error(szBuf);
636 }
637
638 MutexSafeWrapper safeMutexSlot((lockMutex == true ? slotAccessorMutexes[playerIndex] : NULL),CODE_AT_LINE_X(playerIndex));
639 ConnectionSlot *result = slots[playerIndex];
640 return result;
641 }
642
isClientConnected(int playerIndex)643 bool ServerInterface::isClientConnected(int playerIndex) {
644 if(playerIndex < 0 || playerIndex >= GameConstants::maxPlayers) {
645 char szBuf[8096]="";
646 snprintf(szBuf,8096,"In [%s::%s %d] playerIndex is invalid = %d",extractFileFromDirectoryPath(extractFileFromDirectoryPath(__FILE__).c_str()).c_str(),__FUNCTION__,__LINE__,playerIndex);
647 throw megaglest_runtime_error(szBuf);
648 }
649
650 bool result = false;
651 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[playerIndex],CODE_AT_LINE_X(playerIndex));
652 if(slots[playerIndex] != NULL && slots[playerIndex]->isConnected() == true) {
653 result = true;
654 }
655 return result;
656 }
657
hasClientConnection()658 bool ServerInterface::hasClientConnection() {
659 bool result = false;
660 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
661 if(isClientConnected(index) == true) {
662 result = true;
663 break;
664 }
665 }
666 return result;
667 }
668
getSlotCount()669 int ServerInterface::getSlotCount() {
670 int slotCount = 0;
671 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
672 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
673 if(slots[index] != NULL) {
674 ++slotCount;
675 }
676 }
677 return slotCount;
678 }
679
getConnectedSlotCount(bool authenticated)680 int ServerInterface::getConnectedSlotCount(bool authenticated) {
681 int connectedSlotCount = 0;
682 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
683 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
684 if(slots[index] != NULL && slots[index]->isConnected() == true) {
685 if(authenticated == false ||
686 (authenticated == true && slots[index]->getConnectHasHandshaked() == true)) {
687 ++connectedSlotCount;
688 }
689 }
690 }
691 return connectedSlotCount;
692 }
693
getNextEventId()694 int64 ServerInterface::getNextEventId() {
695 nextEventId++;
696 if(nextEventId > INT_MAX) {
697 nextEventId = 1;
698 }
699 return nextEventId;
700 }
701
clientLagCheck(ConnectionSlot * connectionSlot,bool skipNetworkBroadCast)702 std::pair<bool,bool> ServerInterface::clientLagCheck(ConnectionSlot *connectionSlot, bool skipNetworkBroadCast) {
703 std::pair<bool,bool> clientLagExceededOrWarned = std::make_pair(false, false);
704 static bool alreadyInLagCheck = false;
705
706 if(alreadyInLagCheck == true ||
707 (connectionSlot != NULL && (connectionSlot->getSkipLagCheck() == true ||
708 connectionSlot->getConnectHasHandshaked() == false))) {
709 return clientLagExceededOrWarned;
710 }
711
712 try {
713 alreadyInLagCheck = true;
714
715 if((gameStartTime > 0 &&
716 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD) &&
717 (resumeGameStartTime == 0 ||
718 (resumeGameStartTime > 0 &&
719 difftime((long int)time(NULL),resumeGameStartTime) >= LAG_CHECK_GRACE_PERIOD))) {
720 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
721
722 double clientLag = this->getCurrentFrameCount() - connectionSlot->getCurrentFrameCount();
723 double clientLagCount = (gameSettings.getNetworkFramePeriod() > 0 ? (clientLag / gameSettings.getNetworkFramePeriod()) : 0);
724 connectionSlot->setCurrentLagCount(clientLagCount);
725
726 double clientLagTime = difftime((long int)time(NULL),connectionSlot->getLastReceiveCommandListTime());
727
728 if(this->getCurrentFrameCount() > 0) {
729 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] playerIndex = %d, clientLag = %f, clientLagCount = %f, this->getCurrentFrameCount() = %d, connectionSlot->getCurrentFrameCount() = %d, clientLagTime = %f\n",
730 extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,
731 connectionSlot->getPlayerIndex(),
732 clientLag,clientLagCount,
733 this->getCurrentFrameCount(),
734 connectionSlot->getCurrentFrameCount(),
735 clientLagTime);
736 }
737
738 // TEST LAG Error and warnings!!!
739 //clientLagCount = maxFrameCountLagAllowed + 1;
740 //clientLagTime = maxClientLagTimeAllowed + 1;
741 /*
742 if(difftime(time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD + 5) {
743 clientLagTime = maxClientLagTimeAllowed + 1;
744 }
745 else if(difftime(time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD) {
746 clientLagTime = (maxClientLagTimeAllowed * warnFrameCountLagPercent) + 1;
747 }
748 */
749 // END test
750
751
752 //printf("skipNetworkBroadCast [%d] clientLagCount [%f][%f][%f] clientLagTime [%f][%f][%f]\n",skipNetworkBroadCast,clientLagCount,(maxFrameCountLagAllowed * warnFrameCountLagPercent),maxFrameCountLagAllowed,clientLagTime,(maxClientLagTimeAllowed * warnFrameCountLagPercent),maxClientLagTimeAllowed);
753
754 // New lag check
755 if((maxFrameCountLagAllowed > 0 && clientLagCount > maxFrameCountLagAllowed) ||
756 (maxClientLagTimeAllowed > 0 && clientLagTime > maxClientLagTimeAllowed) ||
757 (maxFrameCountLagAllowedEver > 0 && clientLagCount > maxFrameCountLagAllowedEver) ||
758 ( maxClientLagTimeAllowedEver > 0 && clientLagTime > maxClientLagTimeAllowedEver)) {
759
760 clientLagExceededOrWarned.first = true;
761 //printf("#1 Client Warned\n");
762
763 Lang &lang= Lang::getInstance();
764 const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
765 for(unsigned int index = 0; index < languageList.size(); ++index) {
766 char szBuf[4096]="";
767
768 string msgTemplate = "DROPPING %s, exceeded max allowed LAG count of %f [time = %f], clientLag = %f [%f], disconnecting client.";
769 if(lang.hasString("ClientLagDropping") == true) {
770 msgTemplate = lang.getString("ClientLagDropping",languageList[index]);
771 }
772 if(gameSettings.getNetworkPauseGameForLaggedClients() == true &&
773 ((maxFrameCountLagAllowedEver <= 0 || clientLagCount <= maxFrameCountLagAllowedEver) &&
774 (maxClientLagTimeAllowedEver <= 0 || clientLagTime <= maxClientLagTimeAllowedEver))) {
775 msgTemplate = "PAUSING GAME TEMPORARILY for %s, exceeded max allowed LAG count of %f [time = %f], clientLag = %f [%f], waiting for client to catch up...";
776 if(lang.hasString("ClientLagPausing") == true) {
777 msgTemplate = lang.getString("ClientLagPausing",languageList[index]);
778 }
779 }
780 #ifdef WIN32
781 _snprintf(szBuf,4095,msgTemplate.c_str(),connectionSlot->getName().c_str() ,maxFrameCountLagAllowed,maxClientLagTimeAllowed,clientLagCount,clientLagTime);
782 #else
783 snprintf(szBuf,4095,msgTemplate.c_str(),connectionSlot->getName().c_str(),maxFrameCountLagAllowed,maxClientLagTimeAllowed,clientLagCount,clientLagTime);
784 #endif
785 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] %s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,szBuf);
786
787 if(skipNetworkBroadCast == false) {
788 string sMsg = szBuf;
789 bool echoLocal = lang.isLanguageLocal(languageList[index]);
790 sendTextMessage(sMsg,-1, echoLocal, languageList[index], connectionSlot->getPlayerIndex());
791 }
792 }
793
794 if(gameSettings.getNetworkPauseGameForLaggedClients() == false ||
795 (maxFrameCountLagAllowedEver > 0 && clientLagCount > maxFrameCountLagAllowedEver) ||
796 (maxClientLagTimeAllowedEver > 0 && clientLagTime > maxClientLagTimeAllowedEver)) {
797
798 //printf("Closing connection slot lagged out!\n");
799 connectionSlot->close();
800 }
801
802 }
803 // New lag check warning
804 else if((maxFrameCountLagAllowed > 0 && warnFrameCountLagPercent > 0 &&
805 clientLagCount > (maxFrameCountLagAllowed * warnFrameCountLagPercent)) ||
806 (maxClientLagTimeAllowed > 0 && warnFrameCountLagPercent > 0 &&
807 clientLagTime > (maxClientLagTimeAllowed * warnFrameCountLagPercent)) ) {
808
809 clientLagExceededOrWarned.second = true;
810 //printf("#2 Client Warned\n");
811
812 if(connectionSlot->getLagCountWarning() == false) {
813 connectionSlot->setLagCountWarning(true);
814
815 Lang &lang= Lang::getInstance();
816 const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
817 for(unsigned int index = 0; index < languageList.size(); ++index) {
818 char szBuf[4096]="";
819
820 string msgTemplate = "LAG WARNING for %s, may exceed max allowed LAG count of %f [time = %f], clientLag = %f [%f], WARNING...";
821 if(lang.hasString("ClientLagWarning") == true) {
822 msgTemplate = lang.getString("ClientLagWarning",languageList[index]);
823 }
824
825 #ifdef WIN32
826 _snprintf(szBuf,4095,msgTemplate.c_str(),connectionSlot->getName().c_str(),maxFrameCountLagAllowed,maxClientLagTimeAllowed,clientLagCount,clientLagTime);
827 #else
828 snprintf(szBuf,4095,msgTemplate.c_str(),connectionSlot->getName().c_str(),maxFrameCountLagAllowed,maxClientLagTimeAllowed,clientLagCount,clientLagTime);
829 #endif
830 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] %s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,szBuf);
831
832 if(skipNetworkBroadCast == false) {
833 string sMsg = szBuf;
834 bool echoLocal = lang.isLanguageLocal(languageList[index]);
835 sendTextMessage(sMsg,-1, echoLocal, languageList[index], connectionSlot->getPlayerIndex());
836 }
837 }
838 }
839 }
840 else if(connectionSlot->getLagCountWarning() == true) {
841 connectionSlot->setLagCountWarning(false);
842 }
843 }
844 }
845 }
846 catch(const exception &ex) {
847 alreadyInLagCheck = false;
848
849 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
850 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ERROR [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
851 throw megaglest_runtime_error(ex.what());
852 }
853
854 alreadyInLagCheck = false;
855 return clientLagExceededOrWarned;
856 }
857
updateSocketTriggeredList(std::map<PLATFORM_SOCKET,bool> & socketTriggeredList)858 void ServerInterface::updateSocketTriggeredList(std::map<PLATFORM_SOCKET,bool> & socketTriggeredList) {
859 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
860 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
861 ConnectionSlot *connectionSlot = slots[index];
862 if(connectionSlot != NULL) {
863 PLATFORM_SOCKET clientSocket = connectionSlot->getSocketId();
864 if(Socket::isSocketValid(&clientSocket) == true) {
865 socketTriggeredList[clientSocket] = false;
866 }
867 }
868 }
869 }
870
validateConnectedClients()871 void ServerInterface::validateConnectedClients() {
872 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
873 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
874 ConnectionSlot* connectionSlot = slots[index];
875 if(connectionSlot != NULL) {
876 connectionSlot->validateConnection();
877 }
878 }
879 }
880
signalClientReceiveCommands(ConnectionSlot * connectionSlot,int slotIndex,bool socketTriggered,ConnectionSlotEvent & event)881 bool ServerInterface::signalClientReceiveCommands(ConnectionSlot *connectionSlot,
882 int slotIndex, bool socketTriggered, ConnectionSlotEvent & event) {
883 bool slotSignalled = false;
884
885 event.eventType = eReceiveSocketData;
886 event.networkMessage = NULL;
887 event.connectionSlot = connectionSlot;
888 event.socketTriggered = socketTriggered;
889 event.triggerId = slotIndex;
890 event.eventId = getNextEventId();
891
892 if(connectionSlot != NULL) {
893 if(socketTriggered == true || connectionSlot->isConnected() == false) {
894 connectionSlot->signalUpdate(&event);
895 slotSignalled = true;
896 }
897 }
898 return slotSignalled;
899 }
900
signalClientsToRecieveData(std::map<PLATFORM_SOCKET,bool> & socketTriggeredList,std::map<int,ConnectionSlotEvent> & eventList,std::map<int,bool> & mapSlotSignalledList)901 void ServerInterface::signalClientsToRecieveData(std::map<PLATFORM_SOCKET,bool> &socketTriggeredList,
902 std::map<int,ConnectionSlotEvent> &eventList,
903 std::map<int,bool> & mapSlotSignalledList) {
904 //printf("====================================In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
905
906 //printf("Signal clients get new data\n");
907 const bool newThreadManager = Config::getInstance().getBool("EnableNewThreadManager","false");
908 if(newThreadManager == true) {
909 masterController.clearSlaves(true);
910 std::vector<SlaveThreadControllerInterface *> slaveThreadList;
911 for(int i= 0; exitServer == false && i < GameConstants::maxPlayers; ++i) {
912 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[i],CODE_AT_LINE_X(i));
913 ConnectionSlot* connectionSlot = slots[i];
914
915 bool socketTriggered = false;
916
917 if(connectionSlot != NULL) {
918 PLATFORM_SOCKET clientSocket = connectionSlot->getSocketId();
919 if(Socket::isSocketValid(&clientSocket)) {
920 socketTriggered = socketTriggeredList[clientSocket];
921 }
922 else if(this->getGameHasBeenInitiated() == true &&
923 this->getAllowInGameConnections() == true) {
924 socketTriggeredList[clientSocket] = true;
925 socketTriggered = socketTriggeredList[clientSocket];
926 }
927 }
928 ConnectionSlotEvent &event = eventList[i];
929 event.eventType = eReceiveSocketData;
930 event.networkMessage = NULL;
931 event.connectionSlot = connectionSlot;
932 event.socketTriggered = socketTriggered;
933 event.triggerId = i;
934 event.eventId = getNextEventId();
935
936 if(connectionSlot != NULL) {
937 if(socketTriggered == true || connectionSlot->isConnected() == false) {
938 if(connectionSlot->getWorkerThread() != NULL) {
939 slaveThreadList.push_back(connectionSlot->getWorkerThread());
940 mapSlotSignalledList[i] = true;
941 }
942 }
943 }
944 }
945 masterController.setSlaves(slaveThreadList);
946 masterController.signalSlaves(&eventList);
947 }
948 else {
949 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
950 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
951 ConnectionSlot *connectionSlot = slots[index];
952
953 if(connectionSlot != NULL) {
954 bool socketTriggered = false;
955 PLATFORM_SOCKET clientSocket = connectionSlot->getSocketId();
956 if(Socket::isSocketValid(&clientSocket)) {
957 socketTriggered = socketTriggeredList[clientSocket];
958 }
959
960 ConnectionSlotEvent &event = eventList[index];
961 bool socketSignalled = signalClientReceiveCommands(connectionSlot,index,socketTriggered,event);
962 if(connectionSlot != NULL && socketTriggered == true) {
963 mapSlotSignalledList[index] = socketSignalled;
964 }
965 }
966 }
967 }
968 }
969
checkForCompletedClientsUsingThreadManager(std::map<int,bool> & mapSlotSignalledList,std::vector<string> & errorMsgList)970 void ServerInterface::checkForCompletedClientsUsingThreadManager(
971 std::map<int, bool> &mapSlotSignalledList, std::vector<string>& errorMsgList) {
972
973 masterController.waitTillSlavesTrigger(MAX_SLOT_THREAD_WAIT_TIME_MILLISECONDS);
974 masterController.clearSlaves(true);
975
976 for (int i = 0; exitServer == false && i < GameConstants::maxPlayers; ++i) {
977 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[i],CODE_AT_LINE_X(i));
978
979 ConnectionSlot* connectionSlot = slots[i];
980 if (connectionSlot != NULL && mapSlotSignalledList[i] == true) {
981
982 try {
983 std::vector<std::string> errorList =
984 connectionSlot->getThreadErrorList();
985 // Collect any collected errors from threads
986 if (errorList.empty() == false) {
987 for (int iErrIdx = 0; iErrIdx < (int) errorList.size();++iErrIdx) {
988 string &sErr = errorList[iErrIdx];
989
990 if (sErr != "") {
991 errorMsgList.push_back(sErr);
992 }
993 }
994 connectionSlot->clearThreadErrorList();
995 }
996 } catch (const exception &ex) {
997 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
998 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
999
1000 errorMsgList.push_back(ex.what());
1001 }
1002 }
1003 }
1004 }
1005
checkForCompletedClientsUsingLoop(std::map<int,bool> & mapSlotSignalledList,std::vector<string> & errorMsgList,std::map<int,ConnectionSlotEvent> & eventList)1006 void ServerInterface::checkForCompletedClientsUsingLoop(
1007 std::map<int, bool>& mapSlotSignalledList, std::vector<string> &errorMsgList,
1008 std::map<int, ConnectionSlotEvent> &eventList) {
1009
1010 //time_t waitForThreadElapsed = time(NULL);
1011 Chrono waitForThreadElapsed(true);
1012
1013 std::map<int, bool> slotsCompleted;
1014 for (bool threadsDone = false; exitServer == false && threadsDone == false &&
1015 waitForThreadElapsed.getMillis() <= MAX_SLOT_THREAD_WAIT_TIME_MILLISECONDS;) {
1016
1017 threadsDone = true;
1018 // Examine all threads for completion of delegation
1019 for (int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1020 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1021
1022 ConnectionSlot *connectionSlot = slots[index];
1023 if (connectionSlot != NULL && connectionSlot->isConnected() == true &&
1024 mapSlotSignalledList[index] == true &&
1025 connectionSlot->getJoinGameInProgress() == false &&
1026 slotsCompleted.find(index) == slotsCompleted.end()) {
1027
1028 try {
1029 std::vector<std::string> errorList = connectionSlot->getThreadErrorList();
1030 // Collect any collected errors from threads
1031 if (errorList.empty() == false) {
1032
1033 for (int iErrIdx = 0; iErrIdx < (int) errorList.size();++iErrIdx) {
1034
1035 string &sErr = errorList[iErrIdx];
1036 if (sErr != "") {
1037 errorMsgList.push_back(sErr);
1038 }
1039 }
1040 connectionSlot->clearThreadErrorList();
1041 }
1042
1043 // Not done waiting for data yet
1044 bool updateFinished = (connectionSlot != NULL ? connectionSlot->updateCompleted(&eventList[index]) : true);
1045 if (updateFinished == false) {
1046 threadsDone = false;
1047 break;
1048 }
1049 else {
1050 slotsCompleted[index] = true;
1051 }
1052 }
1053 catch (const exception &ex) {
1054 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1055 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1056
1057 errorMsgList.push_back(ex.what());
1058 }
1059 }
1060 }
1061 }
1062 }
1063
getIpAddress(bool mutexLock)1064 std::string ServerInterface::getIpAddress(bool mutexLock) {
1065 string result = serverSocket.getIpAddress();
1066 return result;
1067 }
1068
setClientLagCallbackInterface(ClientLagCallbackInterface * intf)1069 void ServerInterface::setClientLagCallbackInterface(ClientLagCallbackInterface *intf) {
1070 this->clientLagCallbackInterface = intf;
1071 }
1072
getClientsAutoPausedDueToLag()1073 bool ServerInterface::getClientsAutoPausedDueToLag() {
1074 return this->clientsAutoPausedDueToLag;
1075 }
1076
checkForCompletedClients(std::map<int,bool> & mapSlotSignalledList,std::vector<string> & errorMsgList,std::map<int,ConnectionSlotEvent> & eventList)1077 void ServerInterface::checkForCompletedClients(std::map<int,bool> & mapSlotSignalledList,
1078 std::vector <string> &errorMsgList,
1079 std::map<int,ConnectionSlotEvent> &eventList) {
1080
1081 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
1082
1083 const bool newThreadManager = Config::getInstance().getBool("EnableNewThreadManager","false");
1084 if(newThreadManager == true) {
1085 checkForCompletedClientsUsingThreadManager(mapSlotSignalledList, errorMsgList);
1086 }
1087 else {
1088 checkForCompletedClientsUsingLoop(mapSlotSignalledList, errorMsgList, eventList);
1089 }
1090 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
1091 }
1092
checkForAutoPauseForLaggingClient(int index,ConnectionSlot * connectionSlot)1093 void ServerInterface::checkForAutoPauseForLaggingClient(int index,ConnectionSlot* connectionSlot) {
1094
1095 if (gameSettings.getNetworkPauseGameForLaggedClients() == true &&
1096 this->clientsAutoPausedDueToLag == false) {
1097 if (connectionSlot != NULL && connectionSlot->isConnected() == true) {
1098 if (connectionSlot->getAutoPauseGameCountForLag() < MAX_CLIENT_PAUSE_FOR_LAG_COUNT) {
1099 if (this->clientLagCallbackInterface != NULL) {
1100
1101 if (this->clientsAutoPausedDueToLagTimer.isStarted() == false ||
1102 this->clientsAutoPausedDueToLagTimer.getMillis() >= MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS) {
1103
1104 connectionSlot->incrementAutoPauseGameCountForLag();
1105
1106 this->clientsAutoPausedDueToLag = true;
1107 if (this->clientLagCallbackInterface->clientLagHandler(index, true) == false) {
1108 connectionSlot->close();
1109 }
1110 else {
1111 if (this->clientsAutoPausedDueToLagTimer.isStarted()== true) {
1112
1113 this->clientsAutoPausedDueToLagTimer.reset();
1114 this->clientsAutoPausedDueToLagTimer.stop();
1115 }
1116 this->clientsAutoPausedDueToLagTimer.start();
1117 }
1118 }
1119 }
1120 }
1121 }
1122 }
1123 }
1124
checkForLaggingClients(std::map<int,bool> & mapSlotSignalledList,std::map<int,ConnectionSlotEvent> & eventList,std::map<PLATFORM_SOCKET,bool> & socketTriggeredList,std::vector<string> & errorMsgList)1125 void ServerInterface::checkForLaggingClients(std::map<int,bool> &mapSlotSignalledList,
1126 std::map<int,ConnectionSlotEvent> &eventList,
1127 std::map<PLATFORM_SOCKET,bool> &socketTriggeredList,
1128 std::vector <string> &errorMsgList) {
1129 bool lastGlobalLagCheckTimeUpdate = false;
1130 if(gameHasBeenInitiated == true) {
1131
1132 //time_t waitForClientsElapsed = time(NULL);
1133 Chrono waitForClientsElapsed(true);
1134 //time_t waitForThreadElapsed = time(NULL);
1135 Chrono waitForThreadElapsed(true);
1136 std::map<int,bool> slotsCompleted;
1137 std::map<int,bool> slotsWarnedList;
1138
1139 for(bool threadsDone = false;
1140 exitServer == false && threadsDone == false &&
1141 waitForThreadElapsed.getMillis() <= MAX_SLOT_THREAD_WAIT_TIME_MILLISECONDS;) {
1142
1143 threadsDone = true;
1144 // Examine all threads for completion of delegation
1145 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1146 //printf("#1 Check lag for i: %d\n",i);
1147
1148 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1149 ConnectionSlot* connectionSlot = slots[index];
1150
1151 if(connectionSlot != NULL && connectionSlot->isConnected() == true &&
1152 connectionSlot->getSkipLagCheck() == false &&
1153 mapSlotSignalledList[index] == true &&
1154 slotsCompleted.find(index) == slotsCompleted.end()) {
1155
1156 //printf("#2 Check lag for i: %d playerindex: %d name [%s] socket: %d\n",i,connectionSlot->getPlayerIndex(),connectionSlot->getName().c_str(),connectionSlot->getSocketId());
1157 try {
1158 std::vector<std::string> errorList = connectionSlot->getThreadErrorList();
1159 // Show any collected errors from threads
1160 if(errorList.empty() == false) {
1161 for(int iErrIdx = 0; iErrIdx < (int)errorList.size(); ++iErrIdx) {
1162 string &sErr = errorList[iErrIdx];
1163 if(sErr != "") {
1164 errorMsgList.push_back(sErr);
1165 }
1166 }
1167 connectionSlot->clearThreadErrorList();
1168 }
1169
1170 // Not done waiting for data yet
1171 bool updateFinished = (connectionSlot != NULL ? connectionSlot->updateCompleted(&eventList[index]) : true);
1172 if(updateFinished == false) {
1173 //printf("#2a Check lag for i: %d\n",i);
1174 threadsDone = false;
1175 break;
1176 }
1177 else {
1178 // New lag check
1179 std::pair<bool,bool> clientLagExceededOrWarned = std::make_pair(false,false);
1180 if( gameHasBeenInitiated == true && connectionSlot != NULL &&
1181 connectionSlot->isConnected() == true) {
1182 clientLagExceededOrWarned = clientLagCheck(connectionSlot,slotsWarnedList[index]);
1183
1184 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] clientLagExceededOrWarned.first = %d, clientLagExceededOrWarned.second = %d, gameSettings.getNetworkPauseGameForLaggedClients() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientLagExceededOrWarned.first,clientLagExceededOrWarned.second,gameSettings.getNetworkPauseGameForLaggedClients());
1185
1186 if(clientLagExceededOrWarned.first == true) {
1187 slotsWarnedList[index] = true;
1188 }
1189 }
1190 // If the client has exceeded lag and the server wants
1191 // to pause while they catch up, re-trigger the
1192 // client reader thread
1193 if((clientLagExceededOrWarned.second == true &&
1194 gameSettings.getNetworkPauseGameForLaggedClients() == true)) {
1195
1196 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d, clientLagExceededOrWarned.first = %d, clientLagExceededOrWarned.second = %d, waitForClientsElapsed.getMillis() = %d, MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientLagExceededOrWarned.first,clientLagExceededOrWarned.second,(int)waitForClientsElapsed.getMillis(),MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS);
1197
1198 checkForAutoPauseForLaggingClient(index, connectionSlot);
1199
1200 slotsCompleted[index] = true;
1201 }
1202 else {
1203 slotsCompleted[index] = true;
1204 }
1205 }
1206 }
1207 catch(const exception &ex) {
1208 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1209 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1210 errorMsgList.push_back(ex.what());
1211 }
1212 }
1213
1214 //printf("#3 Check lag for i: %d\n",i);
1215 if(connectionSlot != NULL &&
1216 connectionSlot->isConnected() == true &&
1217 connectionSlot->getSkipLagCheck() == false) {
1218 //printf("#4 Check lag for i: %d\n",i);
1219
1220 try {
1221 if(gameHasBeenInitiated == true &&
1222 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD &&
1223 difftime((long int)time(NULL),lastGlobalLagCheckTime) >= LAG_CHECK_INTERVAL_PERIOD) {
1224
1225 //printf("\n\n\n^^^^^^^^^^^^^^ PART A\n\n\n");
1226
1227 // New lag check
1228 std::pair<bool,bool> clientLagExceededOrWarned = std::make_pair(false,false);
1229 if( connectionSlot != NULL && connectionSlot->isConnected() == true) {
1230 //printf("\n\n\n^^^^^^^^^^^^^^ PART B\n\n\n");
1231
1232 lastGlobalLagCheckTimeUpdate = true;
1233 clientLagExceededOrWarned = clientLagCheck(connectionSlot,slotsWarnedList[index]);
1234
1235 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] clientLagExceededOrWarned.first = %d, clientLagExceededOrWarned.second = %d, gameSettings.getNetworkPauseGameForLaggedClients() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientLagExceededOrWarned.first,clientLagExceededOrWarned.second,gameSettings.getNetworkPauseGameForLaggedClients());
1236
1237 if(clientLagExceededOrWarned.first == true) {
1238 slotsWarnedList[index] = true;
1239 }
1240
1241 // If the client has exceeded lag and the server wants
1242 // to pause while they catch up, re-trigger the
1243 // client reader thread
1244 if((clientLagExceededOrWarned.second == true &&
1245 gameSettings.getNetworkPauseGameForLaggedClients() == true)) {
1246
1247 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d, clientLagExceededOrWarned.first = %d, clientLagExceededOrWarned.second = %d, waitForClientsElapsed.getMillis() = %d, MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,clientLagExceededOrWarned.first,clientLagExceededOrWarned.second,(int)waitForClientsElapsed.getMillis(),MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS);
1248
1249 checkForAutoPauseForLaggingClient(index, connectionSlot);
1250 }
1251 }
1252 }
1253 }
1254 catch(const exception &ex) {
1255 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1256 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1257 errorMsgList.push_back(ex.what());
1258 }
1259 }
1260
1261 //printf("#5 Check lag for i: %d\n",i);
1262 }
1263 }
1264 }
1265 if(lastGlobalLagCheckTimeUpdate == true) {
1266 lastGlobalLagCheckTime = time(NULL);
1267 }
1268 }
1269
executeNetworkCommandsFromClients()1270 void ServerInterface::executeNetworkCommandsFromClients() {
1271 if(gameHasBeenInitiated == true) {
1272 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1273 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1274 ConnectionSlot* connectionSlot= slots[index];
1275 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
1276 vector<NetworkCommand> pendingList = connectionSlot->getPendingNetworkCommandList(true);
1277 if(pendingList.empty() == false) {
1278 for(int idx = 0; exitServer == false && idx < (int)pendingList.size(); ++idx) {
1279 NetworkCommand &cmd = pendingList[idx];
1280 this->requestCommand(&cmd);
1281 }
1282 //printf("Executed: %d commands from slot: %d\n",pendingList.size(),index);
1283 }
1284 }
1285 }
1286 }
1287 }
1288
dispatchPendingChatMessages(std::vector<string> & errorMsgList)1289 void ServerInterface::dispatchPendingChatMessages(std::vector <string> &errorMsgList) {
1290 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1291
1292 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1293 ConnectionSlot *connectionSlot = slots[index];
1294
1295 if(connectionSlot != NULL &&
1296 connectionSlot->getChatTextList(false).empty() == false) {
1297 try {
1298 std::vector<ChatMsgInfo> chatText = connectionSlot->getChatTextList(true);
1299 for(int chatIdx = 0;
1300 exitServer == false && slots[index] != NULL &&
1301 chatIdx < (int)chatText.size(); chatIdx++) {
1302
1303 connectionSlot = slots[index];
1304 if(connectionSlot != NULL) {
1305 ChatMsgInfo msg(chatText[chatIdx]);
1306 this->addChatInfo(msg);
1307
1308 string newChatText = msg.chatText.c_str();
1309 int newChatTeamIndex = msg.chatTeamIndex;
1310 int newChatPlayerIndex = msg.chatPlayerIndex;
1311 string newChatLanguage = msg.targetLanguage;
1312
1313 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 about to broadcast nmtText chatText [%s] chatTeamIndex = %d, newChatPlayerIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex,newChatPlayerIndex);
1314
1315 if(newChatLanguage == "" ||
1316 newChatLanguage == connectionSlot->getNetworkPlayerLanguage()) {
1317
1318 NetworkMessageText networkMessageText(newChatText.c_str(),newChatTeamIndex,newChatPlayerIndex,newChatLanguage);
1319 broadcastMessage(&networkMessageText, connectionSlot->getPlayerIndex(),index);
1320 }
1321
1322 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtText chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1323 }
1324 }
1325
1326 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] index = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,index);
1327 // Its possible that the slot is disconnected here
1328 // so check the original pointer again
1329 if(slots[index] != NULL) {
1330 slots[index]->clearChatInfo();
1331 }
1332 }
1333 catch(const exception &ex) {
1334 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1335 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1336 errorMsgList.push_back(ex.what());
1337 }
1338 }
1339 }
1340 }
1341
dispatchPendingMarkCellMessages(std::vector<string> & errorMsgList)1342 void ServerInterface::dispatchPendingMarkCellMessages(std::vector <string> &errorMsgList) {
1343 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1344
1345 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1346 ConnectionSlot* connectionSlot= slots[index];
1347
1348 if(connectionSlot != NULL &&
1349 connectionSlot->getMarkedCellList(false).empty() == false) {
1350
1351 try {
1352 std::vector<MarkedCell> chatText = connectionSlot->getMarkedCellList(true);
1353 for(int chatIdx = 0;
1354 exitServer == false && slots[index] != NULL &&
1355 chatIdx < (int)chatText.size(); chatIdx++) {
1356
1357 connectionSlot= slots[index];
1358 if(connectionSlot != NULL) {
1359 MarkedCell msg(chatText[chatIdx]);
1360 this->addMarkedCell(msg);
1361
1362 NetworkMessageMarkCell networkMessageMarkCell(msg.getTargetPos(),msg.getFactionIndex(),msg.getNote(),msg.getPlayerIndex());
1363 broadcastMessage(&networkMessageMarkCell, connectionSlot->getPlayerIndex(),index);
1364 //if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtText chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1365 }
1366 }
1367
1368 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] i = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,index);
1369 // Its possible that the slot is disconnected here
1370 // so check the original pointer again
1371 if(slots[index] != NULL) {
1372 slots[index]->clearMarkedCellList();
1373 }
1374 }
1375 catch(const exception &ex) {
1376 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1377 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1378 errorMsgList.push_back(ex.what());
1379 }
1380 }
1381 }
1382 }
dispatchPendingHighlightCellMessages(std::vector<string> & errorMsgList)1383 void ServerInterface::dispatchPendingHighlightCellMessages(std::vector <string> &errorMsgList) {
1384 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1385
1386 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1387 ConnectionSlot* connectionSlot= slots[index];
1388 if(connectionSlot != NULL &&
1389 connectionSlot->getHighlightedCellList(false).empty() == false) {
1390
1391 try {
1392 std::vector<MarkedCell> highlightedCells = connectionSlot->getHighlightedCellList(true);
1393 for(int chatIdx = 0;
1394 exitServer == false && slots[index] != NULL &&
1395 chatIdx < (int)highlightedCells.size(); chatIdx++) {
1396
1397 connectionSlot= slots[index];
1398 if(connectionSlot != NULL) {
1399 MarkedCell msg(highlightedCells[chatIdx]);
1400 this->setHighlightedCell(msg);
1401
1402 NetworkMessageHighlightCell networkMessageHighlightCell(msg.getTargetPos(),msg.getFactionIndex());
1403 broadcastMessage(&networkMessageHighlightCell, connectionSlot->getPlayerIndex(),index);
1404 }
1405 }
1406
1407 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] index = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,index);
1408 // Its possible that the slot is disconnected here
1409 // so check the original pointer again
1410 if(slots[index] != NULL) {
1411 slots[index]->clearHighlightedCellList();
1412 }
1413 }
1414 catch(const exception &ex) {
1415 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1416 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1417 errorMsgList.push_back(ex.what());
1418 }
1419 }
1420 }
1421 }
1422
dispatchPendingUnMarkCellMessages(std::vector<string> & errorMsgList)1423 void ServerInterface::dispatchPendingUnMarkCellMessages(std::vector <string> &errorMsgList) {
1424 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1425
1426 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1427 ConnectionSlot* connectionSlot= slots[index];
1428 if(connectionSlot != NULL &&
1429 connectionSlot->getUnMarkedCellList(false).empty() == false) {
1430
1431 try {
1432 std::vector<UnMarkedCell> chatText = connectionSlot->getUnMarkedCellList(true);
1433 for(int chatIdx = 0;
1434 exitServer == false && slots[index] != NULL &&
1435 chatIdx < (int)chatText.size(); chatIdx++) {
1436
1437 connectionSlot = slots[index];
1438 if(connectionSlot != NULL) {
1439 UnMarkedCell msg(chatText[chatIdx]);
1440 this->addUnMarkedCell(msg);
1441
1442 NetworkMessageUnMarkCell networkMessageMarkCell(msg.getTargetPos(),msg.getFactionIndex());
1443 broadcastMessage(&networkMessageMarkCell, connectionSlot->getPlayerIndex(),index);
1444 //if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtText chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1445 }
1446 }
1447
1448 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] i = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,index);
1449 // Its possible that the slot is disconnected here
1450 // so check the original pointer again
1451 if(slots[index] != NULL) {
1452 slots[index]->clearUnMarkedCellList();
1453 }
1454 }
1455 catch(const exception &ex) {
1456 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1457 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1458 errorMsgList.push_back(ex.what());
1459 }
1460 }
1461 }
1462 }
1463
checkForAutoResumeForLaggingClients()1464 void ServerInterface::checkForAutoResumeForLaggingClients() {
1465 if (gameSettings.getNetworkPauseGameForLaggedClients() == true &&
1466 this->clientsAutoPausedDueToLag == true &&
1467 this->clientsAutoPausedDueToLagTimer.getMillis() >= MAX_CLIENT_WAIT_SECONDS_FOR_PAUSE_MILLISECONDS) {
1468
1469 //printf("this->clientsAutoPausedDueToLag: %d [%lld]\n",this->clientsAutoPausedDueToLag,(long long)this->clientsAutoPausedDueToLagTimer.getMillis());
1470 if (this->clientLagCallbackInterface != NULL) {
1471
1472 this->clientsAutoPausedDueToLag = false;
1473 this->clientLagCallbackInterface->clientLagHandler(-1, false);
1474
1475 this->clientsAutoPausedDueToLagTimer.reset();
1476 this->clientsAutoPausedDueToLagTimer.stop();
1477 this->clientsAutoPausedDueToLagTimer.start();
1478 }
1479 }
1480 }
1481
update()1482 void ServerInterface::update() {
1483 //printf("\nServerInterface::update -- A\n");
1484
1485 std::vector <string> errorMsgList;
1486 try {
1487 // The first thing we will do is check all clients to ensure they have
1488 // properly identified themselves within the alloted time period
1489 validateConnectedClients();
1490
1491 //printf("\nServerInterface::update -- B\n");
1492
1493 processTextMessageQueue();
1494 processBroadCastMessageQueue();
1495
1496 checkForAutoResumeForLaggingClients();
1497
1498 //printf("\nServerInterface::update -- C\n");
1499
1500 std::map<PLATFORM_SOCKET,bool> socketTriggeredList;
1501 //update all slots
1502 updateSocketTriggeredList(socketTriggeredList);
1503
1504 //printf("\nServerInterface::update -- D\n");
1505
1506 if(gameHasBeenInitiated == false ||
1507 socketTriggeredList.empty() == false) {
1508 //printf("\nServerInterface::update -- E\n");
1509
1510 std::map<int,ConnectionSlotEvent> eventList;
1511
1512 bool hasData = false;
1513 if(gameHasBeenInitiated == false) {
1514 hasData = Socket::hasDataToRead(socketTriggeredList);
1515 }
1516 else {
1517 hasData = true;
1518 }
1519
1520 if(hasData && SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] hasData == true\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
1521
1522 if(gameHasBeenInitiated == false || hasData == true) {
1523 //printf("START Server update #2\n");
1524 std::map<int,bool> mapSlotSignalledList;
1525
1526 // Step #1 tell all connection slot worker threads to receive socket data
1527 if(gameHasBeenInitiated == false) {
1528 signalClientsToRecieveData(socketTriggeredList, eventList, mapSlotSignalledList);
1529 }
1530 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ============ Step #2\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1531
1532 //printf("START Server update #2\n");
1533 if(gameHasBeenInitiated == false || hasData == true) {
1534 //printf("START Server update #3\n");
1535
1536 // Step #2 check all connection slot worker threads for completed status
1537 if(gameHasBeenInitiated == false) {
1538 checkForCompletedClients(mapSlotSignalledList,errorMsgList, eventList);
1539 }
1540 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ============ Step #3\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1541
1542 //printf("START Server update #4\n");
1543 //printf("In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1544
1545 // Step #3 check clients for any lagging scenarios and try to deal with them
1546 if(gameHasBeenInitiated == false) {
1547 checkForLaggingClients(mapSlotSignalledList, eventList, socketTriggeredList,errorMsgList);
1548 }
1549 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ============ Step #4\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1550
1551 //printf("START Server update #5\n");
1552 //printf("In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1553 // Step #4 dispatch network commands to the pending list so that they are done in proper order
1554 executeNetworkCommandsFromClients();
1555 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ============ Step #5\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1556
1557 //printf("START Server update #6\n");
1558 //printf("In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1559 // Step #5 dispatch pending chat messages
1560 dispatchPendingChatMessages(errorMsgList);
1561 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
1562
1563 dispatchPendingMarkCellMessages(errorMsgList);
1564 dispatchPendingUnMarkCellMessages(errorMsgList);
1565
1566 dispatchPendingHighlightCellMessages(errorMsgList);
1567
1568 if(gameHasBeenInitiated == true &&
1569 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD &&
1570 difftime((long int)time(NULL),lastGlobalLagCheckTime) >= LAG_CHECK_INTERVAL_PERIOD) {
1571
1572 std::map<int,bool> mapSlotSignalledList;
1573 checkForLaggingClients(mapSlotSignalledList, eventList, socketTriggeredList,errorMsgList);
1574 }
1575 //printf("START Server update #7\n");
1576 }
1577 else if(gameHasBeenInitiated == true &&
1578 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD &&
1579 difftime((long int)time(NULL),lastGlobalLagCheckTime) >= LAG_CHECK_INTERVAL_PERIOD) {
1580
1581 //printf("Skip network data process because hasData == false\n");
1582 //printf("START Server update #8\n");
1583
1584 std::map<int,bool> mapSlotSignalledList;
1585 checkForLaggingClients(mapSlotSignalledList, eventList, socketTriggeredList,errorMsgList);
1586 }
1587 //printf("START Server update #9\n");
1588 }
1589 else if(gameHasBeenInitiated == true &&
1590 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD &&
1591 difftime((long int)time(NULL),lastGlobalLagCheckTime) >= LAG_CHECK_INTERVAL_PERIOD) {
1592
1593 //printf("\nServerInterface::update -- E1\n");
1594 //printf("START Server update #10\n");
1595
1596 std::map<int,bool> mapSlotSignalledList;
1597 checkForLaggingClients(mapSlotSignalledList, eventList, socketTriggeredList,errorMsgList);
1598 }
1599 //printf("START Server update #11\n");
1600 }
1601 else if(gameHasBeenInitiated == true &&
1602 difftime((long int)time(NULL),gameStartTime) >= LAG_CHECK_GRACE_PERIOD &&
1603 difftime((long int)time(NULL),lastGlobalLagCheckTime) >= LAG_CHECK_INTERVAL_PERIOD) {
1604
1605 //printf("\nServerInterface::update -- F\n");
1606 //printf("START Server update #12\n");
1607
1608 std::map<int,ConnectionSlotEvent> eventList;
1609 std::map<int,bool> mapSlotSignalledList;
1610
1611 checkForLaggingClients(mapSlotSignalledList, eventList, socketTriggeredList,errorMsgList);
1612 }
1613 //printf("START Server update #13\n");
1614
1615 // Check if we need to switch masterserver admin to a new player because original admin disconnected
1616 if(gameHasBeenInitiated == true &&
1617 this->gameSettings.getMasterserver_admin() > 0) {
1618
1619 bool foundAdminSlot = false;
1620 int iFirstConnectedSlot = -1;
1621 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
1622 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1623 if(slots[index] != NULL && slots[index]->isConnected() == true) {
1624 if(iFirstConnectedSlot < 0) {
1625 iFirstConnectedSlot = index;
1626 }
1627 if(this->gameSettings.getMasterserver_admin() == slots[index]->getSessionKey()) {
1628 foundAdminSlot = true;
1629 break;
1630 }
1631 }
1632 }
1633
1634 if(foundAdminSlot == false && iFirstConnectedSlot >= 0) {
1635 printf("Switching masterserver admin to slot#%d...\n",iFirstConnectedSlot);
1636
1637 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[iFirstConnectedSlot],CODE_AT_LINE_X(iFirstConnectedSlot));
1638 if(slots[iFirstConnectedSlot] != NULL) {
1639 string sMsg = "Switching player to admin mode: " + slots[iFirstConnectedSlot]->getName();
1640 sendTextMessage(sMsg,-1, true,"");
1641
1642 this->gameSettings.setMasterserver_admin(slots[iFirstConnectedSlot]->getSessionKey());
1643 this->gameSettings.setMasterserver_admin_faction_index(slots[iFirstConnectedSlot]->getPlayerIndex());
1644
1645 safeMutexSlot.ReleaseLock();
1646 this->broadcastGameSetup(&this->gameSettings);
1647 }
1648 }
1649 }
1650 //printf("\nServerInterface::update -- G\n");
1651 //printf("START Server update #14\n");
1652
1653 checkListenerSlots();
1654
1655 //printf("START Server update #15\n");
1656 }
1657 catch(const exception &ex) {
1658 //printf("\nServerInterface::update -- H\n");
1659
1660 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1661 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1662 errorMsgList.push_back(ex.what());
1663 }
1664
1665 if(errorMsgList.empty() == false){
1666 for(int iErrIdx = 0; iErrIdx < (int)errorMsgList.size(); ++iErrIdx) {
1667 string &sErr = errorMsgList[iErrIdx];
1668 if(sErr != "") {
1669 DisplayErrorMessage(sErr);
1670 }
1671 }
1672 }
1673 }
1674
updateKeyframe(int frameCount)1675 void ServerInterface::updateKeyframe(int frameCount) {
1676 currentFrameCount = frameCount;
1677 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] currentFrameCount = %d, requestedCommands.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,currentFrameCount,requestedCommands.size());
1678
1679 NetworkMessageCommandList networkMessageCommandList(frameCount);
1680 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
1681 networkMessageCommandList.setNetworkPlayerFactionCRC(index,this->getNetworkPlayerFactionCRC(index));
1682 }
1683
1684 while(requestedCommands.empty() == false) {
1685 // First add the command to the broadcast list (for all clients)
1686 if(networkMessageCommandList.addCommand(&requestedCommands.back())) {
1687 // Add the command to the local server command list
1688 pendingCommands.push_back(requestedCommands.back());
1689 requestedCommands.pop_back();
1690 }
1691 else {
1692 break;
1693 }
1694 }
1695
1696 try {
1697 // Possible cause of out of synch since we have more commands that need
1698 // to be sent in this frame
1699 if(requestedCommands.empty() == false) {
1700 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] WARNING / ERROR, requestedCommands.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,requestedCommands.size());
1701 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] WARNING / ERROR, requestedCommands.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,requestedCommands.size());
1702
1703 string sMsg = "may go out of synch: server requestedCommands.size() = " + intToStr(requestedCommands.size());
1704 sendTextMessage(sMsg,-1, true,"");
1705 }
1706
1707 // broadcast commands
1708 // If we have more than 0 commands to send, automatically broadcast them
1709 bool sendBroadcastMessage = (networkMessageCommandList.getCommandCount() > 0);
1710 if(sendBroadcastMessage == false) {
1711
1712 // Is auto pause due to lag NOT enabled
1713 if(this->getClientsAutoPausedDueToLag() == false) {
1714
1715 // ****NOTE:
1716 // We always need to broadcast when not pause as clients
1717 // look for broadcasts every network frame.
1718 sendBroadcastMessage = true;
1719 }
1720 // Auto pause is enabled due to client lagging, only send empty command
1721 // broadcasts every MAX_EMPTY_NETWORK_COMMAND_LIST_BROADCAST_INTERVAL_MILLISECONDS
1722 else if(this->getClientsAutoPausedDueToLag() == true &&
1723 (lastBroadcastCommandsTimer.isStarted() == false ||
1724 lastBroadcastCommandsTimer.getMillis() >= MAX_EMPTY_NETWORK_COMMAND_LIST_BROADCAST_INTERVAL_MILLISECONDS)) {
1725
1726 sendBroadcastMessage = true;
1727 }
1728 }
1729
1730 if(sendBroadcastMessage == true) {
1731
1732 if(lastBroadcastCommandsTimer.isStarted() == false) {
1733 lastBroadcastCommandsTimer.start();
1734 }
1735 else {
1736 lastBroadcastCommandsTimer.stop();
1737 lastBroadcastCommandsTimer.reset();
1738 lastBroadcastCommandsTimer.start();
1739 }
1740 broadcastMessage(&networkMessageCommandList);
1741 }
1742 }
1743 catch(const exception &ex) {
1744 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1745 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
1746 DisplayErrorMessage(ex.what());
1747 }
1748 }
1749
shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,ConnectionSlot * connectionSlot)1750 bool ServerInterface::shouldDiscardNetworkMessage(NetworkMessageType networkMessageType,
1751 ConnectionSlot *connectionSlot) {
1752 bool discard = false;
1753 if(connectionSlot != NULL) {
1754 switch(networkMessageType) {
1755 case nmtIntro:
1756 {
1757 discard = true;
1758 NetworkMessageIntro msg = NetworkMessageIntro();
1759 connectionSlot->receiveMessage(&msg);
1760 }
1761 break;
1762 case nmtPing:
1763 {
1764 discard = true;
1765 NetworkMessagePing msg = NetworkMessagePing();
1766 connectionSlot->receiveMessage(&msg);
1767 lastPingInfo = msg;
1768 }
1769 break;
1770
1771 case nmtLaunch:
1772 {
1773 discard = true;
1774 NetworkMessageLaunch msg = NetworkMessageLaunch();
1775 connectionSlot->receiveMessage(&msg);
1776 }
1777 break;
1778 case nmtText:
1779 {
1780 discard = true;
1781 NetworkMessageText netMsg = NetworkMessageText();
1782 connectionSlot->receiveMessage(&netMsg);
1783
1784 ChatMsgInfo msg(netMsg.getText().c_str(),netMsg.getTeamIndex(),netMsg.getPlayerIndex(),netMsg.getTargetLanguage());
1785 this->addChatInfo(msg);
1786
1787 string newChatText = msg.chatText.c_str();
1788 //string newChatSender = msg.chatSender.c_str();
1789 int newChatTeamIndex = msg.chatTeamIndex;
1790 int newChatPlayerIndex = msg.chatPlayerIndex;
1791 string newChatLanguage = msg.targetLanguage.c_str();
1792
1793 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 about to broadcast nmtText chatText [%s] chatTeamIndex = %d, newChatPlayerIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex,newChatPlayerIndex);
1794
1795 NetworkMessageText networkMessageText(newChatText.c_str(),newChatTeamIndex,newChatPlayerIndex,newChatLanguage);
1796 broadcastMessage(&networkMessageText, connectionSlot->getPlayerIndex());
1797
1798 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtText chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1799
1800 }
1801 break;
1802
1803 case nmtMarkCell:
1804 {
1805 discard = true;
1806 NetworkMessageMarkCell networkMessageMarkCell;
1807 connectionSlot->receiveMessage(&networkMessageMarkCell);
1808
1809 MarkedCell msg(networkMessageMarkCell.getTarget(),
1810 networkMessageMarkCell.getFactionIndex(),
1811 networkMessageMarkCell.getText().c_str(),
1812 networkMessageMarkCell.getPlayerIndex());
1813
1814 this->addMarkedCell(msg);
1815
1816 NetworkMessageMarkCell networkMessageMarkCellBroadcast(
1817 networkMessageMarkCell.getTarget(),
1818 networkMessageMarkCell.getFactionIndex(),
1819 networkMessageMarkCell.getText().c_str(),
1820 networkMessageMarkCell.getPlayerIndex());
1821 broadcastMessage(&networkMessageMarkCellBroadcast, connectionSlot->getPlayerIndex());
1822
1823 //if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtMarkCell chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1824
1825 }
1826 break;
1827
1828 case nmtUnMarkCell:
1829 {
1830 discard = true;
1831 NetworkMessageUnMarkCell networkMessageMarkCell;
1832 connectionSlot->receiveMessage(&networkMessageMarkCell);
1833
1834 UnMarkedCell msg(networkMessageMarkCell.getTarget(),
1835 networkMessageMarkCell.getFactionIndex());
1836
1837 this->addUnMarkedCell(msg);
1838
1839 NetworkMessageUnMarkCell networkMessageMarkCellBroadcast(
1840 networkMessageMarkCell.getTarget(),
1841 networkMessageMarkCell.getFactionIndex());
1842 broadcastMessage(&networkMessageMarkCellBroadcast, connectionSlot->getPlayerIndex());
1843
1844 //if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtMarkCell chatText [%s] chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatText.c_str(),newChatTeamIndex);
1845
1846 }
1847 break;
1848 case nmtHighlightCell:
1849 {
1850 discard = true;
1851 NetworkMessageHighlightCell networkMessageHighlightCell;
1852 connectionSlot->receiveMessage(&networkMessageHighlightCell);
1853
1854 MarkedCell msg(networkMessageHighlightCell.getTarget(),
1855 networkMessageHighlightCell.getFactionIndex(),
1856 "none",-1);
1857
1858 this->setHighlightedCell(msg);
1859
1860 NetworkMessageHighlightCell networkMessageHighlightCellBroadcast(
1861 networkMessageHighlightCell.getTarget(),
1862 networkMessageHighlightCell.getFactionIndex());
1863 broadcastMessage(&networkMessageHighlightCellBroadcast, connectionSlot->getPlayerIndex());
1864
1865 //if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after broadcast nmtMarkCell chatTeamIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,newChatTeamIndex);
1866
1867 }
1868 break;
1869
1870 case nmtSynchNetworkGameData:
1871 {
1872 discard = true;
1873 NetworkMessageSynchNetworkGameData msg = NetworkMessageSynchNetworkGameData();
1874 connectionSlot->receiveMessage(&msg);
1875 }
1876 break;
1877 case nmtSynchNetworkGameDataStatus:
1878 {
1879 discard = true;
1880 NetworkMessageSynchNetworkGameDataStatus msg = NetworkMessageSynchNetworkGameDataStatus();
1881 connectionSlot->receiveMessage(&msg);
1882 }
1883 break;
1884 case nmtSynchNetworkGameDataFileCRCCheck:
1885 {
1886 discard = true;
1887 NetworkMessageSynchNetworkGameDataFileCRCCheck msg = NetworkMessageSynchNetworkGameDataFileCRCCheck();
1888 connectionSlot->receiveMessage(&msg);
1889 }
1890 break;
1891 case nmtSynchNetworkGameDataFileGet:
1892 {
1893 discard = true;
1894 NetworkMessageSynchNetworkGameDataFileGet msg = NetworkMessageSynchNetworkGameDataFileGet();
1895 connectionSlot->receiveMessage(&msg);
1896 }
1897 break;
1898 case nmtSwitchSetupRequest:
1899 {
1900 discard = true;
1901 SwitchSetupRequest msg = SwitchSetupRequest();
1902 connectionSlot->receiveMessage(&msg);
1903 }
1904 break;
1905 case nmtPlayerIndexMessage:
1906 {
1907 discard = true;
1908 PlayerIndexMessage msg = PlayerIndexMessage(0);
1909 connectionSlot->receiveMessage(&msg);
1910 }
1911 break;
1912 }
1913 }
1914 return discard;
1915 }
1916
waitUntilReady(Checksum * checksum)1917 void ServerInterface::waitUntilReady(Checksum *checksum) {
1918 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] START\n",__FUNCTION__);
1919 Logger & logger = Logger::getInstance();
1920 gameHasBeenInitiated = true;
1921 Chrono chrono;
1922 chrono.start();
1923
1924 bool allReady = false;
1925
1926 if(Config::getInstance().getBool("EnableGameServerLoadCancel","false") == true) {
1927 logger.setCancelLoadingEnabled(true);
1928 }
1929
1930 Lang &lang = Lang::getInstance();
1931 uint64 waitLoopIterationCount = 0;
1932 uint64 MAX_LOOP_COUNT_BEFORE_SLEEP = 10;
1933 MAX_LOOP_COUNT_BEFORE_SLEEP = Config::getInstance().getInt("NetworkServerLoopGameLoadingCap",intToStr(MAX_LOOP_COUNT_BEFORE_SLEEP).c_str());
1934 if(MAX_LOOP_COUNT_BEFORE_SLEEP == 0) {
1935 MAX_LOOP_COUNT_BEFORE_SLEEP = 1;
1936 }
1937 int sleepMillis = Config::getInstance().getInt("NetworkServerLoopGameLoadingCapSleepMillis","10");
1938 int64 lastStatusUpdate = 0;
1939
1940 while(exitServer == false &&
1941 allReady == false &&
1942 logger.getCancelLoading() == false) {
1943
1944 waitLoopIterationCount++;
1945 if(waitLoopIterationCount > 0 &&
1946 waitLoopIterationCount % MAX_LOOP_COUNT_BEFORE_SLEEP == 0) {
1947
1948 sleep(sleepMillis);
1949 waitLoopIterationCount = 0;
1950 }
1951 vector<string> waitingForHosts;
1952 allReady= true;
1953 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
1954
1955 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
1956 ConnectionSlot* connectionSlot= slots[index];
1957 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
1958 if(connectionSlot->isReady() == false) {
1959
1960 NetworkMessageType networkMessageType= connectionSlot->getNextMessageType();
1961
1962 // consume old messages from the lobby
1963 bool discarded = shouldDiscardNetworkMessage(networkMessageType,connectionSlot);
1964 if(discarded == false) {
1965
1966 NetworkMessageReady networkMessageReady;
1967 if(networkMessageType == nmtReady &&
1968 connectionSlot->receiveMessage(&networkMessageReady)) {
1969
1970 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] networkMessageType==nmtReady\n",__FUNCTION__);
1971
1972 connectionSlot->setReady();
1973 connectionSlot->setGameStarted(true);
1974 }
1975 else if(networkMessageType != nmtInvalid) {
1976 string sErr = "Unexpected network message: " + intToStr(networkMessageType);
1977 sendTextMessage(sErr,-1, true,"",index);
1978 DisplayErrorMessage(sErr);
1979 logger.setCancelLoading(false);
1980 return;
1981 }
1982 }
1983 waitingForHosts.push_back(connectionSlot->getName());
1984 allReady= false;
1985 }
1986 }
1987 }
1988
1989 //check for timeout
1990 if(allReady == false) {
1991 if(chrono.getMillis() > readyWaitTimeout) {
1992 Lang &lang= Lang::getInstance();
1993 const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
1994 for(unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {
1995 string sErr = "Timeout waiting for clients.";
1996 if(lang.hasString("TimeoutWaitingForClients") == true) {
1997 sErr = lang.getString("TimeoutWaitingForClients",languageList[langIndex]);
1998 }
1999 bool localEcho = lang.isLanguageLocal(languageList[langIndex]);
2000 sendTextMessage(sErr,-1, localEcho, languageList[langIndex]);
2001 if(localEcho == true) {
2002 DisplayErrorMessage(sErr);
2003 }
2004 }
2005 logger.setCancelLoading(false);
2006 return;
2007 }
2008 else {
2009 if(chrono.getMillis() - lastStatusUpdate > 200) {
2010 lastStatusUpdate = chrono.getMillis();
2011
2012 string waitForHosts = "";
2013 for(int hostIndex = 0; hostIndex < (int)waitingForHosts.size(); hostIndex++) {
2014 if(waitForHosts != "") {
2015 waitForHosts += ", ";
2016 }
2017 waitForHosts += waitingForHosts[hostIndex];
2018 }
2019
2020 char szBuf[8096]="";
2021 string updateTextFormat = lang.getString("NetworkGameServerLoadStatus");
2022 if(updateTextFormat == "" ||
2023 updateTextFormat[0] == '?') {
2024
2025 updateTextFormat = "Waiting for network: %lld seconds elapsed (maximum wait time: %d seconds)";
2026 }
2027 snprintf(szBuf,8096,updateTextFormat.c_str(),(long long int)(chrono.getMillis() / 1000),int(readyWaitTimeout / 1000));
2028
2029 char szBuf1[8096]="";
2030 string statusTextFormat = lang.getString("NetworkGameStatusWaiting");
2031 if(statusTextFormat == "" ||
2032 statusTextFormat[0] == '?') {
2033
2034 statusTextFormat = "Waiting for players: %s";
2035 }
2036 snprintf(szBuf1,8096,statusTextFormat.c_str(),waitForHosts.c_str());
2037
2038 logger.add(szBuf, true, szBuf1);
2039
2040 uint32 loadingStatus = nmls_NONE;
2041 //send ready message after, so clients start delayed
2042 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2043 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2044 ConnectionSlot *connectionSlot= slots[slotIndex];
2045 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
2046 switch(slotIndex) {
2047 case 0:
2048 loadingStatus |= nmls_PLAYER1_CONNECTED;
2049 if(connectionSlot->isReady()) {
2050 loadingStatus |= nmls_PLAYER1_READY;
2051 }
2052 break;
2053 case 1:
2054 loadingStatus |= nmls_PLAYER2_CONNECTED;
2055 if(connectionSlot->isReady()) {
2056 loadingStatus |= nmls_PLAYER2_READY;
2057 }
2058 break;
2059 case 2:
2060 loadingStatus |= nmls_PLAYER3_CONNECTED;
2061 if(connectionSlot->isReady()) {
2062 loadingStatus |= nmls_PLAYER3_READY;
2063 }
2064 break;
2065 case 3:
2066 loadingStatus |= nmls_PLAYER4_CONNECTED;
2067 if(connectionSlot->isReady()) {
2068 loadingStatus |= nmls_PLAYER4_READY;
2069 }
2070 break;
2071 case 4:
2072 loadingStatus |= nmls_PLAYER5_CONNECTED;
2073 if(connectionSlot->isReady()) {
2074 loadingStatus |= nmls_PLAYER5_READY;
2075 }
2076 break;
2077 case 5:
2078 loadingStatus |= nmls_PLAYER6_CONNECTED;
2079 if(connectionSlot->isReady()) {
2080 loadingStatus |= nmls_PLAYER6_READY;
2081 }
2082 break;
2083 case 6:
2084 loadingStatus |= nmls_PLAYER7_CONNECTED;
2085 if(connectionSlot->isReady()) {
2086 loadingStatus |= nmls_PLAYER7_READY;
2087 }
2088 break;
2089 case 7:
2090 loadingStatus |= nmls_PLAYER8_CONNECTED;
2091 if(connectionSlot->isReady()) {
2092 loadingStatus |= nmls_PLAYER8_READY;
2093 }
2094 break;
2095 }
2096 }
2097 }
2098
2099 // send loading status message
2100 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2101 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2102 ConnectionSlot* connectionSlot= slots[slotIndex];
2103 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
2104 NetworkMessageLoadingStatus networkMessageLoadingStatus(loadingStatus);
2105 connectionSlot->sendMessage(&networkMessageLoadingStatus);
2106 }
2107 }
2108
2109 sleep(0);
2110 }
2111 }
2112 }
2113
2114 Shared::Platform::Window::handleEvent();
2115 }
2116
2117 if(logger.getCancelLoading() == true) {
2118 Lang &lang= Lang::getInstance();
2119 const vector<string> languageList = this->gameSettings.getUniqueNetworkPlayerLanguages();
2120 for(unsigned int langIndex = 0; langIndex < languageList.size(); ++langIndex) {
2121
2122 string sErr = lang.getString("GameCancelledByUser",languageList[langIndex]);
2123 bool localEcho = lang.isLanguageLocal(languageList[langIndex]);
2124 sendTextMessage(sErr,-1, localEcho,languageList[langIndex]);
2125
2126 if(localEcho == true) {
2127 DisplayErrorMessage(sErr);
2128 }
2129 }
2130 quitGame(true);
2131 logger.setCancelLoading(false);
2132 return;
2133 }
2134
2135 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] PART B (telling client we are ready!\n",__FUNCTION__);
2136 try {
2137 //send ready message after, so clients start delayed
2138 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2139
2140 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2141 ConnectionSlot* connectionSlot= slots[slotIndex];
2142 if(connectionSlot != NULL && connectionSlot->isConnected() == true) {
2143 NetworkMessageReady networkMessageReady(checksum->getSum());
2144 connectionSlot->sendMessage(&networkMessageReady);
2145 connectionSlot->setGameStarted(true);
2146 }
2147 }
2148
2149 gameStartTime = time(NULL);
2150 }
2151 catch(const exception &ex) {
2152 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2153 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] error detected [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2154 DisplayErrorMessage(ex.what());
2155 }
2156 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s] END\n",__FUNCTION__);
2157 }
2158
processBroadCastMessageQueue()2159 void ServerInterface::processBroadCastMessageQueue() {
2160 MutexSafeWrapper safeMutexSlot(broadcastMessageQueueThreadAccessor,CODE_AT_LINE);
2161 if(broadcastMessageQueue.empty() == false) {
2162 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] broadcastMessageQueue.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,broadcastMessageQueue.size());
2163 for(int index = 0; index < (int)broadcastMessageQueue.size(); ++index) {
2164 pair<NetworkMessage *,int> &item = broadcastMessageQueue[index];
2165 if(item.first != NULL) {
2166 this->broadcastMessage(item.first,item.second);
2167 delete item.first;
2168 }
2169 item.first = NULL;
2170 }
2171 broadcastMessageQueue.clear();
2172 }
2173 }
2174
queueBroadcastMessage(NetworkMessage * networkMessage,int excludeSlot)2175 void ServerInterface::queueBroadcastMessage(NetworkMessage *networkMessage, int excludeSlot) {
2176 MutexSafeWrapper safeMutexSlot(broadcastMessageQueueThreadAccessor,CODE_AT_LINE);
2177 pair<NetworkMessage*,int> item;
2178 item.first = networkMessage;
2179 item.second = excludeSlot;
2180 broadcastMessageQueue.push_back(item);
2181 }
2182
processTextMessageQueue()2183 void ServerInterface::processTextMessageQueue() {
2184 MutexSafeWrapper safeMutexSlot(textMessageQueueThreadAccessor,CODE_AT_LINE);
2185 if(textMessageQueue.empty() == false) {
2186 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] textMessageQueue.size() = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,textMessageQueue.size());
2187 for(int index = 0; index < (int)textMessageQueue.size(); ++index) {
2188 TextMessageQueue &item = textMessageQueue[index];
2189 sendTextMessage(item.text, item.teamIndex, item.echoLocal, item.targetLanguage);
2190 }
2191 textMessageQueue.clear();
2192 }
2193 }
2194
queueTextMessage(const string & text,int teamIndex,bool echoLocal,string targetLanguage)2195 void ServerInterface::queueTextMessage(const string & text, int teamIndex,
2196 bool echoLocal, string targetLanguage) {
2197 //printf("Line: %d text [%s]\n",__LINE__,text.c_str());
2198
2199 MutexSafeWrapper safeMutexSlot(textMessageQueueThreadAccessor,CODE_AT_LINE);
2200 TextMessageQueue item;
2201 item.text = text;
2202 item.teamIndex = teamIndex;
2203 item.echoLocal = echoLocal;
2204 item.targetLanguage = targetLanguage;
2205 textMessageQueue.push_back(item);
2206 }
2207
sendTextMessage(const string & text,int teamIndex,bool echoLocal,string targetLanguage)2208 void ServerInterface::sendTextMessage(const string & text, int teamIndex,
2209 bool echoLocal,string targetLanguage) {
2210 sendTextMessage(text, teamIndex, echoLocal, targetLanguage, -1);
2211 }
2212
sendTextMessage(const string & text,int teamIndex,bool echoLocal,string targetLanguage,int lockedSlotIndex)2213 void ServerInterface::sendTextMessage(const string& text, int teamIndex, bool echoLocal,
2214 string targetLanguage, int lockedSlotIndex) {
2215 //printf("Line: %d text [%s] echoLocal = %d\n",__LINE__,text.c_str(),echoLocal);
2216
2217 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] text [%s] teamIndex = %d, echoLocal = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,text.c_str(),teamIndex,echoLocal,lockedSlotIndex);
2218
2219 NetworkMessageText networkMessageText(text, teamIndex, getHumanPlayerIndex(), targetLanguage);
2220 broadcastMessage(&networkMessageText, -1, lockedSlotIndex);
2221
2222 if(echoLocal == true) {
2223 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2224
2225 ChatMsgInfo msg(text.c_str(),teamIndex,networkMessageText.getPlayerIndex(), targetLanguage);
2226 this->addChatInfo(msg);
2227 }
2228 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2229 }
2230
sendMarkCellMessage(Vec2i targetPos,int factionIndex,string note,int playerIndex)2231 void ServerInterface::sendMarkCellMessage(Vec2i targetPos, int factionIndex, string note,int playerIndex) {
2232 sendMarkCellMessage(targetPos, factionIndex, note, playerIndex, -1);
2233 }
2234
sendMarkCellMessage(Vec2i targetPos,int factionIndex,string note,int playerIndex,int lockedSlotIndex)2235 void ServerInterface::sendMarkCellMessage(Vec2i targetPos, int factionIndex, string note, int playerIndex, int lockedSlotIndex) {
2236 NetworkMessageMarkCell networkMessageMarkCell(targetPos,factionIndex, note, playerIndex);
2237 broadcastMessage(&networkMessageMarkCell, -1, lockedSlotIndex);
2238
2239 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2240 }
2241
sendHighlightCellMessage(Vec2i targetPos,int factionIndex)2242 void ServerInterface::sendHighlightCellMessage(Vec2i targetPos, int factionIndex) {
2243 sendHighlightCellMessage(targetPos, factionIndex, -1);
2244 }
2245
sendHighlightCellMessage(Vec2i targetPos,int factionIndex,int lockedSlotIndex)2246 void ServerInterface::sendHighlightCellMessage(Vec2i targetPos, int factionIndex, int lockedSlotIndex) {
2247 NetworkMessageHighlightCell networkMessageHighlightCell(targetPos,factionIndex);
2248 broadcastMessage(&networkMessageHighlightCell, -1, lockedSlotIndex);
2249
2250 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2251 }
2252
sendUnMarkCellMessage(Vec2i targetPos,int factionIndex)2253 void ServerInterface::sendUnMarkCellMessage(Vec2i targetPos, int factionIndex) {
2254 sendUnMarkCellMessage(targetPos, factionIndex, -1);
2255 }
2256
sendUnMarkCellMessage(Vec2i targetPos,int factionIndex,int lockedSlotIndex)2257 void ServerInterface::sendUnMarkCellMessage(Vec2i targetPos, int factionIndex, int lockedSlotIndex) {
2258 NetworkMessageUnMarkCell networkMessageMarkCell(targetPos,factionIndex);
2259 broadcastMessage(&networkMessageMarkCell, -1, lockedSlotIndex);
2260
2261 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2262 }
2263
quitGame(bool userManuallyQuit)2264 void ServerInterface::quitGame(bool userManuallyQuit) {
2265 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2266
2267 NetworkMessageQuit networkMessageQuit;
2268 broadcastMessage(&networkMessageQuit);
2269
2270 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2271 }
2272
getNetworkStatus()2273 string ServerInterface::getNetworkStatus() {
2274 Lang &lang = Lang::getInstance();
2275 string str = "";
2276 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
2277 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
2278 ConnectionSlot *connectionSlot= slots[index];
2279
2280 str += intToStr(index)+ ": ";
2281
2282 if(connectionSlot!= NULL) {
2283 if(connectionSlot->isConnected()) {
2284 int clientLagCount = connectionSlot->getCurrentLagCount();
2285 double lastClientCommandListTimeLag = difftime((long int)time(NULL),connectionSlot->getLastReceiveCommandListTime());
2286 //float pingTime = connectionSlot->getThreadedPingMS(connectionSlot->getIpAddress().c_str());
2287 char szBuf[8096]="";
2288 snprintf(szBuf,8096,", lag = %d [%.2f]",clientLagCount,lastClientCommandListTimeLag);
2289 str += connectionSlot->getName() + " [" + connectionSlot->getUUID() + "] " + string(szBuf);
2290 }
2291 }
2292 else {
2293 str += lang.getString("NotConnected");
2294 }
2295
2296 str += '\n';
2297 }
2298 return str;
2299 }
2300
launchGame(const GameSettings * gameSettings)2301 bool ServerInterface::launchGame(const GameSettings *gameSettings) {
2302 bool bOkToStart = true;
2303
2304 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2305
2306 for(int index = 0; exitServer == false && index < GameConstants::maxPlayers; ++index) {
2307
2308 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
2309 ConnectionSlot *connectionSlot= slots[index];
2310 if(connectionSlot != NULL &&
2311 (connectionSlot->getAllowDownloadDataSynch() == true || connectionSlot->getAllowGameDataSynchCheck() == true) &&
2312 connectionSlot->isConnected()) {
2313
2314 if(connectionSlot->getNetworkGameDataSynchCheckOk() == false) {
2315
2316 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] map [%d] tile [%d] techtree [%d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,connectionSlot->getNetworkGameDataSynchCheckOkMap(),connectionSlot->getNetworkGameDataSynchCheckOkTile(),connectionSlot->getNetworkGameDataSynchCheckOkTech());
2317 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] map [%d] tile [%d] techtree [%d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,connectionSlot->getNetworkGameDataSynchCheckOkMap(),connectionSlot->getNetworkGameDataSynchCheckOkTile(),connectionSlot->getNetworkGameDataSynchCheckOkTech());
2318
2319 bOkToStart = false;
2320 break;
2321 }
2322 }
2323 }
2324 if(bOkToStart == true) {
2325
2326 bool useInGameBlockingClientSockets = Config::getInstance().getBool("EnableInGameBlockingSockets","true");
2327 if(useInGameBlockingClientSockets == true) {
2328
2329 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2330
2331 for(int index = 0; index < GameConstants::maxPlayers; ++index) {
2332
2333 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[index],CODE_AT_LINE_X(index));
2334 ConnectionSlot *connectionSlot= slots[index];
2335 if(connectionSlot != NULL && connectionSlot->isConnected()) {
2336 connectionSlot->getSocket()->setBlock(true);
2337 }
2338 }
2339 }
2340
2341 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2342
2343 bool requiresUPNPTrigger = false;
2344 for(int startIndex = 0; startIndex < GameConstants::maxPlayers; ++startIndex) {
2345
2346 int factionIndex = gameSettings->getFactionIndexForStartLocation(startIndex);
2347 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[startIndex],CODE_AT_LINE_X(startIndex));
2348 ConnectionSlot *connectionSlot= slots[startIndex];
2349 if((connectionSlot == NULL || connectionSlot->isConnected() == false) &&
2350 this->getAllowInGameConnections() == true) {
2351
2352 // Open slots for joining in progress game
2353 if(gameSettings->getFactionControl(factionIndex) != ctClosed &&
2354 gameSettings->getFactionControl(factionIndex) != ctHuman) {
2355
2356 //printf("Opening slot for in game connections for slot: %d, faction: %d\n",i,factionIndex);
2357 if(connectionSlot == NULL) {
2358 addSlot(startIndex);
2359 connectionSlot = slots[startIndex];
2360 requiresUPNPTrigger = true;
2361 }
2362 connectionSlot->setCanAcceptConnections(true);
2363 }
2364 }
2365 }
2366
2367 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2368
2369 if(this->getAllowInGameConnections() == false) {
2370 serverSocket.stopBroadCastThread();
2371 }
2372
2373 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2374
2375 this->gameSettings = *gameSettings;
2376 //printf("#1 Data synch: lmap %u ltile: %d ltech: %u\n",gameSettings->getMapCRC(),gameSettings->getTilesetCRC(),gameSettings->getTechCRC());
2377
2378 NetworkMessageLaunch networkMessageLaunch(gameSettings,nmtLaunch);
2379 broadcastMessage(&networkMessageLaunch);
2380
2381 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2382
2383 shutdownMasterserverPublishThread();
2384 MutexSafeWrapper safeMutex(masterServerThreadAccessor,CODE_AT_LINE);
2385 lastMasterserverHeartbeatTime = 0;
2386
2387 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ftpServer = %p\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ftpServer);
2388
2389 if(this->getAllowInGameConnections() == false) {
2390 shutdownFTPServer();
2391 }
2392
2393 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2394
2395 if(publishToMasterserverThread == NULL) {
2396 if(needToRepublishToMasterserver == true ||
2397 GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
2398
2399 static string mutexOwnerId = string(extractFileFromDirectoryPath(__FILE__).c_str()) + string("_") + intToStr(__LINE__);
2400 publishToMasterserverThread = new SimpleTaskThread(this,0,125);
2401 publishToMasterserverThread->setUniqueID(mutexOwnerId);
2402 publishToMasterserverThread->start();
2403
2404 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2405 }
2406 }
2407
2408 if(this->getAllowInGameConnections() == false) {
2409 shutdownFTPServer();
2410 }
2411
2412 if((needToRepublishToMasterserver == true ||
2413 GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) &&
2414 requiresUPNPTrigger == true) {
2415
2416 this->getServerSocket()->NETdiscoverUPnPDevices();
2417 }
2418
2419 gameLaunched = true;
2420 }
2421 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2422
2423 return bOkToStart;
2424 }
2425
shutdownFTPServer()2426 void ServerInterface::shutdownFTPServer() {
2427 if(ftpServer != NULL) {
2428 ftpServer->shutdownAndWait();
2429 delete ftpServer;
2430 ftpServer = NULL;
2431 }
2432 }
2433
checkListenerSlots()2434 void ServerInterface::checkListenerSlots() {
2435 if(gameLaunched == true &&
2436 this->getAllowInGameConnections() == true) {
2437
2438 if(difftime((long int)time(NULL),lastListenerSlotCheckTime) >= 7) {
2439
2440 lastListenerSlotCheckTime = time(NULL);
2441 bool useInGameBlockingClientSockets = Config::getInstance().getBool("EnableInGameBlockingSockets","true");
2442
2443 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2444 for(int startIndex = 0; startIndex < GameConstants::maxPlayers; ++startIndex) {
2445
2446 int factionIndex = gameSettings.getFactionIndexForStartLocation(startIndex);
2447 if(gameSettings.getFactionControl(factionIndex) != ctClosed &&
2448 gameSettings.getFactionControl(factionIndex) != ctHuman) {
2449
2450 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[startIndex],CODE_AT_LINE_X(startIndex));
2451 ConnectionSlot *connectionSlot= slots[startIndex];
2452 // Open slots for joining in progress game
2453 if(connectionSlot == NULL) {
2454 printf("Opening slot for in game connections, slot: %d, factionindex: %d name: %s\n",startIndex,factionIndex,gameSettings.getFactionTypeName(factionIndex).c_str());
2455
2456 addSlot(startIndex);
2457 connectionSlot = slots[startIndex];
2458 if(useInGameBlockingClientSockets == true) {
2459 connectionSlot->getSocket()->setBlock(true);
2460 }
2461 connectionSlot->setCanAcceptConnections(true);
2462 }
2463 else if(connectionSlot != NULL &&
2464 connectionSlot->getCanAcceptConnections() == false &&
2465 connectionSlot->isConnected() == false) {
2466 printf("Removing slot for in game connections, slot: %d, factionindex: %d name: %s\n",startIndex,factionIndex,gameSettings.getFactionTypeName(factionIndex).c_str());
2467
2468 this->removeSlot(startIndex);
2469 }
2470 }
2471 }
2472 }
2473 }
2474 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,needToRepublishToMasterserver);
2475 }
2476
broadcastGameSetup(GameSettings * gameSettingsBuffer,bool setGameSettingsBuffer)2477 void ServerInterface::broadcastGameSetup(GameSettings *gameSettingsBuffer, bool setGameSettingsBuffer) {
2478 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2479
2480 if(gameSettingsBuffer == NULL) {
2481 throw megaglest_runtime_error("gameSettingsBuffer == NULL");
2482 }
2483 for(unsigned int factionIndex = 0; factionIndex < (unsigned int)gameSettingsBuffer->getFactionCount(); ++factionIndex) {
2484
2485 int slotIndex = gameSettingsBuffer->getStartLocationIndex(factionIndex);
2486 if(gameSettingsBuffer->getFactionControl(factionIndex) == ctNetwork &&
2487 isClientConnected(slotIndex) == false) {
2488
2489 gameSettingsBuffer->setNetworkPlayerName(factionIndex,GameConstants::NETWORK_SLOT_UNCONNECTED_SLOTNAME);
2490 }
2491 }
2492 if(setGameSettingsBuffer == true) {
2493 validateGameSettings(gameSettingsBuffer);
2494 }
2495
2496 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
2497 if(setGameSettingsBuffer == true) {
2498 gameSettings = *gameSettingsBuffer;
2499 gameSettingsUpdateCount++;
2500 }
2501
2502 NetworkMessageLaunch networkMessageLaunch(gameSettingsBuffer, nmtBroadCastSetup);
2503 broadcastMessage(&networkMessageLaunch);
2504
2505 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2506 }
2507
broadcastMessage(NetworkMessage * networkMessage,int excludeSlot,int lockedSlotIndex)2508 void ServerInterface::broadcastMessage(NetworkMessage *networkMessage, int excludeSlot, int lockedSlotIndex) {
2509 try {
2510 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2511
2512 MutexSafeWrapper safeMutexSlotBroadCastAccessor(inBroadcastMessageThreadAccessor,CODE_AT_LINE);
2513 if(inBroadcastMessage == true &&
2514 dynamic_cast<NetworkMessageText *>(networkMessage) != NULL) {
2515
2516 safeMutexSlotBroadCastAccessor.ReleaseLock();
2517 NetworkMessageText *txtMsg = dynamic_cast<NetworkMessageText *>(networkMessage);
2518 if(txtMsg != NULL) {
2519 NetworkMessageText *msgCopy = txtMsg->getCopy();
2520 queueBroadcastMessage(msgCopy, excludeSlot);
2521 }
2522 return;
2523 }
2524 else {
2525 inBroadcastMessage = true;
2526 safeMutexSlotBroadCastAccessor.ReleaseLock(true);
2527 }
2528
2529 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2530 MutexSafeWrapper safeMutexSlot(NULL,CODE_AT_LINE_X(slotIndex));
2531 if(slotIndex != lockedSlotIndex) {
2532 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] i = %d, lockedSlotIndex = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,slotIndex,lockedSlotIndex);
2533 safeMutexSlot.setMutex(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2534 }
2535
2536 ConnectionSlot* connectionSlot= slots[slotIndex];
2537
2538 if(slotIndex != excludeSlot && connectionSlot != NULL) {
2539 if(connectionSlot->isConnected()) {
2540 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] before sendMessage\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2541
2542 connectionSlot->sendMessage(networkMessage);
2543
2544 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] after sendMessage\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2545 }
2546 if(gameHasBeenInitiated == true && connectionSlot->isConnected() == false) {
2547 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 before removeSlot for slot# %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,slotIndex);
2548
2549 if(this->getAllowInGameConnections() == false) {
2550 removeSlot(slotIndex,slotIndex);
2551 }
2552 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #1 after removeSlot for slot# %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,slotIndex);
2553 }
2554 }
2555 else if(slotIndex == excludeSlot && gameHasBeenInitiated == true &&
2556 connectionSlot != NULL && connectionSlot->isConnected() == false) {
2557
2558 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #2 before removeSlot for slot# %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,slotIndex);
2559
2560 if(this->getAllowInGameConnections() == false) {
2561 removeSlot(slotIndex,slotIndex);
2562 }
2563 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] #2 after removeSlot for slot# %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,slotIndex);
2564 }
2565 }
2566
2567 safeMutexSlotBroadCastAccessor.Lock();
2568
2569 inBroadcastMessage = false;
2570
2571 safeMutexSlotBroadCastAccessor.ReleaseLock();
2572 }
2573 catch(const exception &ex) {
2574 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2575 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ERROR [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2576
2577 MutexSafeWrapper safeMutexSlotBroadCastAccessor(inBroadcastMessageThreadAccessor,CODE_AT_LINE);
2578 inBroadcastMessage = false;
2579 safeMutexSlotBroadCastAccessor.ReleaseLock();
2580
2581 string sMsg = ex.what();
2582 sendTextMessage(sMsg,-1, true, "", lockedSlotIndex);
2583 }
2584 }
2585
broadcastMessageToConnectedClients(NetworkMessage * networkMessage,int excludeSlot)2586 void ServerInterface::broadcastMessageToConnectedClients(NetworkMessage *networkMessage, int excludeSlot) {
2587 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2588 try {
2589 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2590 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2591 ConnectionSlot *connectionSlot= slots[slotIndex];
2592
2593 if(slotIndex != excludeSlot && connectionSlot != NULL) {
2594 if(connectionSlot->isConnected()) {
2595 connectionSlot->sendMessage(networkMessage);
2596 }
2597 }
2598 }
2599 }
2600 catch(const exception &ex) {
2601 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2602 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] ERROR [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
2603 DisplayErrorMessage(ex.what());
2604 }
2605 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2606 }
2607
updateListen()2608 void ServerInterface::updateListen() {
2609 if(gameHasBeenInitiated == true &&
2610 this->getAllowInGameConnections() == false) {
2611 return;
2612 }
2613
2614 int openSlotCount = 0;
2615 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2616
2617 bool isSlotOpen = (slots[slotIndex] != NULL && slots[slotIndex]->isConnected() == false);
2618 if(isSlotOpen == true) {
2619 ++openSlotCount;
2620 }
2621 }
2622
2623 serverSocket.listen(openSlotCount);
2624 }
2625
getOpenSlotCount()2626 int ServerInterface::getOpenSlotCount() {
2627 int openSlotCount = 0;
2628 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2629
2630 bool isSlotOpen = (slots[slotIndex] != NULL && slots[slotIndex]->isConnected() == false);
2631 if(isSlotOpen == true) {
2632 ++openSlotCount;
2633 }
2634 }
2635 return openSlotCount;
2636 }
2637
getGameSettingsUpdateCount()2638 int ServerInterface::getGameSettingsUpdateCount() {
2639 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START gameSettingsUpdateCount = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,gameSettingsUpdateCount);
2640
2641 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
2642 int result = gameSettingsUpdateCount;
2643 safeMutex.ReleaseLock();
2644 return result;
2645 }
2646
validateGameSettings(GameSettings * serverGameSettings)2647 void ServerInterface::validateGameSettings(GameSettings *serverGameSettings) {
2648 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
2649
2650 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
2651 string mapFile = serverGameSettings->getMap();
2652 //printf("Trying to set map to [%s]. Current map is [%s]\n",serverGameSettings->getMap().c_str(),gameSettings.getMap().c_str());
2653 if(gameSettings.getMapFilter()!=serverGameSettings->getMapFilter()){
2654 if( playerSortedMaps[serverGameSettings->getMapFilter()].size()==0){
2655 serverGameSettings->setMapFilter(0);
2656 }
2657 }
2658 int playerIndex=serverGameSettings->getMapFilter();
2659 if(find(playerSortedMaps[playerIndex].begin(),playerSortedMaps[playerIndex].end(),mapFile) == playerSortedMaps[playerIndex].end()) {
2660 // switch to closest map
2661 string foundMap = "";
2662 for (int i = 0 ;i < (int)playerSortedMaps[playerIndex].size(); ++i) {
2663 foundMap=playerSortedMaps[playerIndex][i];
2664 if(toLower(foundMap)>toLower(serverGameSettings->getMap())){
2665 break;
2666 }
2667 }
2668 printf("map %s not found on this server. Switching to map %s\n",serverGameSettings->getMap().c_str(),foundMap.c_str());
2669 serverGameSettings->setMap(foundMap);
2670 }
2671 Checksum checksum;
2672 string file = Config::getMapPath(serverGameSettings->getMap(),"",false);
2673 checksum.addFile(file);
2674 serverGameSettings->setMapCRC(checksum.getSum());
2675
2676 string tilesetFile = serverGameSettings->getTileset();
2677 if(find(tilesetFiles.begin(),tilesetFiles.end(),tilesetFile) == tilesetFiles.end()) {
2678 printf("Reverting tileset from [%s] to [%s]\n",serverGameSettings->getTileset().c_str(),gameSettings.getTileset().c_str());
2679
2680 serverGameSettings->setTileset(gameSettings.getTileset());
2681 serverGameSettings->setTilesetCRC(gameSettings.getTilesetCRC());
2682 }
2683
2684 string techtreeFile = serverGameSettings->getTech();
2685 if(find(techTreeFiles.begin(),techTreeFiles.end(),techtreeFile) == techTreeFiles.end()) {
2686 printf("Reverting tech from [%s] to [%s]\n",serverGameSettings->getTech().c_str(),gameSettings.getTech().c_str());
2687
2688 serverGameSettings->setTech(gameSettings.getTech());
2689 serverGameSettings->setTechCRC(gameSettings.getTechCRC());
2690 }
2691 }
2692
setGameSettings(GameSettings * serverGameSettings,bool waitForClientAck)2693 void ServerInterface::setGameSettings(GameSettings *serverGameSettings, bool waitForClientAck) {
2694 MutexSafeWrapper safeMutex(serverSynchAccessor,CODE_AT_LINE);
2695 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START gameSettingsUpdateCount = %d, waitForClientAck = %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,gameSettingsUpdateCount,waitForClientAck);
2696
2697 if(serverGameSettings->getScenario() == "") {
2698 string mapFile = serverGameSettings->getMap();
2699 if(find(mapFiles.begin(),mapFiles.end(),mapFile) == mapFiles.end()) {
2700 printf("Reverting map from [%s] to [%s]\n",serverGameSettings->getMap().c_str(),gameSettings.getMap().c_str());
2701
2702 serverGameSettings->setMapFilter(gameSettings.getMapFilter());
2703 serverGameSettings->setMap(gameSettings.getMap());
2704 serverGameSettings->setMapCRC(gameSettings.getMapCRC());
2705 }
2706
2707 string tilesetFile = serverGameSettings->getTileset();
2708 if(find(tilesetFiles.begin(),tilesetFiles.end(),tilesetFile) == tilesetFiles.end()) {
2709 printf("Reverting tileset from [%s] to [%s]\n",serverGameSettings->getTileset().c_str(),gameSettings.getTileset().c_str());
2710
2711 serverGameSettings->setTileset(gameSettings.getTileset());
2712 serverGameSettings->setTilesetCRC(gameSettings.getTilesetCRC());
2713 }
2714
2715 string techtreeFile = serverGameSettings->getTech();
2716 if(find(techTreeFiles.begin(),techTreeFiles.end(),techtreeFile) == techTreeFiles.end()) {
2717 printf("Reverting tech from [%s] to [%s]\n",serverGameSettings->getTech().c_str(),gameSettings.getTech().c_str());
2718
2719 serverGameSettings->setTech(gameSettings.getTech());
2720 serverGameSettings->setTechCRC(gameSettings.getTechCRC());
2721 }
2722 }
2723
2724 gameSettings = *serverGameSettings;
2725
2726 if(getAllowGameDataSynchCheck() == true) {
2727 if(waitForClientAck == true && gameSettingsUpdateCount > 0) {
2728 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Waiting for client acks #1\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
2729
2730 time_t tStart = time(NULL);
2731 bool gotAckFromAllClients = false;
2732 while(gotAckFromAllClients == false && difftime((long int)time(NULL),tStart) <= 5) {
2733 gotAckFromAllClients = true;
2734 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2735 //printf("===> START slot %d - About to setGameSettings #1\n",i);
2736
2737 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2738 ConnectionSlot *connectionSlot = slots[slotIndex];
2739 if(connectionSlot != NULL && connectionSlot->isConnected()) {
2740 if(connectionSlot->getReceivedNetworkGameStatus() == false) {
2741 gotAckFromAllClients = false;
2742 }
2743
2744 connectionSlot->update(true,slotIndex);
2745 }
2746 //printf("===> END slot %d - About to setGameSettings #1\n",i);
2747 }
2748 }
2749 }
2750
2751 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2752 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2753 ConnectionSlot *connectionSlot = slots[slotIndex];
2754 if(connectionSlot != NULL && connectionSlot->isConnected()) {
2755 connectionSlot->setReceivedNetworkGameStatus(false);
2756 }
2757 }
2758
2759 NetworkMessageSynchNetworkGameData networkMessageSynchNetworkGameData(getGameSettings());
2760 broadcastMessageToConnectedClients(&networkMessageSynchNetworkGameData);
2761
2762 if(waitForClientAck == true) {
2763 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] Waiting for client acks #2\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
2764
2765 time_t tStart = time(NULL);
2766 bool gotAckFromAllClients = false;
2767 while(gotAckFromAllClients == false && difftime((long int)time(NULL),tStart) <= 5) {
2768 gotAckFromAllClients = true;
2769 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2770 //printf("===> START slot %d - About to setGameSettings 2\n",slotIndex);
2771
2772 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2773 ConnectionSlot *connectionSlot = slots[slotIndex];
2774 if(connectionSlot != NULL && connectionSlot->isConnected()) {
2775 if(connectionSlot->getReceivedNetworkGameStatus() == false) {
2776 gotAckFromAllClients = false;
2777 }
2778
2779 connectionSlot->update(true,slotIndex);
2780 }
2781 //printf("===> END slot %d - About to setGameSettings 2\n",slotIndex);
2782 }
2783 }
2784 }
2785
2786 }
2787 gameSettingsUpdateCount++;
2788 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] END\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
2789 }
2790
close()2791 void ServerInterface::close() {
2792 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s] START\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__);
2793 }
2794
getHumanPlayerName(int index)2795 string ServerInterface::getHumanPlayerName(int index) {
2796 string result = Config::getInstance().getString("NetPlayerName", Socket::getHostName().c_str());
2797 if(index >= 0 || gameSettings.getThisFactionIndex() >= 0) {
2798 if(index < 0) {
2799 index = gameSettings.getThisFactionIndex();
2800 }
2801 if(gameSettings.getNetworkPlayerName(index) != "") {
2802 result = gameSettings.getNetworkPlayerName(index);
2803 }
2804 }
2805
2806 return result;
2807 }
2808
getHumanPlayerIndex() const2809 int ServerInterface::getHumanPlayerIndex() const {
2810 return gameSettings.getStartLocationIndex(gameSettings.getThisFactionIndex());
2811 }
2812
publishToMasterserver()2813 std::map<string,string> ServerInterface::publishToMasterserver() {
2814 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2815 int slotCountUsed = 1;
2816 int slotCountHumans = 1;
2817 int slotCountConnectedPlayers = 1;
2818
2819 if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
2820 slotCountUsed = 0;
2821 slotCountHumans = 0;
2822 slotCountConnectedPlayers = 0;
2823 }
2824
2825 Config & config = Config::getInstance();
2826 std::map < string, string > publishToServerInfo;
2827 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2828 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
2829 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
2830 if(slots[slotIndex] != NULL) {
2831 slotCountUsed++;
2832 slotCountHumans++;
2833 ConnectionSlot* connectionSlot= slots[slotIndex];
2834 if((connectionSlot!=NULL) && (connectionSlot->isConnected())) {
2835 slotCountConnectedPlayers++;
2836 }
2837 }
2838 }
2839 publishToServerInfo["uuid"] = Config::getInstance().getString("PlayerId","");
2840 publishToServerInfo["glestVersion"] = glestVersionString;
2841 publishToServerInfo["platform"] = getPlatformNameString() + "-" + getGITRevisionString();
2842 publishToServerInfo["binaryCompileDate"] = getCompileDateTime();
2843 publishToServerInfo["serverTitle"] = this->getGameSettings()->getGameName();
2844 publishToServerInfo["tech"] = this->getGameSettings()->getTech();
2845 publishToServerInfo["map"] = this->getGameSettings()->getMap();
2846 publishToServerInfo["tileset"] = this->getGameSettings()->getTileset();
2847
2848 bool updateSlots = true;
2849 MutexSafeWrapper safeMutex2(gameStatsThreadAccessor,CODE_AT_LINE);
2850 if(gameStats != NULL) {
2851 for(int factionIndex = 0; factionIndex < gameStats->getFactionCount(); ++factionIndex) {
2852 if(gameStats->getVictory(factionIndex) == true) {
2853 updateSlots = false;
2854 break;
2855 }
2856 }
2857 }
2858 safeMutex2.ReleaseLock();
2859
2860 if(updateSlots == true) {
2861 publishToServerInfo["activeSlots"] = intToStr(slotCountUsed);
2862 publishToServerInfo["networkSlots"] = intToStr(slotCountHumans);
2863 publishToServerInfo["connectedClients"] = intToStr(slotCountConnectedPlayers);
2864 }
2865
2866 string serverPort = config.getString("PortServer", intToStr(GameConstants::serverPort).c_str());
2867 string externalPort = config.getString("PortExternal", serverPort.c_str());
2868 publishToServerInfo["externalconnectport"] = externalPort;
2869 publishToServerInfo["privacyPlease"] = intToStr(config.getBool("PrivacyPlease","false"));
2870 publishToServerInfo["gameStatus"] = intToStr(game_status_in_progress);
2871
2872 if(publishToMasterserverThread == NULL) {
2873 publishToServerInfo["gameCmd"] = "gameOver";
2874 publishToServerInfo["gameStatus"] = intToStr(game_status_finished);
2875 }
2876
2877 //printf("Host game id = %s\n",this->getGameSettings()->getGameUUID().c_str());
2878 publishToServerInfo["gameUUID"] = this->getGameSettings()->getGameUUID();
2879
2880 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2881 return publishToServerInfo;
2882 }
2883
publishToMasterserverStats()2884 std::map<string,string> ServerInterface::publishToMasterserverStats() {
2885 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2886
2887 MutexSafeWrapper safeMutex(gameStatsThreadAccessor,CODE_AT_LINE);
2888 std::map < string, string > publishToServerInfo;
2889 if(gameStats != NULL) {
2890 publishToServerInfo["gameUUID"] = this->getGameSettings()->getGameUUID();
2891 publishToServerInfo["tech"] = this->getGameSettings()->getTech();
2892 publishToServerInfo["factionCount"] = intToStr(gameStats->getFactionCount());
2893 publishToServerInfo["framesPlayed"] = intToStr(gameStats->getFramesPlayed());
2894 publishToServerInfo["framesToCalculatePlaytime"] = intToStr(gameStats->getFramesToCalculatePlaytime());
2895 publishToServerInfo["maxConcurrentUnitCount"] = intToStr(gameStats->getMaxConcurrentUnitCount());
2896 publishToServerInfo["totalEndGameConcurrentUnitCount"] = intToStr(gameStats->getTotalEndGameConcurrentUnitCount());
2897 publishToServerInfo["isHeadlessServer"] = intToStr(gameStats->getIsMasterserverMode());
2898
2899 for(int factionIndex = 0; factionIndex < gameStats->getFactionCount(); ++factionIndex) {
2900 publishToServerInfo["factionIndex_" + intToStr(factionIndex)] = intToStr(factionIndex);
2901 publishToServerInfo["controlType_" + intToStr(factionIndex)] = intToStr(gameStats->getControl(factionIndex));
2902 publishToServerInfo["resourceMultiplier_" + intToStr(factionIndex)] = floatToStr(gameStats->getResourceMultiplier(factionIndex));
2903 publishToServerInfo["factionTypeName_" + intToStr(factionIndex)] = gameStats->getFactionTypeName(factionIndex);
2904 publishToServerInfo["personalityType_" + intToStr(factionIndex)] = intToStr(gameStats->getPersonalityType(factionIndex));
2905 publishToServerInfo["teamIndex_" + intToStr(factionIndex)] = intToStr(gameStats->getTeam(factionIndex));
2906 publishToServerInfo["wonGame_" + intToStr(factionIndex)] = intToStr(gameStats->getVictory(factionIndex));
2907 publishToServerInfo["killCount_" + intToStr(factionIndex)] = intToStr(gameStats->getKills(factionIndex));
2908 publishToServerInfo["enemyKillCount_" + intToStr(factionIndex)] = intToStr(gameStats->getEnemyKills(factionIndex));
2909 publishToServerInfo["deathCount_" + intToStr(factionIndex)] = intToStr(gameStats->getDeaths(factionIndex));
2910 publishToServerInfo["unitsProducedCount_" + intToStr(factionIndex)] = intToStr(gameStats->getUnitsProduced(factionIndex));
2911 publishToServerInfo["resourceHarvestedCount_" + intToStr(factionIndex)] = intToStr(gameStats->getResourcesHarvested(factionIndex));
2912 publishToServerInfo["playerName_" + intToStr(factionIndex)] = gameStats->getPlayerName(factionIndex);
2913 publishToServerInfo["quitBeforeGameEnd_" + intToStr(factionIndex)] = intToStr(gameStats->getPlayerLeftBeforeEnd(factionIndex));
2914 publishToServerInfo["quitTime_" + intToStr(factionIndex)] = intToStr(gameStats->getTimePlayerLeft(factionIndex));
2915 publishToServerInfo["playerUUID_" + intToStr(factionIndex)] = this->getGameSettings()->getNetworkPlayerUUID(factionIndex);
2916 publishToServerInfo["platform_" + intToStr(factionIndex)] = this->getGameSettings()->getNetworkPlayerPlatform(factionIndex);
2917 }
2918 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2919 }
2920 return publishToServerInfo;
2921 }
2922
setGameStats(Stats * stats)2923 void ServerInterface::setGameStats(Stats *stats) {
2924 if(stats == NULL) {
2925 return;
2926 }
2927 MutexSafeWrapper safeMutex(gameStatsThreadAccessor,CODE_AT_LINE);
2928 if(gameStats == NULL) {
2929 gameStats = new Stats();
2930 }
2931 *gameStats = *stats;
2932 }
2933
simpleTask(BaseThread * callingThread,void * userdata)2934 void ServerInterface::simpleTask(BaseThread *callingThread,void *userdata) {
2935 MutexSafeWrapper safeMutex(masterServerThreadAccessor,CODE_AT_LINE);
2936
2937 if(difftime((long int)time(NULL),lastMasterserverHeartbeatTime) >= MASTERSERVER_HEARTBEAT_GAME_STATUS_SECONDS) {
2938 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
2939
2940 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Checking to see masterserver needs an update of the game status [%d] callingThread [%p] publishToMasterserverThread [%p]\n",needToRepublishToMasterserver,callingThread,publishToMasterserverThread);
2941
2942 lastMasterserverHeartbeatTime = time(NULL);
2943 if(needToRepublishToMasterserver == true) {
2944 try {
2945 if(Config::getInstance().getString("Masterserver","") != "") {
2946 string request = Config::getInstance().getString("Masterserver");
2947 if(request != "") {
2948 endPathWithSlash(request,false);
2949 }
2950 request += "addServerInfo.php?";
2951
2952 std::map<string,string> newPublishToServerInfo = publishToMasterserver();
2953
2954 CURL *handle = SystemFlags::initHTTP();
2955 for(std::map<string,string>::const_iterator iterMap = newPublishToServerInfo.begin();
2956 iterMap != newPublishToServerInfo.end(); ++iterMap) {
2957
2958 request += iterMap->first;
2959 request += "=";
2960 request += SystemFlags::escapeURL(iterMap->second,handle);
2961 request += "&";
2962 }
2963
2964 //printf("The Host request is:\n%s\n",request.c_str());
2965 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("The Host request is:\n%s\n",request.c_str());
2966
2967 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d] the request is:\n%s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,request.c_str());
2968
2969 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Calling masterserver [%s]...\n",request.c_str());
2970
2971 std::string serverInfo = SystemFlags::getHTTP(request,handle);
2972 //printf("Result:\n%s\n",serverInfo .c_str());
2973
2974 string requestStats = Config::getInstance().getString("Masterserver");
2975 if(requestStats != "") {
2976 endPathWithSlash(requestStats,false);
2977 }
2978 requestStats += "addGameStats.php?";
2979
2980 std::map<string,string> newPublishToServerInfoStats = publishToMasterserverStats();
2981 if(newPublishToServerInfoStats.empty() == false) {
2982 for(std::map<string,string>::const_iterator iterMap = newPublishToServerInfoStats.begin();
2983 iterMap != newPublishToServerInfoStats.end(); ++iterMap) {
2984
2985 requestStats += iterMap->first;
2986 requestStats += "=";
2987 requestStats += SystemFlags::escapeURL(iterMap->second,handle);
2988 requestStats += "&";
2989 }
2990
2991 //printf("The Host stats request is:\n%s\n",requestStats.c_str());
2992 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("The Host request is:\n%s\n",requestStats.c_str());
2993 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d] the request is:\n%s\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,requestStats.c_str());
2994 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Calling masterserver [%s]...\n",requestStats.c_str());
2995
2996 std::string serverInfoStats = SystemFlags::getHTTP(requestStats,handle);
2997 //printf("Result:\n%s\n",serverInfoStats .c_str());
2998
2999 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d] the result is:\n'%s'\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,serverInfoStats.c_str());
3000 }
3001
3002 SystemFlags::cleanupHTTP(&handle);
3003
3004 if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Done Calling masterserver\n");
3005
3006 //printf("the result is:\n'%s'\n",serverInfo.c_str());
3007 if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d] the result is:\n'%s'\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,serverInfo.c_str());
3008 }
3009 else {
3010 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line %d] error, no masterserver defined!\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__);
3011 }
3012 }
3013 catch(const exception &ex) {
3014 SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line %d] error during game status update: [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ex.what());
3015 }
3016 }
3017 if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
3018 DumpStatsToLog(false);
3019 }
3020 }
3021 if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) {
3022 //printf("Attempt Accept\n");
3023 if(serverSocketAdmin != NULL) {
3024 Socket *cli = serverSocketAdmin->accept(false);
3025 if(cli != NULL) {
3026 printf("Got status request connection, dumping info...\n");
3027
3028 string data = DumpStatsToLog(true);
3029 cli->send(data.c_str(),(int)data.length());
3030 cli->disconnectSocket();
3031 }
3032 }
3033 }
3034 }
3035
DumpStatsToLog(bool dumpToStringOnly) const3036 std::string ServerInterface::DumpStatsToLog(bool dumpToStringOnly) const {
3037 string headlessLogFile = Config::getInstance().getString("HeadlessLogFile","headless.log");
3038 if(getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) != "") {
3039 headlessLogFile = getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) + headlessLogFile ;
3040 }
3041 else {
3042 string userData = Config::getInstance().getString("UserData_Root","");
3043 if(userData != "") {
3044 endPathWithSlash(userData);
3045 }
3046 headlessLogFile = userData + headlessLogFile ;
3047 }
3048
3049 ostringstream out;
3050 out << "=========================================" << std::endl;
3051 out << "Headless Server Current Game information:" << std::endl;
3052 out << "=========================================" << std::endl;
3053
3054 int connectedSlotCount = 0;
3055 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3056
3057 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3058 ConnectionSlot *slot = slots[slotIndex];
3059 if(slot != NULL) {
3060
3061 connectedSlotCount++;
3062 out << "Network connection for index: " << slotIndex << std::endl;
3063 out << "------------------------------" << std::endl;
3064 out << "Connected: " << boolToStr(slot->isConnected()) << std::endl;
3065 out << "Handshake received: " << boolToStr(slot->getConnectHasHandshaked()) << std::endl;
3066 if(slot->isConnected() == true) {
3067
3068 time_t connectTime = slot->getConnectedTime();
3069 //struct tm *loctime = localtime (&connectTime);
3070 struct tm loctime = threadsafe_localtime(connectTime);
3071 char szBuf[8096] = "";
3072 strftime(szBuf,100,"%Y-%m-%d %H:%M:%S",&loctime);
3073
3074 const int HOURS_IN_DAY = 24;
3075 const int MINUTES_IN_HOUR = 60;
3076 const int SECONDS_IN_MINUTE = 60;
3077 int InSeconds = difftime((long int)time(NULL),slot->getConnectedTime());
3078 // compute seconds
3079 int seconds = InSeconds % SECONDS_IN_MINUTE ;
3080 // throw away seconds used in previous statement and convert to minutes
3081 int InMinutes = InSeconds / SECONDS_IN_MINUTE ;
3082 // compute minutes
3083 int minutes = InMinutes % MINUTES_IN_HOUR ;
3084
3085 // throw away minutes used in previous statement and convert to hours
3086 int InHours = InMinutes / MINUTES_IN_HOUR ;
3087 // compute hours
3088 int hours = InHours % HOURS_IN_DAY ;
3089
3090 out << "Connected at: " << szBuf << std::endl;
3091 out << "Connection duration: " << hours << " hours " << minutes << " minutes " << seconds << " seconds." << std::endl;
3092 out << "Player Index: " << slot->getPlayerIndex() << std::endl;
3093 out << "IP Address: " << slot->getIpAddress() << std::endl;
3094 out << "Player name: " << slot->getName() << std::endl;
3095 out << "Player uuid: " << slot->getUUID() << std::endl;
3096 out << "Language: " << slot->getNetworkPlayerLanguage() << std::endl;
3097 out << "Game Version: " << slot->getVersionString() << std::endl;
3098 out << "Session id: " << slot->getSessionKey() << std::endl;
3099 out << "Socket id: " << slot->getSocketId() << std::endl;
3100 }
3101 }
3102 }
3103 out << "Total Slot Count: " << connectedSlotCount << std::endl;
3104 out << "=========================================" << std::endl;
3105
3106 std::string result = out.str();
3107
3108 if(dumpToStringOnly == false) {
3109
3110 #if defined(WIN32) && !defined(__MINGW32__)
3111 FILE *fp = _wfopen(utf8_decode(headlessLogFile ).c_str(), L"w");
3112 std::ofstream logFile(fp);
3113 #else
3114 std::ofstream logFile;
3115 logFile.open(headlessLogFile .c_str(), ios_base::out | ios_base::trunc);
3116 #endif
3117 logFile << result;
3118 logFile.close();
3119 #if defined(WIN32) && !defined(__MINGW32__)
3120 if(fp) {
3121 fclose(fp);
3122 }
3123 #endif
3124 }
3125
3126 return result;
3127 }
3128
notifyBadClientConnectAttempt(string ipAddress)3129 void ServerInterface::notifyBadClientConnectAttempt(string ipAddress) {
3130 //printf("In [%s::%s Line: %d] ipAddress [%s]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,ipAddress.c_str());
3131
3132 if(badClientConnectIPList.find(ipAddress) == badClientConnectIPList.end()) {
3133 badClientConnectIPList[ipAddress] = make_pair(0,(long int)time(NULL));
3134 }
3135
3136 pair<uint64,time_t> &lastBadConnectionAttempt = badClientConnectIPList[ipAddress];
3137
3138 const uint64 BLOCK_BAD_CLIENT_CONNECT_MAX_SECONDS = Config::getInstance().getInt("BlockBadClientConnectMaxSeconds", "60");
3139 const uint64 BLOCK_BAD_CLIENT_CONNECT_MAX_ATTEMPTS = Config::getInstance().getInt("BlockBadClientConnectMaxAttempts", "6");
3140 bool addToBlockedClientsList = false;
3141
3142 if(difftime((long int)time(NULL),lastBadConnectionAttempt.second) <= BLOCK_BAD_CLIENT_CONNECT_MAX_SECONDS) {
3143
3144 if(lastBadConnectionAttempt.first+1 > BLOCK_BAD_CLIENT_CONNECT_MAX_ATTEMPTS) {
3145 addToBlockedClientsList = true;
3146 }
3147 }
3148 else {
3149 // Reset after x seconds
3150 lastBadConnectionAttempt.first = 0;
3151 }
3152
3153 if(this->getAllowInGameConnections() == true) {
3154 printf("notifyBadClientConnectAttempt() #1: %s!\n",ipAddress.c_str());
3155 }
3156
3157 if(addToBlockedClientsList == true) {
3158 serverSocket.addIPAddressToBlockedList(ipAddress);
3159 }
3160
3161 lastBadConnectionAttempt.first++;
3162 lastBadConnectionAttempt.second = time(NULL);
3163 }
3164
getStartInGameConnectionLaunch()3165 bool ServerInterface::getStartInGameConnectionLaunch() {
3166 bool result = false;
3167 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3168 MutexSafeWrapper safeMutex(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3169 if(slots[slotIndex] != NULL) {
3170
3171 ConnectionSlot *slot = slots[slotIndex];
3172 if(slot->getStartInGameConnectionLaunch() == true) {
3173 result = true;
3174 break;
3175 }
3176 }
3177 }
3178 return result;
3179 }
3180
getPauseForInGameConnection()3181 bool ServerInterface::getPauseForInGameConnection() {
3182 bool result = false;
3183 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3184 MutexSafeWrapper safeMutex(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3185 if(slots[slotIndex] != NULL) {
3186
3187 ConnectionSlot *slot = slots[slotIndex];
3188 if(slot->getPauseForInGameConnection() == true) {
3189 result = true;
3190 break;
3191 }
3192 }
3193 }
3194 return result;
3195 }
3196
getUnPauseForInGameConnection()3197 bool ServerInterface::getUnPauseForInGameConnection() {
3198 bool result = false;
3199 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3200 MutexSafeWrapper safeMutex(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3201 if(slots[slotIndex] != NULL) {
3202
3203 ConnectionSlot *slot = slots[slotIndex];
3204 if(slot->isConnected() == true) {
3205 if(slot->isReady() == true) {
3206 result = true;
3207 if(slot->getUnPauseForInGameConnection() == false) {
3208 result = false;
3209 break;
3210 }
3211 }
3212 else {
3213 result = false;
3214 break;
3215 }
3216 }
3217 }
3218 }
3219 if(result == true) {
3220 resumeGameStartTime = time(NULL);
3221 }
3222 return result;
3223 }
3224
findSlotForUUID(string uuid,bool unConnectedOnly)3225 ConnectionSlot * ServerInterface::findSlotForUUID(string uuid, bool unConnectedOnly) {
3226 ConnectionSlot *result = NULL;
3227 if(uuid != "") {
3228 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3229
3230 MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3231 ConnectionSlot *connectionSlot= slots[slotIndex];
3232
3233 if(connectionSlot != NULL) {
3234 if(connectionSlot->getUUID() == uuid) {
3235 if(unConnectedOnly == false ||
3236 (unConnectedOnly == true && connectionSlot->isConnected() == false)) {
3237
3238 if(result == NULL ||
3239 (result->getConnectedTime() > connectionSlot->getConnectedTime()))
3240
3241 result = connectionSlot;
3242 }
3243 }
3244 }
3245 }
3246 }
3247 return result;
3248 }
3249
saveGame(XmlNode * rootNode)3250 void ServerInterface::saveGame(XmlNode *rootNode) {
3251 std::map<string,string> mapTagReplacements;
3252 XmlNode *serverInterfaceNode = rootNode->addChild("ServerInterface");
3253
3254 for(int slotIndex = 0; exitServer == false && slotIndex < GameConstants::maxPlayers; ++slotIndex) {
3255 MutexSafeWrapper safeMutex(slotAccessorMutexes[slotIndex],CODE_AT_LINE_X(slotIndex));
3256 if(slots[slotIndex] != NULL) {
3257
3258 XmlNode *slotNode = serverInterfaceNode->addChild("Slot");
3259
3260 ConnectionSlot *slot = slots[slotIndex];
3261 if(slot != NULL) {
3262 slotNode->addAttribute("isconnected",intToStr(slot->isConnected()), mapTagReplacements);
3263 slotNode->addAttribute("sessionkey",intToStr(slot->getSessionKey()), mapTagReplacements);
3264 slotNode->addAttribute("ipaddress",slot->getSocket(false)->getIpAddress(), mapTagReplacements);
3265 slotNode->addAttribute("name",slot->getName(), mapTagReplacements);
3266 slotNode->addAttribute("uuid",slot->getUUID(), mapTagReplacements);
3267 }
3268 else {
3269 slotNode->addAttribute("isconnected",intToStr(false), mapTagReplacements);
3270 }
3271 }
3272 }
3273 }
3274
3275 }}//end namespace
3276