Support calling a lua function via a protobuf request.

Previously the only way to call lua code was to call scripts
and parse their output to the stream, which is cumbersome.
develop
Alexander Gavrilov 2014-02-10 20:09:06 +04:00
parent 8800cf6f40
commit 7bdb687e4a
5 changed files with 146 additions and 5 deletions

@ -1,5 +1,8 @@
DFHack future
Internals:
- support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua).
New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands
- 'plant create' - spawn a new shrub under the cursor

@ -55,6 +55,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "modules/Units.h"
#include "modules/World.h"
#include "LuaTools.h"
#include "DataDefs.h"
#include "df/ui.h"
#include "df/ui_advmode.h"
@ -656,6 +658,8 @@ CoreService::CoreService() {
addMethod("CoreSuspend", &CoreService::CoreSuspend, SF_DONT_SUSPEND);
addMethod("CoreResume", &CoreService::CoreResume, SF_DONT_SUSPEND);
addMethod("RunLua", &CoreService::RunLua);
// Functions:
addFunction("GetVersion", GetVersion, SF_DONT_SUSPEND);
addFunction("GetDFVersion", GetDFVersion, SF_DONT_SUSPEND);
@ -730,3 +734,85 @@ command_result CoreService::CoreResume(color_ostream &stream, const EmptyMessage
cnt->set_value(--suspend_depth);
return CR_OK;
}
namespace {
struct LuaFunctionData {
command_result rv;
const dfproto::CoreRunLuaRequest *in;
StringListMessage *out;
};
}
command_result CoreService::RunLua(color_ostream &stream,
const dfproto::CoreRunLuaRequest *in,
StringListMessage *out)
{
auto L = Lua::Core::State;
LuaFunctionData data = { CR_FAILURE, in, out };
lua_pushcfunction(L, doRunLuaFunction);
lua_pushlightuserdata(L, &data);
if (!Lua::Core::SafeCall(stream, 1, 0))
return CR_FAILURE;
return data.rv;
}
int CoreService::doRunLuaFunction(lua_State *L)
{
color_ostream &out = *Lua::GetOutput(L);
auto &args = *(LuaFunctionData*)lua_touserdata(L, 1);
// Verify module name
std::string module = args.in->module();
size_t len = module.size();
bool valid = false;
if (len > 4)
{
if (module.substr(0,4) == "rpc.")
valid = true;
else if ((module[len-4] == '.' || module[len-4] == '-') && module.substr(len-3) != "rpc")
valid = true;
}
if (!valid)
{
args.rv = CR_WRONG_USAGE;
out.printerr("Only modules named rpc.* or *.rpc or *-rpc may be called.\n");
return 0;
}
// Prepare function and arguments
lua_settop(L, 0);
if (!Lua::PushModulePublic(out, L, module.c_str(), args.in->function().c_str())
|| lua_isnil(L, 1))
{
args.rv = CR_NOT_FOUND;
return 0;
}
luaL_checkstack(L, args.in->arguments_size(), "too many arguments");
for (int i = 0; i < args.in->arguments_size(); i++)
lua_pushstring(L, args.in->arguments(i).c_str());
// Call
lua_call(L, args.in->arguments_size(), LUA_MULTRET);
// Store results
int nresults = lua_gettop(L);
for (int i = 1; i <= nresults; i++)
{
size_t len;
const char *data = lua_tolstring(L, i, &len);
args.out->add_value(std::string(data, len));
}
args.rv = CR_OK;
return 0;
}

@ -72,12 +72,49 @@ int main (int argc, char *argv[])
if (!client.connect())
return 2;
// Call the command
std::vector<std::string> args;
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
command_result rv;
command_result rv = client.run_command(argv[1], args);
if (strcmp(argv[1], "--lua") == 0)
{
if (argc <= 3)
{
fprintf(stderr, "Usage: dfhack-run --lua <module> <function> [args...]\n");
return 2;
}
RemoteFunction<dfproto::CoreRunLuaRequest,dfproto::StringListMessage> run_call;
if (!run_call.bind(&client, "RunLua"))
{
fprintf(stderr, "No RunLua protocol function found.");
return 3;
}
run_call.in()->set_module(argv[2]);
run_call.in()->set_function(argv[3]);
for (int i = 4; i < argc; i++)
run_call.in()->add_arguments(argv[i]);
rv = run_call();
out.flush();
if (rv == CR_OK)
{
for (int i = 0; i < run_call.out()->value_size(); i++)
printf("%s%s", (i>0?"\t":""), run_call.out()->value(i).c_str());
printf("\n");
}
}
else
{
// Call the command
std::vector<std::string> args;
for (int i = 2; i < argc; i++)
args.push_back(argv[i]);
rv = client.run_command(argv[1], args);
}
out.flush();

@ -48,6 +48,8 @@ namespace DFHack
DFHACK_EXPORT void strVectorToRepeatedField(RepeatedPtrField<std::string> *pf,
const std::vector<std::string> &vec);
using dfproto::StringListMessage;
/**
* Represent bitfield bits as a repeated string field.
*/
@ -131,6 +133,8 @@ namespace DFHack
class CoreService : public RPCService {
int suspend_depth;
static int doRunLuaFunction(lua_State *L);
public:
CoreService();
~CoreService();
@ -144,5 +148,9 @@ namespace DFHack
// For batching
command_result CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result CoreResume(color_ostream &stream, const EmptyMessage*, IntMessage *cnt);
command_result RunLua(color_ostream &stream,
const dfproto::CoreRunLuaRequest *in,
StringListMessage *out);
};
}

@ -81,3 +81,10 @@ message CoreRunCommandRequest {
// RPC CoreSuspend : EmptyMessage -> IntMessage
// RPC CoreResume : EmptyMessage -> IntMessage
// RPC RunLua : CoreRunLuaRequest -> StringListMessage
message CoreRunLuaRequest {
required string module = 1;
required string function = 2;
repeated string arguments = 3;
}