diff --git a/contributors.md b/contributors.md
index 244a5e9921..c56aa370db 100644
--- a/contributors.md
+++ b/contributors.md
@@ -118,6 +118,7 @@ Appreciation for contributors who have provided substantial work, but are no lon
* Wenzhao Qiu (qwzhaox) - Misc.
* Tiago Reul (reul) - Misc.
* Fredrik Tegnell (fredriktegnell) - Misc.
+* Alex Parisi (alex-parisi) - Added API for returning metadata from all registered plugins.
## Bug fixes & Refactors
* (KirilAngelov)
diff --git a/distribution/changelog.txt b/distribution/changelog.txt
index fedf2b08f2..526e594604 100644
--- a/distribution/changelog.txt
+++ b/distribution/changelog.txt
@@ -1,5 +1,6 @@
0.4.9 (in development)
------------------------------------------------------------------------
+- Feature: [#20709] [Plugin] Plugins can now check metadata from all registered plugins.
- Feature: [#21376] Add option to reload an object (for object developers).
- Improved: [#21356] Resize the title bar when moving between displays with different scaling factors on Windows systems.
- Improved: [#21388] Tooltips will now show even when an error message is present.
diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts
index f945bc11d3..4b7e5f28d7 100644
--- a/distribution/openrct2.d.ts
+++ b/distribution/openrct2.d.ts
@@ -56,7 +56,10 @@ declare global {
* Plugin writers should check if ui is available using `typeof ui !== 'undefined'`.
*/
var ui: Ui;
-
+ /**
+ * APIs for managing the installed plugins
+ */
+ var pluginManager: PluginManager;
/**
* Registers the plugin. This may only be called once.
* @param metadata Information about the plugin and the entry point.
@@ -4948,4 +4951,11 @@ declare global {
getAllObjects(type: "scenery_group"): SceneryGroupObject[];
getAllObjects(type: "music"): LoadedObject[];
}
+
+ /**
+ * Interface to handle the plugin manager
+ */
+ interface PluginManager {
+ readonly plugins: PluginMetadata[];
+ }
}
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 45e0a69e65..1f942f678b 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -511,6 +511,7 @@
+
diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp
index 6211471bc5..8ada7bbb7e 100644
--- a/src/openrct2/scripting/ScriptEngine.cpp
+++ b/src/openrct2/scripting/ScriptEngine.cpp
@@ -34,6 +34,7 @@
# include "bindings/game/ScConsole.hpp"
# include "bindings/game/ScContext.hpp"
# include "bindings/game/ScDisposable.hpp"
+# include "bindings/game/ScPlugin.hpp"
# include "bindings/game/ScProfiler.hpp"
# include "bindings/network/ScNetwork.hpp"
# include "bindings/network/ScPlayer.hpp"
@@ -443,6 +444,7 @@ void ScriptEngine::Initialise()
ScScenarioObjective::Register(ctx);
ScPatrolArea::Register(ctx);
ScStaff::Register(ctx);
+ ScPlugin::Register(ctx);
dukglue_register_global(ctx, std::make_shared(), "cheats");
dukglue_register_global(ctx, std::make_shared(), "climate");
@@ -452,6 +454,7 @@ void ScriptEngine::Initialise()
dukglue_register_global(ctx, std::make_shared(ctx), "map");
dukglue_register_global(ctx, std::make_shared(ctx), "network");
dukglue_register_global(ctx, std::make_shared(ctx), "park");
+ dukglue_register_global(ctx, std::make_shared(), "pluginManager");
dukglue_register_global(ctx, std::make_shared(ctx), "profiler");
dukglue_register_global(ctx, std::make_shared(), "scenario");
dukglue_register_global(ctx, std::make_shared(), "objectManager");
diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h
index d186007e6b..ba37b88962 100644
--- a/src/openrct2/scripting/ScriptEngine.h
+++ b/src/openrct2/scripting/ScriptEngine.h
@@ -47,7 +47,7 @@ namespace OpenRCT2
namespace OpenRCT2::Scripting
{
- static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 82;
+ static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 83;
// Versions marking breaking changes.
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
diff --git a/src/openrct2/scripting/bindings/game/ScPlugin.hpp b/src/openrct2/scripting/bindings/game/ScPlugin.hpp
new file mode 100644
index 0000000000..0253b08e56
--- /dev/null
+++ b/src/openrct2/scripting/bindings/game/ScPlugin.hpp
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 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 "../../Duktape.hpp"
+# include "../../ScriptEngine.h"
+# include "../game/ScContext.hpp"
+
+namespace OpenRCT2::Scripting
+{
+ class ScPlugin
+ {
+ public:
+ static void Register(duk_context* ctx)
+ {
+ dukglue_register_property(ctx, &ScPlugin::plugins_get, nullptr, "plugins");
+ }
+
+ private:
+ std::vector plugins_get()
+ {
+ auto ctx = getContext();
+ auto& allPlugins = getallPlugins();
+ return formatMetadata(ctx, allPlugins);
+ }
+
+ duk_context* getContext()
+ {
+ // Get the context from the script engine
+ OpenRCT2::Scripting::ScriptEngine& scriptEngine = GetContext()->GetScriptEngine();
+ return scriptEngine.GetContext();
+ }
+
+ const std::vector> getallPlugins()
+ {
+ // Get all of the plugins from the script engine
+ OpenRCT2::Scripting::ScriptEngine& scriptEngine = GetContext()->GetScriptEngine();
+ return scriptEngine.GetPlugins();
+ }
+
+ const std::vector formatMetadata(
+ duk_context* ctx, const std::vector>& allPlugins)
+ {
+ std::vector formattedMetadata;
+ duk_idx_t dukIdx = DUK_INVALID_INDEX;
+ // Iterate through all plugins and and cast their data to Duk objects
+ for (const auto& pluginPtr : allPlugins)
+ {
+ // Pull out metadata
+ OpenRCT2::Scripting::Plugin& plugin = *pluginPtr;
+ OpenRCT2::Scripting::PluginMetadata metadata = plugin.GetMetadata();
+ // Create object using Duk stack
+ dukIdx = duk_push_object(ctx);
+ // Name and Version
+ duk_push_string(ctx, metadata.Name.c_str());
+ duk_put_prop_string(ctx, dukIdx, "name");
+ duk_push_string(ctx, metadata.Version.c_str());
+ duk_put_prop_string(ctx, dukIdx, "version");
+ // Authors
+ duk_idx_t arrIdx = duk_push_array(ctx);
+ for (auto [s, idx] = std::tuple{ metadata.Authors.begin(), 0 }; s != metadata.Authors.end(); s++, idx++)
+ {
+ auto& str = *s;
+ duk_push_string(ctx, str.c_str());
+ duk_put_prop_index(ctx, arrIdx, idx);
+ }
+ duk_put_prop_string(ctx, dukIdx, "authors");
+ // Take from Duk stack
+ formattedMetadata.push_back(DukValue::take_from_stack(ctx, dukIdx));
+ dukIdx = DUK_INVALID_INDEX;
+ }
+ return formattedMetadata;
+ }
+ };
+} // namespace OpenRCT2::Scripting
+
+#endif