1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-24 00:03:11 +01:00

Use new FileWatcher class

This commit is contained in:
Ted John
2018-03-31 00:08:59 +01:00
parent 834cfad502
commit 1fd33dd86a
5 changed files with 164 additions and 37 deletions

View File

@@ -1,5 +1,6 @@
#include <array> #include <array>
#include <stdexcept> #include <stdexcept>
#include <experimental/filesystem>
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
@@ -14,6 +15,65 @@
#include "../core/String.hpp" #include "../core/String.hpp"
#include "FileWatcher.h" #include "FileWatcher.h"
namespace fs = std::experimental::filesystem;
#ifndef _WIN32
FileWatcher::FileDescriptor::~FileDescriptor()
{
Close();
}
void FileWatcher::FileDescriptor::Initialise()
{
int 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);
Fd = fd;
log_verbose("FileWatcher: inotify_init succeeded");
}
else
{
log_verbose("FileWatcher: inotify_init failed");
throw std::runtime_error("inotify_init failed");
}
}
void FileWatcher::FileDescriptor::Close()
{
if (Fd != -1)
{
close(Fd);
Fd = -1;
}
}
FileWatcher::WatchDescriptor::WatchDescriptor(int fd, const std::string& path)
: Fd(fd),
Wd(inotify_add_watch(fd, path.c_str(), IN_CLOSE_WRITE)),
Path(path)
{
if (Wd >= 0)
{
log_verbose("FileWatcher: inotify watch added for %s", path.c_str());
}
else
{
log_verbose("FileWatcher: inotify_add_watch failed for %s", path.c_str());
throw std::runtime_error("inotify_add_watch failed for '" + path + "'");
}
}
FileWatcher::WatchDescriptor::~WatchDescriptor()
{
inotify_rm_watch(Fd, Wd);
log_verbose("FileWatcher: inotify watch removed");
}
#endif
FileWatcher::FileWatcher(const std::string &directoryPath) FileWatcher::FileWatcher(const std::string &directoryPath)
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -23,19 +83,13 @@ FileWatcher::FileWatcher(const std::string &directoryPath)
throw std::runtime_error("Unable to open directory '" + directoryPath + "'"); throw std::runtime_error("Unable to open directory '" + directoryPath + "'");
} }
#else #else
auto fd = inotify_init(); _fileDesc.Initialise();
if (fd >= 0) _watchDescs.emplace_back(_fileDesc.Fd, directoryPath);
for (auto& p : fs::recursive_directory_iterator(directoryPath))
{ {
auto wd = inotify_add_watch(fd, directoryPath.c_str(), IN_CLOSE_WRITE); if (p.status().type() == fs::file_type::directory)
if (wd >= 0)
{ {
_fileDesc = fd; _watchDescs.emplace_back(_fileDesc.Fd, p.path().string());
_watchDesc = wd;
}
else
{
close(fd);
throw std::runtime_error("Unable to watch directory '" + directoryPath + "'");
} }
} }
#endif #endif
@@ -48,8 +102,8 @@ FileWatcher::~FileWatcher()
CancelIoEx(_directoryHandle, nullptr); CancelIoEx(_directoryHandle, nullptr);
CloseHandle(_directoryHandle); CloseHandle(_directoryHandle);
#else #else
inotify_rm_watch(_fileDesc, _watchDesc); _finished = true;
close(_fileDesc); _fileDesc.Close();
#endif #endif
_watchThread.join(); _watchThread.join();
} }
@@ -79,24 +133,46 @@ void FileWatcher::WatchDirectory()
} }
} }
#else #else
log_verbose("FileWatcher: reading event data...");
std::array<char, 1024> eventData; std::array<char, 1024> eventData;
auto length = read(_fileDesc, eventData.data(), eventData.size()); while (!_finished)
if (length >= 0)
{ {
auto onFileChanged = OnFileChanged; int length = read(_fileDesc.Fd, eventData.data(), eventData.size());
if (onFileChanged) if (length >= 0)
{ {
int offset = 0; log_verbose("FileWatcher: inotify event data received");
while (offset < length) auto onFileChanged = OnFileChanged;
if (onFileChanged)
{ {
auto e = (inotify_event*)(eventData.data() + offset); int offset = 0;
if ((e->mask & IN_CLOSE_WRITE) && !(e->mask & IN_ISDIR)) while (offset < length)
{ {
onFileChanged(e->name); auto e = (inotify_event*)(eventData.data() + offset);
if ((e->mask & IN_CLOSE_WRITE) && !(e->mask & IN_ISDIR))
{
log_verbose("FileWatcher: inotify event received for %s", e->name);
// Find watch descriptor
int wd = e->wd;
auto findResult = std::find_if(_watchDescs.begin(), _watchDescs.end(),
[wd](const WatchDescriptor& watchDesc)
{
return wd == watchDesc.Wd;
});
if (findResult != _watchDescs.end())
{
auto directory = findResult->Path;
auto path = fs::path(directory) / fs::path(e->name);
onFileChanged(path);
}
}
offset += sizeof(inotify_event) + e->len;
} }
offset += sizeof(inotify_event) + e->len;
} }
} }
// Sleep for 1/2 second
usleep(500000);
} }
#endif #endif
} }

View File

@@ -3,6 +3,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector>
#ifdef _WIN32 #ifdef _WIN32
typedef void * HANDLE; typedef void * HANDLE;
@@ -18,8 +19,27 @@ private:
#ifdef _WIN32 #ifdef _WIN32
HANDLE _directoryHandle{}; HANDLE _directoryHandle{};
#else #else
int _fileDesc{}; struct FileDescriptor
int _watchDesc{}; {
int Fd = -1;
~FileDescriptor();
void Initialise();
void Close();
};
struct WatchDescriptor
{
int const Fd;
int const Wd;
std::string const Path;
WatchDescriptor(int fd, const std::string& path);
~WatchDescriptor();
};
FileDescriptor _fileDesc;
std::vector<WatchDescriptor> _watchDescs;
#endif #endif
public: public:
@@ -29,5 +49,7 @@ public:
~FileWatcher(); ~FileWatcher();
private: private:
bool _finished{};
void WatchDirectory(); void WatchDirectory();
}; };

View File

@@ -47,6 +47,8 @@ namespace OpenRCT2::Scripting
bool _hotReloadEnabled{}; bool _hotReloadEnabled{};
public: public:
std::string GetPath() const { return _path; };
Plugin() { } Plugin() { }
Plugin(duk_context * context, const std::string &path); Plugin(duk_context * context, const std::string &path);
Plugin(const Plugin&) = delete; Plugin(const Plugin&) = delete;

View File

@@ -86,7 +86,6 @@ void ScriptEngine::LoadPlugins()
auto plugin = std::make_shared<Plugin>(_context, path); auto plugin = std::make_shared<Plugin>(_context, path);
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
plugin->Load(); plugin->Load();
plugin->EnableHotReload();
_plugins.push_back(std::move(plugin)); _plugins.push_back(std::move(plugin));
} }
catch (const std::exception &e) catch (const std::exception &e)
@@ -94,27 +93,47 @@ void ScriptEngine::LoadPlugins()
_console.WriteLineError(e.what()); _console.WriteLineError(e.what());
} }
} }
// 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);
};
} }
void ScriptEngine::AutoReloadPlugins() void ScriptEngine::AutoReloadPlugins()
{ {
for (auto& plugin : _plugins) if (_changedPluginFiles.size() > 0)
{ {
if (plugin->ShouldHotReload()) std::lock_guard<std::mutex> guard(_changedPluginFilesMutex);
for (auto& path : _changedPluginFiles)
{ {
try auto findResult = std::find_if(_plugins.begin(), _plugins.end(),
[&path](const std::shared_ptr<Plugin>& plugin)
{
return path == plugin->GetPath();
});
if (findResult != _plugins.end())
{ {
_hookEngine.UnsubscribeAll(plugin); auto& plugin = *findResult;
try
{
_hookEngine.UnsubscribeAll(plugin);
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
plugin->Load(); plugin->Load();
plugin->Start(); plugin->Start();
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
_console.WriteLineError(e.what()); _console.WriteLineError(e.what());
}
} }
} }
_changedPluginFiles.clear();
} }
} }

View File

@@ -10,16 +10,20 @@
#pragma once #pragma once
#include "../common.h" #include "../common.h"
#include "../core/FileWatcher.h"
#include "HookEngine.h" #include "HookEngine.h"
#include "Plugin.h" #include "Plugin.h"
#include <future> #include <future>
#include <memory> #include <memory>
#include <mutex>
#include <queue> #include <queue>
#include <string> #include <string>
#include <vector>
struct duk_hthread; struct duk_hthread;
typedef struct duk_hthread duk_context; typedef struct duk_hthread duk_context;
class FileWatcher;
class InteractiveConsole; class InteractiveConsole;
namespace OpenRCT2 namespace OpenRCT2
@@ -88,6 +92,10 @@ namespace OpenRCT2::Scripting
HookEngine _hookEngine; HookEngine _hookEngine;
ScriptExecutionInfo _execInfo; ScriptExecutionInfo _execInfo;
std::unique_ptr<FileWatcher> _pluginFileWatcher;
std::vector<std::string> _changedPluginFiles;
std::mutex _changedPluginFilesMutex;
public: public:
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env); ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
ScriptEngine(ScriptEngine&) = delete; ScriptEngine(ScriptEngine&) = delete;