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:
parent
cf563657b6
commit
2ae74221c7
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user