2016-01-08 20:35:33 +01:00
|
|
|
/* Copyright (C) 2016 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
This module drives the game when running without Atlas (our integrated
|
|
|
|
map editor). It receives input and OS messages via SDL and feeds them
|
|
|
|
into the input dispatcher, where they are passed on to the game GUI and
|
|
|
|
simulation.
|
|
|
|
It also contains main(), which either runs the above controller or
|
|
|
|
that of Atlas depending on commandline parameters.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2006-06-02 05:56:24 +02:00
|
|
|
// not for any PCH effort, but instead for the (common) definitions
|
|
|
|
// included there.
|
2010-03-28 20:21:48 +02:00
|
|
|
#define MINIMAL_PCH 2
|
2006-06-02 05:56:24 +02:00
|
|
|
#include "lib/precompiled.h"
|
2004-06-03 20:38:14 +02:00
|
|
|
|
2010-03-28 20:21:48 +02:00
|
|
|
#include "lib/debug.h"
|
2011-05-03 14:38:42 +02:00
|
|
|
#include "lib/status.h"
|
2010-03-28 20:21:48 +02:00
|
|
|
#include "lib/secure_crt.h"
|
2009-12-03 21:17:22 +01:00
|
|
|
#include "lib/frequency_filter.h"
|
2005-08-15 01:34:37 +02:00
|
|
|
#include "lib/input.h"
|
2009-12-03 21:17:22 +01:00
|
|
|
#include "lib/ogl.h"
|
2005-06-16 23:50:20 +02:00
|
|
|
#include "lib/timer.h"
|
2012-01-13 00:32:27 +01:00
|
|
|
#include "lib/external_libraries/libsdl.h"
|
2004-06-02 22:39:21 +02:00
|
|
|
|
2010-09-18 20:21:00 +02:00
|
|
|
#include "ps/ArchiveBuilder.h"
|
2004-06-02 22:39:21 +02:00
|
|
|
#include "ps/CConsole.h"
|
2012-08-19 22:45:43 +02:00
|
|
|
#include "ps/CLogger.h"
|
2015-08-20 15:22:44 +02:00
|
|
|
#include "ps/ConfigDB.h"
|
2010-06-30 23:41:04 +02:00
|
|
|
#include "ps/Filesystem.h"
|
2004-07-27 23:00:53 +02:00
|
|
|
#include "ps/Game.h"
|
2005-10-31 19:36:36 +01:00
|
|
|
#include "ps/Globals.h"
|
2010-06-30 23:41:04 +02:00
|
|
|
#include "ps/Hotkey.h"
|
|
|
|
#include "ps/Loader.h"
|
|
|
|
#include "ps/Profile.h"
|
2011-11-04 02:35:50 +01:00
|
|
|
#include "ps/Profiler2.h"
|
2010-06-30 23:41:04 +02:00
|
|
|
#include "ps/Pyrogenesis.h"
|
2010-08-07 00:16:05 +02:00
|
|
|
#include "ps/Replay.h"
|
2012-02-25 18:29:27 +01:00
|
|
|
#include "ps/TouchInput.h"
|
2011-02-16 21:40:15 +01:00
|
|
|
#include "ps/UserReport.h"
|
2010-06-30 23:41:04 +02:00
|
|
|
#include "ps/Util.h"
|
2010-06-03 21:07:59 +02:00
|
|
|
#include "ps/VideoMode.h"
|
2011-04-07 04:32:16 +02:00
|
|
|
#include "ps/World.h"
|
2010-06-30 23:41:04 +02:00
|
|
|
#include "ps/GameSetup/GameSetup.h"
|
|
|
|
#include "ps/GameSetup/Atlas.h"
|
|
|
|
#include "ps/GameSetup/Config.h"
|
|
|
|
#include "ps/GameSetup/CmdLineArgs.h"
|
2010-08-07 00:16:05 +02:00
|
|
|
#include "ps/GameSetup/Paths.h"
|
2009-12-03 21:17:22 +01:00
|
|
|
#include "ps/XML/Xeromyces.h"
|
2008-06-16 20:19:35 +02:00
|
|
|
#include "network/NetClient.h"
|
|
|
|
#include "network/NetServer.h"
|
|
|
|
#include "network/NetSession.h"
|
2014-04-27 07:37:34 +02:00
|
|
|
#include "lobby/IXmppClient.h"
|
2007-01-08 02:56:46 +01:00
|
|
|
#include "graphics/Camera.h"
|
2006-03-21 21:55:45 +01:00
|
|
|
#include "graphics/GameView.h"
|
2010-09-10 23:02:10 +02:00
|
|
|
#include "graphics/TextureManager.h"
|
|
|
|
#include "gui/GUIManager.h"
|
|
|
|
#include "renderer/Renderer.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "simulation2/Simulation2.h"
|
2004-09-19 17:58:13 +02:00
|
|
|
|
2012-01-16 04:30:55 +01:00
|
|
|
#if OS_UNIX
|
|
|
|
#include <unistd.h> // geteuid
|
|
|
|
#endif // OS_UNIX
|
|
|
|
|
2006-10-08 05:28:22 +02:00
|
|
|
extern bool g_GameRestarted;
|
2005-06-16 23:50:20 +02:00
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
void kill_mainloop();
|
2005-06-16 23:50:20 +02:00
|
|
|
|
2010-06-03 23:15:45 +02:00
|
|
|
// to avoid redundant and/or recursive resizing, we save the new
|
|
|
|
// size after VIDEORESIZE messages and only update the video mode
|
|
|
|
// once per frame.
|
|
|
|
// these values are the latest resize message, and reset to 0 once we've
|
|
|
|
// updated the video mode
|
|
|
|
static int g_ResizedW;
|
|
|
|
static int g_ResizedH;
|
2004-06-02 22:39:21 +02:00
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
// main app message handler
|
2006-08-26 23:52:18 +02:00
|
|
|
static InReaction MainInputHandler(const SDL_Event_* ev)
|
2004-05-24 22:25:48 +02:00
|
|
|
{
|
2006-08-26 23:52:18 +02:00
|
|
|
switch(ev->ev.type)
|
2004-05-24 22:25:48 +02:00
|
|
|
{
|
2012-02-06 23:47:35 +01:00
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
switch(ev->ev.window.event)
|
|
|
|
{
|
|
|
|
case SDL_WINDOWEVENT_ENTER:
|
|
|
|
RenderCursor(true);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_LEAVE:
|
|
|
|
RenderCursor(false);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
|
|
g_ResizedW = ev->ev.window.data1;
|
|
|
|
g_ResizedH = ev->ev.window.data2;
|
|
|
|
break;
|
2014-09-29 00:10:09 +02:00
|
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
|
|
g_VideoMode.UpdatePosition(ev->ev.window.data1, ev->ev.window.data2);
|
2012-02-06 23:47:35 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_QUIT:
|
|
|
|
kill_mainloop();
|
|
|
|
break;
|
2010-06-03 21:07:59 +02:00
|
|
|
|
2004-07-21 18:34:07 +02:00
|
|
|
case SDL_HOTKEYDOWN:
|
2010-10-23 04:37:00 +02:00
|
|
|
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
|
|
|
if (hotkey == "exit")
|
2004-05-24 22:25:48 +02:00
|
|
|
{
|
2005-08-15 01:34:37 +02:00
|
|
|
kill_mainloop();
|
2005-10-20 19:44:56 +02:00
|
|
|
return IN_HANDLED;
|
2010-10-23 04:37:00 +02:00
|
|
|
}
|
|
|
|
else if (hotkey == "screenshot")
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
WriteScreenshot(L".png");
|
2005-10-20 19:44:56 +02:00
|
|
|
return IN_HANDLED;
|
2010-10-23 04:37:00 +02:00
|
|
|
}
|
|
|
|
else if (hotkey == "bigscreenshot")
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
WriteBigScreenshot(L".bmp", 10);
|
2005-10-24 03:53:03 +02:00
|
|
|
return IN_HANDLED;
|
2010-10-23 04:37:00 +02:00
|
|
|
}
|
|
|
|
else if (hotkey == "togglefullscreen")
|
|
|
|
{
|
2010-06-03 21:07:59 +02:00
|
|
|
g_VideoMode.ToggleFullscreen();
|
|
|
|
return IN_HANDLED;
|
2004-05-24 22:25:48 +02:00
|
|
|
}
|
2014-09-09 20:17:08 +02:00
|
|
|
else if (hotkey == "profile2.toggle")
|
2011-11-04 02:35:50 +01:00
|
|
|
{
|
2014-09-09 20:17:08 +02:00
|
|
|
g_Profiler2.Toggle();
|
2011-11-04 02:35:50 +01:00
|
|
|
return IN_HANDLED;
|
|
|
|
}
|
2005-10-13 20:05:55 +02:00
|
|
|
break;
|
2004-05-24 22:25:48 +02:00
|
|
|
}
|
|
|
|
|
2005-10-20 19:44:56 +02:00
|
|
|
return IN_PASS;
|
2004-05-24 22:25:48 +02:00
|
|
|
}
|
|
|
|
|
2005-06-16 23:50:20 +02:00
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
// dispatch all pending events to the various receivers.
|
|
|
|
static void PumpEvents()
|
2004-06-21 16:14:12 +02:00
|
|
|
{
|
2014-07-14 21:52:35 +02:00
|
|
|
JSContext* cx = g_GUI->GetScriptInterface()->GetContext();
|
|
|
|
JSAutoRequest rq(cx);
|
|
|
|
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("dispatch events");
|
2004-07-24 21:38:12 +02:00
|
|
|
|
2006-08-26 23:52:18 +02:00
|
|
|
SDL_Event_ ev;
|
Improve correctness of hotkeys at low framerates.
SDL queues up all the input events received in a frame. When the hotkey
system saw a key up/down event, it immediately updated its
HotkeyIsPressed state and then pushed a hotkey event onto the end of the
queue.
If the initial queue was e.g. [key-down shift, key-press Z, key-up
shift], the hotkey event triggered by Z would be processed after the
key-up shift had updated the HotkeyIsPressed state, so the handler of
the Z hotkey would not think the shift hotkey was pressed.
If the initial queue was e.g. [key-press Z, mouse-click], the hotkey
triggered by Z would be processed after the mouse-click event, so it
could apply to the wrong building selection.
Fix by pushing the hotkey events onto a special queue that gets
processed before any subsequent SDL input events.
Also update the HotkeyIsPressed status when the HOTKEYDOWN/HOTKEYUP
events are processed, not when they are generated, to guarantee they are
consistent with the DOWN/UP events.
Fixes #1869.
This was SVN commit r14057.
2013-10-30 02:38:32 +01:00
|
|
|
while (in_poll_event(&ev))
|
2011-11-09 14:09:01 +01:00
|
|
|
{
|
|
|
|
PROFILE2("event");
|
|
|
|
if (g_GUI)
|
|
|
|
{
|
2014-07-14 21:52:35 +02:00
|
|
|
JS::RootedValue tmpVal(cx);
|
|
|
|
ScriptInterface::ToJSVal(cx, &tmpVal, ev);
|
2014-08-03 00:21:50 +02:00
|
|
|
std::string data = g_GUI->GetScriptInterface()->StringifyJSON(&tmpVal);
|
2011-11-09 14:09:01 +01:00
|
|
|
PROFILE2_ATTR("%s", data.c_str());
|
|
|
|
}
|
2006-08-26 23:52:18 +02:00
|
|
|
in_dispatch_event(&ev);
|
2011-11-09 14:09:01 +01:00
|
|
|
}
|
2012-02-25 18:29:27 +01:00
|
|
|
|
|
|
|
g_TouchInput.Frame();
|
2004-12-08 16:34:15 +01:00
|
|
|
}
|
|
|
|
|
2005-06-16 23:50:20 +02:00
|
|
|
|
2005-03-22 03:17:55 +01:00
|
|
|
static int ProgressiveLoad()
|
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("progressive load");
|
|
|
|
|
2005-03-22 03:17:55 +01:00
|
|
|
wchar_t description[100];
|
|
|
|
int progress_percent;
|
2011-04-07 04:32:16 +02:00
|
|
|
try
|
2005-03-22 03:17:55 +01:00
|
|
|
{
|
2011-05-03 14:38:42 +02:00
|
|
|
Status ret = LDR_ProgressiveLoad(10e-3, description, ARRAY_SIZE(description), &progress_percent);
|
2011-04-07 04:32:16 +02:00
|
|
|
switch(ret)
|
|
|
|
{
|
|
|
|
// no load active => no-op (skip code below)
|
|
|
|
case INFO::OK:
|
|
|
|
return 0;
|
|
|
|
// current task didn't complete. we only care about this insofar as the
|
|
|
|
// load process is therefore not yet finished.
|
|
|
|
case ERR::TIMED_OUT:
|
|
|
|
break;
|
|
|
|
// just finished loading
|
|
|
|
case INFO::ALL_COMPLETE:
|
|
|
|
g_Game->ReallyStartGame();
|
|
|
|
wcscpy_s(description, ARRAY_SIZE(description), L"Game is starting..");
|
|
|
|
// LDR_ProgressiveLoad returns L""; set to valid text to
|
|
|
|
// avoid problems in converting to JSString
|
|
|
|
break;
|
|
|
|
// error!
|
|
|
|
default:
|
2011-05-03 14:38:42 +02:00
|
|
|
WARN_RETURN_STATUS_IF_ERR(ret);
|
2011-04-07 04:32:16 +02:00
|
|
|
// can't do this above due to legit ERR::TIMED_OUT
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-05-23 00:27:53 +02:00
|
|
|
catch (PSERROR_Game_World_MapLoadFailed& e)
|
2011-04-07 04:32:16 +02:00
|
|
|
{
|
|
|
|
// Map loading failed
|
|
|
|
|
|
|
|
// Call script function to do the actual work
|
|
|
|
// (delete game data, switch GUI page, show error, etc.)
|
|
|
|
CancelLoad(CStr(e.what()).FromUTF8());
|
2005-03-22 03:17:55 +01:00
|
|
|
}
|
|
|
|
|
2005-08-14 17:23:59 +02:00
|
|
|
GUI_DisplayLoadProgress(progress_percent, description);
|
2005-03-22 03:17:55 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
static void RendererIncrementalLoad()
|
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("renderer incremental load");
|
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
const double maxTime = 0.1f;
|
|
|
|
|
|
|
|
double startTime = timer_Time();
|
|
|
|
bool more;
|
|
|
|
do {
|
|
|
|
more = g_Renderer.GetTextureManager().MakeProgress();
|
|
|
|
}
|
|
|
|
while (more && timer_Time() - startTime < maxTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-21 18:26:34 +02:00
|
|
|
static bool quit = false; // break out of main loop
|
|
|
|
|
2004-07-12 18:50:46 +02:00
|
|
|
static void Frame()
|
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
g_Profiler2.RecordFrameStart();
|
|
|
|
PROFILE2("frame");
|
2011-11-09 14:09:01 +01:00
|
|
|
g_Profiler2.IncrementFrameNumber();
|
|
|
|
PROFILE2_ATTR("%d", g_Profiler2.GetFrameNumber());
|
2011-11-04 02:35:50 +01:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-06-25 04:21:26 +02:00
|
|
|
|
2005-10-31 19:36:36 +01:00
|
|
|
// get elapsed time
|
2008-01-07 21:03:19 +01:00
|
|
|
const double time = timer_Time();
|
2007-12-20 21:21:45 +01:00
|
|
|
g_frequencyFilter->Update(time);
|
2005-10-31 19:36:36 +01:00
|
|
|
// .. old method - "exact" but contains jumps
|
2005-10-18 01:35:16 +02:00
|
|
|
#if 0
|
2004-07-12 18:50:46 +02:00
|
|
|
static double last_time;
|
2008-01-07 21:03:19 +01:00
|
|
|
const double time = timer_Time();
|
2004-07-12 18:50:46 +02:00
|
|
|
const float TimeSinceLastFrame = (float)(time-last_time);
|
|
|
|
last_time = time;
|
2005-10-18 01:35:16 +02:00
|
|
|
ONCE(return); // first call: set last_time and return
|
2005-10-31 19:36:36 +01:00
|
|
|
|
|
|
|
// .. new method - filtered and more smooth, but errors may accumulate
|
2005-10-18 01:35:16 +02:00
|
|
|
#else
|
2012-06-06 21:37:03 +02:00
|
|
|
const float realTimeSinceLastFrame = 1.0 / g_frequencyFilter->SmoothedFrequency();
|
2005-10-18 01:35:16 +02:00
|
|
|
#endif
|
2012-06-06 21:37:03 +02:00
|
|
|
ENSURE(realTimeSinceLastFrame > 0.0f);
|
2004-05-24 22:25:48 +02:00
|
|
|
|
2005-10-31 19:36:36 +01:00
|
|
|
// decide if update/render is necessary
|
2007-06-04 09:41:05 +02:00
|
|
|
bool need_render = !g_app_minimized;
|
|
|
|
bool need_update = true;
|
|
|
|
|
|
|
|
// If we are not running a multiplayer game, disable updates when the game is
|
|
|
|
// minimized or out of focus and relinquish the CPU a bit, in order to make
|
|
|
|
// debugging easier.
|
2012-08-15 02:10:44 +02:00
|
|
|
if(g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus)
|
2005-10-31 19:36:36 +01:00
|
|
|
{
|
2011-11-04 02:35:50 +01:00
|
|
|
PROFILE3("non-focus delay");
|
2005-10-31 19:36:36 +01:00
|
|
|
need_update = false;
|
|
|
|
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
|
|
|
|
SDL_Delay(10);
|
|
|
|
}
|
|
|
|
|
2015-08-20 15:22:44 +02:00
|
|
|
// Throttling: limit update and render frequency to the minimum to 50 FPS
|
|
|
|
// in the "inactive" state, so that other windows get enough CPU time,
|
|
|
|
// (and it's always nice for power+thermal management).
|
|
|
|
// TODO: when the game performance is high enough, implementing a limit for
|
|
|
|
// in-game framerate might be sensible.
|
|
|
|
const float maxFPSMenu = 50.0;
|
|
|
|
bool limit_fps = false;
|
|
|
|
CFG_GET_VAL("gui.menu.limitfps", limit_fps);
|
|
|
|
if (limit_fps && (!g_Game || !g_Game->IsGameStarted()))
|
|
|
|
{
|
|
|
|
float remainingFrameTime = (1000.0 / maxFPSMenu) - realTimeSinceLastFrame;
|
|
|
|
if (remainingFrameTime > 0)
|
|
|
|
SDL_Delay(remainingFrameTime);
|
|
|
|
}
|
2005-10-31 19:36:36 +01:00
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
|
|
|
|
// this scans for changed files/directories and reloads them, thus
|
|
|
|
// allowing hotloading (changes are immediately assimilated in-game).
|
2013-11-07 22:06:18 +01:00
|
|
|
ReloadChangedFiles();
|
2004-05-24 22:25:48 +02:00
|
|
|
|
2005-03-22 03:17:55 +01:00
|
|
|
ProgressiveLoad();
|
2004-07-15 21:12:54 +02:00
|
|
|
|
2010-09-10 23:02:10 +02:00
|
|
|
RendererIncrementalLoad();
|
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
PumpEvents();
|
2010-05-20 02:59:01 +02:00
|
|
|
|
2010-08-21 18:26:34 +02:00
|
|
|
// if the user quit by closing the window, the GL context will be broken and
|
|
|
|
// may crash when we call Render() on some drivers, so leave this loop
|
|
|
|
// before rendering
|
|
|
|
if (quit)
|
|
|
|
return;
|
|
|
|
|
2010-06-03 23:15:45 +02:00
|
|
|
// respond to pumped resize events
|
|
|
|
if (g_ResizedW || g_ResizedH)
|
|
|
|
{
|
|
|
|
g_VideoMode.ResizeWindow(g_ResizedW, g_ResizedH);
|
|
|
|
g_ResizedW = g_ResizedH = 0;
|
|
|
|
}
|
|
|
|
|
2010-05-20 02:59:01 +02:00
|
|
|
if (g_NetClient)
|
|
|
|
g_NetClient->Poll();
|
2005-03-30 18:14:19 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-12 06:27:55 +02:00
|
|
|
|
2009-12-03 21:17:22 +01:00
|
|
|
g_GUI->TickObjects();
|
2005-08-09 18:03:36 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-12 06:27:55 +02:00
|
|
|
|
2005-10-31 19:36:36 +01:00
|
|
|
if (g_Game && g_Game->IsGameStarted() && need_update)
|
2004-08-05 15:07:51 +02:00
|
|
|
{
|
2012-06-06 21:37:03 +02:00
|
|
|
g_Game->Update(realTimeSinceLastFrame);
|
2005-03-22 03:17:55 +01:00
|
|
|
|
2012-06-06 21:37:03 +02:00
|
|
|
g_Game->GetView()->Update(float(realTimeSinceLastFrame));
|
2004-10-21 01:16:38 +02:00
|
|
|
}
|
2005-03-30 18:14:19 +02:00
|
|
|
|
2010-07-06 21:54:17 +02:00
|
|
|
// Immediately flush any messages produced by simulation code
|
|
|
|
if (g_NetClient)
|
|
|
|
g_NetClient->Flush();
|
|
|
|
|
2014-04-27 07:37:34 +02:00
|
|
|
// Keep us connected to any XMPP servers
|
|
|
|
if (g_XmppClient)
|
|
|
|
g_XmppClient->recv();
|
|
|
|
|
2011-02-16 21:40:15 +01:00
|
|
|
g_UserReporter.Update();
|
|
|
|
|
2012-06-06 21:37:03 +02:00
|
|
|
g_Console->Update(realTimeSinceLastFrame);
|
2004-07-20 21:30:35 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-31 19:36:36 +01:00
|
|
|
if(need_render)
|
2004-07-12 18:50:46 +02:00
|
|
|
{
|
|
|
|
Render();
|
2011-11-04 02:35:50 +01:00
|
|
|
|
|
|
|
PROFILE3("swap buffers");
|
2012-02-06 23:47:35 +01:00
|
|
|
SDL_GL_SwapWindow(g_VideoMode.GetWindow());
|
2004-07-12 18:50:46 +02:00
|
|
|
}
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-03-30 18:14:19 +02:00
|
|
|
|
|
|
|
g_Profiler.Frame();
|
|
|
|
|
2006-10-08 05:28:22 +02:00
|
|
|
g_GameRestarted = false;
|
2005-08-15 01:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void MainControllerInit()
|
|
|
|
{
|
2005-10-19 08:11:21 +02:00
|
|
|
// add additional input handlers only needed by this controller:
|
2005-08-15 01:34:37 +02:00
|
|
|
|
2005-10-19 08:11:21 +02:00
|
|
|
// must be registered after gui_handler. Should mayhap even be last.
|
2005-08-15 01:34:37 +02:00
|
|
|
in_add_handler(MainInputHandler);
|
2004-07-12 18:50:46 +02:00
|
|
|
}
|
2004-05-24 22:25:48 +02:00
|
|
|
|
2004-07-15 21:12:54 +02:00
|
|
|
|
|
|
|
|
2005-08-15 01:34:37 +02:00
|
|
|
static void MainControllerShutdown()
|
|
|
|
{
|
2014-08-25 18:02:40 +02:00
|
|
|
in_reset_handlers();
|
2005-08-15 01:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-13 20:05:55 +02:00
|
|
|
// stop the main loop and trigger orderly shutdown. called from several
|
|
|
|
// places: the event handler (SDL_QUIT and hotkey) and JS exitProgram.
|
2005-08-15 01:34:37 +02:00
|
|
|
void kill_mainloop()
|
|
|
|
{
|
|
|
|
quit = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-07 23:47:31 +02:00
|
|
|
static bool restart_in_atlas = false;
|
|
|
|
// called by game code to indicate main() should restart in Atlas mode
|
|
|
|
// instead of terminating
|
|
|
|
void restart_mainloop_in_atlas()
|
|
|
|
{
|
|
|
|
quit = true;
|
|
|
|
restart_in_atlas = true;
|
|
|
|
}
|
|
|
|
|
2014-08-25 18:02:40 +02:00
|
|
|
static bool restart = false;
|
|
|
|
// trigger an orderly shutdown and restart the game.
|
|
|
|
void restart_engine()
|
|
|
|
{
|
|
|
|
quit = true;
|
|
|
|
restart = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern CmdLineArgs g_args;
|
|
|
|
|
2007-05-05 19:41:20 +02:00
|
|
|
// moved into a helper function to ensure args is destroyed before
|
|
|
|
// exit(), which may result in a memory leak.
|
2008-07-17 19:00:00 +02:00
|
|
|
static void RunGameOrAtlas(int argc, const char* argv[])
|
2004-07-12 18:50:46 +02:00
|
|
|
{
|
2007-05-04 19:30:32 +02:00
|
|
|
CmdLineArgs args(argc, argv);
|
2005-01-25 19:56:43 +01:00
|
|
|
|
2014-08-25 18:02:40 +02:00
|
|
|
g_args = args;
|
|
|
|
|
2016-01-08 20:35:33 +01:00
|
|
|
if (args.Has("version") || args.Has("-version"))
|
|
|
|
{
|
|
|
|
debug_printf("Pyrogenesis %s\n", engine_version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-03-24 01:55:35 +01:00
|
|
|
// We need to initialise libxml2 in the main thread before
|
|
|
|
// any thread uses it. So initialise it here before we
|
|
|
|
// might run Atlas.
|
|
|
|
CXeromyces::Startup();
|
|
|
|
|
2007-05-05 19:41:20 +02:00
|
|
|
// Atlas handles the whole init/shutdown/etc sequence by itself;
|
2015-12-18 22:16:08 +01:00
|
|
|
if (ATLAS_RunIfOnCmdLine(args, false))
|
2007-05-05 19:41:20 +02:00
|
|
|
return;
|
2006-04-10 23:49:12 +02:00
|
|
|
|
2015-12-18 22:16:08 +01:00
|
|
|
const bool isReplay = args.Has("replay");
|
|
|
|
const bool isVisualReplay = args.Has("replay-visual");
|
|
|
|
const std::string replayFile = isReplay ? args.Get("replay") : (isVisualReplay ? args.Get("replay-visual") : "");
|
|
|
|
|
|
|
|
// Ensure the replay file exists
|
|
|
|
if (isReplay || isVisualReplay)
|
2010-08-07 00:16:05 +02:00
|
|
|
{
|
2015-06-06 10:45:49 +02:00
|
|
|
if (!FileExists(OsPath(replayFile)))
|
|
|
|
{
|
|
|
|
debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
|
|
|
|
return;
|
|
|
|
}
|
2015-12-18 22:16:08 +01:00
|
|
|
if (DirectoryExists(OsPath(replayFile)))
|
|
|
|
{
|
|
|
|
debug_printf("ERROR: The requested replay file '%s' is a directory!\n", replayFile.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// run non-visual simulation replay if requested
|
|
|
|
if (isReplay)
|
|
|
|
{
|
2010-08-07 00:16:05 +02:00
|
|
|
Paths paths(args);
|
|
|
|
g_VFS = CreateVfs(20 * MiB);
|
|
|
|
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE);
|
2014-08-25 18:02:40 +02:00
|
|
|
MountMods(paths, GetMods(args, INIT_MODS));
|
2010-08-07 00:16:05 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
CReplayPlayer replay;
|
2015-06-06 10:45:49 +02:00
|
|
|
replay.Load(replayFile);
|
2015-04-11 20:12:35 +02:00
|
|
|
replay.Replay(args.Has("serializationtest"), args.Has("ooslog"));
|
2010-08-07 00:16:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_VFS.reset();
|
|
|
|
|
|
|
|
CXeromyces::Terminate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-09-18 20:21:00 +02:00
|
|
|
// run in archive-building mode if requested
|
|
|
|
if (args.Has("archivebuild"))
|
|
|
|
{
|
|
|
|
Paths paths(args);
|
|
|
|
|
2011-03-23 14:36:20 +01:00
|
|
|
OsPath mod(args.Get("archivebuild"));
|
|
|
|
OsPath zip;
|
2010-09-18 20:21:00 +02:00
|
|
|
if (args.Has("archivebuild-output"))
|
2011-03-23 14:36:20 +01:00
|
|
|
zip = args.Get("archivebuild-output");
|
2010-09-18 20:21:00 +02:00
|
|
|
else
|
2011-03-23 14:36:20 +01:00
|
|
|
zip = mod.Filename().ChangeExtension(L".zip");
|
2010-09-18 20:21:00 +02:00
|
|
|
|
|
|
|
CArchiveBuilder builder(mod, paths.Cache());
|
2014-06-15 18:43:08 +02:00
|
|
|
|
|
|
|
// Add mods provided on the command line
|
|
|
|
// NOTE: We do not handle mods in the user mod path here
|
|
|
|
std::vector<CStr> mods = args.GetMultiple("mod");
|
|
|
|
for (size_t i = 0; i < mods.size(); ++i)
|
|
|
|
builder.AddBaseMod(paths.RData()/"mods"/mods[i]);
|
|
|
|
|
2012-03-19 22:16:12 +01:00
|
|
|
builder.Build(zip, args.Has("archivebuild-compress"));
|
2010-09-18 20:21:00 +02:00
|
|
|
|
|
|
|
CXeromyces::Terminate();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-05 10:27:33 +02:00
|
|
|
const double res = timer_Resolution();
|
|
|
|
g_frequencyFilter = CreateFrequencyFilter(res, 30.0);
|
|
|
|
|
2007-05-04 19:30:32 +02:00
|
|
|
// run the game
|
2014-08-25 18:02:40 +02:00
|
|
|
int flags = INIT_MODS;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
restart = false;
|
|
|
|
quit = false;
|
|
|
|
if (!Init(args, flags))
|
|
|
|
{
|
|
|
|
flags &= ~INIT_MODS;
|
|
|
|
Shutdown(SHUTDOWN_FROM_CONFIG);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
InitGraphics(args, 0);
|
|
|
|
MainControllerInit();
|
|
|
|
while (!quit)
|
|
|
|
Frame();
|
|
|
|
Shutdown(0);
|
|
|
|
MainControllerShutdown();
|
|
|
|
flags &= ~INIT_MODS;
|
|
|
|
} while (restart);
|
2009-03-24 01:55:35 +01:00
|
|
|
|
2010-07-07 23:47:31 +02:00
|
|
|
if (restart_in_atlas)
|
|
|
|
{
|
|
|
|
ATLAS_RunIfOnCmdLine(args, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-03-24 01:55:35 +01:00
|
|
|
// Shut down libxml2 (done here to match the Startup call)
|
|
|
|
CXeromyces::Terminate();
|
2007-05-05 19:41:20 +02:00
|
|
|
}
|
|
|
|
|
2012-02-15 14:58:58 +01:00
|
|
|
#if OS_ANDROID
|
|
|
|
// In Android we compile the engine as a shared library, not an executable,
|
|
|
|
// so rename main() to a different symbol that the wrapper library can load
|
|
|
|
#undef main
|
|
|
|
#define main pyrogenesis_main
|
2012-02-15 16:40:31 +01:00
|
|
|
extern "C" __attribute__((visibility ("default"))) int main(int argc, char* argv[]);
|
2012-02-15 14:58:58 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
extern "C" int main(int argc, char* argv[])
|
2007-05-05 19:41:20 +02:00
|
|
|
{
|
2012-01-16 04:30:55 +01:00
|
|
|
#if OS_UNIX
|
|
|
|
// Don't allow people to run the game with root permissions,
|
|
|
|
// because bad things can happen, check before we do anything
|
|
|
|
if (geteuid() == 0)
|
|
|
|
{
|
|
|
|
std::cerr << "********************************************************\n"
|
|
|
|
<< "WARNING: Attempted to run the game with root permission!\n"
|
|
|
|
<< "This is not allowed because it can alter home directory \n"
|
|
|
|
<< "permissions and opens your system to vulnerabilities. \n"
|
2013-10-13 13:41:30 +02:00
|
|
|
<< "(You received this message because you were either \n"
|
2014-08-25 18:02:40 +02:00
|
|
|
<<" logged in as root or used e.g. the 'sudo' command.) \n"
|
2012-01-16 04:30:55 +01:00
|
|
|
<< "********************************************************\n\n";
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
#endif // OS_UNIX
|
|
|
|
|
2007-05-27 01:29:20 +02:00
|
|
|
EarlyInit(); // must come at beginning of main
|
2007-05-05 19:41:20 +02:00
|
|
|
|
2008-07-17 19:00:00 +02:00
|
|
|
RunGameOrAtlas(argc, const_cast<const char**>(argv));
|
2004-07-10 16:14:36 +02:00
|
|
|
|
2011-11-09 01:09:19 +01:00
|
|
|
// Shut down profiler initialised by EarlyInit
|
|
|
|
g_Profiler2.Shutdown();
|
|
|
|
|
2010-05-14 23:15:33 +02:00
|
|
|
return EXIT_SUCCESS;
|
2004-05-24 22:25:48 +02:00
|
|
|
}
|