When you 'invoke' lua code, it creates coroutine

This commit is contained in:
2021-11-30 12:39:09 -05:00
parent 742209988b
commit 5db5a8b06f
6 changed files with 81 additions and 48 deletions

View File

@@ -374,7 +374,7 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
assert(stack_is_clear());
}
void World::update_source(util::LuaSourcePtr source) {
void World::update_source(const util::LuaSourcePtr &source) {
assert(stack_is_clear());
if (source != nullptr) {
source_db_.update(*source);
@@ -436,23 +436,28 @@ void World::invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::s
}
void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
assert(stack_is_clear());
// Get the actor and place, which must be the same.
if (actor_id != place_id) return;
// Make sure that actor and place exist.
Tangible *tactor = tangible_get(actor_id);
if (tactor == nullptr) {
Tangible *tplace = tangible_get(place_id);
if ((tactor == nullptr) || (tplace == nullptr)) {
return;
}
// Get a thread ID for the new thread.
int64_t tid = tplace->id_player_pool_.get_one();
// Set up for lua manipulation.
lua_State *L = state();
LuaVar closure;
LuaStack LS(L, closure);
LuaVar func, tangibles, place, mt, thread, thinfo, threads;
LuaStack LS(L, func, tangibles, place, mt, thread, thinfo, threads);
// create the compiled closure.
int status = luaL_loadbuffer(L, action.c_str(), action.size(), "=invoke");
lua_replace(L, closure.index());
lua_replace(L, func.index());
if (status != LUA_OK) {
// The closure is actually an error message. Do nothing.
// This should normally not happen: LuaConsole should filter
@@ -461,31 +466,44 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &ac
return;
}
// Call the closure.
int top = lua_gettop(L);
lua_pushvalue(L, closure.index());
open_lthread_state(actor_id, place_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;
// Get the place.
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(place, tangibles, place_id);
if (!LS.istable(place)) {
LS.result();
return;
}
close_lthread_state();
// Get the place's metatable.
LS.getmetatable(mt, place);
if (!LS.istable(mt)) {
LS.result();
return;
}
// Create a new thread, set up function and parameters.
lua_State *CO = LS.newthread(thread);
lua_pushvalue(L, func.index());
lua_xmove(L, CO, 1);
// Create the thread info table.
LS.newtable(thinfo);
LS.rawset(thinfo, "thread", thread);
LS.rawset(thinfo, "actorid", actor_id);
LS.rawset(thinfo, "nargs", 0);
LS.rawset(thinfo, "useppool", true);
// Store the thread into place's thread table.
LS.rawget(threads, mt, "threads");
if (!LS.istable(threads)) {
LS.result();
return;
}
LS.rawset(threads, tid, thinfo);
LS.result();
thread_sched_.add(0, tid, place_id);
run_scheduled_threads();
assert(stack_is_clear());
}
@@ -554,8 +572,6 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
// Create a new thread, set up function and parameters.
lua_State *CO = LS.newthread(thread);
// Move function and args to new thread.
lua_pushvalue(L, func.index());
lua_pushvalue(L, actor.index());
lua_pushvalue(L, place.index());
@@ -566,7 +582,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
LS.newtable(thinfo);
LS.rawset(thinfo, "thread", thread);
LS.rawset(thinfo, "actorid", actor_id);
LS.rawset(thinfo, "isnew", true);
LS.rawset(thinfo, "nargs", 3); // actor, place, invdata
LS.rawset(thinfo, "useppool", true);
// Store the thread into place's thread table.
@@ -595,10 +611,10 @@ void World::invoke_tick(int64_t actor_id, int64_t place_id, const std::string &a
void World::run_scheduled_threads() {
assert(stack_is_clear());
lua_State *L = state();
LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread;
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LuaVar tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread;
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread);
LS.rawget(tangibles, LuaRegistry, "tangibles");
while (thread_sched_.ready(clock_)) {
SchedEntry sched = thread_sched_.pop();
LS.rawget(place, tangibles, sched.place_id());
@@ -621,8 +637,8 @@ void World::run_scheduled_threads() {
if (!LS.isnumber(actorid)) {
continue;
}
LS.rawget(isnew, thinfo, "isnew");
if (!LS.isboolean(isnew)) {
LS.rawget(nargs, thinfo, "nargs");
if (!LS.isnumber(nargs)) {
continue;
}
LS.rawget(useppool, thinfo, "useppool");
@@ -637,11 +653,13 @@ void World::run_scheduled_threads() {
// Resume the coroutine.
lua_State *CO = LS.ckthread(thread);
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), LS.ckboolean(useppool), true);
int status = lua_resume(CO, nullptr, LS.ckboolean(isnew) ? 3 : 0);
close_lthread_state();
int status = lua_resume(CO, nullptr, LS.ckint(nargs));
std::ostream *ostream = lthread_print_stream();
// Three possible outcomes: finished, yielded, or errored.
if (status == LUA_YIELD) {
if (!util::world_type_authoritative(world_type_)) {
LS.rawset(threads, sched.thread_id(), LuaNil);
} else if (status == LUA_YIELD) {
// If there's nothing on the stack, infer that tangible.nopredict yielded.
if (lua_gettop(CO) == 0) {
// std::cerr << "Thread killed self using tangible.nopredict" << std::endl;
@@ -651,10 +669,10 @@ void World::run_scheduled_threads() {
else if ((lua_gettop(CO) == 1) && (lua_isnumber(CO, 1))) {
lua_Number delay = lua_tonumber(CO, 1);
lua_settop(CO, 0);
LS.rawset(thinfo, "isnew", false);
LS.rawset(thinfo, "nargs", 0);
LS.rawset(thinfo, "useppool", false);
// std::cerr << "Thread wait = " << delay << std::endl;
thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id());
thread_sched_.add(clock_ + int64_t(delay), sched.thread_id(), sched.place_id());
// std::cerr << "Added to schedule." << std::endl;
}
// In any other case, generate an error and kill the coroutine.
@@ -668,11 +686,14 @@ void World::run_scheduled_threads() {
LS.rawset(threads, sched.thread_id(), LuaNil);
} else {
// Generated an error. Add a traceback, print, and kill the coroutine.
// Currently, the error is sent to the actor. That seems... not right in the long run.
traceback_coroutine(CO);
// std::cerr << lua_tostring(CO, -1);
(*ostream) << lua_tostring(CO, -1);
LS.rawset(threads, sched.thread_id(), LuaNil);
}
close_lthread_state();
}
LS.result();
assert(stack_is_clear());
}