LCOV - code coverage report
Current view: top level - source/ps - Game.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 3 207 1.4 %
Date: 2023-01-19 00:18:29 Functions: 2 21 9.5 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       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.
       8             :  *
       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.
      13             :  *
      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/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "Game.h"
      21             : 
      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/World.h"
      40             : #include "ps/GameSetup/GameSetup.h"
      41             : #include "renderer/Renderer.h"
      42             : #include "renderer/SceneRenderer.h"
      43             : #include "renderer/TimeManager.h"
      44             : #include "renderer/WaterManager.h"
      45             : #include "scriptinterface/FunctionWrapper.h"
      46             : #include "scriptinterface/ScriptInterface.h"
      47             : #include "scriptinterface/JSON.h"
      48             : #include "simulation2/Simulation2.h"
      49             : #include "simulation2/components/ICmpPlayer.h"
      50             : #include "simulation2/components/ICmpPlayerManager.h"
      51             : #include "simulation2/system/ReplayTurnManager.h"
      52             : #include "soundmanager/ISoundManager.h"
      53             : #include "tools/atlas/GameInterface/GameLoop.h"
      54             : 
      55             : #include <fstream>
      56             : 
      57             : extern GameLoopState* g_AtlasGameLoop;
      58             : 
      59             : /**
      60             :  * Globally accessible pointer to the CGame object.
      61             :  **/
      62             : CGame *g_Game=NULL;
      63             : 
      64           1 : const CStr CGame::EventNameSimulationUpdate = "SimulationUpdate";
      65             : 
      66             : /**
      67             :  * Constructor
      68             :  *
      69             :  **/
      70           0 : CGame::CGame(bool replayLog):
      71           0 :     m_World(new CWorld(this)),
      72           0 :     m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptContext, m_World->GetTerrain())),
      73           0 :     m_GameView(CRenderer::IsInitialised() ? new CGameView(this) : nullptr),
      74             :     m_GameStarted(false),
      75             :     m_Paused(false),
      76             :     m_SimRate(1.0f),
      77             :     m_PlayerID(-1),
      78             :     m_ViewedPlayerID(-1),
      79             :     m_IsSavedGame(false),
      80             :     m_IsVisualReplay(false),
      81           0 :     m_ReplayStream(NULL)
      82             : {
      83             :     // TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps?
      84           0 :     if (replayLog)
      85           0 :         m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
      86             :     else
      87           0 :         m_ReplayLogger = new CDummyReplayLogger();
      88             : 
      89             :     // Need to set the CObjectManager references after various objects have
      90             :     // been initialised, so do it here rather than via the initialisers above.
      91           0 :     if (m_GameView)
      92           0 :         m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
      93             : 
      94           0 :     m_TurnManager = new CLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
      95             : 
      96           0 :     m_Simulation2->LoadDefaultScripts();
      97           0 : }
      98             : 
      99             : /**
     100             :  * Destructor
     101             :  *
     102             :  **/
     103           0 : CGame::~CGame()
     104             : {
     105             :     // Again, the in-game call tree is going to be different to the main menu one.
     106           0 :     if (CProfileManager::IsInitialised())
     107           0 :         g_Profiler.StructuralReset();
     108             : 
     109           0 :     if (m_ReplayLogger && m_GameStarted)
     110           0 :         m_ReplayLogger->SaveMetadata(*m_Simulation2);
     111             : 
     112           0 :     delete m_TurnManager;
     113           0 :     delete m_GameView;
     114           0 :     delete m_Simulation2;
     115           0 :     delete m_World;
     116           0 :     delete m_ReplayLogger;
     117           0 :     delete m_ReplayStream;
     118           0 : }
     119             : 
     120           0 : void CGame::SetTurnManager(CTurnManager* turnManager)
     121             : {
     122           0 :     if (m_TurnManager)
     123           0 :         delete m_TurnManager;
     124             : 
     125           0 :     m_TurnManager = turnManager;
     126             : 
     127           0 :     if (m_TurnManager)
     128           0 :         m_TurnManager->SetPlayerID(m_PlayerID);
     129           0 : }
     130             : 
     131           0 : int CGame::LoadVisualReplayData()
     132             : {
     133           0 :     ENSURE(m_IsVisualReplay);
     134           0 :     ENSURE(!m_ReplayPath.empty());
     135           0 :     ENSURE(m_ReplayStream);
     136             : 
     137           0 :     CReplayTurnManager* replayTurnMgr = static_cast<CReplayTurnManager*>(GetTurnManager());
     138             : 
     139           0 :     u32 currentTurn = 0;
     140           0 :     std::string type;
     141           0 :     while ((*m_ReplayStream >> type).good())
     142             :     {
     143           0 :         if (type == "turn")
     144             :         {
     145           0 :             u32 turn = 0;
     146           0 :             u32 turnLength = 0;
     147           0 :             *m_ReplayStream >> turn >> turnLength;
     148           0 :             ENSURE(turn == currentTurn && "You tried to replay a commands.txt file of a rejoined client. Please use the host's file.");
     149           0 :             replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength);
     150             :         }
     151           0 :         else if (type == "cmd")
     152             :         {
     153             :             player_id_t player;
     154           0 :             *m_ReplayStream >> player;
     155             : 
     156           0 :             std::string line;
     157           0 :             std::getline(*m_ReplayStream, line);
     158           0 :             replayTurnMgr->StoreReplayCommand(currentTurn, player, line);
     159             :         }
     160           0 :         else if (type == "hash" || type == "hash-quick")
     161             :         {
     162           0 :             bool quick = (type == "hash-quick");
     163           0 :             std::string replayHash;
     164           0 :             *m_ReplayStream >> replayHash;
     165           0 :             replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick);
     166             :         }
     167           0 :         else if (type == "end")
     168           0 :             ++currentTurn;
     169             :         else
     170           0 :             CancelLoad(L"Failed to load replay data (unrecognized content)");
     171             :     }
     172           0 :     SAFE_DELETE(m_ReplayStream);
     173           0 :     m_FinalReplayTurn = currentTurn > 0 ? currentTurn - 1 : 0;
     174           0 :     replayTurnMgr->StoreFinalReplayTurn(m_FinalReplayTurn);
     175           0 :     return 0;
     176             : }
     177             : 
     178           0 : bool CGame::StartVisualReplay(const OsPath& replayPath)
     179             : {
     180           0 :     debug_printf("Starting to replay %s\n", replayPath.string8().c_str());
     181             : 
     182           0 :     m_IsVisualReplay = true;
     183             : 
     184           0 :     SetTurnManager(new CReplayTurnManager(*m_Simulation2, GetReplayLogger()));
     185             : 
     186           0 :     m_ReplayPath = replayPath;
     187           0 :     m_ReplayStream = new std::ifstream(OsString(replayPath).c_str());
     188             : 
     189           0 :     std::string type;
     190           0 :     ENSURE((*m_ReplayStream >> type).good() && type == "start");
     191             : 
     192           0 :     std::string line;
     193           0 :     std::getline(*m_ReplayStream, line);
     194             : 
     195           0 :     const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
     196           0 :     ScriptRequest rq(scriptInterface);
     197             : 
     198           0 :     JS::RootedValue attribs(rq.cx);
     199           0 :     Script::ParseJSON(rq, line, &attribs);
     200           0 :     StartGame(&attribs, "");
     201             : 
     202           0 :     return true;
     203             : }
     204             : 
     205             : /**
     206             :  * Initializes the game with the set of attributes provided.
     207             :  * Makes calls to initialize the game view, world, and simulation objects.
     208             :  * Calls are made to facilitate progress reporting of the initialization.
     209             :  **/
     210           0 : void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& savedState)
     211             : {
     212           0 :     const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
     213           0 :     ScriptRequest rq(scriptInterface);
     214             : 
     215           0 :     m_InitialSavedState = savedState;
     216           0 :     m_IsSavedGame = !savedState.empty();
     217             : 
     218           0 :     m_Simulation2->SetInitAttributes(attribs);
     219             : 
     220           0 :     std::string mapType;
     221           0 :     Script::GetProperty(rq, attribs, "mapType", mapType);
     222             : 
     223             :     float speed;
     224           0 :     if (Script::HasProperty(rq, attribs, "gameSpeed"))
     225             :     {
     226           0 :         if (Script::GetProperty(rq, attribs, "gameSpeed", speed))
     227           0 :             SetSimRate(speed);
     228             :         else
     229           0 :             LOGERROR("GameSpeed could not be parsed.");
     230             :     }
     231             : 
     232           0 :     LDR_BeginRegistering();
     233             : 
     234           0 :     RegMemFun(m_Simulation2, &CSimulation2::ProgressiveLoad, L"Simulation init", 1000);
     235             : 
     236             :     // RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
     237             :     // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
     238             :     // values.  At the minute, it's just lighting settings, but could be extended to store camera position.
     239             :     // Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at
     240             :     // some point to be stored in the world object?
     241           0 :     if (m_GameView)
     242           0 :         m_GameView->RegisterInit();
     243             : 
     244           0 :     if (mapType == "random")
     245             :     {
     246             :         // Load random map attributes
     247           0 :         std::wstring scriptFile;
     248           0 :         JS::RootedValue settings(rq.cx);
     249             : 
     250           0 :         Script::GetProperty(rq, attribs, "script", scriptFile);
     251           0 :         Script::GetProperty(rq, attribs, "settings", &settings);
     252             : 
     253           0 :         m_World->RegisterInitRMS(scriptFile, *scriptInterface.GetContext(), settings, m_PlayerID);
     254             :     }
     255             :     else
     256             :     {
     257           0 :         std::wstring mapFile;
     258           0 :         JS::RootedValue settings(rq.cx);
     259           0 :         Script::GetProperty(rq, attribs, "map", mapFile);
     260           0 :         Script::GetProperty(rq, attribs, "settings", &settings);
     261             : 
     262           0 :         m_World->RegisterInit(mapFile, *scriptInterface.GetContext(), settings, m_PlayerID);
     263             :     }
     264           0 :     if (m_GameView)
     265           0 :         RegMemFun(&g_Renderer.GetSceneRenderer().GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
     266             : 
     267           0 :     if (m_IsSavedGame)
     268           0 :         RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000);
     269             : 
     270           0 :     if (m_IsVisualReplay)
     271           0 :         RegMemFun(this, &CGame::LoadVisualReplayData, L"Loading visual replay data", 1000);
     272             : 
     273           0 :     LDR_EndRegistering();
     274           0 : }
     275             : 
     276           0 : int CGame::LoadInitialState()
     277             : {
     278           0 :     ENSURE(m_IsSavedGame);
     279           0 :     ENSURE(!m_InitialSavedState.empty());
     280             : 
     281           0 :     std::string state;
     282           0 :     m_InitialSavedState.swap(state); // deletes the original to save a bit of memory
     283             : 
     284           0 :     std::stringstream stream(state);
     285             : 
     286           0 :     bool ok = m_Simulation2->DeserializeState(stream);
     287           0 :     if (!ok)
     288             :     {
     289           0 :         CancelLoad(L"Failed to load saved game state. It might have been\nsaved with an incompatible version of the game.");
     290           0 :         return 0;
     291             :     }
     292             : 
     293           0 :     return 0;
     294             : }
     295             : 
     296             : /**
     297             :  * Game initialization has been completed. Set game started flag and start the session.
     298             :  *
     299             :  * @return PSRETURN 0
     300             :  **/
     301           0 : PSRETURN CGame::ReallyStartGame()
     302             : {
     303             :     // Call the script function InitGame only for new games, not saved games
     304           0 :     if (!m_IsSavedGame)
     305             :     {
     306             :         // Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
     307             :         // that needs to be done before setting up the AI and shouldn't be done in Atlas
     308           0 :         if (!g_AtlasGameLoop->running)
     309           0 :             m_Simulation2->PreInitGame();
     310             : 
     311           0 :         m_Simulation2->InitGame();
     312             :     }
     313             : 
     314             :     // We need to do an initial Interpolate call to set up all the models etc,
     315             :     // because Update might never interpolate (e.g. if the game starts paused)
     316             :     // and we could end up rendering before having set up any models (so they'd
     317             :     // all be invisible)
     318           0 :     Interpolate(0, 0);
     319             : 
     320           0 :     m_GameStarted = true;
     321             : 
     322             :     // Preload resources to avoid blinking on a first game frame.
     323           0 :     if (CRenderer::IsInitialised())
     324           0 :         g_Renderer.PreloadResourcesBeforeNextFrame();
     325             : 
     326           0 :     if (g_NetClient)
     327           0 :         g_NetClient->LoadFinished();
     328             : 
     329             :     // Call the reallyStartGame GUI function, but only if it exists
     330           0 :     if (g_GUI && g_GUI->GetPageCount())
     331             :     {
     332           0 :         std::shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface();
     333           0 :         ScriptRequest rq(scriptInterface);
     334             : 
     335           0 :         JS::RootedValue global(rq.cx, rq.globalValue());
     336           0 :         if (Script::HasProperty(rq, global, "reallyStartGame"))
     337           0 :             ScriptFunction::CallVoid(rq, global, "reallyStartGame");
     338             :     }
     339             : 
     340           0 :     debug_printf("GAME STARTED, ALL INIT COMPLETE\n");
     341             : 
     342             :     // The call tree we've built for pregame probably isn't useful in-game.
     343           0 :     if (CProfileManager::IsInitialised())
     344           0 :         g_Profiler.StructuralReset();
     345             : 
     346           0 :     return 0;
     347             : }
     348             : 
     349           0 : int CGame::GetPlayerID()
     350             : {
     351           0 :     return m_PlayerID;
     352             : }
     353             : 
     354           0 : void CGame::SetPlayerID(player_id_t playerID)
     355             : {
     356           0 :     m_PlayerID = playerID;
     357           0 :     m_ViewedPlayerID = playerID;
     358             : 
     359           0 :     if (m_TurnManager)
     360           0 :         m_TurnManager->SetPlayerID(m_PlayerID);
     361           0 : }
     362             : 
     363           0 : int CGame::GetViewedPlayerID()
     364             : {
     365           0 :     return m_ViewedPlayerID;
     366             : }
     367             : 
     368           0 : void CGame::SetViewedPlayerID(player_id_t playerID)
     369             : {
     370           0 :     m_ViewedPlayerID = playerID;
     371           0 : }
     372             : 
     373           0 : void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState)
     374             : {
     375           0 :     if (m_ReplayLogger)
     376           0 :         m_ReplayLogger->StartGame(attribs);
     377             : 
     378           0 :     RegisterInit(attribs, savedState);
     379           0 : }
     380             : 
     381             : // TODO: doInterpolate is optional because Atlas interpolates explicitly,
     382             : // so that it has more control over the update rate. The game might want to
     383             : // do the same, and then doInterpolate should be redundant and removed.
     384             : 
     385           0 : void CGame::Update(const double deltaRealTime, bool doInterpolate)
     386             : {
     387           0 :     if (m_Paused || !m_TurnManager)
     388           0 :         return;
     389             : 
     390           0 :     const double deltaSimTime = deltaRealTime * m_SimRate;
     391             : 
     392           0 :     if (deltaSimTime)
     393             :     {
     394             :         // At the normal sim rate, we currently want to render at least one
     395             :         // frame per simulation turn, so let maxTurns be 1. But for fast-forward
     396             :         // sim rates we want to allow more, so it's not bounded by framerate,
     397             :         // so just use the sim rate itself as the number of turns per frame.
     398           0 :         size_t maxTurns = (size_t)m_SimRate;
     399             : 
     400           0 :         if (m_TurnManager->Update(deltaSimTime, maxTurns))
     401             :         {
     402             :             {
     403           0 :                 PROFILE3("gui sim update");
     404           0 :                 g_GUI->SendEventToAll(EventNameSimulationUpdate);
     405             :             }
     406             : 
     407           0 :             GetView()->GetLOSTexture().MakeDirty();
     408             :         }
     409             : 
     410           0 :         if (CRenderer::IsInitialised())
     411           0 :             g_Renderer.GetTimeManager().Update(deltaSimTime);
     412             :     }
     413             : 
     414           0 :     if (doInterpolate)
     415           0 :         m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
     416             : }
     417             : 
     418           0 : void CGame::Interpolate(float simFrameLength, float realFrameLength)
     419             : {
     420           0 :     if (!m_TurnManager)
     421           0 :         return;
     422             : 
     423           0 :     m_TurnManager->Interpolate(simFrameLength, realFrameLength);
     424             : }
     425             : 
     426             : 
     427           1 : static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
     428             : 
     429           0 : void CGame::CachePlayerColors()
     430             : {
     431           0 :     m_PlayerColors.clear();
     432             : 
     433           0 :     CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
     434           0 :     if (!cmpPlayerManager)
     435           0 :         return;
     436             : 
     437           0 :     int numPlayers = cmpPlayerManager->GetNumPlayers();
     438           0 :     m_PlayerColors.resize(numPlayers);
     439             : 
     440           0 :     for (int i = 0; i < numPlayers; ++i)
     441             :     {
     442           0 :         CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(i));
     443           0 :         if (!cmpPlayer)
     444           0 :             m_PlayerColors[i] = BrokenColor;
     445             :         else
     446           0 :             m_PlayerColors[i] = cmpPlayer->GetDisplayedColor();
     447             :     }
     448             : }
     449             : 
     450           0 : const CColor& CGame::GetPlayerColor(player_id_t player) const
     451             : {
     452           0 :     if (player < 0 || player >= (int)m_PlayerColors.size())
     453           0 :         return BrokenColor;
     454             : 
     455           0 :     return m_PlayerColors[player];
     456             : }
     457             : 
     458           0 : bool CGame::IsGameFinished() const
     459             : {
     460           0 :     for (const std::pair<entity_id_t, IComponent*>& p : m_Simulation2->GetEntitiesWithInterface(IID_Player))
     461             :     {
     462           0 :         CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, p.first);
     463           0 :         if (cmpPlayer && cmpPlayer->GetState() == "won")
     464           0 :             return true;
     465             :     }
     466             : 
     467           0 :     return false;
     468           3 : }

Generated by: LCOV version 1.13