diff --git a/binaries/data/mods/public/gui/common/functions_global_object.js b/binaries/data/mods/public/gui/common/functions_global_object.js index bc2d841d92..2634e17a37 100644 --- a/binaries/data/mods/public/gui/common/functions_global_object.js +++ b/binaries/data/mods/public/gui/common/functions_global_object.js @@ -82,17 +82,3 @@ function cancelOnLoadGameError(msg) Engine.ResetCursor(); } - -/** - * Also called from the C++ side when ending the game. - * The current page can be the summary screen or a message box, so it can't be moved to session/. - */ -function getReplayMetadata() -{ - let extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); - return { - "timeElapsed": extendedSimState.timeElapsed, - "playerStates": extendedSimState.players, - "mapSettings": Engine.GetInitAttributes().settings - }; -} diff --git a/binaries/data/mods/public/gui/credits/texts/programming.json b/binaries/data/mods/public/gui/credits/texts/programming.json index 985995b959..a50d5a78ac 100644 --- a/binaries/data/mods/public/gui/credits/texts/programming.json +++ b/binaries/data/mods/public/gui/credits/texts/programming.json @@ -109,6 +109,7 @@ {"nick": "idanwin"}, {"nick": "Imarok", "name": "J. S."}, {"nick": "infyquest", "name": "Vijay Kiran Kamuju"}, + {"nick": "irishninja", "name": "Brian Broll"}, {"nick": "IronNerd", "name": "Matthew McMullan"}, {"nick": "Itms", "name": "Nicolas Auvray"}, {"nick": "Jaison", "name": "Marco tom Suden"}, diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index a545afe125..168a60ad4e 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -42,7 +42,7 @@ var g_Ambient = ["audio/ambient/dayscape/day_temperate_gen_03.ogg"]; /** * Map, player and match settings set in gamesetup. */ -const g_GameAttributes = deepfreeze(Engine.GetInitAttributes()); +const g_GameAttributes = deepfreeze(Engine.GuiInterfaceCall("GetInitAttributes")); /** * True if this is a multiplayer game. @@ -733,7 +733,7 @@ function leaveGame(willRejoin) // Before ending the game let replayDirectory = Engine.GetCurrentReplayDirectory(); - let simData = getReplayMetadata(); + let simData = Engine.GuiInterfaceCall("GetReplayMetadata"); let playerID = Engine.GetPlayerID(); Engine.EndGame(); diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index 139ed82a3f..22011d6b2c 100644 --- a/binaries/data/mods/public/simulation/components/GuiInterface.js +++ b/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -191,6 +191,27 @@ GuiInterface.prototype.GetExtendedSimulationState = function() return ret; }; +/** + * Returns the gamesettings that were chosen at the time the match started. + */ +GuiInterface.prototype.GetInitAttributes = function() +{ + return InitAttributes; +}; + +/** + * This data will be stored in the replay metadata file after a match has been finished recording. + */ +GuiInterface.prototype.GetReplayMetadata = function() +{ + let extendedSimState = this.GetExtendedSimulationState(); + return { + "timeElapsed": extendedSimState.timeElapsed, + "playerStates": extendedSimState.players, + "mapSettings": InitAttributes.settings + }; +}; + GuiInterface.prototype.GetRenamedEntities = function(player) { if (this.miragedEntities[player]) @@ -1917,6 +1938,8 @@ let exposedFunctions = { "GetSimulationState": 1, "GetExtendedSimulationState": 1, + "GetInitAttributes": 1, + "GetReplayMetadata": 1, "GetRenamedEntities": 1, "ClearRenamedEntities": 1, "GetEntityState": 1, diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 54937b7f49..3206d3b3dc 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -102,6 +102,9 @@ CGame::~CGame() if (CProfileManager::IsInitialised()) g_Profiler.StructuralReset(); + if (m_ReplayLogger) + m_ReplayLogger->SaveMetadata(*m_Simulation2); + delete m_TurnManager; delete m_GameView; delete m_Simulation2; diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index d4e3051ce5..131c6101bd 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -701,10 +701,6 @@ static void ShutdownSDL() void EndGame() { - if (g_Game && g_Game->IsGameStarted() && !g_Game->IsVisualReplay() && - g_AtlasGameLoop && !g_AtlasGameLoop->running && CRenderer::IsInitialised()) - VisualReplay::SaveReplayMetadata(g_GUI->GetActiveGUI()->GetScriptInterface().get()); - SAFE_DELETE(g_NetClient); SAFE_DELETE(g_NetServer); SAFE_DELETE(g_Game); diff --git a/source/ps/Replay.cpp b/source/ps/Replay.cpp index 51b5cc7cea..1a051c6171 100644 --- a/source/ps/Replay.cpp +++ b/source/ps/Replay.cpp @@ -37,8 +37,11 @@ #include "scriptinterface/ScriptInterface.h" #include "scriptinterface/ScriptRuntime.h" #include "scriptinterface/ScriptStats.h" -#include "simulation2/Simulation2.h" +#include "simulation2/components/ICmpGuiInterface.h" +#include "simulation2/helpers/Player.h" #include "simulation2/helpers/SimulationCommand.h" +#include "simulation2/Simulation2.h" +#include "simulation2/system/CmpPtr.h" #include #include @@ -101,6 +104,32 @@ void CReplayLogger::Hash(const std::string& hash, bool quick) *m_Stream << "hash " << Hexify(hash) << "\n"; } +void CReplayLogger::SaveMetadata(const CSimulation2& simulation) +{ + CmpPtr cmpGuiInterface(simulation, SYSTEM_ENTITY); + if (!cmpGuiInterface) + { + LOGERROR("Could not save replay metadata!"); + return; + } + + ScriptInterface& scriptInterface = simulation.GetScriptInterface(); + JSContext* cx = scriptInterface.GetContext(); + JSAutoRequest rq(cx); + + JS::RootedValue arg(cx); + JS::RootedValue metadata(cx); + cmpGuiInterface->ScriptCall(INVALID_PLAYER, L"GetReplayMetadata", arg, &metadata); + + const OsPath fileName = g_Game->GetReplayLogger().GetDirectory() / L"metadata.json"; + CreateDirectories(fileName.Parent(), 0700); + + std::ofstream stream (OsString(fileName).c_str(), std::ofstream::out | std::ofstream::trunc); + stream << scriptInterface.StringifyJSON(&metadata, false); + stream.close(); + debug_printf("Saved replay metadata to %s\n", fileName.string8().c_str()); +} + OsPath CReplayLogger::GetDirectory() const { return m_Directory; diff --git a/source/ps/Replay.h b/source/ps/Replay.h index 7902edbe1e..01b614052d 100644 --- a/source/ps/Replay.h +++ b/source/ps/Replay.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "scriptinterface/ScriptTypes.h" struct SimulationCommand; +class CSimulation2; class ScriptInterface; /** @@ -49,6 +50,11 @@ public: */ virtual void Hash(const std::string& hash, bool quick) = 0; + /** + * Saves metadata.json containing part of the simulation state used for the summary screen. + */ + virtual void SaveMetadata(const CSimulation2& simulation) = 0; + /** * Remember the directory containing the commands.txt file, so that we can save additional files to it. */ @@ -64,6 +70,7 @@ public: virtual void StartGame(JS::MutableHandleValue UNUSED(attribs)) { } virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), std::vector& UNUSED(commands)) { } virtual void Hash(const std::string& UNUSED(hash), bool UNUSED(quick)) { } + virtual void SaveMetadata(const CSimulation2& UNUSED(simulation)) { }; virtual OsPath GetDirectory() const { return OsPath(); } }; @@ -80,6 +87,7 @@ public: virtual void StartGame(JS::MutableHandleValue attribs); virtual void Turn(u32 n, u32 turnLength, std::vector& commands); virtual void Hash(const std::string& hash, bool quick); + virtual void SaveMetadata(const CSimulation2& simulation); virtual OsPath GetDirectory() const; private: diff --git a/source/ps/VisualReplay.cpp b/source/ps/VisualReplay.cpp index 2b58b46443..6e1decc769 100644 --- a/source/ps/VisualReplay.cpp +++ b/source/ps/VisualReplay.cpp @@ -473,30 +473,6 @@ void VisualReplay::AddReplayToCache(const ScriptInterface& scriptInterface, cons StoreCacheFile(scriptInterface, cachedReplaysObject); } -void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface) -{ - JSContext* cx = scriptInterface->GetContext(); - JSAutoRequest rq(cx); - - JS::RootedValue metadata(cx); - JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); - - if (!scriptInterface->CallFunction(global, "getReplayMetadata", &metadata)) - { - LOGERROR("Could not save replay metadata!"); - return; - } - - // Get the directory of the currently active replay - const OsPath fileName = g_Game->GetReplayLogger().GetDirectory() / L"metadata.json"; - CreateDirectories(fileName.Parent(), 0700); - - std::ofstream stream (OsString(fileName).c_str(), std::ofstream::out | std::ofstream::trunc); - stream << scriptInterface->StringifyJSON(&metadata, false); - stream.close(); - debug_printf("Saved replay metadata to %s\n", fileName.string8().c_str()); -} - bool VisualReplay::HasReplayMetadata(const OsPath& directoryName) { const OsPath filePath(GetDirectoryPath() / directoryName / L"metadata.json"); diff --git a/source/ps/VisualReplay.h b/source/ps/VisualReplay.h index 18d9cc085d..eaf0b71b0f 100644 --- a/source/ps/VisualReplay.h +++ b/source/ps/VisualReplay.h @@ -114,11 +114,6 @@ bool HasReplayMetadata(const OsPath& directoryName); */ JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName); -/** - * Saves the metadata from the session to metadata.json. - */ -void SaveReplayMetadata(ScriptInterface* scriptInterface); - /** * Adds a replay to the replayCache. */ diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index 6623f07ba0..4ec71c1190 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -861,6 +861,9 @@ void CSimulation2::LoadMapSettings() // Initialize here instead of in Update() GetScriptInterface().CallFunctionVoid(global, "LoadMapSettings", m->m_MapSettings); + GetScriptInterface().FreezeObject(m->m_InitAttributes, true); + GetScriptInterface().SetGlobal("InitAttributes", m->m_InitAttributes, true, true, true); + if (!m->m_StartupScript.empty()) GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript); diff --git a/source/simulation2/scripting/JSInterface_Simulation.cpp b/source/simulation2/scripting/JSInterface_Simulation.cpp index 618f6199a9..0d37775fd5 100644 --- a/source/simulation2/scripting/JSInterface_Simulation.cpp +++ b/source/simulation2/scripting/JSInterface_Simulation.cpp @@ -34,22 +34,6 @@ #include -JS::Value JSI_Simulation::GetInitAttributes(ScriptInterface::CxPrivate* pCxPrivate) -{ - if (!g_Game) - return JS::UndefinedValue(); - - JSContext* cx = g_Game->GetSimulation2()->GetScriptInterface().GetContext(); - JSAutoRequest rq(cx); - - JS::RootedValue initAttribs(cx); - g_Game->GetSimulation2()->GetInitAttributes(&initAttribs); - - return pCxPrivate->pScriptInterface->CloneValueFromOtherContext( - g_Game->GetSimulation2()->GetScriptInterface(), - initAttribs); -} - JS::Value JSI_Simulation::GuiInterfaceCall(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name, JS::HandleValue data) { if (!g_Game) @@ -147,7 +131,6 @@ void JSI_Simulation::SetBoundingBoxDebugOverlay(ScriptInterface::CxPrivate* UNUS void JSI_Simulation::RegisterScriptFunctions(const ScriptInterface& scriptInterface) { - scriptInterface.RegisterFunction("GetInitAttributes"); scriptInterface.RegisterFunction("GuiInterfaceCall"); scriptInterface.RegisterFunction("PostNetworkCommand"); scriptInterface.RegisterFunction("DumpSimState"); diff --git a/source/simulation2/scripting/JSInterface_Simulation.h b/source/simulation2/scripting/JSInterface_Simulation.h index 21d1aae8df..a998bfe43c 100644 --- a/source/simulation2/scripting/JSInterface_Simulation.h +++ b/source/simulation2/scripting/JSInterface_Simulation.h @@ -23,7 +23,6 @@ namespace JSI_Simulation { - JS::Value GetInitAttributes(ScriptInterface::CxPrivate* pCxPrivate); JS::Value GuiInterfaceCall(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name, JS::HandleValue data); void PostNetworkCommand(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue cmd); entity_id_t PickEntityAtPoint(ScriptInterface::CxPrivate* pCxPrivate, int x, int y);