mirror of https://github.com/OpenRCT2/OpenRCT2.git
Add hot reloading support
This commit is contained in:
parent
de527b3ff7
commit
3556dead74
|
@ -21,6 +21,11 @@
|
|||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
Plugin::Plugin(duk_context * context, const std::string &path)
|
||||
|
@ -29,11 +34,23 @@ Plugin::Plugin(duk_context * context, const std::string &path)
|
|||
{
|
||||
}
|
||||
|
||||
Plugin::Plugin(const Plugin&& src)
|
||||
Plugin::Plugin(Plugin&& src)
|
||||
: _context(src._context),
|
||||
_path(src._path),
|
||||
_metadata(src._metadata)
|
||||
_metadata(src._metadata),
|
||||
_hotReloadData(src._hotReloadData),
|
||||
_hotReloadEnabled(src._hotReloadEnabled)
|
||||
{
|
||||
src._context = nullptr;
|
||||
src._path = std::string();
|
||||
src._metadata = PluginMetadata();
|
||||
src._hotReloadData = HotReloadData();
|
||||
src._hotReloadEnabled = false;
|
||||
}
|
||||
|
||||
Plugin::~Plugin()
|
||||
{
|
||||
DisableHotReload();
|
||||
}
|
||||
|
||||
void Plugin::Load()
|
||||
|
@ -70,6 +87,11 @@ void Plugin::Load()
|
|||
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)
|
||||
|
@ -81,6 +103,66 @@ void Plugin::Start()
|
|||
duk_pop(_context);
|
||||
}
|
||||
|
||||
void Plugin::Update()
|
||||
{
|
||||
}
|
||||
|
||||
void Plugin::EnableHotReload()
|
||||
{
|
||||
auto fd = inotify_init();
|
||||
if (fd >= 0)
|
||||
{
|
||||
// Mark file as non-blocking
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
auto wd = inotify_add_watch(fd, _path.c_str(), IN_CLOSE_WRITE);
|
||||
if (wd >= 0)
|
||||
{
|
||||
_hotReloadData.FileDesc = fd;
|
||||
_hotReloadData.WatchDesc = wd;
|
||||
_hotReloadEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Plugin::ShouldHotReload()
|
||||
{
|
||||
if (_hotReloadEnabled)
|
||||
{
|
||||
std::vector<char> eventData;
|
||||
eventData.resize(1024);
|
||||
|
||||
auto length = read(_hotReloadData.FileDesc, eventData.data(), eventData.size());
|
||||
int offset = 0;
|
||||
while (offset < length)
|
||||
{
|
||||
auto e = (inotify_event*)&eventData[offset];
|
||||
if ((e->mask & IN_CLOSE_WRITE) && !(e->mask & IN_ISDIR))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
offset += sizeof(inotify_event) + e->len;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Plugin::DisableHotReload()
|
||||
{
|
||||
if (_hotReloadEnabled)
|
||||
{
|
||||
inotify_rm_watch(_hotReloadData.FileDesc, _hotReloadData.WatchDesc);
|
||||
close(_hotReloadData.FileDesc);
|
||||
_hotReloadData = HotReloadData();
|
||||
_hotReloadEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata)
|
||||
{
|
||||
PluginMetadata metadata;
|
||||
|
|
|
@ -34,17 +34,32 @@ namespace OpenRCT2::Scripting
|
|||
class Plugin
|
||||
{
|
||||
private:
|
||||
duk_context * const _context;
|
||||
std::string const _path;
|
||||
struct HotReloadData
|
||||
{
|
||||
int FileDesc{};
|
||||
int WatchDesc{};
|
||||
};
|
||||
|
||||
duk_context * _context;
|
||||
std::string _path;
|
||||
PluginMetadata _metadata;
|
||||
HotReloadData _hotReloadData;
|
||||
bool _hotReloadEnabled{};
|
||||
|
||||
public:
|
||||
Plugin() { }
|
||||
Plugin(duk_context * context, const std::string &path);
|
||||
Plugin(const Plugin&) = delete;
|
||||
Plugin(const Plugin&&);
|
||||
Plugin(Plugin&&);
|
||||
~Plugin();
|
||||
|
||||
void Load();
|
||||
void Start();
|
||||
void Update();
|
||||
|
||||
void EnableHotReload();
|
||||
bool ShouldHotReload();
|
||||
void DisableHotReload();
|
||||
|
||||
private:
|
||||
static PluginMetadata GetMetadata(const DukValue& dukMetadata);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "../core/FileScanner.h"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../interface/InteractiveConsole.h"
|
||||
#include "../platform/Platform2.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include <dukglue/dukglue.h>
|
||||
#include <duktape.h>
|
||||
|
@ -67,6 +68,7 @@ void ScriptEngine::LoadPlugins()
|
|||
{
|
||||
Plugin p(_context, path);
|
||||
p.Load();
|
||||
p.EnableHotReload();
|
||||
_plugins.push_back(std::move(p));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
|
@ -76,6 +78,25 @@ void ScriptEngine::LoadPlugins()
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::AutoReloadPlugins()
|
||||
{
|
||||
for (auto& plugin : _plugins)
|
||||
{
|
||||
if (plugin.ShouldHotReload())
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.Load();
|
||||
plugin.Start();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
_console.WriteLineError(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::StartPlugins()
|
||||
{
|
||||
for (auto& plugin : _plugins)
|
||||
|
@ -111,6 +132,13 @@ void ScriptEngine::Update()
|
|||
// Signal the promise so caller can continue
|
||||
promise.set_value();
|
||||
}
|
||||
|
||||
auto tick = Platform::GetTicks();
|
||||
if (tick - _lastHotReloadCheckTick > 1000)
|
||||
{
|
||||
AutoReloadPlugins();
|
||||
_lastHotReloadCheckTick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
std::future<void> ScriptEngine::Eval(const std::string &s)
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace OpenRCT2::Scripting
|
|||
duk_context * _context{};
|
||||
std::queue<std::tuple<std::promise<void>, std::string>> _evalQueue;
|
||||
std::vector<Plugin> _plugins;
|
||||
uint32 _lastHotReloadCheckTick{};
|
||||
|
||||
public:
|
||||
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
|
||||
|
@ -49,5 +50,6 @@ namespace OpenRCT2::Scripting
|
|||
void Initialise();
|
||||
void LoadPlugins();
|
||||
void StartPlugins();
|
||||
void AutoReloadPlugins();
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue