Counter Strike : Global Offensive Source Code
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.

876 lines
25 KiB

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