Index: binaries/data/mods/public/gui/credits/texts/programming.json =================================================================== --- binaries/data/mods/public/gui/credits/texts/programming.json +++ binaries/data/mods/public/gui/credits/texts/programming.json @@ -254,6 +254,7 @@ { "nick": "silure" }, { "nick": "Simikolon", "name": "Yannick & Simon" }, { "nick": "smiley", "name": "M. L." }, + { "nick": "sotirangelo", "name": "Sotirios-Angelos Angelopoulos" }, { "nick": "Spahbod", "name": "Omid Davoodi" }, { "nick": "Stan", "name": "Stanislas Dolcini" }, { "nick": "Stefan" }, Index: binaries/data/mods/public/gui/loadgame/SavegameWriter.js =================================================================== --- binaries/data/mods/public/gui/loadgame/SavegameWriter.js +++ binaries/data/mods/public/gui/loadgame/SavegameWriter.js @@ -57,9 +57,6 @@ reallySaveGame(name, desc, nameIsPrefix) { - let simulationState = Engine.GuiInterfaceCall("GetSimulationState"); - this.savedGameData.timeElapsed = simulationState.timeElapsed; - this.savedGameData.states = simulationState.players.map(pState => pState.state); if (nameIsPrefix) Engine.SaveGamePrefix(name, desc, this.savedGameData); Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -581,8 +581,12 @@ function getSavedGameData() { + let simulationState = GetSimState(); + return { - "groups": g_Groups.groups + "groups": g_Groups.groups, + "timeElapsed": simulationState.timeElapsed, + "states": simulationState.players.map(pState => pState.state) }; } Index: source/simulation2/system/TurnManager.h =================================================================== --- source/simulation2/system/TurnManager.h +++ source/simulation2/system/TurnManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -213,8 +213,6 @@ size_t m_TimeWarpNumTurns; // 0 if disabled std::list m_TimeWarpStates; - std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system - JS::PersistentRootedValue m_QuickSaveMetadata; }; #endif // INCLUDED_TURNMANAGER Index: source/simulation2/system/TurnManager.cpp =================================================================== --- source/simulation2/system/TurnManager.cpp +++ source/simulation2/system/TurnManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "ps/CLogger.h" #include "ps/Replay.h" #include "ps/Util.h" +#include "ps/SavedGame.h" #include "scriptinterface/Object.h" #include "simulation2/Simulation2.h" @@ -42,8 +43,6 @@ m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_Replay(replay), m_FinalTurn(std::numeric_limits::max()), m_TimeWarpNumTurns(0) { - ScriptRequest rq(m_Simulation2.GetScriptInterface()); - m_QuickSaveMetadata.init(rq.cx); m_QueuedCommands.resize(1); } @@ -282,35 +281,47 @@ { TIMER(L"QuickSave"); - std::stringstream stream; - if (!m_Simulation2.SerializeState(stream)) - { - LOGERROR("Failed to quicksave game"); - return; - } - - m_QuickSaveState = stream.str(); - ScriptRequest rq(m_Simulation2.GetScriptInterface()); - - m_QuickSaveMetadata.set(Script::DeepCopy(rq, GUIMetadata)); - // Freeze state to ensure that consectuvie loads don't modify the state - Script::FreezeObject(rq, m_QuickSaveMetadata, true); - - LOGMESSAGERENDER("Quicksaved game"); + Script::StructuredClone GUIMetadataClone = Script::WriteStructuredClone(rq, GUIMetadata); + if (SavedGames::Save(L"quicksave", L"quicksave", m_Simulation2, GUIMetadataClone) < 0) + LOGERROR("Failed to quicksave game"); } void CTurnManager::QuickLoad() { TIMER(L"QuickLoad"); - if (m_QuickSaveState.empty()) + ScriptRequest rq(m_Simulation2.GetScriptInterface()); + + // Load quicksave archive + JS::RootedValue quickSaveMetadata(rq.cx); + std::string savedState; + if (SavedGames::Load(L"quicksave", m_Simulation2.GetScriptInterface(), &quickSaveMetadata, savedState) < 0) + { + LOGERROR("Failed to load save file"); + return; + } + + // Get matchID of quicksave + JS::RootedValue gameInitAttributes(rq.cx); + Script::GetProperty(rq, quickSaveMetadata, "initAttributes", &gameInitAttributes); + + std::wstring savedMatchID; + Script::GetProperty(rq, gameInitAttributes, "matchID", savedMatchID); + + // Get matchID of current game + std::wstring matchID; + JS::RootedValue gameMetadata(rq.cx, m_Simulation2.GetInitAttributes()); + Script::GetProperty(rq, gameMetadata, "matchID", matchID); + + // Compare IDs + if (matchID.compare(savedMatchID) != 0) { - LOGERROR("Cannot quickload game - no game was quicksaved"); + LOGERROR("Cannot quickload game - match not quicksaved"); return; } - std::stringstream stream(m_QuickSaveState); + std::stringstream stream(savedState); if (!m_Simulation2.DeserializeState(stream)) { LOGERROR("Failed to quickload game"); @@ -323,10 +334,8 @@ if (!g_GUI) return; - ScriptRequest rq(m_Simulation2.GetScriptInterface()); - // Provide a copy, so that GUI components don't have to clone to get mutable objects - JS::RootedValue quickSaveMetadataClone(rq.cx, Script::DeepCopy(rq, m_QuickSaveMetadata)); + JS::RootedValue quickSaveMetadataClone(rq.cx, Script::DeepCopy(rq, quickSaveMetadata)); JS::RootedValueArray<1> paramData(rq.cx); paramData[0].set(quickSaveMetadataClone);