mirror of https://github.com/OpenRCT2/OpenRCT2.git
Improve shared storage read / write
This commit is contained in:
parent
0c71886941
commit
ea8890aaaa
|
@ -116,11 +116,6 @@ declare global {
|
|||
*/
|
||||
sharedStorage: Configuration;
|
||||
|
||||
/**
|
||||
* Local generic storage for a each plugin.
|
||||
*/
|
||||
localStorage: Configuration;
|
||||
|
||||
/**
|
||||
* Gets a random integer within the specified range using the game's pseudo-
|
||||
* random number generator. This is part of the game state and shared across
|
||||
|
|
|
@ -240,6 +240,7 @@ const char * PlatformEnvironment::FileNames[] =
|
|||
"highscores.dat", // SCORES
|
||||
"scores.dat", // SCORES (LEGACY)
|
||||
"Saved Games" PATH_SEPARATOR "scores.dat", // SCORES (RCT2)
|
||||
"changelog.txt" // CHANGELOG
|
||||
"changelog.txt", // CHANGELOG
|
||||
"plugin.store.json" // PLUGIN_STORE
|
||||
};
|
||||
// clang-format on
|
||||
|
|
|
@ -66,6 +66,7 @@ namespace OpenRCT2
|
|||
SCORES_LEGACY, // Scenario scores, legacy (scores.dat).
|
||||
SCORES_RCT2, // Scenario scores, rct2 (\Saved Games\scores.dat).
|
||||
CHANGELOG, // Notable changes to the game between versions, distributed with the game.
|
||||
PLUGIN_STORE, // Shared storage for plugins.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,147 +14,172 @@
|
|||
# include <cstdio>
|
||||
# include <dukglue/dukglue.h>
|
||||
# include <duktape.h>
|
||||
# include <optional>
|
||||
# include <stdexcept>
|
||||
|
||||
template<typename T> DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr<T>& value)
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
dukglue::types::DukType<std::shared_ptr<T>>::template push<T>(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
template<typename T> T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete;
|
||||
|
||||
template<> inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::STRING ? value.as_string() : defaultValue;
|
||||
}
|
||||
|
||||
template<> inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue)
|
||||
{
|
||||
return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows creation of an object on the duktape stack and setting properties on it before
|
||||
* retrieving the DukValue instance of it.
|
||||
*/
|
||||
class DukObject
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _idx = DUK_INVALID_INDEX;
|
||||
|
||||
public:
|
||||
DukObject(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
template<typename T> DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr<T>& value)
|
||||
{
|
||||
dukglue::types::DukType<std::shared_ptr<T>>::template push<T>(ctx, value);
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
|
||||
DukObject(const DukObject&) = delete;
|
||||
template<typename T> T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete;
|
||||
|
||||
DukObject(DukObject&& m) noexcept
|
||||
template<> inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue)
|
||||
{
|
||||
_ctx = m._ctx;
|
||||
_idx = m._idx;
|
||||
m._ctx = {};
|
||||
m._idx = {};
|
||||
return value.type() == DukValue::STRING ? value.as_string() : defaultValue;
|
||||
}
|
||||
|
||||
~DukObject()
|
||||
template<> inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue)
|
||||
{
|
||||
PopObjectIfExists();
|
||||
return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue;
|
||||
}
|
||||
|
||||
void Set(const char* name, bool value)
|
||||
/**
|
||||
* Allows creation of an object on the duktape stack and setting properties on it before
|
||||
* retrieving the DukValue instance of it.
|
||||
*/
|
||||
class DukObject
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_boolean(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _idx = DUK_INVALID_INDEX;
|
||||
|
||||
void Set(const char* name, int32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_int(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, uint32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_uint(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, const std::string_view& value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_lstring(_ctx, value.data(), value.size());
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, const DukValue& value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
value.push();
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
DukValue Take()
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
auto result = DukValue::take_from_stack(_ctx, _idx);
|
||||
_idx = DUK_INVALID_INDEX;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void PopObjectIfExists()
|
||||
{
|
||||
if (_idx != DUK_INVALID_INDEX)
|
||||
public:
|
||||
DukObject(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
{
|
||||
duk_remove(_ctx, _idx);
|
||||
}
|
||||
|
||||
DukObject(const DukObject&) = delete;
|
||||
|
||||
DukObject(DukObject&& m) noexcept
|
||||
{
|
||||
_ctx = m._ctx;
|
||||
_idx = m._idx;
|
||||
m._ctx = {};
|
||||
m._idx = {};
|
||||
}
|
||||
|
||||
~DukObject()
|
||||
{
|
||||
PopObjectIfExists();
|
||||
}
|
||||
|
||||
void Set(const char* name, bool value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_boolean(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, int32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_int(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, uint32_t value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_uint(_ctx, value);
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, const std::string_view& value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
duk_push_lstring(_ctx, value.data(), value.size());
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
void Set(const char* name, const DukValue& value)
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
value.push();
|
||||
duk_put_prop_string(_ctx, _idx, name);
|
||||
}
|
||||
|
||||
DukValue Take()
|
||||
{
|
||||
EnsureObjectPushed();
|
||||
auto result = DukValue::take_from_stack(_ctx, _idx);
|
||||
_idx = DUK_INVALID_INDEX;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureObjectPushed()
|
||||
{
|
||||
if (_idx == DUK_INVALID_INDEX)
|
||||
private:
|
||||
void PopObjectIfExists()
|
||||
{
|
||||
_idx = duk_push_object(_ctx);
|
||||
if (_idx != DUK_INVALID_INDEX)
|
||||
{
|
||||
duk_remove(_ctx, _idx);
|
||||
_idx = DUK_INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DukStackFrame
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _top;
|
||||
|
||||
public:
|
||||
DukStackFrame(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
{
|
||||
_top = duk_get_top(ctx);
|
||||
}
|
||||
|
||||
~DukStackFrame()
|
||||
{
|
||||
auto top = duk_get_top(_ctx);
|
||||
if (top != _top)
|
||||
void EnsureObjectPushed()
|
||||
{
|
||||
duk_set_top(_ctx, _top);
|
||||
if (_idx == DUK_INVALID_INDEX)
|
||||
{
|
||||
_idx = duk_push_object(_ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DukStackFrame
|
||||
{
|
||||
private:
|
||||
duk_context* _ctx{};
|
||||
duk_idx_t _top;
|
||||
|
||||
public:
|
||||
DukStackFrame(duk_context* ctx)
|
||||
: _ctx(ctx)
|
||||
{
|
||||
_top = duk_get_top(ctx);
|
||||
}
|
||||
|
||||
~DukStackFrame()
|
||||
{
|
||||
auto top = duk_get_top(_ctx);
|
||||
if (top != _top)
|
||||
{
|
||||
duk_set_top(_ctx, _top);
|
||||
_ctx = {};
|
||||
std::fprintf(stderr, "duktape stack was not returned to original state!");
|
||||
// assert(false);
|
||||
}
|
||||
_ctx = {};
|
||||
std::fprintf(stderr, "duktape stack was not returned to original state!");
|
||||
// assert(false);
|
||||
}
|
||||
_ctx = {};
|
||||
|
||||
DukStackFrame(const DukStackFrame&) = delete;
|
||||
DukStackFrame(DukStackFrame&&) = delete;
|
||||
};
|
||||
|
||||
inline duk_ret_t duk_json_decode_wrapper(duk_context* ctx, void*)
|
||||
{
|
||||
duk_json_decode(ctx, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DukStackFrame(const DukStackFrame&) = delete;
|
||||
DukStackFrame(DukStackFrame&&) = delete;
|
||||
};
|
||||
inline std::optional<DukValue> DuktapeTryParseJson(duk_context* ctx, const std::string_view& json)
|
||||
{
|
||||
duk_push_lstring(ctx, json.data(), json.size());
|
||||
if (duk_safe_call(ctx, duk_json_decode_wrapper, nullptr, 1, 1) == DUK_EXEC_SUCCESS)
|
||||
{
|
||||
return DukValue::take_from_stack(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pop error off stack
|
||||
duk_pop(ctx);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
#endif
|
||||
|
|
|
@ -394,9 +394,7 @@ void ScriptEngine::Initialise()
|
|||
_pluginsLoaded = false;
|
||||
_pluginsStarted = false;
|
||||
|
||||
duk_push_object(ctx);
|
||||
_sharedStorage = std::move(DukValue::take_from_stack(ctx));
|
||||
LoadSharedStorage();
|
||||
InitSharedStorage();
|
||||
}
|
||||
|
||||
void ScriptEngine::LoadPlugins()
|
||||
|
@ -505,7 +503,7 @@ void ScriptEngine::SetupHotReloading()
|
|||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::printf("Unable to enable hot reloading of plugins: %s\n", e.what());
|
||||
std::fprintf(stderr, "Unable to enable hot reloading of plugins: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,6 +553,8 @@ void ScriptEngine::UnloadPlugins()
|
|||
|
||||
void ScriptEngine::StartPlugins()
|
||||
{
|
||||
LoadSharedStorage();
|
||||
|
||||
for (auto& plugin : _plugins)
|
||||
{
|
||||
if (!plugin->HasStarted() && ShouldStartPlugin(plugin))
|
||||
|
@ -713,19 +713,25 @@ std::unique_ptr<GameActionResult> ScriptEngine::QueryOrExecuteCustomGameAction(
|
|||
|
||||
// Deserialise the JSON args
|
||||
std::string argsz(args);
|
||||
duk_push_string(_context, argsz.c_str());
|
||||
duk_json_decode(_context, -1);
|
||||
auto dukArgs = DukValue::take_from_stack(_context);
|
||||
|
||||
auto dukArgs = DuktapeTryParseJson(_context, argsz);
|
||||
if (!dukArgs)
|
||||
{
|
||||
auto action = std::make_unique<GameActionResult>();
|
||||
action->Error = GA_ERROR::INVALID_PARAMETERS;
|
||||
action->ErrorTitle = "Invalid JSON";
|
||||
return action;
|
||||
}
|
||||
|
||||
// Ready to call plugin handler
|
||||
DukValue dukResult;
|
||||
if (!isExecute)
|
||||
{
|
||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Query, { dukArgs }, false);
|
||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Query, { *dukArgs }, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Execute, { dukArgs }, true);
|
||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Execute, { *dukArgs }, true);
|
||||
}
|
||||
return DukToGameActionResult(dukResult);
|
||||
}
|
||||
|
@ -733,7 +739,7 @@ std::unique_ptr<GameActionResult> ScriptEngine::QueryOrExecuteCustomGameAction(
|
|||
{
|
||||
auto action = std::make_unique<GameActionResult>();
|
||||
action->Error = GA_ERROR::UNKNOWN;
|
||||
action->ErrorTitle = OBJECT_ERROR_UNKNOWN;
|
||||
action->ErrorTitle = "Unknown custom action";
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
@ -864,15 +870,25 @@ void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr<
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::InitSharedStorage()
|
||||
{
|
||||
duk_push_object(_context);
|
||||
_sharedStorage = std::move(DukValue::take_from_stack(_context));
|
||||
}
|
||||
|
||||
void ScriptEngine::LoadSharedStorage()
|
||||
{
|
||||
std::string path = "C:\\Users\\Ted\\Documents\\OpenRCT2\\plugin.json";
|
||||
InitSharedStorage();
|
||||
|
||||
auto path = _env.GetFilePath(PATHID::PLUGIN_STORE);
|
||||
try
|
||||
{
|
||||
auto data = File::ReadAllBytes(path);
|
||||
duk_push_lstring(_context, (const char*)data.data(), data.size());
|
||||
duk_json_decode(_context, -1);
|
||||
_sharedStorage = std::move(DukValue::take_from_stack(_context));
|
||||
auto result = DuktapeTryParseJson(_context, std::string_view((const char*)data.data(), data.size()));
|
||||
if (result)
|
||||
{
|
||||
_sharedStorage = std::move(*result);
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
|
@ -881,13 +897,13 @@ void ScriptEngine::LoadSharedStorage()
|
|||
|
||||
void ScriptEngine::SaveSharedStorage()
|
||||
{
|
||||
_sharedStorage.push();
|
||||
auto json = std::string(duk_json_encode(_context, -1));
|
||||
duk_pop(_context);
|
||||
|
||||
std::string path = "C:\\Users\\Ted\\Documents\\OpenRCT2\\plugin.json";
|
||||
auto path = _env.GetFilePath(PATHID::PLUGIN_STORE);
|
||||
try
|
||||
{
|
||||
_sharedStorage.push();
|
||||
auto json = std::string(duk_json_encode(_context, -1));
|
||||
duk_pop(_context);
|
||||
|
||||
File::WriteAllBytes(path, json.c_str(), json.size());
|
||||
}
|
||||
catch (const std::exception&)
|
||||
|
|
|
@ -200,6 +200,7 @@ namespace OpenRCT2::Scripting
|
|||
DukValue GameActionResultToDuk(const GameAction& action, const std::unique_ptr<GameActionResult>& result);
|
||||
DukValue PositionToDuk(const CoordsXYZ& position);
|
||||
|
||||
void InitSharedStorage();
|
||||
void LoadSharedStorage();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue