|
|
|
|
@@ -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());
|
|
|
|
|
}
|
|
|
|
|
|