summaryrefslogtreecommitdiff
path: root/library/Core.cpp
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-05-04 19:47:18 +0400
committerAlexander Gavrilov2012-05-04 19:47:18 +0400
commitd4d6349f48d01b31f59f94238d6656e3b5d08508 (patch)
tree43e47bbb5bab970825ff217ddbc16e21a3711033 /library/Core.cpp
parent5afe2ca0020404d46ad3de840030a7b9e186b059 (diff)
downloaddfhack-d4d6349f48d01b31f59f94238d6656e3b5d08508.tar.gz
dfhack-d4d6349f48d01b31f59f94238d6656e3b5d08508.tar.bz2
dfhack-d4d6349f48d01b31f59f94238d6656e3b5d08508.tar.xz
Expose builtin commands to dfhack-run, and add lua script support.
Move builtin command implementation to Core methods, and fall back to hack/scripts/*.lua for otherwise unrecognized commands.
Diffstat (limited to 'library/Core.cpp')
-rw-r--r--library/Core.cpp218
1 files changed, 166 insertions, 52 deletions
diff --git a/library/Core.cpp b/library/Core.cpp
index 6e1e0986..60e38059 100644
--- a/library/Core.cpp
+++ b/library/Core.cpp
@@ -51,6 +51,8 @@ using namespace std;
#include "RemoteServer.h"
#include "LuaTools.h"
+#include "MiscUtils.h"
+
using namespace DFHack;
#include "df/ui.h"
@@ -72,8 +74,6 @@ using df::global::init;
// FIXME: A lot of code in one file, all doing different things... there's something fishy about it.
-static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent);
-static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command);
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod);
struct Core::Cond
@@ -178,21 +178,10 @@ void fHKthread(void * iodata)
{
color_ostream_proxy out(core->getConsole());
- vector <string> args;
- Core::cheap_tokenise(stuff, args);
- if (args.empty()) {
- out.printerr("Empty hotkey command.\n");
- continue;
- }
+ auto rv = core->runCommand(out, stuff);
- string first = args[0];
- args.erase(args.begin());
- command_result cr = plug_mgr->InvokeCommand(out, first, args);
-
- if(cr == CR_NEEDS_CONSOLE)
- {
- out.printerr("It isn't possible to run an interactive command outside the console.\n");
- }
+ if (rv == CR_NOT_IMPLEMENTED)
+ out.printerr("Invalid hotkey command: '%s'\n", stuff.c_str());
}
}
}
@@ -212,38 +201,122 @@ struct sortable
};
};
-static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command)
+static std::string getLuaHelp(std::string path)
+{
+ ifstream script(path);
+
+ if (script.good())
+ {
+ std::string help;
+ if (getline(script, help) &&
+ help.substr(0,3) == "-- ")
+ return help.substr(3);
+ }
+
+ return "Lua script.";
+}
+
+static std::map<string,string> listLuaScripts(std::string path)
+{
+ std::vector<string> files;
+ getdir(path, files);
+
+ std::map<string,string> pset;
+ for (size_t i = 0; i < files.size(); i++)
+ {
+ if (hasEnding(files[i], ".lua"))
+ {
+ std::string help = getLuaHelp(path + files[i]);
+
+ pset[files[i].substr(0, files[i].size()-4)] = help;
+ }
+ }
+ return pset;
+}
+
+static bool fileExists(std::string path)
+{
+ ifstream script(path);
+ return script.good();
+}
+
+namespace {
+ struct ScriptArgs {
+ const string *pcmd;
+ vector<string> *pargs;
+ };
+}
+
+static bool init_run_script(color_ostream &out, lua_State *state, void *info)
+{
+ auto args = (ScriptArgs*)info;
+ if (!lua_checkstack(state, args->pargs->size()+10))
+ return false;
+ Lua::PushDFHack(state);
+ lua_getfield(state, -1, "run_script");
+ lua_remove(state, -2);
+ lua_pushstring(state, args->pcmd->c_str());
+ for (size_t i = 0; i < args->pargs->size(); i++)
+ lua_pushstring(state, (*args->pargs)[i].c_str());
+ return true;
+}
+
+static command_result runLuaScript(color_ostream &out, std::string filename, vector<string> &args)
+{
+ ScriptArgs data;
+ data.pcmd = &filename;
+ data.pargs = &args;
+
+#ifndef LINUX_BUILD
+ filename = toLower(filename);
+#endif
+
+ bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data);
+
+ return ok ? CR_OK : CR_FAILURE;
+}
+
+command_result Core::runCommand(color_ostream &out, const std::string &command)
{
- Console & con = core->getConsole();
-
if (!command.empty())
{
- // cut the input into parts
vector <string> parts;
Core::cheap_tokenise(command,parts);
if(parts.size() == 0)
- {
- clueless_counter ++;
- return;
- }
+ return CR_NOT_IMPLEMENTED;
+
string first = parts[0];
parts.erase(parts.begin());
- if (first[0] == '#') return;
+ if (first[0] == '#')
+ return CR_OK;
cerr << "Invoking: " << command << endl;
-
+
+ return runCommand(out, first, parts);
+ }
+ else
+ return CR_NOT_IMPLEMENTED;
+}
+
+command_result Core::runCommand(color_ostream &con, const std::string &first, vector<string> &parts)
+{
+ if (!first.empty())
+ {
// let's see what we actually got
if(first=="help" || first == "?" || first == "man")
{
if(!parts.size())
{
- con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n"
- "Some basic editing capabilities are included (single-line text editing).\n"
- "The console also has a command history - you can navigate it with Up and Down keys.\n"
- "On Windows, you may have to resize your console window. The appropriate menu is accessible\n"
- "by clicking on the program icon in the top bar of the window.\n\n"
- "Basic commands:\n"
+ if (con.is_console())
+ {
+ con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n"
+ "Some basic editing capabilities are included (single-line text editing).\n"
+ "The console also has a command history - you can navigate it with Up and Down keys.\n"
+ "On Windows, you may have to resize your console window. The appropriate menu is accessible\n"
+ "by clicking on the program icon in the top bar of the window.\n\n");
+ }
+ con.print("Basic commands:\n"
" help|?|man - This text.\n"
" help COMMAND - Usage help for the given command.\n"
" ls|dir [PLUGIN] - List available commands. Optionally for single plugin.\n"
@@ -274,9 +347,15 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
con.reset_color();
if (!pcmd.usage.empty())
con << "Usage:\n" << pcmd.usage << flush;
- return;
+ return CR_OK;
}
}
+ auto filename = getHackPath() + "scripts/" + parts[0] + ".lua";
+ if (fileExists(filename))
+ {
+ string help = getLuaHelp(filename);
+ con.print("%s: %s\n", parts[0].c_str(), help.c_str());
+ }
con.printerr("Unknown command: %s\n", parts[0].c_str());
}
else
@@ -421,6 +500,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str());
con.reset_color();
}
+ auto scripts = listLuaScripts(getHackPath() + "scripts/");
+ if (!scripts.empty())
+ {
+ con.print("\nscripts:\n");
+ for (auto iter = scripts.begin(); iter != scripts.end(); ++iter)
+ con.print(" %-22s- %s\n", iter->first.c_str(), iter->second.c_str());
+ }
}
}
else if(first == "plug")
@@ -439,10 +525,10 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
std::string keystr = parts[1];
if (parts[0] == "set")
- core->ClearKeyBindings(keystr);
+ ClearKeyBindings(keystr);
for (int i = parts.size()-1; i >= 2; i--)
{
- if (!core->AddKeyBinding(keystr, parts[i])) {
+ if (!AddKeyBinding(keystr, parts[i])) {
con.printerr("Invalid key spec: %s\n", keystr.c_str());
break;
}
@@ -452,7 +538,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
for (size_t i = 1; i < parts.size(); i++)
{
- if (!core->ClearKeyBindings(parts[i])) {
+ if (!ClearKeyBindings(parts[i])) {
con.printerr("Invalid key spec: %s\n", parts[i].c_str());
break;
}
@@ -460,7 +546,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
}
else if (parts.size() == 2 && parts[0] == "list")
{
- std::vector<std::string> list = core->ListKeyBindings(parts[1]);
+ std::vector<std::string> list = ListKeyBindings(parts[1]);
if (list.empty())
con << "No bindings." << endl;
for (size_t i = 0; i < list.size(); i++)
@@ -478,13 +564,19 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
}
else if(first == "fpause")
{
- World * w = core->getWorld();
+ World * w = getWorld();
w->SetPauseState(true);
- con.print("The game was forced to pause!");
+ con.print("The game was forced to pause!\n");
}
else if(first == "cls")
{
- con.clear();
+ if (con.is_console())
+ ((Console&)con).clear();
+ else
+ {
+ con.printerr("No console to clear.\n");
+ return CR_NEEDS_CONSOLE;
+ }
}
else if(first == "die")
{
@@ -494,12 +586,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
{
if(parts.size() == 1)
{
- loadScriptFile(core, plug_mgr, parts[0], false);
+ loadScriptFile(con, parts[0], false);
}
else
{
con << "Usage:" << endl
<< " script <filename>" << endl;
+ return CR_WRONG_USAGE;
}
}
else
@@ -507,35 +600,44 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue
command_result res = plug_mgr->InvokeCommand(con, first, parts);
if(res == CR_NOT_IMPLEMENTED)
{
- con.printerr("%s is not a recognized command.\n", first.c_str());
- clueless_counter ++;
+ auto filename = getHackPath() + "scripts/" + first + ".lua";
+ if (fileExists(filename))
+ res = runLuaScript(con, filename, parts);
+ else
+ con.printerr("%s is not a recognized command.\n", first.c_str());
}
+ else if (res == CR_NEEDS_CONSOLE)
+ con.printerr("%s needs interactive console to work.\n", first.c_str());
+ return res;
}
+
+ return CR_OK;
}
+
+ return CR_NOT_IMPLEMENTED;
}
-static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent)
+bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)
{
if(!silent)
- core->getConsole() << "Loading script at " << fname << std::endl;
+ out << "Loading script at " << fname << std::endl;
ifstream script(fname);
if (script.good())
{
- int tmp = 0;
string command;
while (getline(script, command))
{
if (!command.empty())
- runInteractiveCommand(core, plug_mgr, tmp, command);
+ runCommand(out, command);
}
+ return true;
}
else
{
if(!silent)
- core->getConsole().printerr("Error loading script\n");
+ out.printerr("Error loading script\n");
+ return false;
}
-
- script.close();
}
// A thread function... for the interactive console.
@@ -555,7 +657,7 @@ void fIOthread(void * iodata)
return;
}
- loadScriptFile(core, plug_mgr, "dfhack.init", true);
+ core->loadScriptFile(con, "dfhack.init", true);
con.print("DFHack is ready. Have a nice day!\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
@@ -582,7 +684,10 @@ void fIOthread(void * iodata)
main_history.save("dfhack.history");
}
- runInteractiveCommand(core, plug_mgr, clueless_counter, command);
+ auto rv = core->runCommand(con, command);
+
+ if (rv == CR_NOT_IMPLEMENTED)
+ clueless_counter++;
if(clueless_counter == 3)
{
@@ -636,6 +741,15 @@ void Core::fatal (std::string output, bool deactivate)
#endif
}
+std::string Core::getHackPath()
+{
+#ifdef LINUX_BUILD
+ return p->getPath() + "/hack/";
+#else
+ return p->getPath() + "\\hack\\";
+#endif
+}
+
bool Core::Init()
{
if(started)