1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-19 21:13:05 +01:00

Merge pull request #16685 from IntelOrca/plugin/park-storage

Add Plugin API for storing data in .park files
This commit is contained in:
Ted John
2022-02-25 14:23:10 +00:00
committed by GitHub
12 changed files with 198 additions and 24 deletions

View File

@@ -86,6 +86,11 @@ void GameState::InitAll(const TileCoordsXY& mapSize)
CheatsReset();
ClearRestrictedScenery();
#ifdef ENABLE_SCRIPTING
auto& scriptEngine = GetContext()->GetScriptEngine();
scriptEngine.ClearParkStorage();
#endif
}
/**

View File

@@ -47,6 +47,7 @@
#include "../ride/Vehicle.h"
#include "../scenario/Scenario.h"
#include "../scenario/ScenarioRepository.h"
#include "../scripting/ScriptEngine.h"
#include "../world/Climate.h"
#include "../world/Entrance.h"
#include "../world/Map.h"
@@ -86,6 +87,7 @@ namespace OpenRCT2
// constexpr uint32_t STAFF = 0x35;
constexpr uint32_t CHEATS = 0x36;
constexpr uint32_t RESTRICTED_OBJECTS = 0x37;
constexpr uint32_t PLUGIN_STORAGE = 0x38;
constexpr uint32_t PACKED_OBJECTS = 0x80;
// clang-format on
}; // namespace ParkFileChunkType
@@ -134,6 +136,7 @@ namespace OpenRCT2
ReadWriteInterfaceChunk(os);
ReadWriteCheatsChunk(os);
ReadWriteRestrictedObjectsChunk(os);
ReadWritePluginStorageChunk(os);
if (os.GetHeader().TargetVersion < 0x4)
{
UpdateTrackElementsRideType();
@@ -167,6 +170,7 @@ namespace OpenRCT2
ReadWriteInterfaceChunk(os);
ReadWriteCheatsChunk(os);
ReadWriteRestrictedObjectsChunk(os);
ReadWritePluginStorageChunk(os);
ReadWritePackedObjectsChunk(os);
}
@@ -548,6 +552,35 @@ namespace OpenRCT2
});
}
void ReadWritePluginStorageChunk(OrcaStream& os)
{
auto& park = GetContext()->GetGameState()->GetPark();
if (os.GetMode() == OrcaStream::Mode::WRITING)
{
#ifdef ENABLE_SCRIPTING
// Dump the plugin storage to JSON (stored in park)
auto& scriptEngine = GetContext()->GetScriptEngine();
park.PluginStorage = scriptEngine.GetParkStorageAsJSON();
#endif
if (park.PluginStorage.empty() || park.PluginStorage == "{}")
{
// Don't write the chunk if there is no plugin storage
return;
}
}
os.ReadWriteChunk(
ParkFileChunkType::PLUGIN_STORAGE, [&park](OrcaStream::ChunkStream& cs) { cs.ReadWrite(park.PluginStorage); });
if (os.GetMode() == OrcaStream::Mode::READING)
{
#ifdef ENABLE_SCRIPTING
auto& scriptEngine = GetContext()->GetScriptEngine();
scriptEngine.SetParkStorageFromJSON(park.PluginStorage);
#endif
}
}
void ReadWritePackedObjectsChunk(OrcaStream& os)
{
static constexpr uint8_t DESCRIPTOR_DAT = 0;

View File

@@ -8,7 +8,7 @@ struct ObjectRepositoryItem;
namespace OpenRCT2
{
// Current version that is saved.
constexpr uint32_t PARK_FILE_CURRENT_VERSION = 0x9;
constexpr uint32_t PARK_FILE_CURRENT_VERSION = 0xA;
// The minimum version that is forwards compatible with the current version.
constexpr uint32_t PARK_FILE_MIN_VERSION = 0x9;

View File

@@ -439,6 +439,7 @@ void ScriptEngine::Initialise()
_pluginsStarted = false;
InitSharedStorage();
ClearParkStorage();
}
void ScriptEngine::LoadPlugins()
@@ -1245,6 +1246,29 @@ void ScriptEngine::SaveSharedStorage()
}
}
void ScriptEngine::ClearParkStorage()
{
duk_push_object(_context);
_parkStorage = std::move(DukValue::take_from_stack(_context));
}
std::string ScriptEngine::GetParkStorageAsJSON()
{
_parkStorage.push();
auto json = std::string(duk_json_encode(_context, -1));
duk_pop(_context);
return json;
}
void ScriptEngine::SetParkStorageFromJSON(std::string_view value)
{
auto result = DuktapeTryParseJson(_context, value);
if (result)
{
_parkStorage = std::move(*result);
}
}
IntervalHandle ScriptEngine::AllocateHandle()
{
for (size_t i = 0; i < _intervals.size(); i++)

View File

@@ -46,7 +46,7 @@ namespace OpenRCT2
namespace OpenRCT2::Scripting
{
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 45;
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 46;
// Versions marking breaking changes.
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
@@ -153,6 +153,7 @@ namespace OpenRCT2::Scripting
HookEngine _hookEngine;
ScriptExecutionInfo _execInfo;
DukValue _sharedStorage;
DukValue _parkStorage;
uint32_t _lastIntervalTimestamp{};
std::vector<ScriptInterval> _intervals;
@@ -195,11 +196,19 @@ namespace OpenRCT2::Scripting
{
return _sharedStorage;
}
DukValue GetParkStorage()
{
return _parkStorage;
}
std::vector<std::shared_ptr<Plugin>>& GetPlugins()
{
return _plugins;
}
void ClearParkStorage();
std::string GetParkStorageAsJSON();
void SetParkStorageFromJSON(std::string_view value);
void LoadPlugins();
void UnloadPlugins();
void Tick();

View File

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

View File

@@ -55,7 +55,59 @@ namespace OpenRCT2::Scripting
std::shared_ptr<ScConfiguration> sharedStorage_get()
{
auto& scriptEngine = GetContext()->GetScriptEngine();
return std::make_shared<ScConfiguration>(scriptEngine.GetSharedStorage());
return std::make_shared<ScConfiguration>(ScConfigurationKind::Shared, scriptEngine.GetSharedStorage());
}
std::shared_ptr<ScConfiguration> GetParkStorageForPlugin(std::string_view pluginName)
{
auto& scriptEngine = GetContext()->GetScriptEngine();
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)
@@ -381,6 +433,7 @@ namespace OpenRCT2::Scripting
dukglue_register_property(ctx, &ScContext::apiVersion_get, nullptr, "apiVersion");
dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration");
dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage");
dukglue_register_method(ctx, &ScContext::getParkStorage, "getParkStorage");
dukglue_register_method(ctx, &ScContext::captureImage, "captureImage");
dukglue_register_method(ctx, &ScContext::getObject, "getObject");
dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects");

View File

@@ -258,6 +258,7 @@ money64 Park::GetCompanyValue() const
void Park::Initialise()
{
Name = format_string(STR_UNNAMED_PARK, nullptr);
PluginStorage = {};
gStaffHandymanColour = COLOUR_BRIGHT_RED;
gStaffMechanicColour = COLOUR_LIGHT_BLUE;
gStaffSecurityColour = COLOUR_YELLOW;

View File

@@ -51,6 +51,7 @@ namespace OpenRCT2
{
public:
std::string Name;
std::string PluginStorage;
Park() = default;
Park(const Park&) = delete;