lobbyapp.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /*-------------------------------------------------------------------------
  2. LobbyApp.cpp
  3. Implementation of the lobby
  4. Owner:
  5. Copyright 1986-2000 Microsoft Corporation, All Rights Reserved
  6. *-----------------------------------------------------------------------*/
  7. #include "pch.h"
  8. #include <conio.h>
  9. #include <zreg.h>
  10. ALLOC_MSG_LIST;
  11. CLobbyApp * g_pLobbyApp = NULL;
  12. #ifdef USEAUTH
  13. void CLobbyApp::OnSQLErrorRecord(SSERRORINFO * perror, OLECHAR * postrError)
  14. {
  15. // don't make the event an error event, because this may or may not be fatal.
  16. // But we certainly want to see them all in any case.
  17. m_plas->LogEvent(EVENTLOG_WARNING_TYPE, LE_DatabaseError, perror->pwszMessage,
  18. perror->pwszProcedure, perror->lNative, perror->wLineNumber, postrError);
  19. }
  20. #endif
  21. /*-------------------------------------------------------------------------
  22. * CLobbyApp.ProcessMsgPump
  23. *-------------------------------------------------------------------------
  24. Purpose:
  25. Process all thread messages, which so far are only sql query completion notifications
  26. Returns:
  27. Whether we received a WM_QUIT
  28. */
  29. bool CLobbyApp::ProcessMsgPump()
  30. {
  31. static CTimer timerMsgPump("in message pump", 0.1f);
  32. timerMsgPump.Start();
  33. bool fQuit = false;
  34. // Process the message queue, if any messages were received
  35. MSG msg;
  36. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  37. {
  38. // dispatch Windows Messages to allow for the admin tool's COM to work
  39. TranslateMessage(&msg);
  40. switch (msg.message)
  41. {
  42. #ifdef USEAUTH
  43. case wm_sql_querydone:
  44. {
  45. CSQLQuery * pQuery = (CSQLQuery *) msg.lParam;
  46. pQuery->DataReady();
  47. break;
  48. }
  49. #endif
  50. case WM_QUIT:
  51. fQuit = true;
  52. break;
  53. default:
  54. DispatchMessage(&msg);
  55. }
  56. }
  57. timerMsgPump.Stop();
  58. return fQuit;
  59. }
  60. void CLobbyApp::SetConstantGameInfo()
  61. {
  62. ZGameServerInfoMsg* gameInfo = GetGameServerInfoMsg();
  63. gameInfo->protocolSignature = zGameInfoSignature;
  64. gameInfo->protocolVersion = zGameInfoCurrentProtocolVersion;
  65. gameInfo->numEntries = 1;
  66. ZGameServerInfoMsgEndian(gameInfo);
  67. lstrcpy(gameInfo->info[0].gameInternalName, m_fFreeLobby ? "sOBLI_xx_x02" : "sOBLI_xx_x01");
  68. lstrcpy(gameInfo->info[0].gameFriendlyName, "Allegiance");
  69. lstrcpy(gameInfo->info[0].gameRoomDescription, "Am I supposed to put something here?");
  70. lstrcpy(gameInfo->info[0].setupToken, ""); //reserverd for internal Zone use
  71. }
  72. void CLobbyApp::SetVariableGameInfo()
  73. {
  74. ZGameServerInfoMsg* gameInfo = GetGameServerInfoMsg();
  75. gameInfo->info[0].order = 1; //used for Web page order of view
  76. gameInfo->info[0].blobsize = 0; //reserved for internal Zone use
  77. gameInfo->info[0].maxPopulation = 1000;
  78. gameInfo->info[0].gameAddr = 0; //inet_addr ("127.0.0/1");
  79. gameInfo->info[0].gamePort = 0; //2803; //your game port
  80. gameInfo->info[0].serviceType = GAMEINFO_SERVICE_TYPE_GAME;
  81. gameInfo->info[0].gameState = zGameStateActive;
  82. gameInfo->info[0].gameVersion = 1;
  83. SYSTEMTIME systime;
  84. GetSystemTime (&systime);
  85. SystemTimeToFileTime(&systime, &(gameInfo->info[0].timeGameStart));
  86. gameInfo->info[0].numPlayers = m_pCounters->cPlayersMissions + m_pCounters->cPlayersLobby;
  87. gameInfo->info[0].numSysops = 0;
  88. gameInfo->info[0].numNotPlaying = m_pCounters->cPlayersLobby;
  89. gameInfo->info[0].numGamesServed = m_pCounters->cMissions; // relies on perf counters being updated
  90. // can not use interlocked calls here b/c they're 16bit values
  91. gameInfo->info[0].numTables = 1;
  92. gameInfo->info[0].numTablesInUse = 1;
  93. }
  94. void CLobbyApp::SendGameInfo()
  95. {
  96. ZGameServerInfoMsg* gameInfo = GetGameServerInfoMsg();
  97. SetVariableGameInfo();
  98. //must do endian so both side of network get correct numbers
  99. //gameinfo used on Unix and Intel boxes
  100. //note that doing this once will invalidate all numbers
  101. //so set gameInfo->info[0] numbers again.
  102. ZGameInstanceInfoMsgEndian( gameInfo->info );
  103. //send, usually to many Ip addresses which are Zone Web servers, so add this to your game configuration
  104. for (int i = 0; i < m_cReportServers; i++)
  105. ZGameInfoSendTo(m_rgulIP[i], 2000, GetGameServerInfoMsg(), sizeof(m_GameInfoBuf));
  106. }
  107. CLobbyApp::CLobbyApp(ILobbyAppSite * plas) :
  108. m_plas(plas),
  109. m_fmServers(&m_psiteServer),
  110. m_fmClients(&m_psiteClient),
  111. m_cReportServers(0),
  112. m_sGameInfoInterval(0), // doesn't really matter, but...
  113. m_fProtocol(true)
  114. #ifdef USEAUTH
  115. ,
  116. m_csqlSilentThreads(0),
  117. m_csqlNotifyThreads(0),
  118. m_sql(this)
  119. #endif
  120. {
  121. assert(m_plas);
  122. m_plas->LogEvent(EVENTLOG_INFORMATION_TYPE, LE_Creating);
  123. #ifdef USEAUTH
  124. m_strSQLConfig.Empty();
  125. #endif
  126. // see if we're setup to report to any web servers
  127. HKEY hk;
  128. if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, HKLM_AllLobby, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hk, NULL) == ERROR_SUCCESS)
  129. {
  130. // read AutoUpdate portion of registry
  131. char szServers[c_cReportServersMax * 17];
  132. bool bSuccess = _Module.ReadFromRegistry(hk, true, "GameInfoServers", szServers, 0, true);
  133. if(bSuccess)
  134. {
  135. char * token;
  136. token = strtok((char *) szServers, " ");
  137. while(token)
  138. {
  139. unsigned long ip = inet_addr(token);
  140. if (INADDR_NONE == ip) // then try to resolve by name
  141. {
  142. HOSTENT * phe = gethostbyname(token);
  143. if (phe)
  144. ip = * (unsigned long *) phe->h_addr_list[0];
  145. }
  146. if (INADDR_NONE != ip)
  147. m_rgulIP[m_cReportServers++] = ip;
  148. else
  149. m_plas->LogEvent(EVENTLOG_INFORMATION_TYPE, LE_BadGameInfoSrv, token);
  150. token = strtok(NULL, " ");
  151. }
  152. bSuccess = _Module.ReadFromRegistry(hk, false, "GameInfoInterval", &m_sGameInfoInterval, 25);
  153. }
  154. m_szToken[0] = '\0';
  155. bSuccess = _Module.ReadFromRegistry(hk, true, "Token", m_szToken, NULL);
  156. DWORD dwProtocol;
  157. bSuccess = _Module.ReadFromRegistry(hk, false, "fProtocol", &dwProtocol, (unsigned long) true);
  158. m_fProtocol = !!dwProtocol;
  159. DWORD dwFreeLobby;
  160. bSuccess = _Module.ReadFromRegistry(hk, false, "fFreeLobby", &dwFreeLobby, (unsigned long)
  161. #ifdef USEAUTH
  162. false
  163. #else
  164. true
  165. #endif
  166. );
  167. m_fFreeLobby = !!dwFreeLobby;
  168. DWORD dwCheckKey;
  169. bSuccess = _Module.ReadFromRegistry(hk, false, "fCheckCDKey", &dwCheckKey, (unsigned long)
  170. #ifdef USEAUTH
  171. true
  172. #else
  173. false
  174. #endif
  175. );
  176. m_fCheckCDKey = !!dwCheckKey;
  177. #ifdef USEAUTH
  178. bSuccess = _Module.ReadFromRegistry(hk, false, "SQLThreadsNotify", &m_csqlNotifyThreads, (unsigned long) 5);
  179. bSuccess = _Module.ReadFromRegistry(hk, false, "SQLThreadsSilent", &m_csqlSilentThreads, (unsigned long) 1);
  180. if (FAILED(LoadRegString(hk, "SQLConfig", m_strSQLConfig)))
  181. {
  182. m_strSQLConfig.Empty();
  183. _Module.LogEvent(EVENTLOG_ERROR_TYPE, LE_RegStrMissingNoDef, "SQLConfig");
  184. }
  185. #endif
  186. }
  187. g_pLobbyApp = this;
  188. // stuff for reporting population to zone
  189. WSAData data;
  190. WSAStartup(MAKEWORD(1,0),&data);
  191. //initialize UDP send API
  192. ZGameInfoInit(0);
  193. //initialize structure
  194. SetConstantGameInfo();
  195. // if zone club lobby
  196. #ifdef USEAUTH
  197. m_pzas = CreateZoneAuthServer();
  198. #endif
  199. }
  200. CLobbyApp::~CLobbyApp()
  201. {
  202. m_plas->LogEvent(EVENTLOG_INFORMATION_TYPE, LE_ShuttingDown);
  203. m_pzas = NULL;
  204. m_perfshare.FreeCounters(m_pCounters);
  205. ZGameInfoClose();
  206. WSACleanup();
  207. }
  208. HRESULT CLobbyApp::Init()
  209. {
  210. HRESULT hr = E_FAIL;
  211. m_plas->LogEvent(EVENTLOG_INFORMATION_TYPE, LE_Initializing);
  212. ZVerify(m_perfshare.Initialize());
  213. m_pCounters = (LOBBY_COUNTERS *)m_perfshare.AllocateCounters(
  214. "AllLobby", "0", // if there are ever multiple lobbies running, change this
  215. sizeof(LOBBY_COUNTERS));
  216. ZeroMemory(m_pCounters, sizeof(LOBBY_COUNTERS));
  217. #ifdef USEAUTH
  218. hr = m_sql.Init(m_strSQLConfig.m_str, GetCurrentThreadId(), m_csqlSilentThreads, m_csqlNotifyThreads);
  219. if (FAILED(hr))
  220. {
  221. m_plas->LogEvent(EVENTLOG_ERROR_TYPE, LE_SQLInitFailed);
  222. return hr;
  223. }
  224. #endif
  225. // TODO: Make keep-alives an option
  226. if (FAILED(hr = m_fmServers.HostSession(m_fFreeLobby ? FEDFREELOBBYSERVERS_GUID : FEDLOBBYSERVERS_GUID, false, 0, m_fProtocol)) ||
  227. FAILED(hr = m_fmClients.HostSession(m_fFreeLobby ? FEDFREELOBBYCLIENTS_GUID : FEDLOBBYCLIENTS_GUID, true, 0, m_fProtocol)))
  228. {
  229. m_plas->LogEvent(EVENTLOG_ERROR_TYPE, LE_HostSessionFailure);
  230. return hr;
  231. }
  232. //
  233. // Read Registry
  234. //
  235. DWORD dw; // Gets result of whether it opened or created...
  236. HKEY hk;
  237. if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, HKLM_AllLobby, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hk, &dw) == ERROR_SUCCESS)
  238. {
  239. // read AutoUpdate portion of registry
  240. DWORD dwWantAutoDownload;
  241. bool bSuccess = _Module.ReadFromRegistry(hk, false, "AutoUpdateActive", &dwWantAutoDownload, 0);
  242. if(bSuccess && dwWantAutoDownload)
  243. {
  244. char szFileName[MAX_PATH+16];
  245. strcpy(szFileName, _Module.GetModulePath());
  246. strcat(szFileName, "FileList.txt");
  247. CreateAutoUpdate(hk, szFileName);
  248. }
  249. else
  250. g_pAutoUpdate = NULL;
  251. RegCloseKey(hk);
  252. }
  253. return hr;
  254. }
  255. void CLobbyApp::UpdatePerfCounters()
  256. {
  257. static CTempTimer timerPerfCounters("assembling perf info", .05f);
  258. timerPerfCounters.Start();
  259. m_fmClients.GetSendQueue(&(m_pCounters->cOutboundQueueLength),
  260. &(m_pCounters->cOutboundQueueSize));
  261. m_fmClients.GetReceiveQueue(&(m_pCounters->cInboundQueueLength),
  262. &(m_pCounters->cInboundQueueSize));
  263. m_pCounters->cPlayersLobby = m_fmClients.GetCountConnections();
  264. m_pCounters->cServers = m_fmServers.GetConnectionCount();
  265. // Get all the per server stuff, and agregate the count of missions and players
  266. ListConnections::Iterator iterCnxn(*m_fmServers.GetConnections());
  267. int cMissions = 0;
  268. DWORD cPlayers = 0;
  269. while (!iterCnxn.End())
  270. {
  271. CFLServer * pServerT = CFLServer::FromConnection(*iterCnxn.Value());
  272. cMissions += (pServerT->GetCounters()->cMissions = pServerT->GetMissions()->GetCount());
  273. cPlayers += (pServerT->GetCounters()->cPlayers = pServerT->GetPlayerCount());
  274. pServerT->GetCounters()->percentLoad = pServerT->GetPercentLoad();
  275. iterCnxn.Next();
  276. }
  277. m_pCounters->cMissions = cMissions;
  278. m_pCounters->cPlayersMissions = cPlayers;
  279. timerPerfCounters.Stop();
  280. }
  281. void CLobbyApp::RollCall()
  282. {
  283. ListConnections::Iterator iterCnxn(*m_fmServers.GetConnections());
  284. while (!iterCnxn.End())
  285. {
  286. CFMConnection & cnxn = *iterCnxn.Value();
  287. iterCnxn.Next(); // have to move iterator FIRST, because we might kill the current node
  288. CFLServer * pServerT = CFLServer::FromConnection(cnxn);
  289. if (pServerT->GetHere())
  290. {
  291. // not combining ifs, since order would matter
  292. if (cnxn.IncAbsentCount() > 4) // dead--nuke 'em
  293. {
  294. m_plas->LogEvent(EVENTLOG_WARNING_TYPE, LE_ServerMissedRollCall, cnxn.GetName());
  295. m_fmServers.DeleteConnection(cnxn);
  296. }
  297. }
  298. }
  299. }
  300. int CLobbyApp::Run()
  301. {
  302. const DWORD c_dwUpdateInterval = 200; // milliseconds
  303. DWORD dwSleep = c_dwUpdateInterval;
  304. DWORD dwWait = WAIT_TIMEOUT;
  305. m_plas->LogEvent(EVENTLOG_INFORMATION_TYPE, LE_Running);
  306. _putts("---------Press Q to exit---------");
  307. printf("Ready for clients/servers.\n");
  308. CTempTimer timerIterations("between iterations", .25f);
  309. timerIterations.Start();
  310. CTempTimer timerReceiveClientsMessages("in clients ReceiveMessages()", .05f);
  311. CTempTimer timerReceiveServersMessages("in servers ReceiveMessages()", .05f);
  312. Time timeLastQueueCheck = Time::Now();
  313. Time timeLastGameInfo = Time::Now();
  314. while (true)
  315. {
  316. timerIterations.Stop();
  317. timerIterations.Start();
  318. if (ProcessMsgPump() ||
  319. (_kbhit() && toupper(_getch()) == 'Q'))
  320. return 0;
  321. SetNow();
  322. m_pCounters->timeInnerLoop = timerIterations.LastInterval();
  323. // receive any messages in the queue
  324. timerReceiveClientsMessages.Start();
  325. m_fmClients.ReceiveMessages();
  326. timerReceiveClientsMessages.Stop();
  327. timerReceiveServersMessages.Start();
  328. m_fmServers.ReceiveMessages();
  329. timerReceiveServersMessages.Stop();
  330. if (GetNow() - timeLastQueueCheck >= 1.0f)
  331. {
  332. // count the fairly expensive stuff no more than once a second
  333. UpdatePerfCounters();
  334. timeLastQueueCheck = GetNow();
  335. if (GetNow() - timeLastGameInfo >= (float) m_sGameInfoInterval)
  336. {
  337. SendGameInfo();
  338. timeLastGameInfo = GetNow();
  339. }
  340. // Do a periodic roll call. If we haven't heard from anyone for two roll calls in a row, waste 'em
  341. static Time timeRollCall = Time::Now();
  342. if (GetNow() - timeRollCall >= 5.0f)
  343. {
  344. RollCall();
  345. timeRollCall = GetNow();
  346. }
  347. }
  348. Sleep(1);
  349. }
  350. return 0;
  351. }
  352. int CLobbyApp::OnMessageBox(const char * strText, const char * strCaption, UINT nType)
  353. {
  354. char sz[256];
  355. if (strCaption && *strCaption)
  356. {
  357. lstrcpy(sz, strCaption);
  358. lstrcat(sz, ": ");
  359. }
  360. lstrcat(sz, strText);
  361. return m_plas->LogEvent(EVENTLOG_ERROR_TYPE, LE_ODBC_Error, strText);
  362. }
  363. PER_SERVER_COUNTERS * CLobbyApp::AllocatePerServerCounters(const char * szServername)
  364. {
  365. PER_SERVER_COUNTERS * pPerServerCounters = (PER_SERVER_COUNTERS *)
  366. m_perfshare.AllocateCounters((CHAR *) "AllLobbyPerServer", (CHAR*) szServername, sizeof(PER_SERVER_COUNTERS));
  367. ZeroMemory(pPerServerCounters, sizeof(*pPerServerCounters));
  368. return pPerServerCounters;
  369. }
  370. bool CLobbyApp::OnAssert(const char* psz, const char* pszFile, int line, const char* pszModule)
  371. {
  372. m_plas->LogEvent(EVENTLOG_ERROR_TYPE, LE_Assert, ZString("'")
  373. + psz
  374. + "' ("
  375. + pszFile
  376. + ":"
  377. + ZString(line)
  378. + ")\n"
  379. );
  380. return true;
  381. }
  382. void CLobbyApp::DebugOutput(const char *psz)
  383. {
  384. ::OutputDebugString("AllLobby: ");
  385. #ifdef _DEBUG
  386. Win32App::DebugOutput(psz);
  387. #endif
  388. }
  389. void CLobbyApp::BootPlayersByName(const ZString& strName)
  390. {
  391. PlayerByName::iterator iterPlayer = m_playerByName.find(strName);
  392. // if we think that player is already logged on...
  393. while (iterPlayer != m_playerByName.end()
  394. && (*iterPlayer).first == strName)
  395. {
  396. // boot all old copies
  397. CFLMission * pMissionOld = (*(*iterPlayer).second).second.GetMission();
  398. BEGIN_PFM_CREATE(m_fmServers, pfmRemovePlayer, L, REMOVE_PLAYER)
  399. FM_VAR_PARM((PCC)strName, CB_ZTS)
  400. FM_VAR_PARM(NULL, 0)
  401. END_PFM_CREATE
  402. pfmRemovePlayer->dwMissionCookie = pMissionOld->GetCookie();
  403. pfmRemovePlayer->reason = RPR_duplicateName;
  404. m_fmServers.SendMessages(pMissionOld->GetServer()->GetConnection(),
  405. FM_GUARANTEED, FM_FLUSH);
  406. ++iterPlayer;
  407. }
  408. }
  409. bool CLobbyApp::BootPlayersByCDKey(const ZString& strCDKey, const ZString& strNameExclude, ZString& strOldPlayer)
  410. {
  411. PlayerByCDKey::iterator iterPlayerByCDKey = m_playerByCDKey.find(strCDKey);
  412. bool bBootedSomeone = false;
  413. // if we think that player is already logged on...
  414. while (iterPlayerByCDKey != m_playerByCDKey.end()
  415. && (*iterPlayerByCDKey).first == strCDKey)
  416. {
  417. // boot all old copies
  418. if ((*iterPlayerByCDKey).second.GetName() != strNameExclude)
  419. {
  420. CFLMission * pMissionOld = (*iterPlayerByCDKey).second.GetMission();
  421. BEGIN_PFM_CREATE(m_fmServers, pfmRemovePlayer, L, REMOVE_PLAYER)
  422. FM_VAR_PARM((PCC)(*iterPlayerByCDKey).second.GetName(), CB_ZTS)
  423. FM_VAR_PARM((PCC)(strNameExclude), CB_ZTS)
  424. END_PFM_CREATE
  425. pfmRemovePlayer->dwMissionCookie = pMissionOld->GetCookie();
  426. pfmRemovePlayer->reason = RPR_duplicateCDKey;
  427. m_fmServers.SendMessages(pMissionOld->GetServer()->GetConnection(),
  428. FM_GUARANTEED, FM_FLUSH);
  429. // note: only returns the name of the last player we booted.
  430. strOldPlayer = (*iterPlayerByCDKey).second.GetName();
  431. bBootedSomeone = true;
  432. }
  433. ++iterPlayerByCDKey;
  434. }
  435. return bBootedSomeone;
  436. }
  437. void CLobbyApp::SetPlayerMission(const char* szPlayerName, const char* szCDKey, CFLMission* pMission)
  438. {
  439. ZString strPlayerName = szPlayerName;
  440. ZString strCDKey = szCDKey;
  441. // boot any old copies of this player
  442. #ifdef USEAUTH
  443. BootPlayersByName(strPlayerName);
  444. #endif
  445. if (EnforceCDKey())
  446. {
  447. // make sure the key requested is valid (since we can't guarantee that
  448. // they reported the correct value to the server).
  449. /* // we don't have any "instant" way to validate keys, and it's not worth it (now) to go back to db
  450. if (!CDKeyIsValid(szCDKey))
  451. {
  452. BEGIN_PFM_CREATE(m_fmServers, pfmRemovePlayer, L, REMOVE_PLAYER)
  453. FM_VAR_PARM(szPlayerName, CB_ZTS)
  454. END_PFM_CREATE
  455. pfmRemovePlayer->dwMissionCookie = pMission->GetCookie();
  456. pfmRemovePlayer->reason = RPR_duplicateCDKey;
  457. m_fmServers.SendMessages(pMission->GetServer()->GetConnection(),
  458. FM_GUARANTEED, FM_FLUSH);
  459. GetSite()->LogEvent(EVENTLOG_WARNING_TYPE, LE_BadCDKey, szCDKey,
  460. pMission->GetServer()->GetConnection()->GetName(), szPlayerName);
  461. }
  462. else
  463. */
  464. ZString strOldPlayer;
  465. if (BootPlayersByCDKey(strCDKey, szPlayerName, strOldPlayer))
  466. {
  467. BEGIN_PFM_CREATE(m_fmServers, pfmRemovePlayer, L, REMOVE_PLAYER)
  468. FM_VAR_PARM(szPlayerName, CB_ZTS)
  469. FM_VAR_PARM((PCC)strOldPlayer, CB_ZTS)
  470. END_PFM_CREATE
  471. pfmRemovePlayer->dwMissionCookie = pMission->GetCookie();
  472. pfmRemovePlayer->reason = RPR_duplicateCDKey;
  473. m_fmServers.SendMessages(pMission->GetServer()->GetConnection(),
  474. FM_GUARANTEED, FM_FLUSH);
  475. }
  476. }
  477. // create a new player by creating entries in the maps
  478. PlayerByCDKey::iterator iterPlayerByCDKey =
  479. m_playerByCDKey.insert(PlayerByCDKey::value_type(strCDKey, PlayerLocInfo(strPlayerName, pMission)));
  480. m_playerByName.insert(PlayerByName::value_type(strPlayerName, iterPlayerByCDKey));
  481. pMission->AddPlayer();
  482. }
  483. void CLobbyApp::RemovePlayerFromMission(const char* szPlayerName, CFLMission* pMission)
  484. {
  485. ZString strPlayerName = szPlayerName;
  486. PlayerByName::iterator iterPlayer = m_playerByName.find(strPlayerName);
  487. // we better have found the player
  488. if(iterPlayer != m_playerByName.end())
  489. {
  490. // find the entry coresponding to this mission
  491. while (iterPlayer != m_playerByName.end()
  492. && (*iterPlayer).first == strPlayerName)
  493. {
  494. if ((*(*iterPlayer).second).second.GetMission() == pMission)
  495. {
  496. pMission->RemovePlayer();
  497. // delete the player from the maps
  498. m_playerByCDKey.erase((*iterPlayer).second);
  499. m_playerByName.erase(iterPlayer);
  500. return;
  501. }
  502. ++iterPlayer;
  503. }
  504. }
  505. m_plas->LogEvent(EVENTLOG_WARNING_TYPE, LE_CantRemovePlayer, szPlayerName, pMission->GetCookie());
  506. }
  507. void CLobbyApp::RemoveAllPlayersFromMission(CFLMission* pMission)
  508. {
  509. // remove all players playing in this mission
  510. if (pMission->GetPlayerCount() != 0)
  511. {
  512. // REVIEW: O(AllPlayers). We could do this in O(ln(AllPlayers)) by
  513. // storing a set of players on each mission, but that requires extra
  514. // work for the common case (inserting or removing players) to speed
  515. // up the uncommon case (removing a server with players).
  516. // loop through all of the players
  517. PlayerByName::iterator iterPlayer = m_playerByName.begin();
  518. while (iterPlayer != m_playerByName.end())
  519. {
  520. // if this player was playing in the mission to be deleted...
  521. if ((*(*iterPlayer).second).second.GetMission() == pMission)
  522. {
  523. pMission->RemovePlayer();
  524. // delete the player from the maps
  525. m_playerByCDKey.erase((*iterPlayer).second);
  526. iterPlayer = m_playerByName.erase(iterPlayer);
  527. }
  528. else
  529. ++iterPlayer;
  530. }
  531. assert(pMission->GetPlayerCount() == 0);
  532. }
  533. }
  534. void CLobbyApp::RemoveAllPlayersFromServer(CFLServer* pServer)
  535. {
  536. // remove all players playing in this server
  537. if (pServer->GetPlayerCount() != 0)
  538. {
  539. // REVIEW: O(AllPlayers). We could do this in O(ln(AllPlayers)) by
  540. // storing a set of players on each mission, but that requires extra
  541. // work for the common case (inserting or removing players) to speed
  542. // up the uncommon case (removing a server with players).
  543. // loop through all of the players
  544. PlayerByName::iterator iterPlayer = m_playerByName.begin();
  545. while (iterPlayer != m_playerByName.end())
  546. {
  547. // if this player was playing in the mission to be deleted...
  548. if ((*(*iterPlayer).second).second.GetMission()->GetServer() == pServer)
  549. {
  550. (*(*iterPlayer).second).second.GetMission()->RemovePlayer();
  551. // delete the player from the map
  552. m_playerByCDKey.erase((*iterPlayer).second);
  553. iterPlayer = m_playerByName.erase(iterPlayer);
  554. }
  555. else
  556. ++iterPlayer;
  557. }
  558. assert(pServer->GetPlayerCount() == 0);
  559. }
  560. }
  561. CFLMission* CLobbyApp::FindPlayersMission(const char* szPlayerName)
  562. {
  563. PlayerByName::iterator iterPlayerName = m_playerByName.find(szPlayerName);
  564. if (iterPlayerName != m_playerByName.end())
  565. {
  566. CFLMission* pmission = (*(*iterPlayerName).second).second.GetMission();
  567. return pmission->GetServer()->GetPaused() ? NULL : pmission;
  568. }
  569. else
  570. return NULL;
  571. }
  572. bool CLobbyApp::StringCmpLess::operator () (const ZString& str1, const ZString& str2) const
  573. {
  574. // comparing lengths first is faster than a strcmp and still creates a strong ordering
  575. int nLength1 = str1.GetLength();
  576. int nLength2 = str2.GetLength();
  577. if (nLength1 == nLength2)
  578. return memcmp((PCC)str1, (PCC)str2, nLength1) < 0;
  579. else
  580. return nLength1 < nLength2;
  581. };
  582. bool CLobbyApp::StringICmpLess::operator () (const ZString& str1, const ZString& str2) const
  583. {
  584. // comparing lengths first is faster than a strcmp and still creates a strong ordering
  585. int nLength1 = str1.GetLength();
  586. int nLength2 = str2.GetLength();
  587. if (nLength1 == nLength2)
  588. return _stricmp(str1, str2) < 0;
  589. else
  590. return nLength1 < nLength2;
  591. };