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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user