Support additional script search paths

These can currently be added/removed from C++ or through the Lua API.
develop
lethosor 2015-09-06 16:23:02 -04:00
parent 1c5428c3f0
commit 9eb86c7e38
6 changed files with 148 additions and 43 deletions

@ -1964,6 +1964,33 @@ and are only documented here for completeness:
Wraps strerror() - returns a string describing a platform-specific error code Wraps strerror() - returns a string describing a platform-specific error code
* ``dfhack.internal.addScriptPath(path, search_before)``
Adds ``path`` to the list of paths searched for scripts (both in Lua and Ruby).
If ``search_before`` is passed and ``true``, the path will be searched before
the default paths (e.g. ``raw/scripts``, ``hack/scripts``); otherwise, it will
be searched after.
Returns ``true`` if successful or ``false`` otherwise (e.g. if the path does
not exist or has already been registered).
* ``dfhack.internal.removeScriptPath(path)``
Removes ``path`` from the script search paths and returns ``true`` if successful.
* ``dfhack.internal.getScriptPaths()``
Returns the list of script paths in the order they are searched, including defaults.
(This can change if a world is loaded.)
* ``dfhack.internal.findScript(name)``
Searches script paths for the script ``name`` and returns the path of the first
file found, or ``nil`` on failure.
Note: This requires an extension to be specified (``.lua`` or ``.rb``) -
use ``dfhack.findScript()`` to include the ``.lua`` extension automatically.
Core interpreter context Core interpreter context
======================== ========================

@ -427,24 +427,65 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std::
return false; return false;
} }
string findScript(string path, string name) { bool Core::addScriptPath(string path, bool search_before)
if (df::global::world) { {
//first try the save folder if it exists lock_guard<mutex> lock(*script_path_mutex);
string save = World::ReadWorldFolder(); vector<string> &vec = script_paths[search_before ? 0 : 1];
if ( save != "" ) { if (std::find(vec.begin(), vec.end(), path) != vec.end())
string file = path + "/data/save/" + save + "/raw/scripts/" + name; return false;
if (fileExists(file)) { if (!Filesystem::isdir(path))
return file; return false;
} vec.push_back(path);
return true;
}
bool Core::removeScriptPath(string path)
{
lock_guard<mutex> lock(*script_path_mutex);
bool found = false;
for (int i = 0; i < 2; i++)
{
vector<string> &vec = script_paths[i];
while (1)
{
auto it = std::find(vec.begin(), vec.end(), path);
if (it == vec.end())
break;
vec.erase(it);
found = true;
} }
} }
string file = path + "/raw/scripts/" + name; return found;
if (fileExists(file)) { }
return file;
void Core::getScriptPaths(std::vector<std::string> *dest)
{
lock_guard<mutex> lock(*script_path_mutex);
dest->clear();
string df_path = this->p->getPath();
for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it)
dest->push_back(*it);
if (df::global::world) {
string save = World::ReadWorldFolder();
if (save.size())
dest->push_back(df_path + "/data/save/" + save + "/raw/scripts");
} }
file = path + "/hack/scripts/" + name; dest->push_back(df_path + "/raw/scripts");
if (fileExists(file)) { dest->push_back(df_path + "/hack/scripts");
return file; for (auto it = script_paths[1].begin(); it != script_paths[1].end(); ++it)
dest->push_back(*it);
}
string Core::findScript(string name)
{
vector<string> paths;
getScriptPaths(&paths);
for (auto it = paths.begin(); it != paths.end(); ++it)
{
string path = *it + "/" + name;
if (Filesystem::isfile(path))
return path;
} }
return ""; return "";
} }
@ -592,15 +633,14 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_OK; return CR_OK;
} }
} }
string path = this->p->getPath(); string file = findScript(parts[0] + ".lua");
string file = findScript(path, parts[0] + ".lua");
if ( file != "" ) { if ( file != "" ) {
string help = getScriptHelp(file, "-- "); string help = getScriptHelp(file, "-- ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str()); con.print("%s: %s\n", parts[0].c_str(), help.c_str());
return CR_OK; return CR_OK;
} }
if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) { if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) {
file = findScript(path, parts[0] + ".rb"); file = findScript(parts[0] + ".rb");
if ( file != "" ) { if ( file != "" ) {
string help = getScriptHelp(file, "# "); string help = getScriptHelp(file, "# ");
con.print("%s: %s\n", parts[0].c_str(), help.c_str()); con.print("%s: %s\n", parts[0].c_str(), help.c_str());
@ -682,7 +722,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
if(!plug) if(!plug)
{ {
std::string lua = findScript(this->p->getPath(), part + ".lua"); std::string lua = findScript(part + ".lua");
if (lua.size()) if (lua.size())
{ {
res = enableLuaScript(con, part, enable); res = enableLuaScript(con, part, enable);
@ -873,11 +913,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
{ {
con << " is part of plugin " << plug->getName() << "." << std::endl; con << " is part of plugin " << plug->getName() << "." << std::endl;
} }
else if (findScript(this->p->getPath(), parts[0] + ".lua").size()) else if (findScript(parts[0] + ".lua").size())
{ {
con << " is a Lua script." << std::endl; con << " is a Lua script." << std::endl;
} }
else if (findScript(this->p->getPath(), parts[0] + ".rb").size()) else if (findScript(parts[0] + ".rb").size())
{ {
con << " is a Ruby script." << std::endl; con << " is a Ruby script." << std::endl;
} }
@ -1100,11 +1140,10 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
if(res == CR_NOT_IMPLEMENTED) if(res == CR_NOT_IMPLEMENTED)
{ {
string completed; string completed;
string path = this->p->getPath(); string filename = findScript(first + ".lua");
string filename = findScript(path, first + ".lua");
bool lua = filename != ""; bool lua = filename != "";
if ( !lua ) { if ( !lua ) {
filename = findScript(path, first + ".rb"); filename = findScript(first + ".rb");
} }
if ( lua ) if ( lua )
res = runLuaScript(con, first, parts); res = runLuaScript(con, first, parts);
@ -1273,6 +1312,8 @@ Core::Core()
server = NULL; server = NULL;
color_ostream::log_errors_to_stderr = true; color_ostream::log_errors_to_stderr = true;
script_path_mutex = new mutex();
}; };
void Core::fatal (std::string output, bool deactivate) void Core::fatal (std::string output, bool deactivate)

@ -2637,6 +2637,47 @@ static int internal_getModifiers(lua_State *L)
return 1; return 1;
} }
static int internal_addScriptPath(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
bool search_before = (lua_gettop(L) > 1 && lua_toboolean(L, 2));
lua_pushboolean(L, Core::getInstance().addScriptPath(path, search_before));
return 1;
}
static int internal_removeScriptPath(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
lua_pushboolean(L, Core::getInstance().removeScriptPath(path));
return 1;
}
static int internal_getScriptPaths(lua_State *L)
{
int i = 1;
lua_newtable(L);
std::vector<std::string> paths;
Core::getInstance().getScriptPaths(&paths);
for (auto it = paths.begin(); it != paths.end(); ++it)
{
lua_pushinteger(L, i++);
lua_pushstring(L, it->c_str());
lua_settable(L, -3);
}
return 1;
}
static int internal_findScript(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
std::string path = Core::getInstance().findScript(name);
if (path.size())
lua_pushstring(L, path.c_str());
else
lua_pushnil(L);
return 1;
}
static const luaL_Reg dfhack_internal_funcs[] = { static const luaL_Reg dfhack_internal_funcs[] = {
{ "getAddress", internal_getAddress }, { "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress }, { "setAddress", internal_setAddress },
@ -2652,6 +2693,10 @@ static const luaL_Reg dfhack_internal_funcs[] = {
{ "getDir", filesystem_listdir }, { "getDir", filesystem_listdir },
{ "runCommand", internal_runCommand }, { "runCommand", internal_runCommand },
{ "getModifiers", internal_getModifiers }, { "getModifiers", internal_getModifiers },
{ "addScriptPath", internal_addScriptPath },
{ "removeScriptPath", internal_removeScriptPath },
{ "getScriptPaths", internal_getScriptPaths },
{ "findScript", internal_findScript },
{ NULL, NULL } { NULL, NULL }
}; };

@ -1017,6 +1017,7 @@ void PluginManager::unregisterCommands( Plugin * p )
Plugin *PluginManager::operator[] (std::string name) Plugin *PluginManager::operator[] (std::string name)
{ {
MUTEX_GUARD(plugin_mutex);
if (all_plugins.find(name) == all_plugins.end()) if (all_plugins.find(name) == all_plugins.end())
{ {
if (Filesystem::isfile(getPluginPath(name))) if (Filesystem::isfile(getPluginPath(name)))

@ -159,6 +159,11 @@ namespace DFHack
command_result runCommand(color_ostream &out, const std::string &command); command_result runCommand(color_ostream &out, const std::string &command);
bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false); bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false);
bool addScriptPath(std::string path, bool search_before = false);
bool removeScriptPath(std::string path);
std::string findScript(std::string name);
void getScriptPaths(std::vector<std::string> *dest);
bool ClearKeyBindings(std::string keyspec); bool ClearKeyBindings(std::string keyspec);
bool AddKeyBinding(std::string keyspec, std::string cmdline); bool AddKeyBinding(std::string keyspec, std::string cmdline);
std::vector<std::string> ListKeyBindings(std::string keyspec); std::vector<std::string> ListKeyBindings(std::string keyspec);
@ -231,6 +236,9 @@ namespace DFHack
std::vector <Module *> allModules; std::vector <Module *> allModules;
DFHack::PluginManager * plug_mgr; DFHack::PluginManager * plug_mgr;
std::vector<std::string> script_paths[2];
tthread::mutex *script_path_mutex;
// hotkey-related stuff // hotkey-related stuff
struct KeyBinding { struct KeyBinding {
int modifiers; int modifiers;

@ -420,24 +420,7 @@ local scripts = internal.scripts
local hack_path = dfhack.getHackPath() local hack_path = dfhack.getHackPath()
function dfhack.findScript(name) function dfhack.findScript(name)
local file return dfhack.internal.findScript(name .. '.lua')
file = dfhack.getSavePath()
if file then
file = file .. '/raw/scripts/' .. name .. '.lua'
if dfhack.filesystem.exists(file) then
return file
end
end
local path = dfhack.getDFPath()
file = path..'/raw/scripts/' .. name .. '.lua'
if dfhack.filesystem.exists(file) then
return file
end
file = path..'/hack/scripts/'..name..'.lua'
if dfhack.filesystem.exists(file) then
return file
end
return nil
end end
local valid_script_flags = { local valid_script_flags = {
@ -477,7 +460,7 @@ function dfhack.script_environment(name, strict)
if not scripts[path] or scripts[path]:needs_update() then if not scripts[path] or scripts[path]:needs_update() then
local _, env = dfhack.run_script_with_env(nil, name, { local _, env = dfhack.run_script_with_env(nil, name, {
module=true, module=true,
module_strict=strict and true or false -- ensure that this key is present if 'strict' is nil module_strict=(strict and true or false) -- ensure that this key is present if 'strict' is nil
}) })
return env return env
else else