1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-24 00:03:11 +01:00

Change API for interacting with park storage

This commit is contained in:
Ted John
2022-02-23 21:52:38 +00:00
parent 1182ff5f19
commit ed8b7cb6ee
4 changed files with 111 additions and 31 deletions

View File

@@ -186,15 +186,18 @@ declare global {
sharedStorage: Configuration; sharedStorage: Configuration;
/** /**
* Shared generic storage for all plugins. Data is persisted for the current * Gets the storage for the current plugin if no name is specified.
* loaded park, and is stored inside the .park file. Any references to objects, * If a plugin name is specified, the storage for the plugin with that name will be returned.
* or arrays are copied by reference. If these arrays, objects, or any other * Data is persisted for the current loaded park, and is stored inside the .park file.
* arrays, or objects that they reference change without a subsequent call to * Any references to objects, or arrays are copied by reference. If these arrays, objects,
* or any other arrays, or objects that they reference change without a subsequent call to
* the `set` method, their new state will still be serialised. * the `set` method, their new state will still be serialised.
* Keep in mind that all data here will be serialised every time the park is * Keep in mind that all data here will be serialised every time the park is
* saved, including when the park is periodically saved automatically. * saved, including when the park is periodically saved automatically.
* @param pluginName The name of the plugin to get a store for. If undefined, the
* current plugin's name will be used. Plugin names are case sensitive.
*/ */
parkStorage: Configuration; getParkStorage(pluginName?: string): Configuration;
/** /**
* Render the current state of the map and save to disc. * Render the current state of the map and save to disc.
@@ -313,7 +316,7 @@ declare global {
} }
interface Configuration { interface Configuration {
getAll(namespace: string): { [name: string]: any }; getAll(namespace?: string): { [name: string]: any };
get<T>(key: string): T | undefined; get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T; get<T>(key: string, defaultValue: T): T;
set<T>(key: string, value: T): void; set<T>(key: string, value: T): void;

View File

@@ -172,7 +172,7 @@ if (!h) {
All plugins have access to the same shared storage. All plugins have access to the same shared storage.
If you want to only store data specific to the current park that is loaded, use `context.parkStorage`. Any data stored here will be written to the .park file. If you want to only store data specific to the current park that is loaded, use `context.getParkStorage`. Any data stored here will be written to the .park file.
> Can plugins communicate with other processes, or the internet? > Can plugins communicate with other processes, or the internet?

View File

@@ -19,22 +19,30 @@
namespace OpenRCT2::Scripting namespace OpenRCT2::Scripting
{ {
enum class ScConfigurationKind
{
User,
Shared,
Park
};
class ScConfiguration class ScConfiguration
{ {
private: private:
bool _isUserConfig{}; ScConfigurationKind _kind;
DukValue _backingObject; DukValue _backingObject;
public: public:
// context.configuration // context.configuration
ScConfiguration() ScConfiguration()
: _isUserConfig(true) : _kind(ScConfigurationKind::User)
{ {
} }
// context.sharedStorage // context.sharedStorage / context.getParkStorage
ScConfiguration(const DukValue& backingObject) ScConfiguration(ScConfigurationKind kind, const DukValue& backingObject)
: _backingObject(backingObject) : _kind(kind)
, _backingObject(backingObject)
{ {
} }
@@ -68,15 +76,18 @@ namespace OpenRCT2::Scripting
std::optional<DukValue> GetNamespaceObject(std::string_view ns) const std::optional<DukValue> GetNamespaceObject(std::string_view ns) const
{ {
auto store = _backingObject; auto store = _backingObject;
auto k = ns; if (!ns.empty())
bool end;
do
{ {
auto [next, remainder] = GetNextNamespace(k); auto k = ns;
store = store[next]; bool end;
k = remainder; do
end = store.type() == DukValue::Type::UNDEFINED || remainder.empty(); {
} while (!end); auto [next, remainder] = GetNextNamespace(k);
store = store[next];
k = remainder;
end = store.type() == DukValue::Type::UNDEFINED || remainder.empty();
} while (!end);
}
return store.type() == DukValue::OBJECT ? std::make_optional(store) : std::nullopt; return store.type() == DukValue::OBJECT ? std::make_optional(store) : std::nullopt;
} }
@@ -112,17 +123,26 @@ namespace OpenRCT2::Scripting
bool IsValidNamespace(std::string_view ns) const bool IsValidNamespace(std::string_view ns) const
{ {
if (ns.empty() || ns[0] == '.' || ns[ns.size() - 1] == '.') if (!ns.empty() && (ns[0] == '.' || ns[ns.size() - 1] == '.'))
{ {
return false; return false;
} }
for (size_t i = 1; i < ns.size() - 1; i++)
if (_kind != ScConfigurationKind::Park)
{ {
if (ns[i - 1] == '.' && ns[i] == '.') if (ns.empty())
{ {
return false; return false;
} }
for (size_t i = 1; i < ns.size() - 1; i++)
{
if (ns[i - 1] == '.' && ns[i] == '.')
{
return false;
}
}
} }
return true; return true;
} }
@@ -131,13 +151,24 @@ namespace OpenRCT2::Scripting
return !key.empty() && key.find('.') == std::string_view::npos; return !key.empty() && key.find('.') == std::string_view::npos;
} }
DukValue getAll(const std::string& ns) const DukValue getAll(const DukValue& dukNamespace) const
{ {
DukValue result; DukValue result;
auto ctx = GetContext()->GetScriptEngine().GetContext(); auto ctx = GetContext()->GetScriptEngine().GetContext();
std::string ns = "";
if (dukNamespace.type() == DukValue::Type::STRING)
{
ns = dukNamespace.as_string();
}
else if (dukNamespace.type() != DukValue::Type::UNDEFINED)
{
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
}
if (IsValidNamespace(ns)) if (IsValidNamespace(ns))
{ {
if (_isUserConfig) if (_kind == ScConfigurationKind::User)
{ {
DukObject obj(ctx); DukObject obj(ctx);
if (ns == "general") if (ns == "general")
@@ -163,7 +194,7 @@ namespace OpenRCT2::Scripting
DukValue get(const std::string& key, const DukValue& defaultValue) const DukValue get(const std::string& key, const DukValue& defaultValue) const
{ {
auto ctx = GetContext()->GetScriptEngine().GetContext(); auto ctx = GetContext()->GetScriptEngine().GetContext();
if (_isUserConfig) if (_kind == ScConfigurationKind::User)
{ {
if (key == "general.language") if (key == "general.language")
{ {
@@ -214,7 +245,7 @@ namespace OpenRCT2::Scripting
{ {
auto& scriptEngine = GetContext()->GetScriptEngine(); auto& scriptEngine = GetContext()->GetScriptEngine();
auto ctx = scriptEngine.GetContext(); auto ctx = scriptEngine.GetContext();
if (_isUserConfig) if (_kind == ScConfigurationKind::User)
{ {
try try
{ {

View File

@@ -55,13 +55,59 @@ namespace OpenRCT2::Scripting
std::shared_ptr<ScConfiguration> sharedStorage_get() std::shared_ptr<ScConfiguration> sharedStorage_get()
{ {
auto& scriptEngine = GetContext()->GetScriptEngine(); auto& scriptEngine = GetContext()->GetScriptEngine();
return std::make_shared<ScConfiguration>(scriptEngine.GetSharedStorage()); return std::make_shared<ScConfiguration>(ScConfigurationKind::Shared, scriptEngine.GetSharedStorage());
} }
std::shared_ptr<ScConfiguration> parkStorage_get() std::shared_ptr<ScConfiguration> GetParkStorageForPlugin(std::string_view pluginName)
{ {
auto& scriptEngine = GetContext()->GetScriptEngine(); auto& scriptEngine = GetContext()->GetScriptEngine();
return std::make_shared<ScConfiguration>(scriptEngine.GetParkStorage()); auto parkStore = scriptEngine.GetParkStorage();
auto pluginStore = parkStore[pluginName];
// Create if it doesn't exist
if (pluginStore.type() != DukValue::Type::OBJECT)
{
auto* ctx = scriptEngine.GetContext();
parkStore.push();
duk_push_object(ctx);
duk_put_prop_lstring(ctx, -2, pluginName.data(), pluginName.size());
duk_pop(ctx);
pluginStore = parkStore[pluginName];
}
return std::make_shared<ScConfiguration>(ScConfigurationKind::Park, pluginStore);
}
std::shared_ptr<ScConfiguration> getParkStorage(const DukValue& dukPluginName)
{
auto& scriptEngine = GetContext()->GetScriptEngine();
std::shared_ptr<ScConfiguration> result;
if (dukPluginName.type() == DukValue::Type::STRING)
{
auto& pluginName = dukPluginName.as_string();
if (pluginName.empty())
{
duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name is empty");
}
result = GetParkStorageForPlugin(pluginName);
}
else if (dukPluginName.type() == DukValue::Type::UNDEFINED)
{
auto plugin = _execInfo.GetCurrentPlugin();
if (plugin == nullptr)
{
duk_error(
scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name must be specified when used from console.");
}
result = GetParkStorageForPlugin(plugin->GetMetadata().Name);
}
else
{
duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid plugin name.");
}
return result;
} }
void captureImage(const DukValue& options) void captureImage(const DukValue& options)
@@ -387,7 +433,7 @@ namespace OpenRCT2::Scripting
dukglue_register_property(ctx, &ScContext::apiVersion_get, nullptr, "apiVersion"); dukglue_register_property(ctx, &ScContext::apiVersion_get, nullptr, "apiVersion");
dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration"); dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration");
dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage"); dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage");
dukglue_register_property(ctx, &ScContext::parkStorage_get, nullptr, "parkStorage"); dukglue_register_method(ctx, &ScContext::getParkStorage, "getParkStorage");
dukglue_register_method(ctx, &ScContext::captureImage, "captureImage"); dukglue_register_method(ctx, &ScContext::captureImage, "captureImage");
dukglue_register_method(ctx, &ScContext::getObject, "getObject"); dukglue_register_method(ctx, &ScContext::getObject, "getObject");
dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects"); dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects");