Files
OpenRCT2-Unity/src/openrct2/scripting/ScriptEngine.cpp

212 lines
5.8 KiB
C++
Raw Normal View History

/*****************************************************************************
* 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.
*****************************************************************************/
#include "ScriptEngine.h"
2018-03-18 16:27:48 +00:00
#include "../core/FileScanner.h"
#include "../core/Path.hpp"
#include "../interface/InteractiveConsole.h"
2018-03-18 17:43:47 +00:00
#include "../platform/Platform2.h"
2018-03-18 16:27:48 +00:00
#include "../PlatformEnvironment.h"
2018-03-17 23:26:55 +00:00
#include <dukglue/dukglue.h>
#include <duktape.h>
2018-03-17 23:26:55 +00:00
#include <iostream>
#include <stdexcept>
2018-03-17 23:26:55 +00:00
#include "ScConsole.hpp"
2018-03-18 23:35:58 +00:00
#include "ScContext.hpp"
#include "ScDisposable.hpp"
2018-03-20 19:40:38 +00:00
#include "ScMap.hpp"
2018-03-18 00:31:02 +00:00
#include "ScPark.hpp"
2018-03-20 20:28:15 +00:00
#include "ScTile.hpp"
2018-03-20 19:40:38 +00:00
#include "ScThing.hpp"
2018-03-17 23:26:55 +00:00
using namespace OpenRCT2;
using namespace OpenRCT2::Scripting;
static std::string Stringify(duk_context * ctx, duk_idx_t idx);
DukContext::DukContext()
{
_context = duk_create_heap_default();
if (_context == nullptr)
{
throw std::runtime_error("Unable to initialise duktape context.");
}
}
DukContext::~DukContext()
{
duk_destroy_heap(_context);
}
ScriptEngine::ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env) :
_console(console),
2018-03-18 23:35:58 +00:00
_env(env),
_hookEngine(_execInfo)
{
}
2018-03-17 23:26:55 +00:00
void ScriptEngine::Initialise()
{
auto ctx = (duk_context*)_context;
2018-03-17 23:26:55 +00:00
ScConsole::Register(ctx);
2018-03-18 23:35:58 +00:00
ScContext::Register(ctx);
ScDisposable::Register(ctx);
2018-03-20 19:40:38 +00:00
ScMap::Register(ctx);
2018-03-18 00:31:02 +00:00
ScPark::Register(ctx);
2018-03-20 20:28:15 +00:00
ScTile::Register(ctx);
ScTileElement::Register(ctx);
2018-03-20 19:40:38 +00:00
ScThing::Register(ctx);
2018-03-17 23:26:55 +00:00
2018-03-18 00:31:02 +00:00
dukglue_register_global(ctx, std::make_shared<ScConsole>(_console), "console");
2018-03-18 23:35:58 +00:00
dukglue_register_global(ctx, std::make_shared<ScContext>(_execInfo, _hookEngine), "context");
2018-03-20 19:40:38 +00:00
dukglue_register_global(ctx, std::make_shared<ScMap>(ctx), "map");
2018-03-18 00:31:02 +00:00
dukglue_register_global(ctx, std::make_shared<ScPark>(), "park");
2018-03-18 16:27:48 +00:00
LoadPlugins();
StartPlugins();
}
void ScriptEngine::LoadPlugins()
{
auto base = _env.GetDirectoryPath(DIRBASE::USER, DIRID::PLUGIN);
auto pattern = Path::Combine(base, "*.js");
auto scanner = std::unique_ptr<IFileScanner>(Path::ScanDirectory(pattern, true));
while (scanner->Next())
{
auto path = std::string(scanner->GetPath());
try
{
2018-03-23 22:39:12 +00:00
auto plugin = std::make_shared<Plugin>(_context, path);
2018-03-23 23:24:36 +00:00
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
2018-03-23 22:39:12 +00:00
plugin->Load();
_plugins.push_back(std::move(plugin));
2018-03-18 16:27:48 +00:00
}
catch (const std::exception &e)
{
_console.WriteLineError(e.what());
}
}
2018-03-31 00:08:59 +01:00
// Enable hot reloading
_pluginFileWatcher = std::make_unique<FileWatcher>(base);
_pluginFileWatcher->OnFileChanged =
[this](const std::string &path)
{
std::lock_guard<std::mutex> guard(_changedPluginFilesMutex);
_changedPluginFiles.push_back(path);
};
2018-03-18 16:27:48 +00:00
}
2018-03-18 17:43:47 +00:00
void ScriptEngine::AutoReloadPlugins()
{
2018-03-31 00:08:59 +01:00
if (_changedPluginFiles.size() > 0)
2018-03-18 17:43:47 +00:00
{
2018-03-31 00:08:59 +01:00
std::lock_guard<std::mutex> guard(_changedPluginFilesMutex);
for (auto& path : _changedPluginFiles)
2018-03-18 17:43:47 +00:00
{
2018-03-31 00:08:59 +01:00
auto findResult = std::find_if(_plugins.begin(), _plugins.end(),
[&path](const std::shared_ptr<Plugin>& plugin)
{
2018-03-31 10:27:45 +01:00
return Path::Equals(path, plugin->GetPath());
2018-03-31 00:08:59 +01:00
});
if (findResult != _plugins.end())
2018-03-18 17:43:47 +00:00
{
2018-03-31 00:08:59 +01:00
auto& plugin = *findResult;
try
{
_hookEngine.UnsubscribeAll(plugin);
2018-03-23 23:24:36 +00:00
2018-03-31 00:08:59 +01:00
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
plugin->Load();
plugin->Start();
}
catch (const std::exception &e)
{
_console.WriteLineError(e.what());
}
2018-03-18 17:43:47 +00:00
}
}
2018-03-31 00:08:59 +01:00
_changedPluginFiles.clear();
2018-03-18 17:43:47 +00:00
}
}
2018-03-18 16:27:48 +00:00
void ScriptEngine::StartPlugins()
{
for (auto& plugin : _plugins)
{
2018-03-23 23:24:36 +00:00
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
2018-03-18 23:35:58 +00:00
try
{
2018-03-23 22:39:12 +00:00
plugin->Start();
2018-03-18 23:35:58 +00:00
}
catch (const std::exception &e)
{
_console.WriteLineError(e.what());
}
2018-03-18 16:27:48 +00:00
}
2018-03-17 23:26:55 +00:00
}
void ScriptEngine::Update()
{
2018-03-17 23:26:55 +00:00
if (!_initialised)
{
Initialise();
_initialised = true;
}
while (_evalQueue.size() > 0)
{
auto item = std::move(_evalQueue.front());
_evalQueue.pop();
auto promise = std::move(std::get<0>(item));
auto command = std::move(std::get<1>(item));
if (duk_peval_string(_context, command.c_str()) != 0)
{
std::string result = std::string(duk_safe_to_string(_context, -1));
_console.WriteLineError(result);
}
else
{
std::string result = Stringify(_context, -1);
_console.WriteLine(result);
}
duk_pop(_context);
// Signal the promise so caller can continue
promise.set_value();
}
2018-03-18 17:43:47 +00:00
auto tick = Platform::GetTicks();
if (tick - _lastHotReloadCheckTick > 1000)
{
AutoReloadPlugins();
_lastHotReloadCheckTick = tick;
}
}
std::future<void> ScriptEngine::Eval(const std::string &s)
{
std::promise<void> barrier;
auto future = barrier.get_future();
_evalQueue.emplace(std::move(barrier), s);
return future;
}
static std::string Stringify(duk_context * ctx, duk_idx_t idx)
{
auto type = duk_get_type(ctx, idx);
if (type == DUK_TYPE_OBJECT && !duk_is_function(ctx, idx))
{
return duk_json_encode(ctx, idx);
}
else
{
return duk_safe_to_string(ctx, idx);
}
}