DedicatedServer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "StdAfx.h"
  13. #include <GameMP/Game.h>
  14. #define DECL_DLL
  15. #if 0 /* rcg10042001 Doesn't seem to exist. */
  16. #include <Entities/Global.h>
  17. #endif
  18. // application state variables
  19. extern BOOL _bRunning = TRUE;
  20. static BOOL _bForceRestart = FALSE;
  21. static BOOL _bForceNextMap = FALSE;
  22. extern CTString _strSamVersion = "no version information";
  23. extern INDEX ded_iMaxFPS = 100;
  24. extern CTString ded_strConfig = "";
  25. extern CTString ded_strLevel = "";
  26. extern INDEX ded_bRestartWhenEmpty = TRUE;
  27. extern FLOAT ded_tmTimeout = -1;
  28. extern CGame *_pGame = NULL;
  29. extern CTString sam_strFirstLevel = "Levels\\KarnakDemo.wld";
  30. extern CTString sam_strIntroLevel = "Levels\\Intro.wld";
  31. extern CTString sam_strGameName = "serioussam";
  32. CTimerValue _tvLastLevelEnd(-1i64);
  33. void InitializeGame(void)
  34. {
  35. try {
  36. #ifndef NDEBUG
  37. #define GAMEDLL _fnmApplicationExe.FileDir()+"Game"+_strModExt+"D.dll"
  38. #else
  39. #define GAMEDLL _fnmApplicationExe.FileDir()+"Game"+_strModExt+".dll"
  40. #endif
  41. CTFileName fnmExpanded;
  42. ExpandFilePath(EFP_READ, CTString(GAMEDLL), fnmExpanded);
  43. CPrintF(TRANS("Loading game library '%s'...\n"), (const char *)fnmExpanded);
  44. HMODULE hGame = LoadLibraryA(fnmExpanded);
  45. if (hGame==NULL) {
  46. ThrowF_t("%s", GetWindowsError(GetLastError()));
  47. }
  48. CGame* (*GAME_Create)(void) = (CGame* (*)(void))GetProcAddress(hGame, "GAME_Create");
  49. if (GAME_Create==NULL) {
  50. ThrowF_t("%s", GetWindowsError(GetLastError()));
  51. }
  52. _pGame = GAME_Create();
  53. } catch (char *strError) {
  54. FatalError("%s", strError);
  55. }
  56. // init game - this will load persistent symbols
  57. _pGame->Initialize(CTString("Data\\DedicatedServer.gms"));
  58. }
  59. static void QuitGame(void)
  60. {
  61. _bRunning = FALSE;
  62. }
  63. static void RestartGame(void)
  64. {
  65. _bForceRestart = TRUE;
  66. }
  67. static void NextMap(void)
  68. {
  69. _bForceNextMap = TRUE;
  70. }
  71. void End(void);
  72. // limit current frame rate if neeeded
  73. void LimitFrameRate(void)
  74. {
  75. // measure passed time for each loop
  76. static CTimerValue tvLast(-1.0f);
  77. CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
  78. TIME tmCurrentDelta = (tvNow-tvLast).GetSeconds();
  79. // limit maximum frame rate
  80. ded_iMaxFPS = ClampDn( ded_iMaxFPS, 1L);
  81. TIME tmWantedDelta = 1.0f / ded_iMaxFPS;
  82. if( tmCurrentDelta<tmWantedDelta) Sleep( (tmWantedDelta-tmCurrentDelta)*1000.0f);
  83. // remember new time
  84. tvLast = _pTimer->GetHighPrecisionTimer();
  85. }
  86. // break/close handler
  87. BOOL WINAPI HandlerRoutine(
  88. DWORD dwCtrlType // control signal type
  89. )
  90. {
  91. if (dwCtrlType == CTRL_C_EVENT
  92. || dwCtrlType == CTRL_BREAK_EVENT
  93. || dwCtrlType == CTRL_CLOSE_EVENT
  94. || dwCtrlType == CTRL_LOGOFF_EVENT
  95. || dwCtrlType == CTRL_SHUTDOWN_EVENT) {
  96. _bRunning = FALSE;
  97. }
  98. return TRUE;
  99. }
  100. #define REFRESHTIME (0.1f)
  101. static void LoadingHook_t(CProgressHookInfo *pphi)
  102. {
  103. // measure time since last call
  104. static CTimerValue tvLast(0I64);
  105. CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
  106. if (!_bRunning) {
  107. ThrowF_t(TRANS("User break!"));
  108. }
  109. // if not first or final update, and not enough time passed
  110. if (pphi->phi_fCompleted!=0 && pphi->phi_fCompleted!=1 &&
  111. (tvNow-tvLast).GetSeconds() < REFRESHTIME) {
  112. // do nothing
  113. return;
  114. }
  115. tvLast = tvNow;
  116. // print status text
  117. CTString strRes;
  118. printf("\r ");
  119. printf("\r%s : %3.0f%%\r", pphi->phi_strDescription, pphi->phi_fCompleted*100);
  120. }
  121. // loading hook functions
  122. void EnableLoadingHook(void)
  123. {
  124. printf("\n");
  125. SetProgressHook(LoadingHook_t);
  126. }
  127. void DisableLoadingHook(void)
  128. {
  129. SetProgressHook(NULL);
  130. printf("\n");
  131. }
  132. BOOL StartGame(CTString &strLevel)
  133. {
  134. _pGame->gm_aiStartLocalPlayers[0] = -1;
  135. _pGame->gm_aiStartLocalPlayers[1] = -1;
  136. _pGame->gm_aiStartLocalPlayers[2] = -1;
  137. _pGame->gm_aiStartLocalPlayers[3] = -1;
  138. _pGame->gam_strCustomLevel = strLevel;
  139. _pGame->gm_strNetworkProvider = "TCP/IP Server";
  140. CUniversalSessionProperties sp;
  141. _pGame->SetMultiPlayerSession(sp);
  142. return _pGame->NewGame( _pGame->gam_strSessionName, strLevel, sp);
  143. }
  144. void ExecScript(const CTString &str)
  145. {
  146. CPrintF("Executing: '%s'\n", str);
  147. CTString strCmd;
  148. strCmd.PrintF("include \"%s\"", str);
  149. _pShell->Execute(strCmd);
  150. }
  151. BOOL Init(int argc, char* argv[])
  152. {
  153. _bDedicatedServer = TRUE;
  154. if (argc!=1+1 && argc!=2+1) {
  155. // NOTE: this cannot be translated - translations are not loaded yet
  156. printf("Usage: DedicatedServer <configname> [<modname>]\n"
  157. "This starts a server reading configs from directory 'Scripts\\Dedicated\\<configname>\\'\n");
  158. getch();
  159. exit(0);
  160. }
  161. SetConsoleTitleA(argv[1]);
  162. ded_strConfig = CTString("Scripts\\Dedicated\\")+argv[1]+"\\";
  163. if (argc==2+1) {
  164. _fnmMod = CTString("Mods\\")+argv[2]+"\\";
  165. }
  166. _strLogFile = CTString("Dedicated_")+argv[1];
  167. // initialize engine
  168. SE_InitEngine(sam_strGameName);
  169. // ParseCommandLine(strCmdLine);
  170. // load all translation tables
  171. InitTranslation();
  172. CTFileName fnmTransTable;
  173. try {
  174. fnmTransTable = CTFILENAME("Data\\Translations\\Engine.txt");
  175. AddTranslationTable_t(fnmTransTable);
  176. fnmTransTable = CTFILENAME("Data\\Translations\\Game.txt");
  177. AddTranslationTable_t(fnmTransTable);
  178. fnmTransTable = CTFILENAME("Data\\Translations\\Entities.txt");
  179. AddTranslationTable_t(fnmTransTable);
  180. fnmTransTable = CTFILENAME("Data\\Translations\\SeriousSam.txt");
  181. AddTranslationTable_t(fnmTransTable);
  182. fnmTransTable = CTFILENAME("Data\\Translations\\Levels.txt");
  183. AddTranslationTable_t(fnmTransTable);
  184. FinishTranslationTable();
  185. } catch (char *strError) {
  186. FatalError("%s %s", CTString(fnmTransTable), strError);
  187. }
  188. // always disable all warnings when in serious sam
  189. _pShell->Execute( "con_bNoWarnings=1;");
  190. // declare shell symbols
  191. _pShell->DeclareSymbol("persistent user INDEX ded_iMaxFPS;", &ded_iMaxFPS);
  192. _pShell->DeclareSymbol("user void Quit(void);", &QuitGame);
  193. _pShell->DeclareSymbol("user CTString ded_strLevel;", &ded_strLevel);
  194. _pShell->DeclareSymbol("user FLOAT ded_tmTimeout;", &ded_tmTimeout);
  195. _pShell->DeclareSymbol("user INDEX ded_bRestartWhenEmpty;", &ded_bRestartWhenEmpty);
  196. _pShell->DeclareSymbol("user void Restart(void);", &RestartGame);
  197. _pShell->DeclareSymbol("user void NextMap(void);", &NextMap);
  198. _pShell->DeclareSymbol("persistent user CTString sam_strIntroLevel;", &sam_strIntroLevel);
  199. _pShell->DeclareSymbol("persistent user CTString sam_strGameName;", &sam_strGameName);
  200. _pShell->DeclareSymbol("user CTString sam_strFirstLevel;", &sam_strFirstLevel);
  201. // init game - this will load persistent symbols
  202. InitializeGame();
  203. _pNetwork->md_strGameID = sam_strGameName;
  204. LoadStringVar(CTString("Data\\Var\\Sam_Version.var"), _strSamVersion);
  205. CPrintF(TRANS("Serious Sam version: %s\n"), _strSamVersion);
  206. SetConsoleCtrlHandler(HandlerRoutine, TRUE);
  207. // if there is a mod
  208. if (_fnmMod!="") {
  209. // execute the mod startup script
  210. _pShell->Execute(CTString("include \"Scripts\\Mod_startup.ini\";"));
  211. }
  212. return TRUE;
  213. }
  214. void End(void)
  215. {
  216. // cleanup level-info subsystem
  217. // ClearDemosList();
  218. // end game
  219. _pGame->End();
  220. // end engine
  221. SE_EndEngine();
  222. }
  223. static INDEX iRound = 1;
  224. static BOOL _bHadPlayers = 0;
  225. static BOOL _bRestart = 0;
  226. CTString strBegScript;
  227. CTString strEndScript;
  228. void RoundBegin(void)
  229. {
  230. // repeat generate script names
  231. FOREVER {
  232. strBegScript.PrintF("%s%d_begin.ini", ded_strConfig, iRound);
  233. strEndScript.PrintF("%s%d_end.ini", ded_strConfig, iRound);
  234. // if start script exists
  235. if (FileExists(strBegScript)) {
  236. // stop searching
  237. break;
  238. // if start script doesn't exist
  239. } else {
  240. // if this is first round
  241. if (iRound==1) {
  242. // error
  243. CPrintF(TRANS("No scripts present!\n"));
  244. _bRunning = FALSE;
  245. return;
  246. }
  247. // try again with first round
  248. iRound = 1;
  249. }
  250. }
  251. // run start script
  252. ExecScript(strBegScript);
  253. // start the level specified there
  254. if (ded_strLevel=="") {
  255. CPrintF(TRANS("ERROR: No next level specified!\n"));
  256. _bRunning = FALSE;
  257. } else {
  258. EnableLoadingHook();
  259. StartGame(ded_strLevel);
  260. _bHadPlayers = 0;
  261. _bRestart = 0;
  262. DisableLoadingHook();
  263. _tvLastLevelEnd = CTimerValue(-1i64);
  264. CPrintF(TRANS("\nALL OK: Dedicated server is now running!\n"));
  265. CPrintF(TRANS("Use Ctrl+C to shutdown the server.\n"));
  266. CPrintF(TRANS("DO NOT use the 'Close' button, it might leave the port hanging!\n\n"));
  267. }
  268. }
  269. void ForceNextMap(void)
  270. {
  271. EnableLoadingHook();
  272. StartGame(ded_strLevel);
  273. _bHadPlayers = 0;
  274. _bRestart = 0;
  275. DisableLoadingHook();
  276. _tvLastLevelEnd = CTimerValue(-1i64);
  277. }
  278. void RoundEnd(void)
  279. {
  280. CPrintF("end of round---------------------------\n");
  281. ExecScript(strEndScript);
  282. iRound++;
  283. }
  284. // do the main game loop and render screen
  285. void DoGame(void)
  286. {
  287. // do the main game loop
  288. if( _pGame->gm_bGameOn) {
  289. _pGame->GameMainLoop();
  290. // if any player is connected
  291. if (_pGame->GetPlayersCount()) {
  292. if (!_bHadPlayers) {
  293. // unpause server
  294. if (_pNetwork->IsPaused()) {
  295. _pNetwork->TogglePause();
  296. }
  297. }
  298. // remember that
  299. _bHadPlayers = 1;
  300. // if no player is connected,
  301. } else {
  302. // if was before
  303. if (_bHadPlayers) {
  304. // make it restart
  305. _bRestart = TRUE;
  306. // if never had any player yet
  307. } else {
  308. // keep the server paused
  309. if (!_pNetwork->IsPaused()) {
  310. _pNetwork->TogglePause();
  311. }
  312. }
  313. }
  314. // if game is not started
  315. } else {
  316. // just handle broadcast messages
  317. _pNetwork->GameInactive();
  318. }
  319. // limit current frame rate if needed
  320. LimitFrameRate();
  321. }
  322. int SubMain(int argc, char* argv[])
  323. {
  324. // initialize
  325. if( !Init(argc, argv)) {
  326. End();
  327. return -1;
  328. }
  329. // initialy, application is running
  330. _bRunning = TRUE;
  331. // execute dedicated server startup script
  332. ExecScript(CTFILENAME("Scripts\\Dedicated_startup.ini"));
  333. // execute startup script for this config
  334. ExecScript(ded_strConfig+"init.ini");
  335. // start first round
  336. RoundBegin();
  337. // while it is still running
  338. while( _bRunning)
  339. {
  340. // do the main game loop
  341. DoGame();
  342. // if game is finished
  343. if (_pNetwork->IsGameFinished()) {
  344. // if not yet remembered end of level
  345. if (_tvLastLevelEnd.tv_llValue<0) {
  346. // remember end of level
  347. _tvLastLevelEnd = _pTimer->GetHighPrecisionTimer();
  348. // finish this round
  349. RoundEnd();
  350. // if already remembered
  351. } else {
  352. // if time is out
  353. if ((_pTimer->GetHighPrecisionTimer()-_tvLastLevelEnd).GetSeconds()>ded_tmTimeout) {
  354. // start next round
  355. RoundBegin();
  356. }
  357. }
  358. }
  359. if (_bRestart||_bForceRestart) {
  360. if (ded_bRestartWhenEmpty||_bForceRestart) {
  361. _bForceRestart = FALSE;
  362. _bRestart = FALSE;
  363. RoundEnd();
  364. CPrintF(TRANS("\nNOTE: Restarting server!\n\n"));
  365. RoundBegin();
  366. } else {
  367. _bRestart = FALSE;
  368. _bHadPlayers = FALSE;
  369. }
  370. }
  371. if (_bForceNextMap) {
  372. ForceNextMap();
  373. _bForceNextMap = FALSE;
  374. }
  375. } // end of main application loop
  376. _pGame->StopGame();
  377. End();
  378. return 0;
  379. }
  380. int main(int argc, char* argv[])
  381. {
  382. int iResult;
  383. CTSTREAM_BEGIN {
  384. iResult = SubMain(argc, argv);
  385. } CTSTREAM_END;
  386. return iResult;
  387. }