Skip to content

Commit

Permalink
Add file watcher and auto reload
Browse files Browse the repository at this point in the history
Move reload functionality to its own function, ideally we should move the reload command away from the reload hook..

Co-authored-by: robinsch <[email protected]>
  • Loading branch information
Foereaper and robinsch committed Jun 2, 2024
1 parent 9c6f726 commit bac4124
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 32 deletions.
1 change: 1 addition & 0 deletions ElunaConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void ElunaConfig::Initialize()
SetConfig(CONFIG_ELUNA_ENABLED, "Eluna.Enabled", true);
SetConfig(CONFIG_ELUNA_COMPATIBILITY_MODE, "Eluna.CompatibilityMode", true);
SetConfig(CONFIG_ELUNA_TRACEBACK, "Eluna.TraceBack", false);
SetConfig(CONFIG_ELUNA_SCRIPT_RELOADER, "Eluna.ScriptReloader", false);

// Load strings
SetConfig(CONFIG_ELUNA_SCRIPT_PATH, "Eluna.ScriptPath", "lua_scripts");
Expand Down
1 change: 1 addition & 0 deletions ElunaConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum ElunaConfigBoolValues
CONFIG_ELUNA_ENABLED,
CONFIG_ELUNA_COMPATIBILITY_MODE,
CONFIG_ELUNA_TRACEBACK,
CONFIG_ELUNA_SCRIPT_RELOADER,
CONFIG_ELUNA_BOOL_COUNT
};

Expand Down
82 changes: 82 additions & 0 deletions ElunaLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,28 @@ extern "C" {
#include <lauxlib.h>
}

#ifdef TRINITY
void ElunaUpdateListener::handleFileAction(efsw::WatchID, std::string const& dir, std::string const& filename, efsw::Action, std::string oldFilename)
{
auto const path = fs::absolute(filename, dir);
if (!path.has_extension())
return;

std::string ext = path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return std::tolower(c); });

if (ext != ".lua" && ext != ".ext")
return;

sElunaLoader->ReloadElunaForMap(RELOAD_ALL_STATES);
}
#endif

ElunaLoader::ElunaLoader()
{
#ifdef TRINITY
lua_scriptWatcher = -1;
#endif
}

ElunaLoader* ElunaLoader::instance()
Expand All @@ -48,6 +68,13 @@ ElunaLoader* ElunaLoader::instance()

ElunaLoader::~ElunaLoader()
{
#ifdef TRINITY
if (lua_scriptWatcher >= 0)
{
lua_fileWatcher.removeWatch(lua_scriptWatcher);
lua_scriptWatcher = -1;
}
#endif
}

void ElunaLoader::LoadScripts()
Expand Down Expand Up @@ -263,6 +290,25 @@ void ElunaLoader::ProcessScript(lua_State* L, std::string filename, const std::s
ELUNA_LOG_DEBUG("[Eluna]: ProcessScript processed `%s` successfully", fullpath.c_str());
}

#ifdef TRINITY
void ElunaLoader::InitializeFileWatcher()
{
lua_scriptWatcher = lua_fileWatcher.addWatch(lua_folderpath, &elunaUpdateListener, false);
if (lua_scriptWatcher >= 0)
{
ELUNA_LOG_INFO("[Eluna]: Script reloader is listening on `%s`.",
lua_folderpath.c_str());
}
else
{
ELUNA_LOG_INFO("[Eluna]: Failed to initialize the script reloader on `%s`.",
lua_folderpath.c_str());
}

lua_fileWatcher.watch();
}
#endif

static bool ScriptPathComparator(const LuaScript& first, const LuaScript& second)
{
return first.filepath < second.filepath;
Expand All @@ -283,3 +329,39 @@ bool ElunaLoader::ShouldMapLoadEluna(uint32 id)

return (std::find(requiredMaps.begin(), requiredMaps.end(), id) != requiredMaps.end());
}

void ElunaLoader::ReloadElunaForMap(int mapId)
{
const int mapid_reload_cache_only = -3;
const int mapid_reload_all = -2; // reserved for reloading all states (default if no args)
const int mapid_reload_global = -1; // reserved for reloading global state
// otherwise reload the state of the specific mapid
// If a mapid is provided but does not match any map or reserved id then only script storage is loaded

sElunaLoader->LoadScripts();
if (mapId != mapid_reload_cache_only)
{
if (mapId == mapid_reload_global || mapId == mapid_reload_all)
#ifdef TRINITY
if (sWorld->GetEluna())
sWorld->GetEluna()->ReloadEluna();
#else
if (sWorld.GetEluna())
sWorld.GetEluna()->ReloadEluna();
#endif

#ifdef TRINITY
sMapMgr->DoForAllMaps([&](Map* map)
#else
sMapMgr.DoForAllMaps([&](Map* map)
#endif
{
if (mapId == mapid_reload_all || mapId == static_cast<int>(map->GetId()))
{
if (map->GetEluna())
map->GetEluna()->ReloadEluna();
}
}
);
}
}
35 changes: 35 additions & 0 deletions ElunaLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,23 @@

#include "ElunaUtility.h"

#ifdef TRINITY
#include <filesystem>
#include <efsw/efsw.hpp>
#endif

extern "C"
{
#include "lua.h"
};

enum ElunaReloadActions
{
RELOAD_CACHE_ONLY = -3,
RELOAD_ALL_STATES = -2,
RELOAD_GLOBAL_STATE = -1
};

struct LuaScript;

class ElunaLoader
Expand All @@ -37,6 +49,7 @@ class ElunaLoader
bool ShouldMapLoadEluna(uint32 mapId);
bool CompileScript(lua_State* L, LuaScript& script);
static int LoadBytecodeChunk(lua_State* L, uint8* bytes, size_t len, BytecodeBuffer* buffer);
void ReloadElunaForMap(int mapId);

// Lua script folder path
std::string lua_folderpath;
Expand All @@ -49,8 +62,30 @@ class ElunaLoader
ScriptList lua_extensions;
std::vector<LuaScript> combined_scripts;
std::list<uint32> requiredMaps;

#ifdef TRINITY
// efsw file watcher
void InitializeFileWatcher();
efsw::FileWatcher lua_fileWatcher;
efsw::WatchID lua_scriptWatcher;
#endif
};

#ifdef TRINITY
/// File watcher responsible for watching lua scripts
class ElunaUpdateListener : public efsw::FileWatchListener
{
public:
ElunaUpdateListener() { }
virtual ~ElunaUpdateListener() { }

void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
std::string const& filename, efsw::Action /*action*/, std::string oldFilename = "") final override;
};

static ElunaUpdateListener elunaUpdateListener;
#endif

#define sElunaLoader ElunaLoader::instance()

#endif
34 changes: 2 additions & 32 deletions hooks/PlayerHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,43 +80,13 @@ bool Eluna::OnCommand(Player* player, const char* text)
const std::string reload_command = "reload eluna";
if (reload.find(reload_command) == 0)
{
const int mapid_reload_cache_only = -3;
const int mapid_reload_all = -2; // reserved for reloading all states (default if no args)
const int mapid_reload_global = -1; // reserved for reloading global state
// otherwise reload the state of the specific mapid
// If a mapid is provided but does not match any map or reserved id then only script storage is loaded

int mapId = mapid_reload_all;
int mapId = RELOAD_ALL_STATES;
std::string args = reload.substr(reload_command.length());
if (!args.empty())
mapId = strtol(args.c_str(), nullptr, 10);

sElunaLoader->LoadScripts();
if (mapid_reload_cache_only != mapId)
{
if (mapId == mapid_reload_global || mapId == mapid_reload_all)
#ifdef TRINITY
if (sWorld->GetEluna())
sWorld->GetEluna()->ReloadEluna();
#else
if (sWorld.GetEluna())
sWorld.GetEluna()->ReloadEluna();
#endif
sElunaLoader->ReloadElunaForMap(mapId);

#ifdef TRINITY
sMapMgr->DoForAllMaps([&](Map* map)
#else
sMapMgr.DoForAllMaps([&](Map* map)
#endif
{
if (mapId == mapid_reload_all || mapId == static_cast<int>(map->GetId()))
{
if (map->GetEluna())
map->GetEluna()->ReloadEluna();
}
}
);
}
return false;
}
}
Expand Down

0 comments on commit bac4124

Please sign in to comment.