Overhaul of thread handling to support blocking functions other than wait
This commit is contained in:
@@ -45,14 +45,15 @@ LuaDefine(global_table, "globalname", "get a table where global data can be stor
|
||||
LuaRet globaltab;
|
||||
LuaVar globaldb;
|
||||
LuaStack LS(L, globalname, globaltab, globaldb);
|
||||
LS.checkstring(globalname);
|
||||
|
||||
// Get a pointer to the globaldb.
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
if (!LS.istable(globaldb)) {
|
||||
return lua_yield(L, 0); // donotpredict
|
||||
}
|
||||
|
||||
LS.checkstring(globalname);
|
||||
// nopredict
|
||||
if (lua_isyieldable(L) && (!LS.istable(globaldb))) {
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
// Get the globaltab from the globaldb, sanity check it.
|
||||
LS.rawget(globaltab, globaldb, globalname);
|
||||
|
||||
@@ -20,11 +20,13 @@ using ClientVector = eng::vector<UniqueClient>;
|
||||
|
||||
class LpxServer : public DrivenEngine {
|
||||
public:
|
||||
using StringVec = LuaConsole::StringVec;
|
||||
UniqueWorld master_;
|
||||
LuaConsole console_;
|
||||
ClientVector clients_;
|
||||
PrintChanneler print_channeler_;
|
||||
int64_t admin_id_;
|
||||
Gui gui_;
|
||||
|
||||
public:
|
||||
virtual void event_init(int argc, char *argv[]) {
|
||||
@@ -61,6 +63,22 @@ public:
|
||||
stdostream() << "Syntax Error: " << words[1] << std::endl;
|
||||
}
|
||||
|
||||
void do_menu_command(const StringVec &cmd) {
|
||||
int64_t place = sv::to_int64(cmd[1], admin_id_);
|
||||
master_->update_gui(admin_id_, place, &gui_);
|
||||
stdostream() << gui_.menu_debug_string();
|
||||
}
|
||||
|
||||
void do_choose_command(const StringVec &cmd) {
|
||||
eng::string action = gui_.get_action(sv::to_int64(cmd[1]));
|
||||
if (action == "") {
|
||||
stdostream() << "Invalid menu item #" << std::endl;
|
||||
return;
|
||||
}
|
||||
stdostream() << "Invoking plan: " << action << std::endl;
|
||||
master_->invoke(Invocation(Invocation::KIND_PLAN, admin_id_, gui_.place(), action));
|
||||
}
|
||||
|
||||
void do_tick_command(const util::StringVec &words) {
|
||||
master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, ""));
|
||||
}
|
||||
@@ -78,6 +96,8 @@ public:
|
||||
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] == "menu") do_menu_command(words);
|
||||
else if (words[0] == "choose") do_choose_command(words);
|
||||
else if (words[0] == "tick") do_tick_command(words);
|
||||
else if (words[0] == "cpl") do_cpl_command(words);
|
||||
else if (words[0] == "quit") do_quit_command(words);
|
||||
|
||||
@@ -120,8 +120,8 @@ static void tabify(Inspector &insp, int level) {
|
||||
|
||||
static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
||||
lua_checkstack(insp.L, 20);
|
||||
LuaVar idv, pairs, key, val;
|
||||
LuaStack LS(insp.L, idv, pairs, key, val);
|
||||
LuaVar idv, pairs, key, val, nextseq;
|
||||
LuaStack LS(insp.L, idv, pairs, key, val, nextseq);
|
||||
|
||||
// If it's anything but a table, use 'atomic_print'.
|
||||
if (!LS.istable(root)) {
|
||||
@@ -175,9 +175,9 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
||||
}
|
||||
|
||||
// State variables.
|
||||
int nextseq = 1;
|
||||
bool needcomma = false;
|
||||
bool multiline = false;
|
||||
LS.set(nextseq, 1);
|
||||
|
||||
// Open the brackets.
|
||||
(*insp.stream) << "{";
|
||||
@@ -190,10 +190,10 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
||||
LS.rawget(val, pairs, i+1);
|
||||
if (needcomma) (*insp.stream) << ",";
|
||||
needcomma = true;
|
||||
if (LS.isnumber(key) && (LS.ckint(key) == nextseq)) {
|
||||
if (LS.rawequal(key, nextseq)) {
|
||||
(*insp.stream) << " ";
|
||||
pprint_r(insp, level + 1, val);
|
||||
nextseq = nextseq + 1;
|
||||
LS.set(nextseq, LS.ckinteger(nextseq) + 1);
|
||||
} else {
|
||||
multiline = true;
|
||||
tabify(insp, level + 1);
|
||||
@@ -227,7 +227,7 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
|
||||
// Close the brackets.
|
||||
if (multiline) {
|
||||
tabify(insp, level);
|
||||
} else if (nextseq > 1) {
|
||||
} else if (LS.ckinteger(nextseq) > 1) {
|
||||
(*insp.stream) << " ";
|
||||
}
|
||||
(*insp.stream) << "}";
|
||||
|
||||
@@ -24,6 +24,8 @@ eng::string SchedEntry::debug_string() const {
|
||||
}
|
||||
|
||||
void Schedule::add(int64_t clk, int64_t thid, int64_t plid) {
|
||||
assert(plid != 0);
|
||||
assert(thid != 0);
|
||||
schedule_.insert(SchedEntry(clk, thid, plid));
|
||||
}
|
||||
|
||||
|
||||
@@ -328,9 +328,10 @@ static eng::string source_load_lfunctions(lua_State *L) {
|
||||
|
||||
// Call the closure. If there's an error, collect it.
|
||||
lua_pushvalue(L, closure.index());
|
||||
if (traceback_pcall(L, 0, 0) != 0) {
|
||||
lua_replace(L, err.index());
|
||||
errss << LS.ckstring(err);
|
||||
eng::string msg = traceback_pcall(L, 0, 0);
|
||||
if (!msg.empty()) {
|
||||
LS.set(err, msg);
|
||||
errss << msg << std::endl;
|
||||
}
|
||||
}
|
||||
LS.result();
|
||||
@@ -380,9 +381,10 @@ void SourceDB::run_unittests() {
|
||||
LS.rawget(func, unittests, name);
|
||||
|
||||
lua_pushvalue(L, func.index());
|
||||
if (traceback_pcall(L, 0, 0) != 0) {
|
||||
lua_replace(L, err.index());
|
||||
std::cerr << LS.ckstring(err);
|
||||
eng::string msg = traceback_pcall(L, 0, 0);
|
||||
if (!msg.empty()) {
|
||||
LS.set(err, msg);
|
||||
std::cerr << msg << std::endl;
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,19 @@ int traceback_coroutine(lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
int traceback_pcall(lua_State *L, int narg, int nret) {
|
||||
eng::string traceback_pcall(lua_State *L, int narg, int nret) {
|
||||
int status;
|
||||
int base = lua_gettop(L) - narg; /* function index */
|
||||
lua_pushcfunction(L, traceback_coroutine); /* push traceback function */
|
||||
lua_insert(L, base); /* put it under chunk and args */
|
||||
status = lua_pcall(L, narg, nret, base);
|
||||
lua_remove(L, base); /* remove traceback function */
|
||||
return status;
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if ((msg == NULL) || (msg[0] == 0)) {
|
||||
msg = "unknown error";
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@ int traceback_coroutine(lua_State *L);
|
||||
|
||||
// traceback_pcall
|
||||
//
|
||||
// same as lua_pcall, except that it automatically supplies
|
||||
// traceback_coroutine as a message handler.
|
||||
// Similar to lua_pcall, except that it automatically supplies
|
||||
// traceback_coroutine as a message handler. It also automatically
|
||||
// returns any error message. Returns empty string if there's
|
||||
// no error.
|
||||
//
|
||||
int traceback_pcall(lua_State *L, int narg, int nret);
|
||||
eng::string traceback_pcall(lua_State *L, int narg, int nret);
|
||||
|
||||
#endif // TRACEBACK_HPP
|
||||
|
||||
@@ -510,10 +510,6 @@ double distance_squared(double x1, double y1, double x2, double y2) {
|
||||
return dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
bool world_type_authoritative(util::WorldType wt) {
|
||||
return (wt == WORLD_TYPE_MASTER) || (wt == WORLD_TYPE_STANDALONE);
|
||||
}
|
||||
|
||||
LuaSourcePtr make_lua_source(const eng::string &code) {
|
||||
LuaSourcePtr result(new LuaSourceVec);
|
||||
eng::string fn = "file.lua";
|
||||
|
||||
@@ -212,9 +212,6 @@ eng::string toupper(eng::string input);
|
||||
// Calculate distance between two points
|
||||
double distance_squared(double x1, double y1, double x2, double y2);
|
||||
|
||||
// Return true if a world type is authoritative.
|
||||
bool world_type_authoritative(util::WorldType wt);
|
||||
|
||||
// Make a LuaSourceVec with one element, for unit testing.
|
||||
LuaSourcePtr make_lua_source(const eng::string &code);
|
||||
|
||||
|
||||
@@ -294,23 +294,40 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
||||
|
||||
LuaDefine(wait, "nticks",
|
||||
"|Wait the specified number of ticks.") {
|
||||
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
|
||||
luaL_error(L, "Argument to wait must be a number.");
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
LuaArg seconds;
|
||||
LuaStack LS(L, seconds);
|
||||
int64_t n = LS.ckinteger(seconds);
|
||||
if ((n < 0) || (n > 1000000)) {
|
||||
luaL_error(L, "Argument to wait must be between 0 and 1000000");
|
||||
return LS.result();
|
||||
}
|
||||
if (!lua_isyieldable(L)) {
|
||||
// in a probe, wait throws an error.
|
||||
luaL_error(L, "cannot wait in a probe.");
|
||||
return LS.result();
|
||||
} else if (!w->is_authoritative()) {
|
||||
// in a nonauth model, yield is converted to nopredict.
|
||||
lua_yield(L, 0);
|
||||
return LS.result();
|
||||
} else {
|
||||
// in an authoritative model, wait schedules a continuation.
|
||||
w->thread_sched_.add(w->clock_ + n, w->lthread_thread_id_, w->lthread_place_id_);
|
||||
lua_yield(L, 0);
|
||||
return LS.result();
|
||||
}
|
||||
return lua_yield(L, 1);
|
||||
}
|
||||
|
||||
LuaDefine(tangible_nopredict, "",
|
||||
LuaDefine(nopredict, "",
|
||||
"|Stop predictive execution of this thread.") {
|
||||
if (lua_gettop(L) != 0) {
|
||||
luaL_error(L, "tangible.nopredict takes no arguments");
|
||||
}
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
if (util::world_type_authoritative(w->world_type_)) {
|
||||
return 0;
|
||||
} else {
|
||||
if (lua_isyieldable(L) && !w->is_authoritative()) {
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LuaDefine(math_random, "(args...)",
|
||||
|
||||
@@ -64,7 +64,7 @@ World::World(util::WorldType wt) {
|
||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||
|
||||
// Create the globaldb and oncedb in the registry.
|
||||
if (util::world_type_authoritative(wt)) {
|
||||
if ((wt == util::WORLD_TYPE_MASTER) || (wt == util::WORLD_TYPE_STANDALONE)) {
|
||||
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
|
||||
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
|
||||
}
|
||||
@@ -291,23 +291,20 @@ eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) {
|
||||
// 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);
|
||||
open_lthread_state(actor_id, actor_id, 0, false, true);
|
||||
eng::string msg = 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) {
|
||||
if (msg.empty()) {
|
||||
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)";
|
||||
}
|
||||
assert(msg != "attempt to yield from outside a coroutine");
|
||||
(*ostream) << msg << std::endl;
|
||||
}
|
||||
|
||||
@@ -363,13 +360,14 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
||||
lua_pushvalue(L, actor.index());
|
||||
lua_pushvalue(L, place.index());
|
||||
Gui::store_global_pointer(L, gui);
|
||||
open_lthread_state(actor_id, place_id, false, false);
|
||||
int status = traceback_pcall(L, 2, 0);
|
||||
open_lthread_state(actor_id, place_id, 0, false, false);
|
||||
eng::string msg = traceback_pcall(L, 2, 0);
|
||||
close_lthread_state();
|
||||
Gui::store_global_pointer(L, nullptr);
|
||||
if (status != 0) {
|
||||
if (!msg.empty()) {
|
||||
gui->clear(0);
|
||||
std::cerr << lua_tostring(L, -1);
|
||||
assert(msg != "attempt to yield from outside a coroutine");
|
||||
std::cerr << msg << std::endl;
|
||||
LS.result();
|
||||
return;
|
||||
}
|
||||
@@ -503,7 +501,7 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, const eng::string &ac
|
||||
LS.newtable(thinfo);
|
||||
LS.rawset(thinfo, "thread", thread);
|
||||
LS.rawset(thinfo, "actorid", actor_id);
|
||||
LS.rawset(thinfo, "nargs", 0);
|
||||
LS.rawset(thinfo, "isnew", true);
|
||||
LS.rawset(thinfo, "useppool", true);
|
||||
LS.rawset(thinfo, "print", true);
|
||||
|
||||
@@ -596,7 +594,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &a
|
||||
LS.newtable(thinfo);
|
||||
LS.rawset(thinfo, "thread", thread);
|
||||
LS.rawset(thinfo, "actorid", actor_id);
|
||||
LS.rawset(thinfo, "nargs", 3); // actor, place, invdata
|
||||
LS.rawset(thinfo, "isnew", true);
|
||||
LS.rawset(thinfo, "useppool", true);
|
||||
LS.rawset(thinfo, "print", false);
|
||||
|
||||
@@ -617,7 +615,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &a
|
||||
}
|
||||
|
||||
void World::invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
||||
if (!util::world_type_authoritative(world_type_)) {
|
||||
if (!is_authoritative()) {
|
||||
return;
|
||||
}
|
||||
clock_ += 1;
|
||||
@@ -625,7 +623,7 @@ void World::invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &a
|
||||
}
|
||||
|
||||
void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) {
|
||||
if (!util::world_type_authoritative(world_type_)) {
|
||||
if (!is_authoritative()) {
|
||||
return;
|
||||
}
|
||||
// We need some kind of authentication here.
|
||||
@@ -642,8 +640,8 @@ void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::str
|
||||
void World::run_scheduled_threads() {
|
||||
assert(stack_is_clear());
|
||||
lua_State *L = state();
|
||||
LuaVar tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread, print;
|
||||
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread, print);
|
||||
LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread, print;
|
||||
LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread, print);
|
||||
|
||||
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||
while (thread_sched_.ready(clock_)) {
|
||||
@@ -668,8 +666,8 @@ void World::run_scheduled_threads() {
|
||||
if (!LS.isnumber(actorid)) {
|
||||
continue;
|
||||
}
|
||||
LS.rawget(nargs, thinfo, "nargs");
|
||||
if (!LS.isnumber(nargs)) {
|
||||
LS.rawget(isnew, thinfo, "isnew");
|
||||
if (!LS.isboolean(isnew)) {
|
||||
continue;
|
||||
}
|
||||
LS.rawget(useppool, thinfo, "useppool");
|
||||
@@ -683,35 +681,16 @@ 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.ckint(nargs));
|
||||
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), sched.thread_id(), LS.ckboolean(useppool), true);
|
||||
int nargs = LS.ckboolean(isnew) ? (lua_gettop(CO) - 1) : lua_gettop(CO);
|
||||
int status = lua_resume(CO, nullptr, nargs);
|
||||
std::ostream *ostream = lthread_print_stream();
|
||||
|
||||
// Three possible outcomes: finished, yielded, or errored.
|
||||
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) {
|
||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||
}
|
||||
// If there's a single number on the stack, infer that 'wait' yielded.
|
||||
else if ((lua_gettop(CO) == 1) && (lua_isnumber(CO, 1))) {
|
||||
lua_Number delay = lua_tonumber(CO, 1);
|
||||
lua_settop(CO, 0);
|
||||
LS.rawset(thinfo, "nargs", 0);
|
||||
LS.rawset(thinfo, "useppool", false);
|
||||
thread_sched_.add(clock_ + int64_t(delay), sched.thread_id(), sched.place_id());
|
||||
}
|
||||
// In any other case, generate an error and kill the coroutine.
|
||||
else {
|
||||
std::cerr << "Thread yielded incorrectly. Killing it." << std::endl;
|
||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||
}
|
||||
} else if (status == LUA_OK) {
|
||||
if (status == LUA_OK) {
|
||||
// Successfully ran to completion. Print any return values.
|
||||
// Remove from thread table.
|
||||
LS.rawget(print, thinfo, "print");
|
||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||
LuaStack LSCO(CO);
|
||||
if (LS.ckboolean(print)) {
|
||||
for (int i = 1; i <= lua_gettop(CO); i++) {
|
||||
@@ -719,12 +698,21 @@ void World::run_scheduled_threads() {
|
||||
(*ostream) << std::endl;
|
||||
}
|
||||
}
|
||||
} else if (status == LUA_YIELD) {
|
||||
if (is_authoritative()) {
|
||||
LS.rawset(thinfo, "isnew", false);
|
||||
LS.rawset(thinfo, "useppool", false);
|
||||
} else {
|
||||
// In a nonauth model, a yield is converted to a 'nopredict'.
|
||||
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.
|
||||
if (is_authoritative()) {
|
||||
traceback_coroutine(CO);
|
||||
(*ostream) << lua_tostring(CO, -1);
|
||||
}
|
||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||
}
|
||||
close_lthread_state();
|
||||
@@ -757,12 +745,14 @@ void World::clear_lthread_state() {
|
||||
lthread_prints_.reset();
|
||||
lthread_actor_id_ = 0;
|
||||
lthread_place_id_ = 0;
|
||||
lthread_thread_id_ = 0;
|
||||
lthread_use_ppool_ = false;
|
||||
}
|
||||
|
||||
void World::open_lthread_state(int64_t actor, int64_t place, bool ppool, bool prints) {
|
||||
void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, bool ppool, bool prints) {
|
||||
lthread_actor_id_ = actor;
|
||||
lthread_place_id_ = place;
|
||||
lthread_thread_id_ = thread;
|
||||
lthread_use_ppool_ = ppool;
|
||||
if (prints) {
|
||||
lthread_prints_.reset(new eng::ostringstream);
|
||||
@@ -780,8 +770,7 @@ void World::close_lthread_state() {
|
||||
const eng::string &output = lthread_prints_->str();
|
||||
Tangible *actor = tangible_get(lthread_actor_id_);
|
||||
if (actor != nullptr) {
|
||||
bool auth = util::world_type_authoritative(world_type_);
|
||||
actor->print_buffer_.add_string(output, auth);
|
||||
actor->print_buffer_.add_string(output, is_authoritative());
|
||||
}
|
||||
}
|
||||
// Now clean up everything.
|
||||
|
||||
@@ -212,6 +212,10 @@ public:
|
||||
//
|
||||
static World *fetch_global_pointer(lua_State *L);
|
||||
|
||||
// Check if the world is authoritative.
|
||||
//
|
||||
bool is_authoritative() const { return (world_type_ == util::WORLD_TYPE_MASTER) || (world_type_ == util::WORLD_TYPE_STANDALONE); }
|
||||
|
||||
// Serialize and deserialize.
|
||||
//
|
||||
void serialize(StreamBuffer *sb);
|
||||
@@ -245,7 +249,7 @@ public:
|
||||
// cleared.
|
||||
//
|
||||
void clear_lthread_state();
|
||||
void open_lthread_state(int64_t actor_id, int64_t place_id, bool ppool, bool prints);
|
||||
void open_lthread_state(int64_t actor_id, int64_t place_id, int64_t thread_id, bool ppool, bool prints);
|
||||
void close_lthread_state();
|
||||
|
||||
std::ostream *lthread_print_stream() const;
|
||||
@@ -260,7 +264,6 @@ public:
|
||||
int64_t alloc_id_predictable();
|
||||
|
||||
private:
|
||||
|
||||
// Store a pointer to a world model into a lua registry.
|
||||
//
|
||||
static void store_global_pointer(lua_State *L, World *w);
|
||||
@@ -489,6 +492,7 @@ private:
|
||||
//
|
||||
int64_t lthread_actor_id_;
|
||||
int64_t lthread_place_id_;
|
||||
int64_t lthread_thread_id_;
|
||||
int64_t lthread_use_ppool_;
|
||||
std::unique_ptr<eng::ostringstream> lthread_prints_;
|
||||
|
||||
@@ -503,6 +507,8 @@ private:
|
||||
friend int lfn_tangible_scan(lua_State *L);
|
||||
friend int lfn_math_random(lua_State *L);
|
||||
friend int lfn_math_randomstate(lua_State *L);
|
||||
friend int lfn_wait(lua_State *L);
|
||||
friend int lfn_nopredict(lua_State *L);
|
||||
};
|
||||
|
||||
using UniqueWorld = std::unique_ptr<World>;
|
||||
|
||||
@@ -3,6 +3,7 @@ makeclass('login')
|
||||
function login.interface(actor, place)
|
||||
gui.menu_item("cb_becomeplayer", "Become a Player")
|
||||
gui.menu_item("cb_p123", "Print 1, 2, 3")
|
||||
gui.menu_item("cb_p123_nopredict", "Print 1, 2, 3 nopredict")
|
||||
end
|
||||
|
||||
function login.cb_becomeplayer(actor, place, dialog)
|
||||
@@ -20,9 +21,21 @@ function login.cb_p123(actor, place, dialog)
|
||||
print(3)
|
||||
end
|
||||
|
||||
function login.cb_p123_nopredict(actor, place, dialog)
|
||||
nopredict()
|
||||
print(1)
|
||||
wait(1)
|
||||
print(2)
|
||||
wait(1)
|
||||
print(3)
|
||||
end
|
||||
|
||||
-- this is function documentation for setfoo.
|
||||
function setfoo(n)
|
||||
tangible.nopredict()
|
||||
if (n == nil) then
|
||||
error("setfoo(n) - n must be an integer")
|
||||
end
|
||||
nopredict()
|
||||
tangible.actor().inventory.foo = n
|
||||
end
|
||||
|
||||
|
||||
@@ -591,6 +591,9 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
|
||||
return 0; /* return to 'luaD_hook' */
|
||||
}
|
||||
|
||||
LUA_API int lua_isyieldable (lua_State *L) {
|
||||
return (L->nny == 0);
|
||||
}
|
||||
|
||||
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t old_top, ptrdiff_t ef) {
|
||||
|
||||
@@ -277,6 +277,7 @@ LUA_API int (lua_yieldk) (lua_State *L, int nresults, int ctx,
|
||||
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
|
||||
LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg);
|
||||
LUA_API int (lua_status) (lua_State *L);
|
||||
LUA_API int (lua_isyieldable) (lua_State *L);
|
||||
|
||||
/*
|
||||
** garbage-collection function and options
|
||||
|
||||
Reference in New Issue
Block a user