1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-22 23:33:04 +01:00
Files
OpenRCT2/src/openrct2/scripting/Plugin.cpp
2020-04-26 14:35:03 +01:00

169 lines
4.7 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2018 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.
*****************************************************************************/
#ifdef __ENABLE_SCRIPTING__
# include "Plugin.h"
# include "../OpenRCT2.h"
# include "Duktape.hpp"
# include <algorithm>
# include <fstream>
# include <memory>
using namespace OpenRCT2::Scripting;
Plugin::Plugin(duk_context* context, const std::string& path)
: _context(context)
, _path(path)
{
}
void Plugin::SetCode(const std::string_view& code)
{
_code = code;
}
void Plugin::Load()
{
if (!_path.empty())
{
LoadCodeFromFile();
}
std::string projectedVariables = "console,context,date,map,network,park";
if (!gOpenRCT2Headless)
{
projectedVariables += ",ui";
}
// Wrap the script in a function and pass the global objects as variables
// so that if the script modifies them, they are not modified for other scripts.
// clang-format off
auto code = _code;
code =
" (function(" + projectedVariables + ") {"
" var __metadata__ = null;"
" var registerPlugin = function(m) { __metadata__ = m };"
" (function(__metadata__) {"
+ code +
" })();"
" return __metadata__;"
" })(" + projectedVariables + ");";
// clang-format on
auto flags = DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME;
auto result = duk_eval_raw(_context, code.c_str(), code.size(), flags);
if (result != DUK_ERR_NONE)
{
auto val = std::string(duk_safe_to_string(_context, -1));
duk_pop(_context);
throw std::runtime_error("Failed to load plug-in script: " + val);
}
_metadata = GetMetadata(DukValue::take_from_stack(_context));
}
void Plugin::Start()
{
const auto& mainFunc = _metadata.Main;
if (mainFunc.context() == nullptr)
{
throw std::runtime_error("No main function specified.");
}
mainFunc.push();
auto result = duk_pcall(_context, 0);
if (result != DUK_ERR_NONE)
{
auto val = std::string(duk_safe_to_string(_context, -1));
duk_pop(_context);
throw std::runtime_error("[" + _metadata.Name + "] " + val);
}
duk_pop(_context);
_hasStarted = true;
}
void Plugin::Stop()
{
_hasStarted = false;
}
void Plugin::Update()
{
}
void Plugin::LoadCodeFromFile()
{
std::string code;
std::ifstream fs(_path);
if (fs.is_open())
{
fs.seekg(0, std::ios::end);
code.reserve(fs.tellg());
fs.seekg(0, std::ios::beg);
code.assign(std::istreambuf_iterator<char>(fs), std::istreambuf_iterator<char>());
}
_code = std::move(code);
}
static std::string TryGetString(const DukValue& value, const std::string& message)
{
if (value.type() != DukValue::Type::STRING)
throw std::runtime_error(message);
return value.as_string();
}
PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata)
{
PluginMetadata metadata;
if (dukMetadata.type() == DukValue::Type::OBJECT)
{
metadata.Name = TryGetString(dukMetadata["name"], "Plugin name not specified.");
metadata.Version = TryGetString(dukMetadata["version"], "Plugin version not specified.");
metadata.Type = ParsePluginType(TryGetString(dukMetadata["type"], "Plugin type not specified."));
auto dukMinApiVersion = dukMetadata["minApiVersion"];
if (dukMinApiVersion.type() == DukValue::Type::NUMBER)
{
metadata.MinApiVersion = dukMinApiVersion.as_int();
}
auto dukAuthors = dukMetadata["authors"];
dukAuthors.push();
if (dukAuthors.is_array())
{
auto elements = dukAuthors.as_array();
std::transform(elements.begin(), elements.end(), std::back_inserter(metadata.Authors), [](const DukValue& v) {
return v.as_string();
});
}
else if (dukAuthors.type() == DukValue::Type::STRING)
{
metadata.Authors = { dukAuthors.as_string() };
}
metadata.Main = dukMetadata["main"];
}
return metadata;
}
PluginType Plugin::ParsePluginType(const std::string_view& type)
{
if (type == "local")
return PluginType::Local;
if (type == "remote")
return PluginType::Remote;
throw std::invalid_argument("Unknown plugin type.");
}
#endif