1
0
forked from 0ad/0ad

Save replay metadata for non-visual games too, refs #4577, #5548, fixes #5565.

Remove gui/common/ hack by going through the GUIInterface and fragile
EndGame() hardcoding by saving if and only if a ReplayLogger exists
following 7470e88624, refs #4020.
Refs D2211, D2213.

Differential Revision: https://code.wildfiregames.com/D2197
Based on patch by: irishninja
Tested on: clang 8.0.1, Jenkins

This was SVN commit r22991.
This commit is contained in:
elexis 2019-09-25 10:06:12 +00:00
parent 9425572bb1
commit 35408e7e7e
13 changed files with 71 additions and 69 deletions

View File

@ -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
};
}

View File

@ -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"},

View File

@ -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();

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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 <ctime>
#include <fstream>
@ -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<ICmpGuiInterface> 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;

View File

@ -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<SimulationCommand>& 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<SimulationCommand>& commands);
virtual void Hash(const std::string& hash, bool quick);
virtual void SaveMetadata(const CSimulation2& simulation);
virtual OsPath GetDirectory() const;
private:

View File

@ -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");

View File

@ -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.
*/

View File

@ -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);

View File

@ -34,22 +34,6 @@
#include <fstream>
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<JS::Value, &GetInitAttributes>("GetInitAttributes");
scriptInterface.RegisterFunction<JS::Value, std::wstring, JS::HandleValue, &GuiInterfaceCall>("GuiInterfaceCall");
scriptInterface.RegisterFunction<void, JS::HandleValue, &PostNetworkCommand>("PostNetworkCommand");
scriptInterface.RegisterFunction<void, &DumpSimState>("DumpSimState");

View File

@ -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);