1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-20 13:33:02 +01:00
Files
OpenRCT2/src/openrct2/scripting/ScConfiguration.hpp
Ted John 82371fb003 Fix #11796: Unable to use sharedStorage (#11798)
isUserConfig must only be true for configuration not sharedStorage
2020-05-23 18:22:09 +02:00

277 lines
9.1 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#pragma once
#ifdef ENABLE_SCRIPTING
# include "../Context.h"
# include "../config/Config.h"
# include "../localisation/LocalisationService.h"
# include "Duktape.hpp"
# include "ScriptEngine.h"
namespace OpenRCT2::Scripting
{
class ScConfiguration
{
private:
bool _isUserConfig{};
DukValue _backingObject;
public:
// context.configuration
ScConfiguration()
: _isUserConfig(true)
{
}
// context.sharedStorage
ScConfiguration(const DukValue& backingObject)
: _backingObject(backingObject)
{
}
static void Register(duk_context* ctx)
{
dukglue_register_method(ctx, &ScConfiguration::getAll, "getAll");
dukglue_register_method(ctx, &ScConfiguration::get, "get");
dukglue_register_method(ctx, &ScConfiguration::set, "set");
dukglue_register_method(ctx, &ScConfiguration::has, "has");
}
private:
std::pair<std::string_view, std::string_view> GetNextNamespace(const std::string_view& input) const
{
auto pos = input.find('.');
if (pos == std::string_view::npos)
{
return std::make_pair(input, std::string_view());
}
else
{
return std::make_pair(input.substr(0, pos), input.substr(pos + 1));
}
}
std::pair<std::string_view, std::string_view> GetNamespaceAndKey(const std::string_view& input) const
{
auto pos = input.find_last_of('.');
return pos == std::string_view::npos ? std::make_pair(std::string_view(), input)
: std::make_pair(input.substr(0, pos), input.substr(pos + 1));
}
std::optional<DukValue> GetNamespaceObject(const std::string_view& ns) const
{
auto store = _backingObject;
auto k = ns;
bool end;
do
{
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;
}
DukValue GetOrCreateNamespaceObject(duk_context* ctx, const std::string_view& ns) const
{
auto store = _backingObject;
if (!ns.empty())
{
std::string_view k = ns;
bool end;
do
{
auto [next, remainder] = GetNextNamespace(k);
auto subStore = store[next];
k = remainder;
if (subStore.type() == DukValue::Type::UNDEFINED)
{
store.push();
duk_push_object(ctx);
store = DukValue::copy_from_stack(ctx);
duk_put_prop_lstring(ctx, -2, next.data(), next.size());
duk_pop(ctx);
}
else
{
store = subStore;
}
end = remainder.empty();
} while (!end);
}
return store;
}
bool IsValidNamespace(const std::string_view& ns) const
{
if (ns.empty() || ns[0] == '.' || ns[ns.size() - 1] == '.')
{
return false;
}
for (size_t i = 1; i < ns.size() - 1; i++)
{
if (ns[i - 1] == '.' && ns[i] == '.')
{
return false;
}
}
return true;
}
bool IsValidKey(const std::string_view& key) const
{
return !key.empty() && key.find('.') == std::string_view::npos;
}
DukValue getAll(const std::string& ns) const
{
DukValue result;
auto ctx = GetContext()->GetScriptEngine().GetContext();
if (IsValidNamespace(ns))
{
if (_isUserConfig)
{
DukObject obj(ctx);
if (ns == "general")
{
obj.Set("general.language", gConfigGeneral.language);
obj.Set("general.showFps", gConfigGeneral.show_fps);
}
result = obj.Take();
}
else
{
auto obj = GetNamespaceObject(ns);
result = obj ? *obj : DukObject(ctx).Take();
}
}
else
{
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
}
return result;
}
DukValue get(const std::string& key, const DukValue& defaultValue) const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
if (_isUserConfig)
{
if (key == "general.language")
{
auto& localisationService = GetContext()->GetLocalisationService();
auto language = localisationService.GetCurrentLanguage();
auto locale = "";
if (language >= 0 && static_cast<size_t>(language) < std::size(LanguagesDescriptors))
{
locale = LanguagesDescriptors[language].locale;
}
duk_push_string(ctx, locale);
return DukValue::take_from_stack(ctx);
}
else if (key == "general.showFps")
{
duk_push_boolean(ctx, gConfigGeneral.show_fps);
return DukValue::take_from_stack(ctx);
}
}
else
{
auto [ns, n] = GetNamespaceAndKey(key);
if (!IsValidNamespace(ns))
{
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
}
else if (!IsValidKey(n))
{
duk_error(ctx, DUK_ERR_ERROR, "Key was invalid.");
}
else
{
auto obj = GetNamespaceObject(ns);
if (obj)
{
auto val = (*obj)[n];
if (val.type() != DukValue::Type::UNDEFINED)
{
return val;
}
}
}
}
return defaultValue;
}
void set(const std::string& key, const DukValue& value) const
{
auto& scriptEngine = GetContext()->GetScriptEngine();
auto ctx = scriptEngine.GetContext();
if (_isUserConfig)
{
try
{
if (key == "general.showFps")
{
gConfigGeneral.show_fps = value.as_bool();
}
else
{
duk_error(ctx, DUK_ERR_ERROR, "Property does not exist.");
}
}
catch (const DukException&)
{
duk_error(ctx, DUK_ERR_ERROR, "Invalid value for this property.");
}
}
else
{
auto [ns, n] = GetNamespaceAndKey(key);
if (!IsValidNamespace(ns))
{
duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid.");
}
else if (!IsValidKey(n))
{
duk_error(ctx, DUK_ERR_ERROR, "Key was invalid.");
}
else
{
auto obj = GetOrCreateNamespaceObject(ctx, ns);
obj.push();
if (value.type() == DukValue::Type::UNDEFINED)
{
duk_del_prop_string(ctx, -1, key.c_str());
}
else
{
value.push();
duk_put_prop_lstring(ctx, -2, n.data(), n.size());
}
duk_pop(ctx);
scriptEngine.SaveSharedStorage();
}
}
}
bool has(const std::string& key) const
{
auto val = get(key, DukValue());
return val.type() != DukValue::Type::UNDEFINED;
}
};
} // namespace OpenRCT2::Scripting
#endif