In preparation of renaming and grouping main.cpp shutdown variables:
[0ad.git] / source / ps / Game.cpp
blob6ed28df9a7cee3ec35e57d789893a352a65232da
1 /* Copyright (C) 2018 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "Game.h"
22 #include "graphics/GameView.h"
23 #include "graphics/LOSTexture.h"
24 #include "graphics/ParticleManager.h"
25 #include "graphics/UnitManager.h"
26 #include "gui/GUIManager.h"
27 #include "gui/CGUI.h"
28 #include "lib/config2.h"
29 #include "lib/timer.h"
30 #include "network/NetClient.h"
31 #include "network/NetServer.h"
32 #include "ps/CConsole.h"
33 #include "ps/CLogger.h"
34 #include "ps/CStr.h"
35 #include "ps/Loader.h"
36 #include "ps/LoaderThunks.h"
37 #include "ps/Profile.h"
38 #include "ps/Replay.h"
39 #include "ps/Shapes.h"
40 #include "ps/World.h"
41 #include "ps/GameSetup/GameSetup.h"
42 #include "renderer/Renderer.h"
43 #include "renderer/TimeManager.h"
44 #include "renderer/WaterManager.h"
45 #include "scriptinterface/ScriptInterface.h"
46 #include "simulation2/Simulation2.h"
47 #include "simulation2/components/ICmpPlayer.h"
48 #include "simulation2/components/ICmpPlayerManager.h"
49 #include "simulation2/system/ReplayTurnManager.h"
50 #include "soundmanager/ISoundManager.h"
52 #include "tools/atlas/GameInterface/GameLoop.h"
54 extern bool g_GameRestarted;
55 extern GameLoopState* g_AtlasGameLoop;
57 /**
58 * Globally accessible pointer to the CGame object.
59 **/
60 CGame *g_Game=NULL;
62 /**
63 * Constructor
65 **/
66 CGame::CGame(bool disableGraphics, bool replayLog):
67 m_World(new CWorld(this)),
68 m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptRuntime, m_World->GetTerrain())),
69 m_GameView(disableGraphics ? NULL : new CGameView(this)),
70 m_GameStarted(false),
71 m_Paused(false),
72 m_SimRate(1.0f),
73 m_PlayerID(-1),
74 m_ViewedPlayerID(-1),
75 m_IsSavedGame(false),
76 m_IsVisualReplay(false),
77 m_ReplayStream(NULL)
79 // TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps?
80 if (replayLog)
81 m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
82 else
83 m_ReplayLogger = new CDummyReplayLogger();
85 // Need to set the CObjectManager references after various objects have
86 // been initialised, so do it here rather than via the initialisers above.
87 if (m_GameView)
88 m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
90 m_TurnManager = new CLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
92 m_Simulation2->LoadDefaultScripts();
95 /**
96 * Destructor
98 **/
99 CGame::~CGame()
101 // Again, the in-game call tree is going to be different to the main menu one.
102 if (CProfileManager::IsInitialised())
103 g_Profiler.StructuralReset();
105 delete m_TurnManager;
106 delete m_GameView;
107 delete m_Simulation2;
108 delete m_World;
109 delete m_ReplayLogger;
110 delete m_ReplayStream;
113 void CGame::SetTurnManager(CTurnManager* turnManager)
115 if (m_TurnManager)
116 delete m_TurnManager;
118 m_TurnManager = turnManager;
120 if (m_TurnManager)
121 m_TurnManager->SetPlayerID(m_PlayerID);
124 int CGame::LoadVisualReplayData()
126 ENSURE(m_IsVisualReplay);
127 ENSURE(!m_ReplayPath.empty());
128 ENSURE(m_ReplayStream);
130 CReplayTurnManager* replayTurnMgr = static_cast<CReplayTurnManager*>(GetTurnManager());
132 u32 currentTurn = 0;
133 std::string type;
134 while ((*m_ReplayStream >> type).good())
136 if (type == "turn")
138 u32 turn = 0;
139 u32 turnLength = 0;
140 *m_ReplayStream >> turn >> turnLength;
141 ENSURE(turn == currentTurn && "You tried to replay a commands.txt file of a rejoined client. Please use the host's file.");
142 replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength);
144 else if (type == "cmd")
146 player_id_t player;
147 *m_ReplayStream >> player;
149 std::string line;
150 std::getline(*m_ReplayStream, line);
151 replayTurnMgr->StoreReplayCommand(currentTurn, player, line);
153 else if (type == "hash" || type == "hash-quick")
155 bool quick = (type == "hash-quick");
156 std::string replayHash;
157 *m_ReplayStream >> replayHash;
158 replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick);
160 else if (type == "end")
161 ++currentTurn;
162 else
163 CancelLoad(L"Failed to load replay data (unrecognized content)");
165 SAFE_DELETE(m_ReplayStream);
166 m_FinalReplayTurn = currentTurn > 0 ? currentTurn - 1 : 0;
167 replayTurnMgr->StoreFinalReplayTurn(m_FinalReplayTurn);
168 return 0;
171 bool CGame::StartVisualReplay(const OsPath& replayPath)
173 debug_printf("Starting to replay %s\n", replayPath.string8().c_str());
175 m_IsVisualReplay = true;
177 SetTurnManager(new CReplayTurnManager(*m_Simulation2, GetReplayLogger()));
179 m_ReplayPath = replayPath;
180 m_ReplayStream = new std::ifstream(OsString(replayPath).c_str());
182 std::string type;
183 ENSURE((*m_ReplayStream >> type).good() && type == "start");
185 std::string line;
186 std::getline(*m_ReplayStream, line);
188 const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
189 JSContext* cx = scriptInterface.GetContext();
190 JSAutoRequest rq(cx);
192 JS::RootedValue attribs(cx);
193 scriptInterface.ParseJSON(line, &attribs);
194 StartGame(&attribs, "");
196 return true;
200 * Initializes the game with the set of attributes provided.
201 * Makes calls to initialize the game view, world, and simulation objects.
202 * Calls are made to facilitate progress reporting of the initialization.
204 void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& savedState)
206 const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
207 JSContext* cx = scriptInterface.GetContext();
208 JSAutoRequest rq(cx);
210 m_InitialSavedState = savedState;
211 m_IsSavedGame = !savedState.empty();
213 m_Simulation2->SetInitAttributes(attribs);
215 std::string mapType;
216 scriptInterface.GetProperty(attribs, "mapType", mapType);
218 float speed;
219 if (scriptInterface.HasProperty(attribs, "gameSpeed") && scriptInterface.GetProperty(attribs, "gameSpeed", speed))
220 SetSimRate(speed);
222 LDR_BeginRegistering();
224 RegMemFun(m_Simulation2, &CSimulation2::ProgressiveLoad, L"Simulation init", 1000);
226 // RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
227 // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
228 // values. At the minute, it's just lighting settings, but could be extended to store camera position.
229 // Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at
230 // some point to be stored in the world object?
231 if (m_GameView)
232 m_GameView->RegisterInit();
234 if (mapType == "random")
236 // Load random map attributes
237 std::wstring scriptFile;
238 JS::RootedValue settings(cx);
240 scriptInterface.GetProperty(attribs, "script", scriptFile);
241 scriptInterface.GetProperty(attribs, "settings", &settings);
243 m_World->RegisterInitRMS(scriptFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID);
245 else
247 std::wstring mapFile;
248 JS::RootedValue settings(cx);
249 scriptInterface.GetProperty(attribs, "map", mapFile);
250 scriptInterface.GetProperty(attribs, "settings", &settings);
252 m_World->RegisterInit(mapFile, scriptInterface.GetJSRuntime(), settings, m_PlayerID);
254 if (m_GameView)
255 RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
257 if (m_IsSavedGame)
258 RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000);
260 if (m_IsVisualReplay)
261 RegMemFun(this, &CGame::LoadVisualReplayData, L"Loading visual replay data", 1000);
263 LDR_EndRegistering();
266 int CGame::LoadInitialState()
268 ENSURE(m_IsSavedGame);
269 ENSURE(!m_InitialSavedState.empty());
271 std::string state;
272 m_InitialSavedState.swap(state); // deletes the original to save a bit of memory
274 std::stringstream stream(state);
276 bool ok = m_Simulation2->DeserializeState(stream);
277 if (!ok)
279 CancelLoad(L"Failed to load saved game state. It might have been\nsaved with an incompatible version of the game.");
280 return 0;
283 return 0;
287 * Game initialization has been completed. Set game started flag and start the session.
289 * @return PSRETURN 0
291 PSRETURN CGame::ReallyStartGame()
293 JSContext* cx = m_Simulation2->GetScriptInterface().GetContext();
294 JSAutoRequest rq(cx);
296 // Call the script function InitGame only for new games, not saved games
297 if (!m_IsSavedGame)
299 // Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
300 // that needs to be done before setting up the AI and shouldn't be done in Atlas
301 if (!g_AtlasGameLoop->running)
302 m_Simulation2->PreInitGame();
304 m_Simulation2->InitGame();
307 // We need to do an initial Interpolate call to set up all the models etc,
308 // because Update might never interpolate (e.g. if the game starts paused)
309 // and we could end up rendering before having set up any models (so they'd
310 // all be invisible)
311 Interpolate(0, 0);
313 m_GameStarted=true;
315 // Render a frame to begin loading assets
316 if (CRenderer::IsInitialised())
317 Render();
319 if (g_NetClient)
320 g_NetClient->LoadFinished();
322 // Call the reallyStartGame GUI function, but only if it exists
323 if (g_GUI && g_GUI->HasPages())
325 JS::RootedValue global(cx, g_GUI->GetActiveGUI()->GetGlobalObject());
326 if (g_GUI->GetActiveGUI()->GetScriptInterface()->HasProperty(global, "reallyStartGame"))
327 g_GUI->GetActiveGUI()->GetScriptInterface()->CallFunctionVoid(global, "reallyStartGame");
330 debug_printf("GAME STARTED, ALL INIT COMPLETE\n");
332 // The call tree we've built for pregame probably isn't useful in-game.
333 if (CProfileManager::IsInitialised())
334 g_Profiler.StructuralReset();
336 g_GameRestarted = true;
338 return 0;
341 int CGame::GetPlayerID()
343 return m_PlayerID;
346 void CGame::SetPlayerID(player_id_t playerID)
348 m_PlayerID = playerID;
349 m_ViewedPlayerID = playerID;
351 if (m_TurnManager)
352 m_TurnManager->SetPlayerID(m_PlayerID);
355 int CGame::GetViewedPlayerID()
357 return m_ViewedPlayerID;
360 void CGame::SetViewedPlayerID(player_id_t playerID)
362 m_ViewedPlayerID = playerID;
365 void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState)
367 if (m_ReplayLogger)
368 m_ReplayLogger->StartGame(attribs);
370 RegisterInit(attribs, savedState);
373 // TODO: doInterpolate is optional because Atlas interpolates explicitly,
374 // so that it has more control over the update rate. The game might want to
375 // do the same, and then doInterpolate should be redundant and removed.
377 void CGame::Update(const double deltaRealTime, bool doInterpolate)
379 if (m_Paused || !m_TurnManager)
380 return;
382 const double deltaSimTime = deltaRealTime * m_SimRate;
384 if (deltaSimTime)
386 // To avoid confusing the profiler, we need to trigger the new turn
387 // while we're not nested inside any PROFILE blocks
388 if (m_TurnManager->WillUpdate(deltaSimTime))
389 g_Profiler.Turn();
391 // At the normal sim rate, we currently want to render at least one
392 // frame per simulation turn, so let maxTurns be 1. But for fast-forward
393 // sim rates we want to allow more, so it's not bounded by framerate,
394 // so just use the sim rate itself as the number of turns per frame.
395 size_t maxTurns = (size_t)m_SimRate;
397 if (m_TurnManager->Update(deltaSimTime, maxTurns))
400 PROFILE3("gui sim update");
401 g_GUI->SendEventToAll("SimulationUpdate");
404 GetView()->GetLOSTexture().MakeDirty();
407 if (CRenderer::IsInitialised())
408 g_Renderer.GetTimeManager().Update(deltaSimTime);
411 if (doInterpolate)
413 m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
415 if ( g_SoundManager )
416 g_SoundManager->IdleTask();
420 void CGame::Interpolate(float simFrameLength, float realFrameLength)
422 if (!m_TurnManager)
423 return;
425 m_TurnManager->Interpolate(simFrameLength, realFrameLength);
429 static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
431 void CGame::CachePlayerColors()
433 m_PlayerColors.clear();
435 CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
436 if (!cmpPlayerManager)
437 return;
439 int numPlayers = cmpPlayerManager->GetNumPlayers();
440 m_PlayerColors.resize(numPlayers);
442 for (int i = 0; i < numPlayers; ++i)
444 CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(i));
445 if (!cmpPlayer)
446 m_PlayerColors[i] = BrokenColor;
447 else
448 m_PlayerColors[i] = cmpPlayer->GetDisplayedColor();
453 CColor CGame::GetPlayerColor(player_id_t player) const
455 if (player < 0 || player >= (int)m_PlayerColors.size())
456 return BrokenColor;
458 return m_PlayerColors[player];
461 bool CGame::IsGameFinished() const
463 for (const std::pair<entity_id_t, IComponent*>& p : m_Simulation2->GetEntitiesWithInterface(IID_Player))
465 CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, p.first);
466 if (cmpPlayer && cmpPlayer->GetState() == "won")
467 return true;
470 return false;