Add invoke_lua function to world model
This commit is contained in:
@@ -1,6 +1,15 @@
|
|||||||
|
|
||||||
#include "invocation.hpp"
|
#include "invocation.hpp"
|
||||||
|
|
||||||
|
const std::string &InvocationData::get(const std::string &key) const {
|
||||||
|
static std::string blank_;
|
||||||
|
auto iter = find(key);
|
||||||
|
if (iter == end()) {
|
||||||
|
return blank_;
|
||||||
|
} else {
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InvocationData::serialize(StreamBuffer *sb) const {
|
void InvocationData::serialize(StreamBuffer *sb) const {
|
||||||
assert(int(size()) < 65536);
|
assert(int(size()) < 65536);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
class InvocationData : public std::map<std::string, std::string> {
|
class InvocationData : public std::map<std::string, std::string> {
|
||||||
public:
|
public:
|
||||||
|
const std::string &get(const std::string &key) const;
|
||||||
|
|
||||||
void serialize(StreamBuffer *sb) const;
|
void serialize(StreamBuffer *sb) const;
|
||||||
void deserialize(StreamBuffer *sb);
|
void deserialize(StreamBuffer *sb);
|
||||||
};
|
};
|
||||||
@@ -18,6 +20,7 @@ public:
|
|||||||
enum Kind {
|
enum Kind {
|
||||||
KIND_INVALID,
|
KIND_INVALID,
|
||||||
KIND_PLAN,
|
KIND_PLAN,
|
||||||
|
KIND_LUA,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -47,60 +47,6 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static lua_State *globalL = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
static void lstop(lua_State *L, lua_Debug *ar)
|
|
||||||
{
|
|
||||||
(void)ar; /* unused arg. */
|
|
||||||
lua_sethook(L, NULL, 0, 0);
|
|
||||||
/* Avoid luaL_error -- a C hook doesn't add an extra frame. */
|
|
||||||
luaL_where(L, 0);
|
|
||||||
lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1));
|
|
||||||
lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void laction(int i)
|
|
||||||
{
|
|
||||||
signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
|
|
||||||
terminate process (default action) */
|
|
||||||
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void l_message(const char *msg)
|
|
||||||
{
|
|
||||||
fputs(msg, stderr);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextGame::do_lua(const std::string &exp) {
|
|
||||||
assert(world_->stack_is_clear());
|
|
||||||
lua_State *L = world_->state();
|
|
||||||
// push the compiled function.
|
|
||||||
int status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin");
|
|
||||||
assert(status == LUA_OK);
|
|
||||||
globalL = L;
|
|
||||||
signal(SIGINT, laction);
|
|
||||||
status = traceback_pcall(L, 0, LUA_MULTRET);
|
|
||||||
signal(SIGINT, SIG_DFL);
|
|
||||||
if (status == LUA_OK) {
|
|
||||||
if (lua_gettop(L) > 0) {
|
|
||||||
lfn_pprint_pprint(L);
|
|
||||||
lua_settop(L, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char *msg = lua_tostring(L, -1);
|
|
||||||
if (msg == NULL) {
|
|
||||||
msg = "(error object is not a string)";
|
|
||||||
}
|
|
||||||
l_message(msg);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
lua_gc(L, LUA_GCCOLLECT, 0);
|
|
||||||
}
|
|
||||||
assert(world_->stack_is_clear());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextGame::do_view_command(const StringVec &cmd) {
|
void TextGame::do_view_command(const StringVec &cmd) {
|
||||||
if (cmd.size() != 1) {
|
if (cmd.size() != 1) {
|
||||||
std::cerr << "v command (view) takes no arguments" << std::endl;
|
std::cerr << "v command (view) takes no arguments" << std::endl;
|
||||||
@@ -154,6 +100,13 @@ void TextGame::do_choose_command(const StringVec &cmd) {
|
|||||||
world_->invoke(inv);
|
world_->invoke(inv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextGame::do_lua(const std::string &exp) {
|
||||||
|
assert(world_->stack_is_clear());
|
||||||
|
InvocationData dummyresult;
|
||||||
|
Invocation inv(Invocation::KIND_LUA, actor_id_, actor_id_, exp, dummyresult);
|
||||||
|
world_->invoke(inv);
|
||||||
|
}
|
||||||
|
|
||||||
void TextGame::do_snapshot_command(const StringVec &cmd) {
|
void TextGame::do_snapshot_command(const StringVec &cmd) {
|
||||||
if (cmd.size() != 1) {
|
if (cmd.size() != 1) {
|
||||||
std::cerr << "s command (snapshot) takes no arguments" << std::endl;
|
std::cerr << "s command (snapshot) takes no arguments" << std::endl;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "animqueue.hpp"
|
#include "animqueue.hpp"
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "traceback.hpp"
|
#include "traceback.hpp"
|
||||||
|
#include "print.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void World::store_global_pointer(lua_State *L, World *v) {
|
void World::store_global_pointer(lua_State *L, World *v) {
|
||||||
@@ -320,11 +321,21 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
|||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::update_source(util::LuaSourcePtr source) {
|
||||||
|
if (source != nullptr) {
|
||||||
|
source_db_.update(*source);
|
||||||
|
source_db_.rebuild(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void World::invoke(const Invocation &inv) {
|
void World::invoke(const Invocation &inv) {
|
||||||
switch (inv.kind()) {
|
switch (inv.kind()) {
|
||||||
case Invocation::KIND_PLAN:
|
case Invocation::KIND_PLAN:
|
||||||
invoke_plan(inv.actor(), inv.place(), inv.action(), inv.data());
|
invoke_plan(inv.actor(), inv.place(), inv.action(), inv.data());
|
||||||
break;
|
break;
|
||||||
|
case Invocation::KIND_LUA:
|
||||||
|
invoke_lua(inv.actor(), inv.place(), inv.action(), inv.data());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing. Standard behavior for any invalid command is to
|
// Do nothing. Standard behavior for any invalid command is to
|
||||||
// simply do nothing at all. Perhaps eventually we may add a flag
|
// simply do nothing at all. Perhaps eventually we may add a flag
|
||||||
@@ -335,14 +346,56 @@ void World::invoke(const Invocation &inv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::update_source(util::LuaSourcePtr source) {
|
void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
|
||||||
if (source != nullptr) {
|
assert(stack_is_clear());
|
||||||
source_db_.update(*source);
|
|
||||||
source_db_.rebuild(true);
|
// Get the actor and place, which must be the same.
|
||||||
}
|
if (actor_id != place_id) return;
|
||||||
|
Tangible *tactor = tangible_get(actor_id);
|
||||||
|
if (tactor == nullptr) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata) {
|
lua_State *L = state();
|
||||||
|
LuaVar closure;
|
||||||
|
LuaStack LS(L, closure);
|
||||||
|
|
||||||
|
// create the compiled closure.
|
||||||
|
int status = luaL_loadbuffer(L, action.c_str(), action.size(), "=invoke");
|
||||||
|
lua_replace(L, closure.index());
|
||||||
|
if (status != LUA_OK) {
|
||||||
|
// The closure is actually an error message. Do nothing.
|
||||||
|
LS.result();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the closure.
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
lua_pushvalue(L, closure.index());
|
||||||
|
set_lthread_state(actor_id, place_id, false);
|
||||||
|
status = traceback_pcall(L, 0, LUA_MULTRET);
|
||||||
|
set_lthread_state(0, 0, false);
|
||||||
|
|
||||||
|
// If there's an error message, print it.
|
||||||
|
// Otherwise, pretty-print the results.
|
||||||
|
if (status == LUA_OK) {
|
||||||
|
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
||||||
|
LuaSpecial root(i);
|
||||||
|
pprint(LS, root, true, &std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const char *msg = lua_tostring(L, top);
|
||||||
|
if (msg == NULL) {
|
||||||
|
msg = "(error object is not a string)";
|
||||||
|
}
|
||||||
|
std::cerr << msg << std::endl;
|
||||||
|
}
|
||||||
|
LS.result();
|
||||||
|
assert(stack_is_clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
|
||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
|
|
||||||
// Validate that the action is legal.
|
// Validate that the action is legal.
|
||||||
@@ -401,7 +454,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
|
|||||||
|
|
||||||
// Convert the InvocationData into a lua table.
|
// Convert the InvocationData into a lua table.
|
||||||
LS.newtable(invdata);
|
LS.newtable(invdata);
|
||||||
for (const auto &p : idata) {
|
for (const auto &p : data) {
|
||||||
LS.rawset(invdata, p.first, p.second);
|
LS.rawset(invdata, p.first, p.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,11 @@ private:
|
|||||||
|
|
||||||
// Invoke a plan.
|
// Invoke a plan.
|
||||||
//
|
//
|
||||||
void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata);
|
void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
|
||||||
|
|
||||||
|
// Invoke a lua string.
|
||||||
|
//
|
||||||
|
void invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user