1
0
forked from 0ad/0ad

Fixes saving/loading problems introduced in 28bdd8540f.

* CGameLoader created a new JS::Value when assigning to m_Metadata.
This means it didn't actually update metadata in SavedGames::Load. The
new approach solves this problem and should work well if
CScriptValRooted gets replaced in the future.
 * The cloning in ScriptFunctions.cpp was required. Removing it caused
compartment mismatches.
 * Now CGameLoader loads the metadata unconditinally because we didn't
actually use the option to not load load it.

Ref #2415

This was SVN commit r15589.
This commit is contained in:
Yves 2014-07-31 14:44:51 +00:00
parent cf563657b6
commit 2ae74221c7
3 changed files with 56 additions and 29 deletions

View File

@ -219,11 +219,14 @@ void StartGame(ScriptInterface::CxPrivate* pCxPrivate, CScriptVal attribs, int p
g_Game->StartGame(gameAttribs, "");
}
CScriptVal StartSavedGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring name)
CScriptVal StartSavedGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name)
{
CSimulation2* sim = g_Game->GetSimulation2();
JSContext* cx = sim->GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
// We need to be careful with different compartments and contexts.
// The GUI calls this function from the GUI context and expects the return value in the same context.
// The game we start from here creates another context and expects data in this context.
JSContext* cxGui = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cxGui);
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
@ -231,27 +234,33 @@ CScriptVal StartSavedGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::w
ENSURE(!g_Game);
// Load the saved game data from disk
CScriptValRooted metadata;
JS::RootedValue guiContextMetadata(cxGui);
std::string savedState;
Status err = SavedGames::Load(name, sim->GetScriptInterface(), metadata, savedState);
Status err = SavedGames::Load(name, *(pCxPrivate->pScriptInterface), &guiContextMetadata, savedState);
if (err < 0)
return CScriptVal();
return JS::UndefinedValue();
g_Game = new CGame();
JS::RootedValue gameMetadata(cx, metadata.get());
{
CSimulation2* sim = g_Game->GetSimulation2();
JSContext* cxGame = sim->GetScriptInterface().GetContext();
JSAutoRequest rq(cxGame);
JS::RootedValue gameContextMetadata(cxGame,
sim->GetScriptInterface().CloneValueFromOtherContext(*(pCxPrivate->pScriptInterface), guiContextMetadata));
JS::RootedValue gameInitAttributes(cxGame);
sim->GetScriptInterface().GetProperty(gameContextMetadata, "initAttributes", &gameInitAttributes);
JS::RootedValue gameInitAttributes(cx);
sim->GetScriptInterface().GetProperty(gameMetadata, "initAttributes", &gameInitAttributes);
int playerID;
sim->GetScriptInterface().GetProperty(gameContextMetadata, "player", playerID);
int playerID;
sim->GetScriptInterface().GetProperty(gameMetadata, "player", playerID);
// Start the game
g_Game->SetPlayerID(playerID);
g_Game->StartGame(CScriptValRooted(cxGame, gameInitAttributes), savedState);
}
// Start the game
g_Game->SetPlayerID(playerID);
g_Game->StartGame(CScriptValRooted(cx, gameInitAttributes), savedState);
return gameMetadata.get();
return guiContextMetadata.get();
}
void SaveGame(ScriptInterface::CxPrivate* pCxPrivate, std::wstring filename, std::wstring description, CScriptVal GUIMetadata)

View File

@ -137,8 +137,19 @@ class CGameLoader
{
NONCOPYABLE(CGameLoader);
public:
CGameLoader(ScriptInterface& scriptInterface, CScriptValRooted* metadata, std::string* savedState) :
m_ScriptInterface(scriptInterface), m_Metadata(metadata), m_SavedState(savedState)
/**
* @param scriptInterface the ScriptInterface used for loading metadata.
* @param[out] savedState serialized simulation state stored as string of bytes,
* loaded from simulation.dat inside the archive.
*
* Note: We use a different approach for returning the string and the metadata JS::Value.
* We use a pointer for the string to avoid copies (efficiency). We don't use this approach
* for the metadata because it would be error prone with rooting and the stack-based rooting
* types and confusing (a chain of pointers pointing to other pointers).
*/
CGameLoader(ScriptInterface& scriptInterface, std::string* savedState) :
m_ScriptInterface(scriptInterface), m_SavedState(savedState)
{
}
@ -149,12 +160,12 @@ public:
void ReadEntry(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile)
{
if (pathname == L"metadata.json" && m_Metadata)
if (pathname == L"metadata.json")
{
std::string buffer;
buffer.resize(fileInfo.Size());
WARN_IF_ERR(archiveFile->Load("", DummySharedPtr((u8*)buffer.data()), buffer.size()));
*m_Metadata = m_ScriptInterface.ParseJSON(buffer);
m_Metadata = m_ScriptInterface.ParseJSON(buffer);
}
else if (pathname == L"simulation.dat" && m_SavedState)
{
@ -162,13 +173,20 @@ public:
WARN_IF_ERR(archiveFile->Load("", DummySharedPtr((u8*)m_SavedState->data()), m_SavedState->size()));
}
}
JS::Value GetMetadata()
{
return m_Metadata.get();
}
private:
ScriptInterface& m_ScriptInterface;
CScriptValRooted* m_Metadata;
CScriptValRooted m_Metadata;
std::string* m_SavedState;
};
Status SavedGames::Load(const std::wstring& name, ScriptInterface& scriptInterface, CScriptValRooted& metadata, std::string& savedState)
Status SavedGames::Load(const std::wstring& name, ScriptInterface& scriptInterface, JS::MutableHandleValue metadata, std::string& savedState)
{
// Determine the filename to load
const VfsPath basename(L"saves/" + name);
@ -185,8 +203,9 @@ Status SavedGames::Load(const std::wstring& name, ScriptInterface& scriptInterfa
if (!archiveReader)
WARN_RETURN(ERR::FAIL);
CGameLoader loader(scriptInterface, &metadata, &savedState);
CGameLoader loader(scriptInterface, &savedState);
WARN_RETURN_STATUS_IF_ERR(archiveReader->ReadEntries(CGameLoader::ReadEntryCallback, (uintptr_t)&loader));
metadata.set(loader.GetMetadata());
return INFO::OK;
}
@ -223,16 +242,15 @@ std::vector<CScriptValRooted> SavedGames::GetSavedGames(ScriptInterface& scriptI
continue; // skip this file
}
JS::RootedValue metadata(cx);
CScriptValRooted tmpMetada(cx, metadata);
CGameLoader loader(scriptInterface, &tmpMetada, NULL);
CGameLoader loader(scriptInterface, NULL);
err = archiveReader->ReadEntries(CGameLoader::ReadEntryCallback, (uintptr_t)&loader);
if (err < 0)
{
DEBUG_WARN_ERR(err);
continue; // skip this file
}
JS::RootedValue metadata(cx, loader.GetMetadata());
JS::RootedValue game(cx);
scriptInterface.Eval("({})", &game);
scriptInterface.SetProperty(game, "id", pathnames[i].Basename());

View File

@ -74,7 +74,7 @@ Status SavePrefix(const std::wstring& prefix, const std::wstring& description, C
* loaded from simulation.dat inside the archive.
* @return INFO::OK if successfully loaded, else an error Status
*/
Status Load(const std::wstring& name, ScriptInterface& scriptInterface, CScriptValRooted& metadata, std::string& savedState);
Status Load(const std::wstring& name, ScriptInterface& scriptInterface, JS::MutableHandleValue metadata, std::string& savedState);
/**
* Get list of saved games for GUI script usage