1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2004-2015 Ingo Ruhnke <grumbel@gmx.de>
4 //  Copyright (C) 2006-2015 SuperTuxKart-Team
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 3
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 
20 #include "main_loop.hpp"
21 
22 #ifdef MOBILE_STK
23 #include "addons/addons_manager.hpp"
24 #endif
25 #include "audio/music_manager.hpp"
26 #include "audio/sfx_manager.hpp"
27 #include "config/player_manager.hpp"
28 #include "config/user_config.hpp"
29 #include "graphics/central_settings.hpp"
30 #include "graphics/irr_driver.hpp"
31 #include "graphics/material_manager.hpp"
32 #include "graphics/sp/sp_texture_manager.hpp"
33 #include "guiengine/engine.hpp"
34 #include "guiengine/message_queue.hpp"
35 #include "guiengine/modaldialog.hpp"
36 #include "guiengine/screen_keyboard.hpp"
37 #include "input/input_manager.hpp"
38 #include "modes/world.hpp"
39 #include "modes/profile_world.hpp"
40 #include "network/network_config.hpp"
41 #include "network/network_timer_synchronizer.hpp"
42 #include "network/protocols/client_lobby.hpp"
43 #include "network/protocols/game_protocol.hpp"
44 #include "network/protocol_manager.hpp"
45 #include "network/race_event_manager.hpp"
46 #include "network/rewind_manager.hpp"
47 #include "network/server.hpp"
48 #include "network/stk_host.hpp"
49 #include "online/request_manager.hpp"
50 #include "race/history.hpp"
51 #include "race/race_manager.hpp"
52 #include "states_screens/dialogs/server_info_dialog.hpp"
53 #include "states_screens/online/server_selection.hpp"
54 #include "states_screens/state_manager.hpp"
55 #include "utils/profiler.hpp"
56 #include "utils/string_utils.hpp"
57 #include "utils/time.hpp"
58 #include "utils/translation.hpp"
59 
60 #ifndef WIN32
61 #include <unistd.h>
62 #endif
63 
64 MainLoop* main_loop = 0;
65 
66 #ifdef WIN32
separateProcessProc(_In_ HWND hwnd,_In_ UINT uMsg,_In_ WPARAM wParam,_In_ LPARAM lParam)67 LRESULT CALLBACK separateProcessProc(_In_ HWND hwnd, _In_ UINT uMsg,
68                                      _In_ WPARAM wParam, _In_ LPARAM lParam)
69 {
70     if (uMsg == WM_DESTROY)
71     {
72         PostQuitMessage(0);
73         return 0;
74     }
75     return DefWindowProc(hwnd, uMsg, wParam, lParam);
76 };
77 #endif
78 
79 // ----------------------------------------------------------------------------
MainLoop(unsigned parent_pid,bool download_assets)80 MainLoop::MainLoop(unsigned parent_pid, bool download_assets)
81         : m_abort(false), m_request_abort(false), m_paused(false),
82           m_ticks_adjustment(0), m_parent_pid(parent_pid)
83 {
84     m_curr_time       = 0;
85     m_prev_time       = 0;
86     m_throttle_fps    = true;
87     m_allow_large_dt  = false;
88     m_frame_before_loading_world = false;
89     m_download_assets = download_assets;
90 #ifdef WIN32
91     if (parent_pid != 0)
92     {
93         core::stringw class_name = L"separate_process";
94         class_name += StringUtils::toWString(GetCurrentProcessId());
95         WNDCLASSEX wx = {};
96         wx.cbSize = sizeof(WNDCLASSEX);
97         wx.lpfnWndProc = separateProcessProc;
98         wx.hInstance = GetModuleHandle(0);
99         wx.lpszClassName = class_name.c_str();
100         if (RegisterClassEx(&wx))
101         {
102             CreateWindowEx(0, class_name.c_str(), L"stk_server_only",
103                 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
104         }
105     }
106 #endif
107 }  // MainLoop
108 
109 //-----------------------------------------------------------------------------
~MainLoop()110 MainLoop::~MainLoop()
111 {
112 }   // ~MainLoop
113 
114 #ifdef MOBILE_STK
115 //-----------------------------------------------------------------------------
pause_mainloop()116 extern "C" void pause_mainloop()
117 {
118     if (!main_loop)
119         return;
120     if (music_manager)
121         music_manager->pauseMusic();
122     if (SFXManager::get())
123         SFXManager::get()->pauseAll();
124     PlayerManager::get()->save();
125     if (addons_manager->hasDownloadedIcons())
126         addons_manager->saveInstalled();
127     Online::RequestManager::get()->setPaused(true);
128     IrrlichtDevice* dev = irr_driver->getDevice();
129     if (dev)
130         dev->resetPaused();
131 }  // pause_mainloop
132 
133 //-----------------------------------------------------------------------------
resume_mainloop()134 extern "C" void resume_mainloop()
135 {
136     if (!main_loop)
137         return;
138     IrrlichtDevice* dev = irr_driver->getDevice();
139     if (dev)
140         dev->resetUnpaused();
141     if (music_manager)
142         music_manager->resumeMusic();
143     if (SFXManager::get())
144         SFXManager::get()->resumeAll();
145     // Improve rubber banding effects of rewinders when going
146     // back to phone, because the smooth timer is paused
147     if (World::getWorld() && RewindManager::isEnabled())
148         RewindManager::get()->resetSmoothNetworkBody();
149     Online::RequestManager::get()->setPaused(false);
150 }  // resume_mainloop
151 #endif
152 
153 //-----------------------------------------------------------------------------
154 /** Returns the current dt, which guarantees a limited frame rate. If dt is
155  *  too low (the frame rate too high), the process will sleep to reach the
156  *  maximum frame rate.
157  */
getLimitedDt()158 float MainLoop::getLimitedDt()
159 {
160     m_prev_time = m_curr_time;
161 
162 #ifdef IOS_STK
163     if (m_paused.load())
164     {
165         // When iOS apps entering background it should not run any
166         // opengl command from apple document, so we stop here
167         pause_mainloop();
168         while (true)
169         {
170             // iOS will pause this thread completely when fully in background
171             IrrlichtDevice* dev = irr_driver->getDevice();
172             bool quit = false;
173             if (dev)
174                 quit = !dev->run();
175             if (quit || !m_paused.load())
176             {
177                 if (quit)
178                     return 1.0f/60.0f;
179                 break;
180             }
181         }
182         resume_mainloop();
183     }
184 #endif
185     float dt = 0;
186 
187     // In profile mode without graphics, run with a fixed dt of 1/60
188     if ((ProfileWorld::isProfileMode() && GUIEngine::isNoGraphics()) ||
189         UserConfigParams::m_arena_ai_stats)
190     {
191         return 1.0f/60.0f;
192     }
193 
194     while( 1 )
195     {
196         m_curr_time = StkTime::getMonoTimeMs();
197         if (m_prev_time > m_curr_time)
198         {
199             m_prev_time = m_curr_time;
200             // If system time adjusted backwards, return fixed dt and
201             // resynchronize network timer if exists in client
202             if (STKHost::existHost())
203             {
204 #ifndef SERVER_ONLY
205                 if (UserConfigParams::m_artist_debug_mode &&
206                     !GUIEngine::isNoGraphics())
207                 {
208                     core::stringw err = L"System clock running backwards in"
209                         " networking game.";
210                     MessageQueue::add(MessageQueue::MT_ERROR, err);
211                 }
212 #endif
213                 Log::error("MainLoop", "System clock running backwards in"
214                     " networking game.");
215                 if (STKHost::get()->getNetworkTimerSynchronizer())
216                 {
217                     STKHost::get()->getNetworkTimerSynchronizer()
218                         ->resynchroniseTimer();
219                 }
220             }
221         }
222         dt = (float)(m_curr_time - m_prev_time);
223         // On a server (i.e. without graphics) the frame rate can be under
224         // 1 ms, i.e. dt = 0. Additionally, the resolution of a sleep
225         // statement is not that precise either: if the sleep statement
226         // would be consistent < 1ms, but the stk time would increase by
227         // 1 ms, the stk clock would be desynchronised from real time
228         // (it would go faster), resulting in synchronisation problems
229         // with clients (server time is supposed to be behind client time).
230         // So we play it safe by adding a loop to make sure at least 1ms
231         // (minimum time that can be handled by the integer timer) delay here.
232         while (dt == 0)
233         {
234             StkTime::sleep(1);
235             m_curr_time = StkTime::getMonoTimeMs();
236             if (m_prev_time > m_curr_time)
237             {
238                 Log::error("MainLopp", "System clock keeps backwards!");
239                 m_prev_time = m_curr_time;
240             }
241             dt = (float)(m_curr_time - m_prev_time);
242         }
243 
244         const World* const world = World::getWorld();
245         if (UserConfigParams::m_fps_debug && world)
246         {
247             const LinearWorld *lw = dynamic_cast<const LinearWorld*>(world);
248             if (lw)
249             {
250                 Log::verbose("fps", "time %f distance %f dt %f fps %f",
251                              lw->getTime(),
252                              lw->getDistanceDownTrackForKart(0, true),
253                              dt*0.001f, 1000.0f / dt);
254             }
255             else
256             {
257                 Log::verbose("fps", "time %f dt %f fps %f",
258                              world->getTime(), dt*0.001f, 1000.0f / dt);
259             }
260 
261         }
262 
263         // Don't allow the game to run slower than a certain amount.
264         // when the computer can't keep it up, slow down the shown time instead
265         // But this can not be done in networking, otherwise the game time on
266         // client and server will not be in synch anymore
267         if ((!NetworkConfig::get()->isNetworking() || !World::getWorld()) &&
268             !m_allow_large_dt)
269         {
270             /* time 3 internal substeps take */
271             const float MAX_ELAPSED_TIME = 3.0f*1.0f / 60.0f*1000.0f;
272             if (dt > MAX_ELAPSED_TIME) dt = MAX_ELAPSED_TIME;
273         }
274         if (!m_throttle_fps || ProfileWorld::isProfileMode()) break;
275 
276         // Throttle fps if more than maximum, which can reduce
277         // the noise the fan on a graphics card makes.
278         // When in menus, reduce FPS much, it's not necessary to push to the
279         // maximum for plain menus
280 #ifdef IOS_STK
281         // For iOS devices most at locked at 60, for new iPad Pro supports 120
282         // which is currently m_max_fps
283         const int max_fps =
284             UserConfigParams::m_swap_interval == 2 ? 30 :
285             UserConfigParams::m_swap_interval == 1 ? 60 :
286             UserConfigParams::m_max_fps;
287 #else
288         const int max_fps = (irr_driver->isRecording() &&
289                              UserConfigParams::m_limit_game_fps )
290                           ? UserConfigParams::m_record_fps
291                           : ( StateManager::get()->throttleFPS()
292                               ? 60
293                               : UserConfigParams::m_max_fps     );
294 #endif
295         const int current_fps = (int)(1000.0f / dt);
296         if (!m_throttle_fps || current_fps <= max_fps ||
297             ProfileWorld::isProfileMode()                )  break;
298 
299         int wait_time = 1000 / max_fps - 1000 / current_fps;
300         if (wait_time < 1) wait_time = 1;
301 
302         PROFILER_PUSH_CPU_MARKER("Throttle framerate", 0, 0, 0);
303         StkTime::sleep(wait_time);
304         PROFILER_POP_CPU_MARKER();
305     }   // while(1)
306 
307     dt *= 0.001f;
308     return dt;
309 }   // getLimitedDt
310 
311 //-----------------------------------------------------------------------------
312 /** Updates all race related objects.
313  *  \param ticks Number of ticks (physics steps) to simulate - should be 1.
314  *  \param fast_forward If true, then only rewinders in network will be
315  *  updated, but not the physics.
316  */
updateRace(int ticks,bool fast_forward)317 void MainLoop::updateRace(int ticks, bool fast_forward)
318 {
319     if (!World::getWorld())  return;   // No race on atm - i.e. we are in menu
320 
321     // The race event manager will update world in case of an online race
322     if (RaceEventManager::get() && RaceEventManager::get()->isRunning())
323         RaceEventManager::get()->update(ticks, fast_forward);
324     else
325         World::getWorld()->updateWorld(ticks);
326 }   // updateRace
327 
328 //-----------------------------------------------------------------------------
329 /** Run the actual main loop.
330  *  The sequnce in which various parts of STK are updated is:
331  *  - Determine next time step size (`getLimitedDt`). This takes maximum fps
332  *    into account (i.e. sleep if the fps would be too high), and will actually
333  *    slow down the in-game clock if the fps are too low (if more than 3/60 of
334  *    a second have passed, more than 3 physics time steps would be needed,
335  *    and physics do at most 3 time steps).
336  *  - if a race is taking place (i.e. not only a menu being shown), call
337  *    `updateRace()`, which is a thin wrapper around a call to
338  *    `World::updateWorld()`:
339  *    - Update history manager (which will either set the kart position and/or
340  *      controls when replaying, or store the current info for a replay).
341  *      This is mostly for debugging only (though available even in release
342  *      mode).
343  *    - Updates Replays - either storing data when not replaying, or
344  *      updating kart positions/control when replaying).
345  *    - Calls `WorldStatus::update()`, which updates the race state (e.g.
346  *      go from 'ready' to 'set' etc), and clock.
347  *    - Updates the physics (`Physics::update()`). This will simulate all
348  *      physical objects for the specified time with bullet.
349  *    - Updates all karts (`Kart::update()`). Obviously the update function
350  *      does a lot more than what is described here, this is only supposed to
351  *      be a _very_ high level overview:
352  *      - Updates its rewinder (to store potentially changed controls
353  *        as events) in `KartRewinder::update()`.
354  *      - Calls `Moveable::update()`, which takes the new position from
355  *        the physics and saves it (and computes dependent values, like
356  *        heading, local velocity).
357  *      - Updates its controller. This is either:
358  *        - an AI using `SkiddingController::update()` (which then will
359  *          compute the new controls), or
360  *        - a player controller using `PlayerController::update()`, which will
361  *          handle smooth steering (in case of digital input devices steering
362  *          is adjusted a bit over time to avoid an instant change from all
363  *          left to all right). Input events will be handled when updating
364  *          the irrlicht driver later at the end of the main loop.
365  *      - Updates kart animation (like rescue, ...) if one is shown atm.
366  *      - Update attachments.
367  *      - update physics, i.e. taking the current steering and updating
368  *        the bullet raycast vehicle with that data. The settings are actually
369  *        only used in the next frame when the physics are updated.
370  *    - Updates all cameras via `Camera::update()`. The camera position and
371  *      rotation is adjusted according to the position etc of the kart (and
372  *      special circumstances like rescue, falling).
373  *    - Updates all projectiles using the projectile manager. Some of the
374  *      projectiles are mostly handled by the physics (e.g. a cake will mainly
375  *      check if it's out of bounds), others (like basket ball) do all
376  *      their aiming and movement here.
377  *    - Updates the rewind manager to store rewind states.
378  *  - Updates the music manager.
379  *  - Updates the input manager (which only updates internal time, actual
380  *    input handling follows late)
381  *  - Updates the wiimote manager. This will read the data of all wiimotes
382  *    and feed the corresponding events to the irrlicht event system.
383  *  - Updates the STK internal gui engine. This updates all widgets, and
384  *    e.g. takes care of the rotation of the karts in the KartSelection
385  *    screen using the ModelViewWidget.
386  *  - Updates STK's irrlicht driver `IrrDriver::update()`:
387  *    - Calls Irrlicht's `beginScene()` .
388  *    - Renders the scene (several times with different viewport if
389  *      split screen is being used)
390  *    - Calls `GUIEngine::render()`, which renders all widgets with the
391  *      help of Irrlicht's GUIEnvironment (`drawAll()`). This will also
392  *      handle all events, i.e. all input is now handled (e.g. steering,
393  *      firing etc are all set in the corresponding karts depending on
394  *      user input).
395  *    - Calls Irrlicht's `endScene()`
396  */
run()397 void MainLoop::run()
398 {
399     m_curr_time = StkTime::getMonoTimeMs();
400     // DT keeps track of the leftover time, since the race update
401     // happens in fixed timesteps
402     float left_over_time = 0;
403 
404 #ifdef WIN32
405     HANDLE parent = 0;
406     if (m_parent_pid != 0)
407     {
408         parent = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_parent_pid);
409         if (parent == 0 || parent == INVALID_HANDLE_VALUE)
410         {
411             Log::warn("MainLoop", "Cannot open parent handle, this child "
412                 "may not be auto destroyed when parent is terminated");
413         }
414     }
415 #endif
416 
417     while (!m_abort)
418     {
419 #ifdef WIN32
420         if (parent != 0 && parent != INVALID_HANDLE_VALUE)
421         {
422             MSG msg;
423             while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
424             {
425                 TranslateMessage(&msg);
426                 DispatchMessage(&msg);
427                 if (msg.message == WM_QUIT)
428                 {
429                     m_request_abort = true;
430                 }
431             }
432             // If parent is killed, abort the child main loop too
433             if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT)
434             {
435                 m_request_abort = true;
436             }
437         }
438 #else
439         // POSIX equivalent
440         if (m_parent_pid != 0 && getppid() != (int)m_parent_pid)
441         {
442             m_request_abort = true;
443         }
444 #endif
445 
446         PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7);
447 
448         left_over_time += getLimitedDt();
449         int num_steps   = stk_config->time2Ticks(left_over_time);
450         float dt = stk_config->ticks2Time(1);
451         left_over_time -= num_steps * dt ;
452 
453         // Shutdown next frame if shutdown request is sent while loading the
454         // world
455         bool was_server = NetworkConfig::get()->isNetworking() &&
456             NetworkConfig::get()->isServer();
457         if ((STKHost::existHost() && STKHost::get()->requestedShutdown()) ||
458             m_request_abort)
459         {
460             bool was_lan = NetworkConfig::get()->isLAN();
461             std::shared_ptr<Server> rejoin_server;
462             if (auto cl = LobbyProtocol::get<ClientLobby>())
463             {
464                 if (cl->getJoinedServer()->reconnectWhenQuitLobby())
465                     rejoin_server = cl->getJoinedServer();
466             }
467             bool exist_host = STKHost::existHost();
468             core::stringw msg = _("Server connection timed out.");
469 
470             if (!m_request_abort)
471             {
472                 if (!GUIEngine::isNoGraphics())
473                 {
474                     SFXManager::get()->quickSound("anvil");
475                     if (!STKHost::get()->getErrorMessage().empty())
476                     {
477                         msg = STKHost::get()->getErrorMessage();
478                     }
479                 }
480             }
481 
482             if (exist_host == true)
483             {
484                 STKHost::get()->shutdown();
485             }
486 
487 #ifndef SERVER_ONLY
488             if (CVS->isGLSL() && !m_download_assets)
489             {
490                 // Flush all command before delete world, avoid later access
491                 SP::SPTextureManager::get()
492                     ->checkForGLCommand(true/*before_scene*/);
493                 // Reset screen in case the minimap was drawn
494                 glViewport(0, 0, irr_driver->getActualScreenSize().Width,
495                     irr_driver->getActualScreenSize().Height);
496             }
497 #endif
498 
499             // In case the user opened a race pause dialog
500             GUIEngine::ModalDialog::dismiss();
501             GUIEngine::ScreenKeyboard::dismiss();
502 
503             if (World::getWorld())
504             {
505                 RaceManager::get()->clearNetworkGrandPrixResult();
506                 RaceManager::get()->exitRace();
507             }
508 
509             if (exist_host == true)
510             {
511                 if (!GUIEngine::isNoGraphics())
512                 {
513                     StateManager::get()->resetAndSetStack(
514                         NetworkConfig::get()->getResetScreens().data());
515                     MessageQueue::add(MessageQueue::MT_ERROR, msg);
516                 }
517                 if (rejoin_server)
518                 {
519                     if (was_lan)
520                         NetworkConfig::get()->setIsLAN();
521                     else
522                         NetworkConfig::get()->setIsWAN();
523                     NetworkConfig::get()->setIsServer(false);
524                     ServerSelection::getInstance()->push();
525                     rejoin_server->setReconnectWhenQuitLobby(false);
526                     new ServerInfoDialog(rejoin_server);
527                 }
528                 else
529                     NetworkConfig::get()->unsetNetworking();
530             }
531 
532             if (m_request_abort)
533             {
534                 m_abort = true;
535             }
536         }
537 
538         if (was_server && !STKHost::existHost())
539             m_abort = true;
540 
541         if (!m_abort)
542         {
543             float frame_duration = num_steps * dt;
544             if (!GUIEngine::isNoGraphics())
545             {
546                 PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
547                 if (World::getWorld())
548                     World::getWorld()->updateGraphics(frame_duration);
549                 PROFILER_POP_CPU_MARKER();
550 
551                 // Render the previous frame, and also handle all user input.
552                 PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
553                 irr_driver->update(frame_duration);
554                 PROFILER_POP_CPU_MARKER();
555 
556                 PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00);
557                 input_manager->update(frame_duration);
558                 GUIEngine::update(frame_duration);
559                 PROFILER_POP_CPU_MARKER();
560                 if (!m_download_assets)
561                 {
562                     PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00);
563                     SFXManager::get()->update();
564                     PROFILER_POP_CPU_MARKER();
565                 }
566             }
567             // Some protocols in network will use RequestManager
568             if (!m_download_assets)
569             {
570                 PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F);
571                 Online::RequestManager::get()->update(frame_duration);
572                 PROFILER_POP_CPU_MARKER();
573             }
574 
575             m_ticks_adjustment.lock();
576             if (m_ticks_adjustment.getData() != 0)
577             {
578                 if (m_ticks_adjustment.getData() > 0)
579                 {
580                     num_steps += m_ticks_adjustment.getData();
581                     m_ticks_adjustment.getData() = 0;
582                 }
583                 else if (m_ticks_adjustment.getData() < 0)
584                 {
585                     int new_steps = num_steps + m_ticks_adjustment.getData();
586                     if (new_steps < 0)
587                     {
588                         num_steps = 0;
589                         m_ticks_adjustment.getData() = new_steps;
590                     }
591                     else
592                     {
593                         num_steps = new_steps;
594                         m_ticks_adjustment.getData() = 0;
595                     }
596                 }
597             }
598             m_ticks_adjustment.unlock();
599 
600             // Avoid hang when some function in world takes too long time or
601             // when leave / come back from android home button
602             bool fast_forward = NetworkConfig::get()->isNetworking() &&
603                 NetworkConfig::get()->isClient() &&
604                 num_steps > stk_config->time2Ticks(1.0f);
605             for (int i = 0; i < num_steps; i++)
606             {
607                 if (World::getWorld() && history->replayHistory())
608                 {
609                     history->updateReplay(
610                                        World::getWorld()->getTicksSinceStart());
611                 }
612 
613                 PROFILER_PUSH_CPU_MARKER("Protocol manager update",
614                                          0x7F, 0x00, 0x7F);
615                 if (auto pm = ProtocolManager::lock())
616                 {
617                     pm->update(1);
618                 }
619                 PROFILER_POP_CPU_MARKER();
620 
621                 PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
622                 if (World::getWorld())
623                 {
624                     updateRace(1, fast_forward);
625                 }
626                 PROFILER_POP_CPU_MARKER();
627 
628                 // We need to check again because update_race may have requested
629                 // the main loop to abort; and it's not a good idea to continue
630                 // since the GUI engine is no more to be called then.
631                 if (m_abort || m_request_abort)
632                     break;
633 
634                 if (m_frame_before_loading_world)
635                 {
636                     // This will be called when changing introcutscene 1 and 2
637                     // in CutsceneWorld::enterRaceOverState
638                     // Reset the timer for correct time for cutscene
639                     m_frame_before_loading_world = false;
640                     m_curr_time = StkTime::getMonoTimeMs();
641                     left_over_time = 0.0f;
642                     break;
643                 }
644 
645                 if (World::getWorld())
646                 {
647                     if (World::getWorld()->getPhase()==WorldStatus::SETUP_PHASE)
648                     {
649                         // Skip the large num steps contributed by loading time
650                         World::getWorld()->updateTime(1);
651                         break;
652                     }
653                     World::getWorld()->updateTime(1);
654                 }
655             }   // for i < num_steps
656 
657             // Do it after all pending rewinding is done
658             if (World::getWorld() && RewindManager::isEnabled())
659                  RewindManager::get()->handleResetSmoothNetworkBody();
660 
661             // Handle controller the last to avoid slow PC sending actions too
662             // late
663             if (!GUIEngine::isNoGraphics())
664             {
665                 // User aborted (e.g. closed window)
666                 bool abort = !irr_driver->getDevice()->run();
667 
668                 if (m_frame_before_loading_world)
669                 {
670                     // irr_driver->getDevice()->run() loads the world
671                     m_frame_before_loading_world = false;
672                     m_curr_time = StkTime::getMonoTimeMs();
673                     left_over_time = 0.0f;
674                 }
675 
676                 if (abort)
677                 {
678                     m_request_abort = true;
679                 }
680             }
681 
682             if (auto gp = GameProtocol::lock())
683             {
684                 gp->sendActions();
685             }
686         }
687         PROFILER_POP_CPU_MARKER();   // MainLoop pop
688         PROFILER_SYNC_FRAME();
689     }  // while !m_abort
690 
691 #ifdef WIN32
692     if (parent != 0 && parent != INVALID_HANDLE_VALUE)
693         CloseHandle(parent);
694 #endif
695 
696 }   // run
697 
698 // ----------------------------------------------------------------------------
699 /** Renders the GUI. This function is used during loading a track to get a
700  *  responsive GUI, and allow GUI animations (like a progress bar) to be
701  *  shown.
702  *  \param phase An integer indicated a phase. The maximum number of phases
703  *         is used to show a progress bar. The values are between 0 and 8200.
704  *  \param loop_index If the call is from a loop, the current loop index.
705  *  \param loop_size The number of loop iterations. Used to smooth update
706  *         e.g. a progress bar.
707  */
renderGUI(int phase,int loop_index,int loop_size)708 void MainLoop::renderGUI(int phase, int loop_index, int loop_size)
709 {
710 #ifdef SERVER_ONLY
711     return;
712 #else
713     if ((NetworkConfig::get()->isNetworking() &&
714         NetworkConfig::get()->isServer()) ||
715         GUIEngine::isNoGraphics())
716     {
717         return;
718     }
719     // Atm ignore all input when loading only
720     irr_driver->getDevice()->setEventReceiver(NULL);
721     irr_driver->getDevice()->run();
722     irr_driver->getDevice()->setEventReceiver(GUIEngine::EventHandler::get());
723     return;
724     // Rendering past phase 7000 causes the minimap to not work
725     // on higher graphical settings
726     if (phase > 7000)
727     {
728         m_request_abort = !irr_driver->getDevice()->run();
729         return;
730     }
731 
732     uint64_t now = StkTime::getMonoTimeMs();
733     float dt = (now - m_curr_time)/1000.0f;
734 
735     if (dt < 1.0 / 30.0f) return;
736 
737     m_curr_time = now;
738 
739     // TODO: remove debug output
740     //Log::verbose("mainloop", "Rendergui t %llu dt %f phase %d  index %d / %d",
741     //             now, dt, phase, loop_index, loop_size);
742 
743     irr_driver->update(dt, /*is_loading*/true);
744     GUIEngine::update(dt);
745     m_request_abort = !irr_driver->getDevice()->run();
746 
747     //TODO: remove debug output
748     // uint64_t now2 = StkTime::getMonoTimeMs();
749     // Log::verbose("mainloop", "  duration t %llu dt %llu", now, now2-now);
750 #endif
751 }   // renderGUI
752 /* EOF */
753 
754 #ifdef IOS_STK
755 // For iOS STK we need to make sure no rendering command is executed after pause
756 // so we need a handle_app_event callback
757 #include "SDL_events.h"
handle_app_event(void * userdata,SDL_Event * event)758 extern "C" int handle_app_event(void* userdata, SDL_Event* event)
759 {
760     if (!main_loop)
761         return 1;
762     switch (event->type)
763     {
764     case SDL_APP_WILLENTERBACKGROUND:
765         main_loop->setPaused(true);
766         break;
767     case SDL_APP_DIDENTERFOREGROUND:
768         main_loop->setPaused(false);
769         break;
770     default:
771         break;
772     }
773     return 1;
774 }
775 #endif
776