OpenClonk
C4Application.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Main class to initialize configuration and execute the game */
19 
20 #include "C4Include.h"
22 #include "game/C4Application.h"
23 
24 #include "C4Version.h"
25 #include "editor/C4Console.h"
26 #include "game/C4FullScreen.h"
27 #include "game/C4GraphicsSystem.h"
28 #include "graphics/C4Draw.h"
30 #include "graphics/StdPNG.h"
31 #include "gui/C4GameLobby.h"
32 #include "gui/C4GfxErrorDlg.h"
33 #include "gui/C4MessageInput.h"
34 #ifdef _WIN32
35 #include "gui/C4UpdateDlg.h"
36 #endif
37 #include "gui/C4Startup.h"
38 #include "landscape/C4Particles.h"
39 #include "network/C4Network2.h"
40 #include "network/C4Network2IRC.h"
41 #include "platform/C4GamePadCon.h"
42 #include "C4Licenses.h"
43 
44 #include <getopt.h>
45 
46 static C4Network2IRCClient ApplicationIRCClient;
47 const std::string C4Application::Revision{ C4REVISION };
48 
50  IRCClient(ApplicationIRCClient)
51 {
52 }
53 
55 {
56  // clear gamepad
57  if (pGamePadControl) delete pGamePadControl;
58  // Close log
59  CloseLog();
60 }
61 
62 bool C4Application::DoInit(int argc, char * argv[])
63 {
64  assert(AppState == C4AS_None);
65  // Config overwrite by parameter
66  StdStrBuf sConfigFilename;
67  for (int32_t iPar=0; iPar < argc; iPar++)
68  if (SEqual2NoCase(argv[iPar], "--config="))
69  sConfigFilename.Copy(argv[iPar] + 9);
70  // Config check
71  Config.Init();
72  Config.Load(sConfigFilename.getData());
73  Config.Save();
74  // sometimes, the configuration can become corrupted due to loading errors or w/e
75  // check this and reset defaults if necessary
76  if (Config.IsCorrupted())
77  {
78  if (sConfigFilename)
79  {
80  // custom config corrupted: Fail
81  Log("ERROR: Custom configuration corrupted - program abort!\n");
82  return false;
83  }
84  else
85  {
86  // default config corrupted: Restore default
87  Log("Warning: Configuration corrupted - restoring default!\n");
88  Config.Default();
89  Config.Save();
90  Config.Load();
91  }
92  }
93  // Open log
94  OpenLog();
95 
96  // Engine header message
97  Log(C4ENGINECAPTION);
98  LogF("Version: %s %s (%s - %s)", C4VERSION, C4_OS, GetRevision(), C4REVISION_TS);
99  LogF(R"(ExePath: "%s")", Config.General.ExePath.getData());
100  LogF(R"(SystemDataPath: "%s")", Config.General.SystemDataPath);
101  LogF(R"(UserDataPath: "%s")", Config.General.UserDataPath);
102 
103  // Init C4Group
107 
108  // Cleanup temp folders left behind
110 
111  // Initialize game data paths
112  Reloc.Init();
113 
114  // init system group
116  {
117  // Error opening system group - no LogFatal, because it needs language table.
118  // This will *not* use the FatalErrors stack, but this will cause the game
119  // to instantly halt, anyway.
120  const char *szMessage = "Error opening system group file (System.ocg)!";
121  Log(szMessage);
122  // Fatal error, game cannot start - have player notice
123  MessageDialog(szMessage);
124  return false;
125  }
126  // Parse command line
127  ParseCommandLine(argc, argv);
128 
129  // Open additional logs that depend on command line
130  OpenExtraLogs();
131 
132  // Init external language packs
133  Languages.Init();
134  // Load language string table
136  // No language table was loaded - bad luck...
137  if (!Languages.HasStringTable())
138  Log("WARNING: No language string table loaded!");
139 
140 #if defined(WIN32) && defined(WITH_AUTOMATIC_UPDATE)
141  // Windows: handle incoming updates directly, even before starting up the gui
142  // because updates will be applied in the console anyway.
143  if (!Application.IncomingUpdate.empty())
144  if (C4UpdateDlg::ApplyUpdate(Application.IncomingUpdate.c_str(), false, nullptr))
145  return true;
146 #endif
147 
148  // Fixup resolution
149  if (!Config.Graphics.Windowed)
151 
152  // activate
153  Active=true;
154 
155  // Init carrier window
156  if (!isEditor)
157  {
158  if (!(pWindow = FullScreen.Init(this)))
159  { Clear(); ShowGfxErrorDialog(); return false; }
160  }
161  else
162  {
163  if (!(pWindow = Console.Init(this)))
164  { Clear(); return false; }
165  }
166 
167  // init timers (needs window)
169 
170  // Initialize OpenGL
171  bool success = DDrawInit(this, GetConfigWidth(), GetConfigHeight(), Config.Graphics.Monitor);
172  if (!success) { LogFatal(LoadResStr("IDS_ERR_DDRAW")); Clear(); ShowGfxErrorDialog(); return false; }
173 
174  if (!isEditor)
175  {
178  }
179 
180  // Initialize gamepad
183 
184  AppState = C4AS_PreInit;
185 
186  return true;
187 }
188 
190 {
191  *Game.PlayerFilenames = 0;
192 }
193 
194 void C4Application::ParseCommandLine(int argc, char * argv[])
195 {
196  argv0 = argv[0];
197  StdStrBuf CmdLine("Command line:");
198  for(int i = 0; i < argc; ++i) {
199  CmdLine.Append(" ");
200  CmdLine.Append(argv[i]);
201  }
202  Log(CmdLine.getData());
203 
205  Game.NetworkActive = false;
206  isEditor = 2;
207  int c;
208  while (true)
209  {
210 
211  static struct option long_options[] =
212  {
213  // option, w/ argument?, set directly, set to...
214  {"editor", no_argument, &isEditor, 1},
215  {"fullscreen", no_argument, &isEditor, 0},
216  {"debugwait", no_argument, &Game.DebugWait, 1},
217  {"update", no_argument, &CheckForUpdates, 1},
218  {"noruntimejoin", no_argument, &Config.Network.NoRuntimeJoin, 1},
219  {"runtimejoin", no_argument, &Config.Network.NoRuntimeJoin, 0},
220  {"noleague", no_argument, &Config.Network.LeagueServerSignUp, 0},
221  {"league", no_argument, &Config.Network.LeagueServerSignUp, 1},
222  {"nosignup", no_argument, &Config.Network.MasterServerSignUp, 0},
223  {"signup", no_argument, &Config.Network.MasterServerSignUp, 1},
224 
225  {"debugrecread", required_argument, nullptr, 'K'},
226  {"debugrecwrite", required_argument, nullptr, 'w'},
227 
228  {"client", required_argument, nullptr, 'c'},
229  {"host", no_argument, nullptr, 'h'},
230  {"debughost", required_argument, nullptr, 'H'},
231  {"debugpass", required_argument, nullptr, 'P'},
232  {"debug", required_argument, nullptr, 'D'},
233  {"data", required_argument, nullptr, 'd'},
234  {"startup", required_argument, nullptr, 's'},
235  {"stream", required_argument, nullptr, 'e'},
236  {"recdump", required_argument, nullptr, 'R'},
237  {"comment", required_argument, nullptr, 'm'},
238  {"pass", required_argument, nullptr, 'p'},
239  {"udpport", required_argument, nullptr, 'u'},
240  {"tcpport", required_argument, nullptr, 't'},
241  {"join", required_argument, nullptr, 'j'},
242  {"language", required_argument, nullptr, 'L'},
243  {"scenpar", required_argument, nullptr, 'S'},
244 
245  {"observe", no_argument, nullptr, 'o'},
246  {"nonetwork", no_argument, nullptr, 'N'},
247  {"network", no_argument, nullptr, 'n'},
248  {"record", no_argument, nullptr, 'r'},
249 
250  {"lobby", optional_argument, nullptr, 'l'},
251 
252  {"debug-opengl", no_argument, &Config.Graphics.DebugOpenGL, 1},
253  {"config", required_argument, nullptr, 0},
254  {"show-licenses", no_argument, nullptr, 0},
255  {nullptr, 0, nullptr, 0}
256  };
257  int option_index = 0;
258  c = getopt_long (argc, argv, "abc:d:f:",
259  long_options, &option_index);
260  // no more options
261  if (c == -1)
262  break;
263  switch (c)
264  {
265  case 0:
266  // Signup
267  if (SEqualNoCase(long_options[option_index].name, "signup"))
268  {
269  Game.NetworkActive = true;
270  }
271  // League
272  if (SEqualNoCase(long_options[option_index].name, "league"))
273  {
274  Game.NetworkActive = true;
276  }
277  // Legal stuff
278  if (SEqualNoCase(long_options[option_index].name, "show-licenses"))
279  {
280  std::string sep{"\n=================================\n"};
281  for (const auto& license : OCLicenses)
282  Log((sep + license.path + ": " + license.name + sep + license.content + "\n").c_str());
283  Quit();
284  }
285  // Config: Already handled earlier.
286  break;
287  // Lobby
288  case 'l':
289  Game.fLobby = true;
290  // lobby timeout specified? (e.g. --lobby=120)
291  if (optarg)
292  {
293  Game.iLobbyTimeout = atoi(optarg);
294  if (Game.iLobbyTimeout < 0) Game.iLobbyTimeout = 0;
295  }
296  break;
297  case 'o': Game.fObserve = true; break;
298  // Direct join
299  case 'j':
300  Game.NetworkActive = true;
302  break;
303  case 'K':
304  if (optarg && optarg[0])
305  {
306  LogF("Reading from DebugRec file '%s'", optarg);
308  }
309  else
310  Log("Reading DebugRec from CtrlRec file in scenario record");
311  Config.General.DebugRec = 1;
313  break;
314  case 'w':
315  if (optarg && optarg[0])
316  {
317  LogF("Writing to DebugRec file '%s'", optarg);
319  }
320  else
321  Log("Writing DebugRec to CtrlRec file in scenario record");
322  Config.General.DebugRec = 1;
324  break;
325  case 'r': Game.Record = true; break;
326  case 'n': Game.NetworkActive = true; break;
327  case 'N': Game.NetworkActive = false; break;
328  // Language override by parameter
329  case 'L': SCopy(optarg, Config.General.LanguageEx, CFG_MaxString); break;
330  // port overrides
331  case 't': Config.Network.PortTCP = atoi(optarg); break;
332  case 'u': Config.Network.PortUDP = atoi(optarg); break;
333  // network game password
334  case 'p': Network.SetPassword(optarg); break;
335  // network game comment
336  case 'm': Config.Network.Comment.CopyValidated(optarg); break;
337  // record dump
338  case 'R': Game.RecordDumpFile.Copy(optarg); break;
339  // record stream
340  case 'e': Game.RecordStream.Copy(optarg); break;
341  // startup start screen
342  case 's': C4Startup::SetStartScreen(optarg); break;
343  // additional read-only data path
344  case 'd': Reloc.AddPath(optarg); break;
345  // debug options
346  case 'D': Game.DebugPort = atoi(optarg); break;
347  case 'P': Game.DebugPassword = optarg; break;
348  case 'H': Game.DebugHost = optarg; break;
349  // set custom scenario parameter by command line
350  case 'S':
351  {
352  StdStrBuf sopt, soptval; sopt.Copy(optarg);
353  int32_t val=1;
354  if (sopt.SplitAtChar('=', &soptval)) val=atoi(soptval.getData());
355  Game.StartupScenarioParameters.SetValue(sopt.getData(), val, false);
356  }
357  break;
358  // debug configs
359  case 'h':
360  Game.NetworkActive = true;
361  Game.fLobby = true;
362  Config.Network.PortTCP = 11112;
363  Config.Network.PortUDP = 11113;
365  break;
366  case 'c':
367  Game.NetworkActive = true;
368  SCopy("localhost", Game.DirectJoinAddress, _MAX_PATH);
369  Game.fLobby = true;
370  Config.Network.PortTCP = 11112 + 2*(atoi(optarg)+1);
371  Config.Network.PortUDP = 11113 + 2*(atoi(optarg)+1);
372  break;
373  case '?': /* getopt_long already printed an error message. */ break;
374  default: assert(!"unexpected getopt_long return value");
375  }
376  }
379  if (Game.fObserve || Game.fLobby)
380  Game.NetworkActive = true;
381 
382  while (optind < argc)
383  {
384  char * szParameter = argv[optind++];
385  { // Strip trailing / that result from tab-completing unpacked c4groups
386  int iLen = SLen(szParameter);
387  if (iLen > 5 && szParameter[iLen-1] == '/' && szParameter[iLen-5] == '.' && szParameter[iLen-4] == 'o' && szParameter[iLen-3] == 'c')
388  {
389  szParameter[iLen-1] = '\0';
390  }
391  }
392  // Scenario file
393  if (SEqualNoCase(GetExtension(szParameter),"ocs"))
394  {
395  if(IsGlobalPath(szParameter))
396  Game.SetScenarioFilename(szParameter);
397  else
398  Game.SetScenarioFilename((std::string(GetWorkingDirectory()) + DirSep + szParameter).c_str());
399 
400  continue;
401  }
402  if (SEqualNoCase(GetFilename(szParameter),"scenario.txt"))
403  {
404  Game.SetScenarioFilename(szParameter);
405  continue;
406  }
407  // Player file
408  if (SEqualNoCase(GetExtension(szParameter),"ocp"))
409  {
410  if(IsGlobalPath(szParameter))
411  SAddModule(Game.PlayerFilenames, szParameter);
412  else
413  SAddModule(Game.PlayerFilenames, (std::string(GetWorkingDirectory()) + DirSep + szParameter).c_str());
414 
415  continue;
416  }
417  // Definition file
418  if (SEqualNoCase(GetExtension(szParameter),"ocd"))
419  {
420  SAddModule(Game.DefinitionFilenames,szParameter);
421  continue;
422  }
423  // Key file
424  if (SEqualNoCase(GetExtension(szParameter),"c4k"))
425  {
426  Application.IncomingKeyfile = szParameter;
427  continue;
428  }
429  // Update file
430  if (SEqualNoCase(GetExtension(szParameter),"ocu"))
431  {
432  Application.IncomingUpdate = szParameter;
433  continue;
434  }
435  // record stream
436  if (SEqualNoCase(GetExtension(szParameter),"c4r"))
437  {
438  Game.RecordStream.Copy(szParameter);
439  }
440  // Direct join by URL
441  if (SEqual2NoCase(szParameter, "clonk:") || SEqual2NoCase(szParameter, "openclonk:"))
442  {
443  // Store address
444  SCopy(SAdvancePast(szParameter, ':'), Game.DirectJoinAddress, _MAX_PATH);
446  // Special case: if the target address is "update" then this is used for update initiation by url
447  if (SEqualNoCase(Game.DirectJoinAddress, "update"))
448  {
450  Game.DirectJoinAddress[0] = 0;
451  continue;
452  }
453  // Special case: start the mod dialog and initiate installation of a mod.
454  const char* install_mod_command = "installmod";
455  const auto install_mod_command_length = strlen(install_mod_command);
456  if (SEqualNoCase(Game.DirectJoinAddress, install_mod_command, install_mod_command_length))
457  {
458  // Advance the string to the parameter after the command.
459  const char *id = Game.DirectJoinAddress + install_mod_command_length;
460  if (SLen(id) > 1)
461  {
462  ++id; // Remove slash.
463  C4Startup::SetStartScreen("mods", id);
464  }
465  Game.DirectJoinAddress[0] = 0;
466  continue;
467  }
468  // Self-enable network
469  Game.NetworkActive = true;
470  continue;
471  }
472  }
473 
474 #ifdef _WIN32
475  // Clean up some forward/backward slach confusion since many internal OC file functions cannot handle both
482 #endif
483 
484  // Default to editor if scenario given, player mode otherwise
485  if (isEditor == 2)
487 
488  // record?
490 
491  // startup dialog required?
493 }
494 
496 {
497  // Not changing the resolution always works anyway
498  if (Config.Graphics.ResX == -1 && Config.Graphics.ResY == -1)
499  return;
500  // Enumerate display modes
501  int32_t idx = -1, iXRes, iYRes, iBitDepth, iRefreshRate;
502  int32_t best_match = -1;
503  uint32_t best_delta = ~0;
504  while (GetIndexedDisplayMode(++idx, &iXRes, &iYRes, &iBitDepth, &iRefreshRate, Config.Graphics.Monitor))
505  {
506  if (iBitDepth != C4Draw::COLOR_DEPTH) continue;
507  uint32_t delta = std::abs(Config.Graphics.ResX*Config.Graphics.ResY - iXRes*iYRes);
508  if (!delta && iBitDepth == C4Draw::COLOR_DEPTH && iRefreshRate == Config.Graphics.RefreshRate)
509  return; // Exactly the expected mode
510  if (delta < best_delta)
511  {
512  // Better match than before
513  best_match = idx;
514  best_delta = delta;
515  }
516  }
517  if (best_match != -1)
518  {
519  // Apply next-best mode
520  GetIndexedDisplayMode(best_match, &iXRes, &iYRes, &iBitDepth, &iRefreshRate, Config.Graphics.Monitor);
521  if (iXRes != Config.Graphics.ResX || iYRes != Config.Graphics.ResY)
522  // Don't warn if only bit depth changes
523  // Also, lang table not loaded yet
524  LogF("Warning: The selected resolution %dx%d is not available and has been changed to %dx%d.", Config.Graphics.ResX, Config.Graphics.ResY, iXRes, iYRes);
525  Config.Graphics.ResX = iXRes; Config.Graphics.ResY = iYRes;
526  Config.Graphics.RefreshRate = iRefreshRate;
527  }
528 }
529 
531 {
532  // startup dialog: Only use if no next mission has been provided
533  bool fUseStartupDialog = !Game.HasScenario();
534 
535  // Load graphics early, before we draw anything, since we need shaders
536  // loaded to draw.
537  Game.SetInitProgress(0.0f);
538  Log(LoadResStr("IDS_PRC_GFXRES"));
539  if (!GraphicsResource.Init()) return false;
540  Game.SetInitProgress(fUseStartupDialog ? 10.0f : 1.0f);
541 
542  // Startup message board
543  if (!isEditor)
545  {
547  GraphicsSystem.MessageBoard->Init(cgo,true);
548  }
549 
550  // init loader: Black screen for first start if a video is to be shown; otherwise default spec
551  if (fUseStartupDialog && !isEditor)
552  {
554  { LogFatal(LoadResStr("IDS_PRC_ERRLOADER")); return false; }
555  }
556  Game.SetInitProgress(fUseStartupDialog ? 20.0f : 2.0f);
557 
558  if (!Game.PreInit()) return false;
559 
560  // Music
561  if (!MusicSystem.Init("frontend"))
562  Log(LoadResStr("IDS_PRC_NOMUSIC"));
563 
564  Game.SetInitProgress(fUseStartupDialog ? 34.0f : 2.0f);
565 
566  // Sound
567  if (!SoundSystem.Init())
568  Log(LoadResStr("IDS_PRC_NOSND"));
569 
570  // Play some music! - after sound init because sound system might be needed by music system
571  if (fUseStartupDialog && !isEditor && Config.Sound.FEMusic)
572  MusicSystem.Play();
573 
574  Game.SetInitProgress(fUseStartupDialog ? 35.0f : 3.0f);
575 
576  if (fUseStartupDialog)
577  {
578  AppState = C4AS_Startup;
579  // default record?
581  // if no scenario or direct join has been specified, get game startup parameters by startup dialog
582  if (!isEditor)
584  }
585  // directly launch scenario / network game
586  else
587  {
588  AppState = C4AS_StartGame;
589  }
590 
591  return true;
592 }
593 
594 bool C4Application::ProcessCallback(const char *szMessage, int iProcess)
595 {
596  Console.Out(szMessage);
597  return true;
598 }
599 
601 {
602  Game.Clear();
603  NextMission.clear();
604  // stop timer
605  if (pGameTimer)
606  {
608  delete pGameTimer; pGameTimer = nullptr;
609  }
610  // quit irc
611  IRCClient.Close();
612  // close system group (System.ocg)
613  SystemGroup.Close();
614  // Log
615  if (::Languages.HasStringTable()) // Avoid (double and undefined) message on (second?) shutdown...
616  Log(LoadResStr("IDS_PRC_DEINIT"));
617  // Clear external language packs and string table
618  Languages.Clear();
620  // gamepad clear
621  if (pGamePadControl) { delete pGamePadControl; pGamePadControl=nullptr; }
622  // music system clear
623  MusicSystem.Clear();
624  SoundSystem.Clear();
626  // clear editcursor holding graphics before clearing draw
628  // Clear direct draw (late, because it's needed for e.g. Log)
629  if (pDraw) { delete pDraw; pDraw=nullptr; }
630  // Close window
631  FullScreen.Clear();
632  Console.Clear();
633  // There might be pending saves - do them after the fullscreen windows got closed
634  // so the app just remains as a lingering process until saving is done
636  // The very final stuff
638 }
639 
641 {
642  // Participants should not be cleared for usual startup dialog
643 
644  // Save config if there was no loading error
646  // make sure startup data is unloaded
648  // fonts are loaded at start and never unloaded
650  // quit app
652  AppState = C4AS_Quit;
653 }
654 
655 void C4Application::OpenGame(const char * scenario)
656 {
657  if (AppState == C4AS_Startup)
658  {
659  if (scenario) Game.SetScenarioFilename(scenario);
660  AppState = C4AS_StartGame;
661  }
662  else
663  {
664  SetNextMission(scenario);
665  AppState = C4AS_AfterGame;
666  }
667 
668 }
669 
671 {
672  // reinit desired? Do restart
673  if (!QuitAfterGame || !NextMission.empty())
674  {
675  AppState = C4AS_AfterGame;
676  }
677  else
678  {
679  Quit();
680  }
681 }
682 
684 {
685  // Exec depending on game state
686  switch (AppState)
687  {
688  case C4AS_None:
689  assert(AppState != C4AS_None);
690  break;
691  case C4AS_Quit:
692  // Do nothing, the main loop will exit soon
693  break;
694  case C4AS_PreInit:
695  if (!PreInit()) Quit();
696  break;
697  case C4AS_Startup:
701  // wait for the user to start a game
702  break;
703  case C4AS_StartGame:
704  // immediate progress to next state; OpenGame will enter HandleMessage-loops in startup and lobby!
706  AppState = C4AS_Game;
707 #ifdef WITH_QT_EDITOR
708  // Notify console
710 #endif
711  // first-time game initialization
712  if (!Game.Init())
713  {
714  // set error flag (unless this was a lobby user abort)
716  Game.fQuitWithError = true;
717  // no start: Regular QuitGame; this may reset the engine to startup mode if desired
718  QuitGame();
719  break;
720  }
721  if(Config.Graphics.Windowed == 2 && FullScreenMode())
723  if (!isEditor)
724  pWindow->GrabMouse(true);
725  // Gamepad events have to be polled here so that the controller
726  // connection state is always up-to-date before players are
727  // joining.
729  break;
730  case C4AS_AfterGame:
731  // stop game
732  Game.Clear();
733  if(Config.Graphics.Windowed == 2 && NextMission.empty() && !isEditor)
735  if (!isEditor)
736  pWindow->GrabMouse(false);
737  AppState = C4AS_PreInit;
738  // if a next mission is desired, set to start it
739  if (!NextMission.empty())
740  {
741  Game.SetScenarioFilename(NextMission.c_str());
743  Game.fObserve = false;
744  NextMission.clear();
745  }
746  break;
747  case C4AS_Game:
748  // Game
749  if (Game.IsRunning)
750  Game.Execute();
751  // Sound
754  // Gamepad
756  break;
757  }
758 }
759 
761 {
762  // Graphics
763 
764  // Fullscreen mode
765  if (!isEditor)
767  // Console mode
768  else
769  Console.Execute();
770 }
771 
773 {
774  if (!pGameTimer) return;
775  pGameTimer->SetGameTickDelay(iDelay);
776 }
777 
778 void C4Application::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
779 {
780  // notify game
781  if (pDraw)
782  {
783  Game.OnResolutionChanged(iXRes, iYRes);
784  pDraw->OnResolutionChanged(iXRes, iYRes);
785  }
786  if (pWindow)
787  {
788  if (pWindow->pSurface)
789  pWindow->pSurface->UpdateSize(iXRes, iYRes);
790  if (!FullScreenMode())
791  {
792  C4Rect r;
793  pWindow->GetSize(&r);
796  }
797  }
798 }
799 
801 {
802  // re-resolve all keys
805 }
806 
807 bool C4Application::SetGameFont(const char *szFontFace, int32_t iFontSize)
808 {
809 #ifndef USE_CONSOLE
810  // safety
811  if (!szFontFace || !*szFontFace || iFontSize<1 || SLen(szFontFace)>=static_cast<int>(sizeof Config.General.RXFontName)) return false;
812  // first, check if the selected font can be created at all
813  // check regular font only - there's no reason why the other fonts couldn't be created
814  CStdFont TestFont;
815  if (!::FontLoader.InitFont(&TestFont, szFontFace, C4FontLoader::C4FT_Main, iFontSize, &::GraphicsResource.Files))
816  return false;
817  // OK; reinit all fonts
818  StdStrBuf sOldFont; sOldFont.Copy(Config.General.RXFontName);
819  int32_t iOldFontSize = Config.General.RXFontSize;
820  SCopy(szFontFace, Config.General.RXFontName);
821  Config.General.RXFontSize = iFontSize;
823  {
824  // failed :o
825  // shouldn't happen. Better restore config.
826  SCopy(sOldFont.getData(), Config.General.RXFontName);
827  Config.General.RXFontSize = iOldFontSize;
828  return false;
829  }
830 #endif
831  // save changes
832  return true;
833 }
834 
835 void C4Application::OnCommand(const char *szCmd)
836 {
837  if (AppState == C4AS_Game)
839  else if (AppState == C4AS_Startup)
840  {
841  AppState = C4AS_PreInit;
842  Game.SetScenarioFilename(szCmd);
843  }
844 }
845 
847 {
848 #ifdef USE_WIN32_WINDOWS
849  BringWindowToTop(FullScreen.hWindow);
850  ShowWindow(FullScreen.hWindow, SW_SHOW);
851 #endif
852 }
853 
854 void C4Application::SetNextMission(const char *szMissionFilename)
855 {
856  // set next mission if any is desired
857  if (szMissionFilename)
858  {
859  NextMission = szMissionFilename;
860  // scenarios tend to use the wrong slash
861  std::replace(begin(NextMission), end(NextMission), AltDirectorySeparator, DirectorySeparator);
862  }
863  else
864  NextMission.clear();
865 }
866 
868 {
869  if (!pGameTimer) return;
870  pGameTimer->Set();
871 }
872 
874 {
875  if(isEditor)
876  return false;
878  return true;
880  return true;
881  return false;
882 }
883 
884 // *** C4ApplicationGameTimer
885 
888  tLastGameTick(C4TimeMilliseconds::NegativeInfinity)
889 {
890 }
891 
893 {
894  // Remember delay
895  iGameTickDelay = iDelay;
896  // Smaller than minimum refresh delay?
897  if (iDelay < uint32_t(Config.Graphics.MaxRefreshDelay))
898  {
899  // Set critical timer
900  SetDelay(iDelay);
901  // No additional breaking needed
902  iExtraGameTickDelay = 0;
903  }
904  else
905  {
906  // Set critical timer
907  SetDelay(Config.Graphics.MaxRefreshDelay);
908  // Slow down game tick
909  iExtraGameTickDelay = iDelay;
910  }
911 }
912 
913 bool C4ApplicationGameTimer::Execute(int iTimeout, pollfd *)
914 {
915  // Check timer and reset
916  if (!CheckAndReset()) return true;
918  // Execute
919  if (tNow >= tLastGameTick + iExtraGameTickDelay || Game.GameGo)
920  {
921  if (iGameTickDelay)
922  tLastGameTick += iGameTickDelay;
923  else
924  tLastGameTick = tNow;
925 
926  // Compensate if things get too slow
927  if (tNow > tLastGameTick + iGameTickDelay)
928  tLastGameTick += (tNow - tLastGameTick) / 2;
929 
931  }
932  // Draw
933  if (!Game.DoSkipFrame)
934  {
936 
937  Application.Draw();
938 
939  // Automatic frame skip if graphics are slowing down the game (skip max. every 2nd frame)
940  Game.DoSkipFrame = Game.Parameters.AutoFrameSkip && (tPreGfxTime + iGameTickDelay < C4TimeMilliseconds::Now());
941  } else {
942  Game.DoSkipFrame=false;
943  }
944  return true;
945 }
946 
#define C4CFN_System
Definition: C4Components.h:29
C4Config Config
Definition: C4Config.cpp:930
@ CFG_MaxString
Definition: C4Config.h:28
bool DDrawInit(C4AbstractApp *pApp, unsigned int iXRes, unsigned int iYRes, unsigned int iMonitor)
Definition: C4Draw.cpp:790
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4FontLoader FontLoader
void ShowGfxErrorDialog()
C4Game Game
Definition: C4Globals.cpp:52
C4Console Console
Definition: C4Globals.cpp:45
C4Application Application
Definition: C4Globals.cpp:44
C4Network2 Network
Definition: C4Globals.cpp:53
C4FullScreen FullScreen
Definition: C4Globals.cpp:46
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
C4GraphicsResource GraphicsResource
void C4Group_SetTempPath(const char *path)
Definition: C4Group.cpp:74
const char * C4CFN_FLS[]
Definition: C4Group.cpp:32
void C4Group_SetSortList(const char **sort_list)
Definition: C4Group.cpp:69
void C4Group_SetProcessCallback(bool(*callback)(const char *, int))
Definition: C4Group.cpp:64
C4Language Languages
Definition: C4Language.cpp:42
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
const std::vector< OCLicenseInfo > OCLicenses
bool OpenExtraLogs()
Definition: C4Log.cpp:83
bool OpenLog()
Definition: C4Log.cpp:48
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
bool CloseLog()
Definition: C4Log.cpp:107
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:239
C4MessageInput MessageInput
C4Reloc Reloc
Definition: C4Reloc.cpp:21
#define C4CFN_StartupBackgroundMain
Definition: C4Startup.h:23
#define DirectorySeparator
#define C4_OS
#define AltDirectorySeparator
#define _MAX_PATH
#define DirSep
void SReplaceChar(char *str, char fc, char tc)
Definition: Standard.cpp:354
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
int SClearFrontBack(char *szString, char cClear)
Definition: Standard.cpp:461
bool SAddModule(char *szList, const char *szModule, bool fCaseSensitive)
Definition: Standard.cpp:563
bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:226
const char * SAdvancePast(const char *szSPos, char cPast)
Definition: Standard.cpp:438
size_t SLen(const char *sptr)
Definition: Standard.h:74
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:118
char * GetFilename(char *szPath)
Definition: StdFile.cpp:42
bool IsGlobalPath(const char *szPath)
Definition: StdFile.cpp:224
const char * GetWorkingDirectory()
Definition: StdFile.cpp:627
#define CStdMultimediaTimerProc
Definition: StdScheduler.h:216
void MessageDialog(const char *message)
Definition: C4AppMac.mm:63
bool SetVideoMode(int iXRes, int iYRes, unsigned int iRefreshRate, unsigned int iMonitor, bool fFullScreen)
Definition: C4AppSDL.cpp:354
virtual void Quit()
Definition: C4AppSDL.cpp:110
bool GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
Definition: C4AppSDL.cpp:335
bool Active
Definition: C4App.h:63
void RestoreVideoMode()
Definition: C4AppSDL.cpp:443
C4Window * pWindow
Definition: C4App.h:80
virtual void Clear()
Definition: C4AppSDL.cpp:105
bool Execute(int iTimeout, pollfd *) override
bool IsLowPriority() override
void SetGameTickDelay(uint32_t iDelay)
std::string IncomingUpdate
Definition: C4Application.h:78
static bool ProcessCallback(const char *szMessage, int iProcess)
const char * argv0
Definition: C4Application.h:75
C4Group SystemGroup
Definition: C4Application.h:40
void OnKeyboardLayoutChanged() override
bool DoInit(int argc, char *argv[]) override
void ApplyResolutionConstraints()
C4MusicSystem MusicSystem
Definition: C4Application.h:41
void OpenGame(const char *scenario=nullptr)
C4ApplicationGameTimer * pGameTimer
Definition: C4Application.h:88
enum C4Application::State C4AS_None
std::string IncomingKeyfile
Definition: C4Application.h:99
C4GamePadControl * pGamePadControl
Definition: C4Application.h:43
void Clear() override
void SetNextMission(const char *szMissionFilename)
void ClearCommandLine()
void SetGameTickDelay(int iDelay)
int GetConfigWidth()
Definition: C4Application.h:83
int GetConfigHeight()
Definition: C4Application.h:84
void OnResolutionChanged(unsigned int iXRes, unsigned int iYRes) override
C4Network2IRCClient & IRCClient
Definition: C4Application.h:47
void Quit() override
C4SoundSystem SoundSystem
Definition: C4Application.h:42
void ParseCommandLine(int argc, char *argv[])
void OnCommand(const char *szCmd) override
bool SetGameFont(const char *szFontFace, int32_t iFontSize)
const char * GetRevision() const
Definition: C4Application.h:72
~C4Application() override
StdCopyStrBuf ExePath
Definition: C4Config.h:54
char SystemDataPath[CFG_MaxString+1]
Definition: C4Config.h:58
StdCopyStrBuf TempPath
Definition: C4Config.h:55
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:38
char DebugRecExternalFile[_MAX_PATH_LEN]
Definition: C4Config.h:65
bool GamepadEnabled
Definition: C4Config.h:61
char UserDataPath[CFG_MaxString+1]
Definition: C4Config.h:56
int32_t DefRec
Definition: C4Config.h:49
int32_t DebugRecWrite
Definition: C4Config.h:64
int32_t OpenScenarioInGameMode
Definition: C4Config.h:41
int32_t DebugRec
Definition: C4Config.h:63
char RXFontName[CFG_MaxString+1]
Definition: C4Config.h:42
int32_t RXFontSize
Definition: C4Config.h:43
int32_t ResX
Definition: C4Config.h:104
int32_t ResY
Definition: C4Config.h:104
int32_t RefreshRate
Definition: C4Config.h:106
int32_t DebugOpenGL
Definition: C4Config.h:117
int32_t WindowY
Definition: C4Config.h:105
int32_t WindowX
Definition: C4Config.h:105
int32_t MaxRefreshDelay
Definition: C4Config.h:113
int32_t Windowed
Definition: C4Config.h:107
int32_t ShowStartupMessages
Definition: C4Config.h:99
int32_t Monitor
Definition: C4Config.h:112
C4ConfigGeneral General
Definition: C4Config.h:255
C4ConfigNetwork Network
Definition: C4Config.h:259
bool IsCorrupted()
Definition: C4Config.h:288
void CleanupTempUpdateFolder()
Definition: C4Config.cpp:901
bool Save()
Definition: C4Config.cpp:439
bool Init()
Definition: C4Config.cpp:801
bool Load(const char *config_file=nullptr)
Definition: C4Config.cpp:327
void Default()
Definition: C4Config.cpp:296
bool ConfigLoaded
Definition: C4Config.h:266
C4ConfigGraphics Graphics
Definition: C4Config.h:257
C4ConfigSound Sound
Definition: C4Config.h:258
ValidatedStdCopyStrBuf< C4InVal::VAL_Comment > Comment
Definition: C4Config.h:146
int32_t MasterServerSignUp
Definition: C4Config.h:147
int32_t PortUDP
Definition: C4Config.h:153
int32_t LeagueServerSignUp
Definition: C4Config.h:151
int32_t NoRuntimeJoin
Definition: C4Config.h:142
int32_t PortTCP
Definition: C4Config.h:153
int32_t FEMusic
Definition: C4Config.h:128
void Out(const char *message)
Definition: C4Console.cpp:684
void OnStartGame()
Definition: C4ConsoleGUI.h:114
void Execute()
Definition: C4Console.cpp:581
void Clear() override
Definition: C4Console.cpp:418
virtual C4Window * Init(C4AbstractApp *app)
Definition: C4Console.cpp:58
C4EditCursor EditCursor
Definition: C4Console.h:90
static constexpr int COLOR_DEPTH
Definition: C4Draw.h:89
virtual bool OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)=0
void Set(C4Surface &rSfc)
Definition: C4Facet.cpp:459
bool InitFont(CStdFont *Font, const char *szFontName, FontType eType, int32_t iSize, C4GroupSet *pGfxGroups, bool fDoShadow=true)
void Clear() override
C4Window * Init(C4AbstractApp *application)
StdStrBuf RecordStream
Definition: C4Game.h:126
void OnResolutionChanged(unsigned int res_x, unsigned int res_y)
Definition: C4Game.cpp:4228
StdStrBuf DebugPassword
Definition: C4Game.h:149
int DebugWait
Definition: C4Game.h:149
char PlayerFilenames[20 *_MAX_PATH_LEN]
Definition: C4Game.h:104
bool Record
Definition: C4Game.h:124
StdStrBuf DebugHost
Definition: C4Game.h:149
char DirectJoinAddress[_MAX_PATH_LEN]
Definition: C4Game.h:106
bool HasScenario()
Definition: C4Game.h:163
bool IsRunning
Definition: C4Game.h:140
void SetScenarioFilename(const char *)
Definition: C4Game.cpp:660
void Clear()
Definition: C4Game.cpp:672
void SetInitProgress(float to_progress)
Definition: C4Game.cpp:4207
bool DoSkipFrame
Definition: C4Game.h:138
char ScenarioFilename[_MAX_PATH_LEN]
Definition: C4Game.h:102
int32_t iLobbyTimeout
Definition: C4Game.h:120
bool Init()
Definition: C4Game.cpp:397
class C4ScenarioParameters & StartupScenarioParameters
Definition: C4Game.h:68
void OnKeyboardLayoutChanged()
Definition: C4Game.cpp:4242
C4GameParameters & Parameters
Definition: C4Game.h:67
bool NetworkActive
Definition: C4Game.h:123
bool PreInit()
Definition: C4Game.cpp:333
bool GameGo
Definition: C4Game.h:136
bool Execute()
Definition: C4Game.cpp:868
bool fQuitWithError
Definition: C4Game.h:143
bool fObserve
Definition: C4Game.h:121
StdStrBuf RecordDumpFile
Definition: C4Game.h:125
char DefinitionFilenames[20 *_MAX_PATH_LEN]
Definition: C4Game.h:105
bool fLobby
Definition: C4Game.h:119
uint16_t DebugPort
Definition: C4Game.h:149
bool InitLoaderScreen(const char *image_name)
std::unique_ptr< C4MessageBoard > MessageBoard
bool Close()
Definition: C4Group.cpp:971
void ClearLanguage()
Definition: C4Language.cpp:410
bool Init()
Definition: C4Language.cpp:55
void Clear()
Definition: C4Language.cpp:114
bool LoadLanguage(const char *strLanguages)
Definition: C4Language.cpp:361
bool HasStringTable() const
Definition: C4Language.h:70
bool ProcessInput(const char *szText)
bool Play(const char *szSongname=nullptr, bool fLoop=false, int fadetime_ms=0, double max_resume_time=0.0, bool allow_break=false)
bool Init(const char *PlayList=nullptr)
void Execute(bool force_buffer_checks=false)
void SetPassword(const char *szToPassword)
Definition: C4Network2.cpp:772
bool Close() override
Definition: C4Rect.h:28
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
void Init()
Definition: C4Reloc.cpp:23
bool AddPath(const char *path, PathType pathType=PATH_Regular)
Definition: C4Reloc.cpp:62
bool Open(C4Group &group, const char *filename) const
Definition: C4Reloc.cpp:156
void SetValue(const char *id, int32_t value, bool only_if_larger)
static C4Startup * Get()
Definition: C4Startup.h:147
void OnKeyboardLayoutChanged()
Definition: C4Startup.cpp:365
C4StartupGraphics Graphics
Definition: C4Startup.h:112
static void Unload()
Definition: C4Startup.cpp:313
static bool SetStartScreen(const char *szScreen, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:332
static void InitStartup()
Definition: C4Startup.cpp:319
static void CloseStartup()
Definition: C4Startup.cpp:292
bool UpdateSize(int wdt, int hgt)
Definition: C4Surface.cpp:301
static C4TimeMilliseconds Now()
bool GetSize(C4Rect *pRect)
Definition: C4AppT.cpp:108
C4Surface * pSurface
Definition: C4Window.h:275
void SetSize(unsigned int cx, unsigned int cy)
Definition: C4AppT.cpp:113
void GrabMouse(bool grab)
Definition: C4AppT.cpp:107
static void WaitForSaves()
Definition: StdPNG.cpp:362
void Remove(StdSchedulerProc *pProc)
void Add(StdSchedulerProc *pProc)
const char * getData() const
Definition: StdBuf.h:442
bool SplitAtChar(char cSplit, StdStrBuf *psSplit)
Definition: StdBuf.h:619
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
int ReplaceChar(char cOld, char cNew)
Definition: StdBuf.cpp:336
int32_t GetScreenHgt()
Definition: C4Gui.h:2825
int32_t GetScreenWdt()
Definition: C4Gui.h:2824
void CopyValidated(const char *szFromVal)