Index: binaries/data/l10n/engine.pot =================================================================== --- binaries/data/l10n/engine.pot +++ binaries/data/l10n/engine.pot @@ -4,39 +4,39 @@ msgid "" msgstr "" "Project-Id-Version: Pyrogenesis\n" -"POT-Creation-Date: 2022-01-07 08:19+0000\n" -"PO-Revision-Date: 2022-01-07 08:19+0000\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"POT-Creation-Date: 2022-06-19 21:07+0135\n" +"PO-Revision-Date: 2022-06-19 21:07+0135\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit" -#: graphics/CameraController.cpp:657 +#: graphics/CameraController.cpp:659 #, c-format msgid "Scroll speed increased to %.1f" msgstr "" -#: graphics/CameraController.cpp:663 +#: graphics/CameraController.cpp:665 #, c-format msgid "Scroll speed decreased to %.1f" msgstr "" -#: graphics/CameraController.cpp:670 +#: graphics/CameraController.cpp:672 #, c-format msgid "Rotate speed increased to X=%.3f, Y=%.3f" msgstr "" -#: graphics/CameraController.cpp:677 +#: graphics/CameraController.cpp:679 #, c-format msgid "Rotate speed decreased to X=%.3f, Y=%.3f" msgstr "" -#: graphics/CameraController.cpp:683 +#: graphics/CameraController.cpp:685 #, c-format msgid "Zoom speed increased to %.1f" msgstr "" -#: graphics/CameraController.cpp:689 +#: graphics/CameraController.cpp:691 #, c-format msgid "Zoom speed decreased to %.1f" msgstr "" @@ -240,17 +240,25 @@ msgid "Failed to verify signature." msgstr "" -#: ps/SavedGame.cpp:142 +#: ps/SavedGame.cpp:144 #, c-format msgid "Saved game to '%s'" msgstr "" +#: ps/SavedGame.cpp:341 +msgid "Cannot quickload game - match not quicksaved" +msgstr "" + +#: ps/SavedGame.cpp:362 +msgid "Quickloaded game" +msgstr "" + #: ps/scripting/JSInterface_Debug.cpp:87 msgid "custom build" msgstr "" -#: renderer/Renderer.cpp:614 renderer/Renderer.cpp:617 renderer/Renderer.cpp:754 -#: renderer/Renderer.cpp:757 +#: renderer/Renderer.cpp:565 renderer/Renderer.cpp:568 renderer/Renderer.cpp:680 +#: renderer/Renderer.cpp:683 #, c-format msgid "Screenshot written to '%s'" msgstr "" 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 @@ -255,6 +255,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() { + const simulationState = GetSimState(); + return { - "groups": g_Groups.groups + "groups": g_Groups.groups, + "timeElapsed": simulationState.timeElapsed, + "states": simulationState.players.map(pState => pState.state) }; } Index: source/ps/SavedGame.h =================================================================== --- source/ps/SavedGame.h +++ source/ps/SavedGame.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 @@ -87,6 +87,27 @@ * @return true if deletion was successful, or false on error */ bool DeleteSavedGame(const std::wstring& name); + + /** + * Create new saved game archive with "quicksave" name and given simulation data + * + * @param simulation + * @param guiMetadataClone if not NULL, store some UI-related data with the saved game + * @return INFO::OK if successfully saved, else an error Status + */ + Status QuickSave(CSimulation2& simulation, const Script::StructuredClone& guiMetadataClone); + + /** + * Load saved game with "quicksave" name + * + * Reads the matchID property from the game metadata of "quicksave" saved game archive. + * Proceeds to load the saved game, if said matchID is equal to the matchID of the given + * simulation object reference. + * + * @param simulation + * @return INFO::OK if successfully loaded, else another info or error Status + */ + Status QuickLoad(CSimulation2& simulation); } #endif // INCLUDED_SAVEDGAME Index: source/ps/SavedGame.cpp =================================================================== --- source/ps/SavedGame.cpp +++ source/ps/SavedGame.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 @@ -20,6 +20,7 @@ #include "SavedGame.h" #include "graphics/GameView.h" +#include "gui/GUIManager.h" #include "i18n/L10n.h" #include "lib/allocators/shared_ptr.h" #include "lib/file/archive/archive_zip.h" @@ -35,6 +36,7 @@ #include "scriptinterface/JSON.h" #include "scriptinterface/StructuredClone.h" #include "simulation2/Simulation2.h" +#include "simulation2/system/TurnManager.h" // TODO: we ought to check version numbers when loading files @@ -303,3 +305,60 @@ // Successfully deleted file return true; } + +Status SavedGames::QuickSave(CSimulation2& simulation, const Script::StructuredClone& GUIMetadataClone) +{ + return Save(L"quicksave", L"quicksave", simulation, GUIMetadataClone); +} + +Status SavedGames::QuickLoad(CSimulation2& simulation) +{ + ScriptRequest rq(simulation.GetScriptInterface()); + + // Load quicksave archive + JS::RootedValue quickSaveMetadata(rq.cx); + std::string savedState; + if (SavedGames::Load(L"quicksave", simulation.GetScriptInterface(), &quickSaveMetadata, savedState) < 0) + { + LOGERROR("Failed to load quicksave file"); + return ERR::FAIL; + } + + // 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, simulation.GetInitAttributes()); + Script::GetProperty(rq, gameMetadata, "matchID", matchID); + + // Compare IDs + if (matchID.compare(savedMatchID) != 0) + { + LOGMESSAGERENDER(g_L10n.Translate("Cannot quickload game - match not quicksaved")); + return INFO::CANNOT_HANDLE; + } + + std::stringstream stream(savedState); + if (!simulation.DeserializeState(stream)) + { + LOGERROR("Failed to quickload game"); + return ERR::FAIL; + } + + g_Game->GetTurnManager()->RewindTimeWarp(); + + // Provide a copy, so that GUI components don't have to clone to get mutable objects + JS::RootedValue quickSaveMetadataClone(rq.cx, Script::DeepCopy(rq, quickSaveMetadata)); + + JS::RootedValueArray<1> paramData(rq.cx); + paramData[0].set(quickSaveMetadataClone); + CStr EventNameSavegameLoaded = "SavegameLoaded"; + g_GUI->SendEventToAll(EventNameSavegameLoaded, paramData); + + LOGMESSAGERENDER(g_L10n.Translate("Quickloaded game")); + return INFO::OK; +} Index: source/ps/scripting/JSInterface_SavedGame.cpp =================================================================== --- source/ps/scripting/JSInterface_SavedGame.cpp +++ source/ps/scripting/JSInterface_SavedGame.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 @@ -55,12 +55,16 @@ LOGERROR("Failed to save game"); } -void QuickSave(JS::HandleValue GUIMetadata) +void QuickSave(const ScriptRequest& rq, JS::HandleValue GUIMetadata) { if (g_NetServer || g_NetClient) LOGERROR("Can't store quicksave during multiplayer!"); else if (g_Game) - g_Game->GetTurnManager()->QuickSave(GUIMetadata); + { + Script::StructuredClone GUIMetadataClone = Script::WriteStructuredClone(rq, GUIMetadata); + if (SavedGames::QuickSave(*g_Game->GetSimulation2(), GUIMetadataClone) < 0) + LOGERROR("Failed to quicksave game"); + } else LOGERROR("Can't store quicksave if game is not running!"); } @@ -70,7 +74,10 @@ if (g_NetServer || g_NetClient) LOGERROR("Can't load quicksave during multiplayer!"); else if (g_Game) - g_Game->GetTurnManager()->QuickLoad(); + { + if (SavedGames::QuickLoad(*g_Game->GetSimulation2()) < 0) + LOGERROR("Failed to quickload game"); + } else LOGERROR("Can't load quicksave if game is not running!"); } 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 @@ -146,9 +146,6 @@ */ void RewindTimeWarp(); - void QuickSave(JS::HandleValue GUIMetadata); - void QuickLoad(); - u32 GetCurrentTurn() const { return m_CurrentTurn; } /** @@ -209,12 +206,9 @@ u32 m_FinalTurn; private: - static const CStr EventNameSavegameLoaded; 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" @@ -35,15 +36,11 @@ #define NETTURN_LOG(...) #endif -const CStr CTurnManager::EventNameSavegameLoaded = "SavegameLoaded"; - CTurnManager::CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, u32 commandDelay, int clientId, IReplayLogger& replay) : m_Simulation2(simulation), m_CurrentTurn(0), m_CommandDelay(commandDelay), m_ReadyTurn(commandDelay - 1), m_TurnLength(defaultTurnLength), 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); } @@ -277,60 +274,3 @@ // here, but this is simpler for now.) ResetState(1, m_CommandDelay); } - -void CTurnManager::QuickSave(JS::HandleValue GUIMetadata) -{ - 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"); -} - -void CTurnManager::QuickLoad() -{ - TIMER(L"QuickLoad"); - - if (m_QuickSaveState.empty()) - { - LOGERROR("Cannot quickload game - no game was quicksaved"); - return; - } - - std::stringstream stream(m_QuickSaveState); - if (!m_Simulation2.DeserializeState(stream)) - { - LOGERROR("Failed to quickload game"); - return; - } - - // See RewindTimeWarp - ResetState(1, m_CommandDelay); - - 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::RootedValueArray<1> paramData(rq.cx); - paramData[0].set(quickSaveMetadataClone); - g_GUI->SendEventToAll(EventNameSavegameLoaded, paramData); - - LOGMESSAGERENDER("Quickloaded game"); -}