Implement probe_lua and add it to lpxclient/lpxserver
This commit is contained in:
@@ -671,12 +671,7 @@ LuaDefine(unittests_animqueue, "c") {
|
||||
// Change the queue size limit.
|
||||
aq.set_limit(13);
|
||||
LuaAssert(L, diff_works(aq, aqds));
|
||||
|
||||
// compare again, should be no differences.
|
||||
LuaAssert(L, aqds.version_identical(aq));
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
LuaAssert(L, diff_works(aq, aqds));
|
||||
|
||||
|
||||
// Discard all but the last action.
|
||||
aq.set_limit(1);
|
||||
LuaAssert(L, diff_works(aq, aqds));
|
||||
|
||||
@@ -94,10 +94,16 @@ public:
|
||||
inv.serialize(sb);
|
||||
}
|
||||
|
||||
void do_lua_command(const StringVec &words) {
|
||||
void do_luainvoke_command(const StringVec &words) {
|
||||
send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
|
||||
}
|
||||
|
||||
void do_luaprobe_command(const StringVec &words) {
|
||||
world_to_asynchronous();
|
||||
stdostream() << world_->probe_lua(actor_id_, words[1]);
|
||||
world_to_synchronous();
|
||||
}
|
||||
|
||||
void do_syntax_command(const StringVec &words) {
|
||||
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
||||
}
|
||||
@@ -131,7 +137,8 @@ public:
|
||||
|
||||
void do_command(const util::StringVec &words) {
|
||||
if (words.empty()) return;
|
||||
else if (words[0] == "lua") do_lua_command(words);
|
||||
else if (words[0] == "luainvoke") do_luainvoke_command(words);
|
||||
else if (words[0] == "luaprobe") do_luaprobe_command(words);
|
||||
else if (words[0] == "syntax") do_syntax_command(words);
|
||||
else if (words[0] == "view") do_view_command(words);
|
||||
else if (words[0] == "menu") do_menu_command(words);
|
||||
|
||||
@@ -45,10 +45,16 @@ public:
|
||||
get_stdio_channel()->set_prompt(console_.get_prompt());
|
||||
}
|
||||
|
||||
void do_lua_command(const util::StringVec &words) {
|
||||
void do_luainvoke_command(const util::StringVec &words) {
|
||||
master_->invoke(Invocation(Invocation::KIND_LUA, admin_id_, admin_id_, words[1]));
|
||||
}
|
||||
|
||||
void do_luaprobe_command(const util::StringVec &words) {
|
||||
master_->snapshot();
|
||||
stdostream() << master_->probe_lua(admin_id_, words[1]);;
|
||||
master_->rollback();
|
||||
}
|
||||
|
||||
void do_syntax_command(const util::StringVec &words) {
|
||||
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
||||
}
|
||||
@@ -59,7 +65,8 @@ public:
|
||||
|
||||
void do_command(const util::StringVec &words) {
|
||||
if (words.empty()) return;
|
||||
else if (words[0] == "lua") do_lua_command(words);
|
||||
else if (words[0] == "luainvoke") do_luainvoke_command(words);
|
||||
else if (words[0] == "luaprobe") do_luaprobe_command(words);
|
||||
else if (words[0] == "syntax") do_syntax_command(words);
|
||||
else if (words[0] == "quit") do_quit_command(words);
|
||||
else {
|
||||
|
||||
@@ -78,14 +78,6 @@ void LuaConsole::simplify(const StringVec &words) {
|
||||
if (words.size() != 1) {
|
||||
synerr("/quit takes no arguments");
|
||||
}
|
||||
} else if (words[0] == "snap") {
|
||||
if (words.size() != 1) {
|
||||
synerr("/snap takes no arguments");
|
||||
}
|
||||
} else if (words[0] == "roll") {
|
||||
if (words.size() != 1) {
|
||||
synerr("/roll takes no arguments");
|
||||
}
|
||||
} else if (words[0] == "tick") {
|
||||
if ((words.size() == 2)&&(util::validinteger(words[1]))) {
|
||||
// OK
|
||||
@@ -120,11 +112,20 @@ void LuaConsole::add(std::string line) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Translate lua expression with leading '=' to 'return'
|
||||
// Translate lua expression, deal with initial prefix.
|
||||
std::string partial;
|
||||
if (raw_input_[0] == '=') {
|
||||
std::string lua_mode;
|
||||
if (util::has_prefix(raw_input_, "?=")) {
|
||||
lua_mode = "luaprobe";
|
||||
partial = std::string("return ") + raw_input_.substr(2);
|
||||
} else if (util::has_prefix(raw_input_, "?")) {
|
||||
lua_mode = "luaprobe";
|
||||
partial = raw_input_.substr(1);
|
||||
} else if (util::has_prefix(raw_input_, "=")) {
|
||||
lua_mode = "luainvoke";
|
||||
partial = std::string("return ") + raw_input_.substr(1);
|
||||
} else {
|
||||
lua_mode = "luainvoke";
|
||||
partial = raw_input_;
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ void LuaConsole::add(std::string line) {
|
||||
clear_raw_input();
|
||||
}
|
||||
} else {
|
||||
words_.push_back("lua");
|
||||
words_.push_back(lua_mode);
|
||||
words_.push_back(util::rtrim(partial));
|
||||
clear_raw_input();
|
||||
}
|
||||
|
||||
@@ -18,15 +18,21 @@
|
||||
// /quit - exit the program
|
||||
// /view - display the nearby tangibles
|
||||
// /menu [tanid] - display the menu for tangible
|
||||
// /snap - snapshot current state
|
||||
// /roll - rollback to previous state
|
||||
// /tick [timevalue] - advance the simulation clock
|
||||
// /choose [number] - choose menu item number
|
||||
// /1234 - choose menu item 1234
|
||||
//
|
||||
// If you type anything else, the LuaConsole will generate a
|
||||
// syntax error. Note that not all of the commands above are
|
||||
// supported in all interpreters.
|
||||
//
|
||||
// Lua commands can be one of the following:
|
||||
//
|
||||
// <expression> - invoke "<expression>""
|
||||
// = <expression> - invoke "return <expression>"
|
||||
// ? <expression> - probe "<expression>"
|
||||
// ?= <expression> - probe "return <expression>"
|
||||
//
|
||||
// Once a command has been typed (or a syntax error has been
|
||||
// typed), the LuaConsole will return the command. The command
|
||||
// can be fetched using command(), int_arg(), and str_arg()
|
||||
|
||||
@@ -28,10 +28,16 @@ private:
|
||||
Gui gui_;
|
||||
int64_t actor_id_;
|
||||
|
||||
void do_lua_command(const StringVec &words) {
|
||||
void do_luainvoke_command(const StringVec &words) {
|
||||
world_->invoke(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1]));
|
||||
}
|
||||
|
||||
void do_luaprobe_command(const StringVec &words) {
|
||||
world_->snapshot();
|
||||
stdostream() << world_->probe_lua(actor_id_, words[1]);
|
||||
world_->rollback();
|
||||
}
|
||||
|
||||
void do_syntax_command(const StringVec &words) {
|
||||
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
||||
}
|
||||
@@ -57,14 +63,6 @@ private:
|
||||
world_->invoke(inv);
|
||||
}
|
||||
|
||||
void do_snapshot_command(const StringVec &cmd) {
|
||||
world_->snapshot();
|
||||
}
|
||||
|
||||
void do_rollback_command(const StringVec &cmd) {
|
||||
world_->rollback();
|
||||
}
|
||||
|
||||
void do_tick_command(const StringVec &cmd) {
|
||||
world_->run_scheduled_threads(util::strtoint(cmd[1], -1));
|
||||
}
|
||||
@@ -75,13 +73,12 @@ private:
|
||||
|
||||
void do_command(const StringVec &words) {
|
||||
if (words.empty()) return;
|
||||
else if (words[0] == "lua") do_lua_command(words);
|
||||
else if (words[0] == "luainvoke") do_luainvoke_command(words);
|
||||
else if (words[0] == "luaprobe") do_luaprobe_command(words);
|
||||
else if (words[0] == "syntax") do_syntax_command(words);
|
||||
else if (words[0] == "view") do_view_command(words);
|
||||
else if (words[0] == "menu") do_menu_command(words);
|
||||
else if (words[0] == "quit") do_quit_command(words);
|
||||
else if (words[0] == "snap") do_snapshot_command(words);
|
||||
else if (words[0] == "roll") do_rollback_command(words);
|
||||
else if (words[0] == "tick") do_tick_command(words);
|
||||
else if (words[0] == "choose") do_choose_command(words);
|
||||
else {
|
||||
|
||||
@@ -185,6 +185,18 @@ std::string toupper(std::string input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
bool has_prefix(const std::string &s, const std::string &prefix) {
|
||||
return 0 == s.compare(0, prefix.size(), prefix);
|
||||
}
|
||||
|
||||
bool has_suffix(const std::string &s, const std::string &suffix) {
|
||||
if (s.length() >= suffix.length()) {
|
||||
return (0 == s.compare (s.length() - suffix.length(), suffix.length(), suffix));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool validinteger(const std::string &value) {
|
||||
char *endptr;
|
||||
if (value.size() == 0) return false;
|
||||
|
||||
@@ -77,6 +77,10 @@ int common_prefix_length(const std::string &a, const std::string &b);
|
||||
std::string tolower(std::string input);
|
||||
std::string toupper(std::string input);
|
||||
|
||||
// Return true if the string has the specified prefix or suffix.
|
||||
bool has_prefix(const std::string &s, const std::string &prefix);
|
||||
bool has_suffix(const std::string &s, const std::string &suffix);
|
||||
|
||||
// Return true if the string can be parsed as an integer.
|
||||
bool validinteger(const std::string &value);
|
||||
|
||||
|
||||
@@ -257,6 +257,65 @@ int64_t World::create_login_actor() {
|
||||
return tan->id();
|
||||
}
|
||||
|
||||
std::string World::probe_lua(int64_t actor_id, const std::string &lua) {
|
||||
assert(stack_is_clear());
|
||||
lua_State *L = state();
|
||||
|
||||
Tangible *actor = tangible_get(actor_id);
|
||||
if (actor == nullptr) {
|
||||
return "";
|
||||
}
|
||||
|
||||
LuaVar closure;
|
||||
LuaStack LS(L, closure);
|
||||
|
||||
// create the compiled closure.
|
||||
int status = luaL_loadbuffer(L, lua.c_str(), lua.size(), "=probe");
|
||||
lua_replace(L, closure.index());
|
||||
if (status != LUA_OK) {
|
||||
// The closure is actually an error message. Do nothing.
|
||||
// This should normally not happen: LuaConsole should filter
|
||||
// out syntax errors.
|
||||
LS.result();
|
||||
return "";
|
||||
}
|
||||
|
||||
// Call the closure.
|
||||
int top = lua_gettop(L);
|
||||
lua_pushvalue(L, closure.index());
|
||||
open_lthread_state(actor_id, actor_id, false, true);
|
||||
status = traceback_pcall(L, 0, LUA_MULTRET);
|
||||
|
||||
// If there's an error message, print it.
|
||||
// Otherwise, pretty-print the results.
|
||||
std::ostream *ostream = lthread_print_stream();
|
||||
if (status == LUA_OK) {
|
||||
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
||||
LuaSpecial root(i);
|
||||
pprint(LS, root, true, ostream);
|
||||
(*ostream) << std::endl;
|
||||
}
|
||||
} else {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) {
|
||||
msg = "(error object is not a string)";
|
||||
}
|
||||
(*ostream) << msg << std::endl;
|
||||
}
|
||||
|
||||
// Collect the lthread_prints (and also make sure they
|
||||
// don't go into the printbuffer).
|
||||
std::string result = lthread_prints_->str();
|
||||
lthread_prints_.reset();
|
||||
|
||||
close_lthread_state();
|
||||
|
||||
// And we're done.
|
||||
LS.result();
|
||||
assert(stack_is_clear());
|
||||
return result;
|
||||
}
|
||||
|
||||
void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
||||
assert(stack_is_clear());
|
||||
gui->clear(place_id);
|
||||
@@ -391,6 +450,8 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &ac
|
||||
lua_replace(L, closure.index());
|
||||
if (status != LUA_OK) {
|
||||
// The closure is actually an error message. Do nothing.
|
||||
// This should normally not happen: LuaConsole should filter
|
||||
// out syntax errors.
|
||||
LS.result();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,6 +166,15 @@ public:
|
||||
//
|
||||
Redirects fetch_redirects();
|
||||
|
||||
// Probe an arbitrary lua expression.
|
||||
//
|
||||
// Any print-statements in the lua code are sent into
|
||||
// a stringstream. The return value of probe_lua is the string
|
||||
// from the stringstream. If the lua expression returns a
|
||||
// value, that is also printed to the stringstream.
|
||||
//
|
||||
std::string probe_lua(int64_t actor_id, const std::string &lua);
|
||||
|
||||
// Probe the 'interface' function of the specified sprite.
|
||||
//
|
||||
void update_gui(int64_t actor_id, int64_t place_id, Gui *gui);
|
||||
|
||||
Reference in New Issue
Block a user