Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

872 lines
25 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifdef _WIN32
  8. #include <stdio.h>
  9. #include "CreateMultiplayerGameServerPage.h"
  10. #include <Winsock2.h>
  11. using namespace vgui;
  12. #include <vgui_controls/Controls.h>
  13. #include <KeyValues.h>
  14. #include <vgui_controls/ListPanel.h>
  15. #include <vgui_controls/ComboBox.h>
  16. #include <vgui_controls/MessageBox.h>
  17. #include <vgui_controls/CheckButton.h>
  18. #include <vgui/IVGui.h>
  19. #include <OfflineMode.h>
  20. #include "filesystem.h"
  21. #include "mainpanel.h"
  22. #include "tier0/icommandline.h"
  23. #include "netapi.h"
  24. // for SRC
  25. #include <vstdlib/random.h>
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. //#define ALLOW_OLD_ENGINE_GAMES
  29. bool IsEp1EraAppID( int iSteamAppId )
  30. {
  31. return iSteamAppId == 211 || iSteamAppId == 215;
  32. }
  33. // Checks the liblist.gam file for a "fallback_dir"
  34. const char *GetLiblistFallbackDir( const char *pszGameDir )
  35. {
  36. static char szFallback[512];
  37. char szTemp[512];
  38. szFallback[0] = 0;
  39. V_sprintf_safe( szTemp, "%s\\liblist.gam", pszGameDir );
  40. g_pFullFileSystem->GetLocalCopy( szTemp );
  41. FileHandle_t hFile = g_pFullFileSystem->Open( szTemp, "rt" );
  42. if ( hFile )
  43. {
  44. char szLine[512];
  45. // look for the line starting with 'fallback_dir'
  46. while ( !g_pFullFileSystem->EndOfFile( hFile ) )
  47. {
  48. // get a single line
  49. szLine[0] = 0;
  50. g_pFullFileSystem->ReadLine( szLine, sizeof(szLine) - 1, hFile );
  51. szLine[sizeof(szLine) - 1] = 0;
  52. if ( !strnicmp( szLine, "fallback_dir", 12 ) )
  53. {
  54. // we got the line, get the value between the quotes
  55. char *start = strchr( szLine, '\"' );
  56. if ( !start )
  57. {
  58. break;
  59. }
  60. char *end = strchr( start + 1, '\"' );
  61. if ( !end )
  62. {
  63. break;
  64. }
  65. // copy out between start and end
  66. int bytesToCopy = end - start - 1;
  67. if ( bytesToCopy >= sizeof(szFallback) - 1 )
  68. {
  69. bytesToCopy = sizeof(szFallback) - 1;
  70. break;
  71. }
  72. if ( bytesToCopy > 0 )
  73. {
  74. strncpy( szFallback, start + 1, bytesToCopy );
  75. szFallback[bytesToCopy] = 0;
  76. }
  77. }
  78. }
  79. g_pFullFileSystem->Close( hFile );
  80. }
  81. return szFallback;
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Constructor
  85. //-----------------------------------------------------------------------------
  86. CCreateMultiplayerGameServerPage::CCreateMultiplayerGameServerPage(vgui::Panel *parent, const char *name) : Frame(parent, name)
  87. {
  88. memset(&m_iServer,0x0,sizeof(serveritem_t));
  89. m_MainPanel = parent; // as we are a popup frame we need to store this seperately
  90. m_pSavedData = NULL;
  91. m_pGameInfo = NULL;
  92. SetMinimumSize(310, 350);
  93. SetSize(310, 350);
  94. SetSizeable(false);
  95. SetTitle("#Start_Server_Title",true);
  96. m_pMapList = new ComboBox(this, "MapList",10,false);
  97. m_pMapList->SetEnabled(false); // a mod needs to be chosen first to populate the map list
  98. m_pMapList->SetEditable(false);
  99. m_pNetworkCombo = new ComboBox(this, "NetworkCombo",10,false);
  100. int defaultItem = m_pNetworkCombo->AddItem("#Internet", NULL);
  101. int lanItem = m_pNetworkCombo->AddItem("#LAN", NULL);
  102. if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() )
  103. {
  104. defaultItem = lanItem;
  105. }
  106. m_pNetworkCombo->ActivateItem(defaultItem);
  107. m_pNumPlayers = new ComboBox(this, "NumPlayers",10,false);
  108. char num[3];
  109. int i;
  110. for( i = 1 ; i <= MAX_PLAYERS ; i++ )
  111. {
  112. V_sprintf_safe(num, "%i", i);
  113. m_pNumPlayers->AddItem(num, NULL);
  114. }
  115. m_pNumPlayers->ActivateItemByRow(23); // 24 players by default
  116. m_pGameCombo = new ComboBox(this,"MODCombo", 10, false);
  117. m_pStartServerButton = new Button(this, "StartButton", "#Start_Server_Button");
  118. m_pStartServerButton->SetCommand("start");
  119. m_pCancelButton = new Button(this, "CancelButton", "#Start_Server_Cancel");
  120. m_pCancelButton->SetCommand("cancel");
  121. m_pSecureCheck = new CheckButton(this, "SecureCheck", "#Start_Server_Secure");
  122. m_pSecureCheck->SetSelected(true);
  123. LoadControlSettingsAndUserConfig("Admin/CreateMultiplayerGameServerPage.res");
  124. // load some defaults into the controls
  125. SetControlString("ServerNameEdit", "Half-Life dedicated server");
  126. V_strcpy_safe(m_szGameName, "Half-Life");
  127. LoadMODList();
  128. m_pGameCombo->RequestFocus();
  129. // get default port from commandline if possible
  130. m_iPort = 27015;
  131. const char *portVal = NULL;
  132. if (CommandLine()->CheckParm("-port", &portVal) && portVal && atoi(portVal) > 0)
  133. {
  134. m_iPort = atoi(portVal);
  135. }
  136. SetControlInt("PortEdit", m_iPort);
  137. LoadConfig();
  138. m_szMapName[0] = 0;
  139. m_szHostName[0] = 0;
  140. m_szPassword[0] = 0;
  141. m_iMaxPlayers = 24;
  142. if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() )
  143. {
  144. m_pNetworkCombo->SetEnabled( false );
  145. }
  146. SetVisible(true);
  147. if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() )
  148. {
  149. MessageBox *box = new vgui::MessageBox( "#Start_Server_Offline_Title", "#Start_Server_Offline_Warning" );
  150. box->DoModal();
  151. }
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose: Destructor
  155. //-----------------------------------------------------------------------------
  156. CCreateMultiplayerGameServerPage::~CCreateMultiplayerGameServerPage()
  157. {
  158. SaveConfig();
  159. if (m_pSavedData)
  160. {
  161. m_pSavedData->deleteThis();
  162. m_pSavedData = NULL;
  163. }
  164. if ( m_pGameInfo )
  165. {
  166. m_pGameInfo->deleteThis();
  167. m_pGameInfo = NULL;
  168. }
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. void CCreateMultiplayerGameServerPage::OnResetData()
  174. {
  175. m_pGameCombo->SetEnabled(true);
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: loads settings from a config file
  179. //-----------------------------------------------------------------------------
  180. void CCreateMultiplayerGameServerPage::LoadConfig()
  181. {
  182. // free any old filters
  183. if (m_pSavedData)
  184. {
  185. m_pSavedData->deleteThis();
  186. }
  187. m_pSavedData = new KeyValues ("Server");
  188. if (!m_pSavedData->LoadFromFile(g_pFullFileSystem, "Server.vdf", "CONFIG"))
  189. {
  190. // file not successfully loaded
  191. }
  192. else
  193. {
  194. if (m_pSavedData->FindKey("RconPassword", false))
  195. {
  196. const char *password = m_pSavedData->GetString("RconPassword", "");
  197. if (strlen(password)>0)
  198. {
  199. SetControlString("RCONPasswordEdit", password);
  200. }
  201. }
  202. if (m_pSavedData->FindKey("MaxPlayers", false))
  203. {
  204. int maxPlayers = m_pSavedData->GetInt("MaxPlayers", -1);
  205. if (maxPlayers > 0 && maxPlayers <= 32)
  206. {
  207. m_pNumPlayers->ActivateItemByRow(maxPlayers - 1);
  208. }
  209. }
  210. if (m_pSavedData->FindKey("MOD", false))
  211. {
  212. const char *mod = m_pSavedData->GetString("MOD", "");
  213. if (strlen(mod) > 0)
  214. {
  215. // look for the item in the dropdown
  216. m_szMod[0] = 0;
  217. for (int i = 0; i < m_pGameCombo->GetItemCount(); i++)
  218. {
  219. if (!m_pGameCombo->IsItemIDValid(i))
  220. continue;
  221. if (!stricmp(m_pGameCombo->GetItemUserData(i)->GetString("gamedir"), mod))
  222. {
  223. // item found in list, activate
  224. m_pGameCombo->ActivateItem(i);
  225. break;
  226. }
  227. }
  228. }
  229. }
  230. if (m_pSavedData->FindKey("Map", false))
  231. {
  232. const char *map = m_pSavedData->GetString("Map", "");
  233. if (strlen(map) > 0)
  234. {
  235. SetControlString("MapList", map);
  236. }
  237. }
  238. if (m_pSavedData->FindKey("Network", false))
  239. {
  240. int nwIndex = m_pSavedData->GetInt("Network");
  241. if (nwIndex > 0 && nwIndex < 2)
  242. {
  243. m_pNetworkCombo->ActivateItemByRow(nwIndex);
  244. }
  245. }
  246. if (m_pSavedData->FindKey("Secure", false))
  247. {
  248. int secure = m_pSavedData->GetInt("Secure");
  249. m_pSecureCheck->SetSelected(secure);
  250. }
  251. if (m_pSavedData->FindKey("ServerName", false))
  252. {
  253. const char *serverName = m_pSavedData->GetString("ServerName","");
  254. if (strlen(serverName) > 0)
  255. {
  256. SetControlString("ServerNameEdit", serverName);
  257. }
  258. }
  259. m_iPort = m_pSavedData->GetInt("Port", m_iPort);
  260. SetControlInt("PortEdit", m_iPort);
  261. }
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: data accessor
  265. //-----------------------------------------------------------------------------
  266. void CCreateMultiplayerGameServerPage::SetConfig(const char *serverName, const char *rconPassword, int maxPlayers, const char *mod, const char *map, int network, int secure, int port)
  267. {
  268. m_pSavedData->SetInt("MaxPlayers", maxPlayers);
  269. m_pSavedData->SetString("RconPassword", rconPassword);
  270. m_pSavedData->SetString("ServerName", serverName);
  271. m_pSavedData->SetString("MOD", mod);
  272. m_pSavedData->SetString("Map", map);
  273. m_pSavedData->SetInt("Secure", secure);
  274. m_pSavedData->SetInt("Network", network);
  275. m_pSavedData->SetInt("Port", port);
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. void CCreateMultiplayerGameServerPage::SaveConfig()
  281. {
  282. m_pSavedData->SaveToFile(g_pFullFileSystem, "Server.vdf", "CONFIG");
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose: returns true if one of the characters in the string is not a valid alpha numeric character
  286. //-----------------------------------------------------------------------------
  287. bool CCreateMultiplayerGameServerPage::BadRconChars(const char *pass)
  288. {
  289. bool bad = false;
  290. for(unsigned int i=0;i<strlen(pass);i++)
  291. {
  292. bad |= !( V_isalnum(pass[i]) ? true : false );
  293. }
  294. return bad;
  295. }
  296. const char *ToString( int val )
  297. {
  298. static char text[256];
  299. Q_snprintf( text, sizeof(text), "%i", val );
  300. return text;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: called to get the info from the dialog
  304. //-----------------------------------------------------------------------------
  305. void CCreateMultiplayerGameServerPage::OnCommand(const char *cmd)
  306. {
  307. char cvars[1024];
  308. int secure = GetControlInt("SecureCheck", 1);
  309. m_pNumPlayers->GetText(cvars, 1024);
  310. m_iMaxPlayers = atoi(cvars);
  311. V_strcpy_safe(m_szHostName, GetControlString("ServerNameEdit", ""));
  312. V_strcpy_safe(m_szPassword, GetControlString("RCONPasswordEdit", ""));
  313. m_iPort = GetControlInt("PortEdit", 27015);
  314. if (!stricmp(cmd, "cancel"))
  315. {
  316. vgui::ivgui()->PostMessage( m_MainPanel->GetVPanel(), new KeyValues("Quit"), NULL);
  317. Close();
  318. }
  319. else if (!stricmp(cmd, "start"))
  320. {
  321. // save our current settings
  322. SetConfig(m_szHostName, m_szPassword, m_iMaxPlayers, m_szMod, GetMapName(), m_pNetworkCombo->GetActiveItem() != 0, secure, m_iPort);
  323. SaveConfig();
  324. // create the command to execute
  325. bool isLanOnly = (m_pNetworkCombo->GetActiveItem() != 0);
  326. CommandLine()->AppendParm("-game", m_szMod);
  327. CommandLine()->AppendParm("-maxplayers", ToString(m_iMaxPlayers));
  328. CommandLine()->AppendParm("+sv_lan", ToString(isLanOnly));
  329. CommandLine()->AppendParm("+map", GetMapName());
  330. CommandLine()->AppendParm("-port", ToString(m_iPort));
  331. if (!secure) // if they don't want it secure...
  332. {
  333. CommandLine()->AppendParm("-insecure", "");
  334. }
  335. // V_strcpy_safe(m_szPassword, GetControlString("RCONPasswordEdit", ""));
  336. if (strlen(m_szPassword) < 3 || BadRconChars(m_szPassword))
  337. {
  338. MessageBox *dlg = new MessageBox("#Start_Server_RCON_Error_Title", "#Start_Server_RCON_Error");
  339. dlg->DoModal();
  340. }
  341. else
  342. {
  343. V_sprintf_safe(cvars, "rcon_password \"%s\"\nsetmaster enable\nhostname \"%s\"\n", m_szPassword, m_szHostName);
  344. m_pGameCombo->SetEnabled(false);
  345. m_pNumPlayers->SetEnabled(false);
  346. netadr_t local;
  347. net->GetLocalIP(&local);
  348. local.port = ::htons(m_iPort);
  349. for (int i = 0; i < 4; i++)
  350. {
  351. m_iServer.ip[i] = local.ip[i];
  352. }
  353. m_iServer.port = (local.port & 0xff) << 8 | (local.port & 0xff00) >> 8;;
  354. V_strcpy_safe(m_iServer.name, m_szHostName);
  355. V_strcpy_safe(m_iServer.map, GetMapName());
  356. V_strcpy_safe(m_iServer.gameDir, m_szMod);
  357. m_iServer.maxPlayers = m_iMaxPlayers;
  358. SetVisible(false);
  359. KeyValues *gameData = m_pGameCombo->GetActiveItemUserData();
  360. // // mount the caches
  361. // if (CommandLine()->CheckParm("-steam"))
  362. // {
  363. // if (gameData)
  364. // {
  365. // KeyValues *pFileSystem = gameData->FindKey( "FileSystem" );
  366. // if ( !pFileSystem )
  367. // Error( "Game %s missing FileSystem key.", gameData->GetString( "game" ) );
  368. //
  369. // // Mods just specify their app ID (CS, HL2, HL2MP, etc), and it mounts all the necessary caches.
  370. // int iAppId = pFileSystem->GetInt( "SteamAppId" );
  371. // if ( iAppId )
  372. // {
  373. // CUtlVector<unsigned int> depList;
  374. // MountDependencies( iAppId, depList );
  375. //
  376. // char gameinfoFilename[MAX_PATH];
  377. // Q_snprintf( gameinfoFilename, sizeof( gameinfoFilename ), "%s\\gameinfo.txt", m_iServer.gameDir );
  378. // g_pFullFileSystem->GetLocalCopy( gameinfoFilename );
  379. // }
  380. // }
  381. // }
  382. // Launch the old dedicated server if necessary.
  383. if ( LaunchOldDedicatedServer( gameData ) )
  384. {
  385. vgui::ivgui()->PostMessage( m_MainPanel->GetVPanel(), new KeyValues("Quit"), NULL);
  386. Close();
  387. }
  388. CMainPanel::GetInstance()->StartServer(cvars);
  389. }
  390. }
  391. }
  392. bool CCreateMultiplayerGameServerPage::LaunchOldDedicatedServer( KeyValues *pGameInfo )
  393. {
  394. #if defined( ALLOW_OLD_ENGINE_GAMES )
  395. // Validate the gameinfo.txt format..
  396. KeyValues *pSub = pGameInfo->FindKey( "FileSystem" );
  397. if ( pSub )
  398. {
  399. int iSteamAppId = pSub->GetInt( "SteamAppId", -1 );
  400. if ( iSteamAppId != -1 )
  401. {
  402. if ( IsEp1EraAppID( iSteamAppId ) )
  403. {
  404. // Old-skool app. Launch the old dedicated server.
  405. char steamDir[MAX_PATH];
  406. if ( !system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\SteamPath", steamDir, sizeof( steamDir ) ) )
  407. Error( "LaunchOldDedicatedServer: can't get SteamPath." );
  408. V_FixSlashes( steamDir );
  409. V_AppendSlash( steamDir, sizeof( steamDir ) );
  410. char commandLine[1024 * 4];
  411. commandLine[0] = 0;
  412. V_snprintf( commandLine, sizeof( commandLine ), "\"%ssteam.exe\" -applaunch 205 -HiddenLaunch", steamDir );
  413. // Feed it all the parameters chosen in the UI so it doesn't redisplay the UI.
  414. STARTUPINFO si;
  415. memset( &si, 0, sizeof( si ) );
  416. si.cb = sizeof( si );
  417. PROCESS_INFORMATION pi;
  418. if ( !CreateProcess( NULL, commandLine, NULL, NULL, false, 0, NULL, steamDir, &si, &pi ) )
  419. {
  420. Error( "LaunchOldDedicatedServer: Unable to launch old srcds." );
  421. }
  422. return true;
  423. }
  424. }
  425. }
  426. #endif
  427. return false;
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose: loads the list of available maps into the map list
  431. //-----------------------------------------------------------------------------
  432. void CCreateMultiplayerGameServerPage::LoadMODList()
  433. {
  434. m_pGameCombo->DeleteAllItems();
  435. // add steam games
  436. if (CommandLine()->CheckParm("-steam"))
  437. {
  438. const char *pSteamGamesFilename = "hlds_steamgames.vdf";
  439. KeyValues *gamesFile = new KeyValues( pSteamGamesFilename );
  440. if ( gamesFile->LoadFromFile( g_pFullFileSystem, pSteamGamesFilename, NULL ) )
  441. {
  442. for ( KeyValues *kv = gamesFile->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey() )
  443. {
  444. const char *pGameDir = kv->GetString( "gamedir", NULL );
  445. if ( !pGameDir )
  446. Error( "Mod %s in %s missing 'gamedir'.", kv->GetName(), pSteamGamesFilename );
  447. AddMod( pGameDir, pSteamGamesFilename, kv );
  448. }
  449. }
  450. gamesFile->deleteThis();
  451. gamesFile = NULL;
  452. }
  453. // For backward compatibility, check inside the dedicated server's own directory for mods.
  454. LoadModListInDirectory( "." );
  455. // Also, check in SourceMods.
  456. char sourceModsDir[MAX_PATH];
  457. if ( system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\SourceModInstallPath", sourceModsDir, sizeof( sourceModsDir ) ) )
  458. LoadModListInDirectory( sourceModsDir );
  459. m_pGameCombo->ActivateItem(0);
  460. }
  461. void CCreateMultiplayerGameServerPage::LoadModListInDirectory( const char *pDirectoryName )
  462. {
  463. char searchString[MAX_PATH*2];
  464. V_strcpy_safe( searchString, pDirectoryName );
  465. Q_AppendSlash( searchString, sizeof( searchString ) );
  466. Q_strncat( searchString, "*.*", sizeof( searchString ), COPY_ALL_CHARACTERS );
  467. FileFindHandle_t findHandle = NULL;
  468. const char *filename = g_pFullFileSystem->FindFirst( searchString, &findHandle );
  469. while ( filename )
  470. {
  471. // add to the mod list
  472. if (filename[0] != '.' && g_pFullFileSystem->FindIsDirectory(findHandle))
  473. {
  474. char fullFilename[MAX_PATH];
  475. if ( Q_stricmp( pDirectoryName, "." ) == 0 )
  476. {
  477. // If we don't do this, then the games in hlds_steamgames.vdf will get listed twice
  478. // since their gamedir is listed as "cstrike" and "hl2mp", not ".\cstrike" or ".\hl2mp".
  479. V_strcpy_safe( fullFilename, filename );
  480. }
  481. else
  482. {
  483. V_strcpy_safe( fullFilename, pDirectoryName );
  484. Q_AppendSlash( fullFilename, sizeof( fullFilename ) );
  485. Q_strncat( fullFilename, filename, sizeof( fullFilename ), COPY_ALL_CHARACTERS );
  486. }
  487. LoadPossibleMod( fullFilename );
  488. }
  489. filename = g_pFullFileSystem->FindNext(findHandle);
  490. }
  491. g_pFullFileSystem->FindClose(findHandle);
  492. }
  493. void CCreateMultiplayerGameServerPage::LoadPossibleMod( const char *pGameDirName )
  494. {
  495. char gameInfoFilename[1024];
  496. Q_snprintf(gameInfoFilename, sizeof(gameInfoFilename) - 1, "%s\\gameinfo.txt", pGameDirName);
  497. if ( !g_pFullFileSystem->FileExists(gameInfoFilename) )
  498. return;
  499. // don't want to add single player games to the list
  500. KeyValues *pGameInfo = new KeyValues( "GameInfo" );
  501. bool loadedFile = pGameInfo->LoadFromFile( g_pFullFileSystem, gameInfoFilename );
  502. if ( !loadedFile )
  503. return;
  504. AddMod( pGameDirName, gameInfoFilename, pGameInfo );
  505. pGameInfo->deleteThis();
  506. pGameInfo = NULL;
  507. }
  508. void CCreateMultiplayerGameServerPage::AddMod( const char *pGameDirName, const char *pGameInfoFilename, KeyValues *pGameInfo )
  509. {
  510. // Don't re-add something with the same gamedir name (this can happen with games listed in hlds_steamgames.vdf,
  511. // since after the first time a game is run, it'll also have a gameinfo.txt that will be found).
  512. for ( int i=0; i < m_pGameCombo->GetItemCount(); i++ )
  513. {
  514. if ( !m_pGameCombo->IsItemIDValid(i) )
  515. continue;
  516. if ( Q_stricmp( m_pGameCombo->GetItemUserData(i)->GetString("gamedir"), pGameDirName ) == 0 )
  517. return;
  518. }
  519. // If this mod supports multiplayer, then we'll add it.
  520. const char *gameType = pGameInfo->GetString( "type", "singleplayer_only" );
  521. if ( Q_stricmp(gameType, "singleplayer_only") != 0 )
  522. {
  523. // Validate the gameinfo.txt format..
  524. KeyValues *pSub = pGameInfo->FindKey( "FileSystem" );
  525. if ( !pSub )
  526. Error( "%s missing FileSystem key.", pGameInfoFilename );
  527. int iSteamAppId = pSub->GetInt( "SteamAppId", -1 );
  528. if ( iSteamAppId == -1 )
  529. Error( "%s missing FileSystem\\SteamAppId key.", pGameInfoFilename );
  530. #if !defined( ALLOW_OLD_ENGINE_GAMES )
  531. // If we're not supporting old games in here and its appid is old, forget about it.
  532. if ( IsEp1EraAppID( iSteamAppId ) )
  533. return;
  534. #endif
  535. const char *gameName = pGameInfo->GetString( "game", NULL );
  536. if ( !gameName )
  537. Error( "%s missing 'game' key.", pGameInfoFilename );
  538. // add to drop-down combo and mod list
  539. KeyValues *kv = pGameInfo->MakeCopy();
  540. kv->SetString( "gamedir", pGameDirName );
  541. m_pGameCombo->AddItem( gameName, kv );
  542. kv->deleteThis();
  543. kv = NULL;
  544. }
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Purpose: loads the list of available maps for the given path into the map list
  548. // Returns: number of maps loaded into the list
  549. //-----------------------------------------------------------------------------
  550. int CCreateMultiplayerGameServerPage::LoadMaps( const char *pszMod )
  551. {
  552. // iterate the filesystem getting the list of all the files
  553. // UNDONE: steam wants this done in a special way, need to support that
  554. FileFindHandle_t findHandle = NULL;
  555. char szSearch[256];
  556. sprintf( szSearch, "%s/maps/*.bsp", pszMod );
  557. int iMapsFound = 0;
  558. const char *pszFilename = g_pFullFileSystem->FindFirst( szSearch, &findHandle );
  559. KeyValues *hiddenMaps = NULL;
  560. if ( m_pGameInfo )
  561. {
  562. hiddenMaps = m_pGameInfo->FindKey( "hidden_maps" );
  563. }
  564. while ( pszFilename )
  565. {
  566. // remove the text 'maps/' and '.bsp' from the file name to get the map name
  567. char mapname[256];
  568. const char *str = strstr( pszFilename, "maps" );
  569. if ( str )
  570. {
  571. V_strcpy_safe( mapname, str + 5 ); // maps + \\ = 5
  572. }
  573. else
  574. {
  575. V_strcpy_safe( mapname, pszFilename );
  576. }
  577. char *ext = strstr( mapname, ".bsp" );
  578. if ( ext )
  579. {
  580. *ext = 0;
  581. }
  582. //!! hack: strip out single player HL maps
  583. // this needs to be specified in a seperate file
  584. if ( ( mapname[0] == 'c' || mapname[0] == 't' ) && mapname[2] == 'a' && mapname[1] >= '0' && mapname[1] <= '5' )
  585. {
  586. goto nextFile;
  587. }
  588. // strip out maps that shouldn't be displayed
  589. if ( hiddenMaps )
  590. {
  591. if ( hiddenMaps->GetInt( mapname, 0 ) )
  592. {
  593. goto nextFile;
  594. }
  595. }
  596. iMapsFound++;
  597. // add to the map list
  598. m_pMapList->AddItem( mapname, NULL );
  599. // get the next file
  600. nextFile:
  601. pszFilename = g_pFullFileSystem->FindNext( findHandle );
  602. }
  603. g_pFullFileSystem->FindClose( findHandle );
  604. return iMapsFound;
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose: loads the list of available maps into the map list
  608. //-----------------------------------------------------------------------------
  609. void CCreateMultiplayerGameServerPage::LoadMapList()
  610. {
  611. int iMapsFound = 0;
  612. // clear the current list (if any)
  613. m_pMapList->DeleteAllItems();
  614. Assert( strlen(m_szMod ) > 0);
  615. if ( strlen( m_szMod ) < 1)
  616. {
  617. m_pMapList->SetEnabled( false );
  618. return;
  619. }
  620. m_pMapList->SetEnabled( true );
  621. m_pStartServerButton->SetEnabled( true );
  622. if ( CommandLine()->CheckParm( "-steam" ) )
  623. {
  624. KeyValues *userData = m_pGameCombo->GetActiveItemUserData();
  625. if ( userData && userData->GetString( "DedicatedServerStartMap", NULL ) )
  626. {
  627. // set only
  628. m_pMapList->AddItem( userData->GetString( "DedicatedServerStartMap" ), NULL );
  629. m_pMapList->ActivateItemByRow( 0 );
  630. m_pMapList->SetEnabled( false );
  631. return;
  632. }
  633. }
  634. // Load the maps for the GameDir
  635. iMapsFound += LoadMaps( m_szMod );
  636. // If we're using a "fallback_dir" in liblist.gam then include those maps...
  637. const char *pszFallback = GetLiblistFallbackDir( m_szMod );
  638. if ( pszFallback[0] )
  639. {
  640. iMapsFound += LoadMaps( pszFallback );
  641. }
  642. if ( iMapsFound < 1 )
  643. {
  644. m_pMapList->SetEnabled( false );
  645. }
  646. // set the first item to be selected
  647. m_pMapList->ActivateItemByRow( 0 );
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Purpose: returns the name of the map selected from the map combo
  651. //-----------------------------------------------------------------------------
  652. const char *CCreateMultiplayerGameServerPage::GetMapName()
  653. {
  654. m_pMapList->GetText(m_szMapName, DATA_STR_LENGTH);
  655. return m_szMapName;
  656. }
  657. //-----------------------------------------------------------------------------
  658. // Purpose: data accessor
  659. //-----------------------------------------------------------------------------
  660. const char *CCreateMultiplayerGameServerPage::GetRconPassword()
  661. {
  662. return m_szPassword;
  663. }
  664. //-----------------------------------------------------------------------------
  665. // Purpose: updates "s" with the details of the chosen server to run
  666. //-----------------------------------------------------------------------------
  667. void CCreateMultiplayerGameServerPage::GetServer(serveritem_t &s)
  668. {
  669. s=m_iServer;
  670. V_strcpy_safe(s.name,m_iServer.name);
  671. V_strcpy_safe(s.rconPassword,m_iServer.rconPassword);
  672. memcpy(s.ip,m_iServer.ip,sizeof(m_iServer.ip));
  673. memcpy(s.pings,m_iServer.pings,3*sizeof(int));
  674. V_strcpy_safe(s.gameDir,m_iServer.gameDir);
  675. V_strcpy_safe(s.map,m_iServer.map);
  676. V_strcpy_safe(s.gameDescription,m_iServer.gameDescription);
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: Handles changes to combo boxes
  680. //-----------------------------------------------------------------------------
  681. void CCreateMultiplayerGameServerPage::OnTextChanged(Panel *panel)
  682. {
  683. if (panel == m_pGameCombo)
  684. {
  685. // see if we should update the game name
  686. bool updateHostname = false;
  687. char hostname[256];
  688. GetControlString("ServerNameEdit", m_szHostName, sizeof(m_szHostName));
  689. V_sprintf_safe(hostname, "%s dedicated server", m_szGameName);
  690. if (!stricmp(m_szHostName, hostname))
  691. {
  692. updateHostname = true;
  693. }
  694. // update the game name
  695. m_pGameCombo->GetText( m_szGameName, sizeof(m_szGameName) );
  696. // Copy the gamedir into m_szMod.
  697. KeyValues *gameData = m_pGameCombo->GetActiveItemUserData();
  698. if ( !gameData )
  699. Error( "Missing gameData for active item." );
  700. const char *pGameDir = gameData->GetString( "gamedir", NULL );
  701. if ( !pGameDir )
  702. Error( "Game %s missing 'gamedir' key.", m_szGameName );
  703. V_strcpy_safe( m_szMod, pGameDir );
  704. // re-load the GameInfo KeyValues
  705. if ( m_pGameInfo )
  706. {
  707. m_pGameInfo->deleteThis();
  708. }
  709. char liblist[1024];
  710. Q_snprintf(liblist, sizeof(liblist) - 1, "%s\\gameinfo.txt", m_szMod);
  711. m_pGameInfo = new KeyValues( "GameInfo" );
  712. m_pGameInfo->LoadFromFile( g_pFullFileSystem, liblist );
  713. // redo the hostname with the new game name
  714. if (updateHostname)
  715. {
  716. V_sprintf_safe(hostname, "%s dedicated server", m_szGameName);
  717. SetControlString("ServerNameEdit", hostname);
  718. }
  719. // reload the list of maps we display
  720. LoadMapList();
  721. }
  722. }
  723. #endif // _WIN32