added /tick command

This commit is contained in:
2021-11-26 15:45:36 -05:00
parent 1e93533246
commit 742209988b
9 changed files with 61 additions and 21 deletions

View File

@@ -63,6 +63,7 @@ std::string Invocation::debug_string() {
case KIND_PLAN: oss << "plan"; break; case KIND_PLAN: oss << "plan"; break;
case KIND_LUA: oss << "lua"; break; case KIND_LUA: oss << "lua"; break;
case KIND_FLUSH_PRINTS: oss << "flush_prints"; break; case KIND_FLUSH_PRINTS: oss << "flush_prints"; break;
case KIND_TICK: oss << "tick"; break;
default: oss << "UNKNOWN"; break; default: oss << "UNKNOWN"; break;
} }
oss << " a=" << actor_; oss << " a=" << actor_;

View File

@@ -22,6 +22,7 @@ public:
KIND_PLAN, KIND_PLAN,
KIND_LUA, KIND_LUA,
KIND_FLUSH_PRINTS, KIND_FLUSH_PRINTS,
KIND_TICK,
}; };
private: private:

View File

@@ -130,6 +130,10 @@ public:
send_invocation(inv); send_invocation(inv);
} }
void do_tick_command(const util::StringVec &words) {
send_invocation(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, ""));
}
void do_quit_command(const util::StringVec &words) { void do_quit_command(const util::StringVec &words) {
abandon_server(); abandon_server();
stop_driver(); stop_driver();
@@ -142,8 +146,9 @@ public:
else if (words[0] == "syntax") do_syntax_command(words); else if (words[0] == "syntax") do_syntax_command(words);
else if (words[0] == "view") do_view_command(words); else if (words[0] == "view") do_view_command(words);
else if (words[0] == "menu") do_menu_command(words); else if (words[0] == "menu") do_menu_command(words);
else if (words[0] == "quit") do_quit_command(words);
else if (words[0] == "choose") do_choose_command(words); else if (words[0] == "choose") do_choose_command(words);
else if (words[0] == "tick") do_tick_command(words);
else if (words[0] == "quit") do_quit_command(words);
else { else {
stdostream() << "Unsupported command: " << words[0] << std::endl; stdostream() << "Unsupported command: " << words[0] << std::endl;
} }

View File

@@ -59,6 +59,10 @@ public:
stdostream() << "Syntax Error: " << words[1] << std::endl; stdostream() << "Syntax Error: " << words[1] << std::endl;
} }
void do_tick_command(const util::StringVec &words) {
master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, ""));
}
void do_quit_command(const util::StringVec &words) { void do_quit_command(const util::StringVec &words) {
stop_driver(); stop_driver();
} }
@@ -68,6 +72,7 @@ public:
else if (words[0] == "luainvoke") do_luainvoke_command(words); else if (words[0] == "luainvoke") do_luainvoke_command(words);
else if (words[0] == "luaprobe") do_luaprobe_command(words); else if (words[0] == "luaprobe") do_luaprobe_command(words);
else if (words[0] == "syntax") do_syntax_command(words); else if (words[0] == "syntax") do_syntax_command(words);
else if (words[0] == "tick") do_tick_command(words);
else if (words[0] == "quit") do_quit_command(words); else if (words[0] == "quit") do_quit_command(words);
else { else {
stdostream() << "Unsupported command: " << words[0] << std::endl; stdostream() << "Unsupported command: " << words[0] << std::endl;

View File

@@ -79,10 +79,8 @@ void LuaConsole::simplify(const StringVec &words) {
synerr("/quit takes no arguments"); synerr("/quit takes no arguments");
} }
} else if (words[0] == "tick") { } else if (words[0] == "tick") {
if ((words.size() == 2)&&(util::validinteger(words[1]))) { if (words.size() != 1) {
// OK synerr("/tick takes no arguments");
} else {
synerr("/tick [optional-timestamp]");
} }
} else { } else {
synerr("unrecognized command"); synerr("unrecognized command");

View File

@@ -64,7 +64,7 @@ private:
} }
void do_tick_command(const StringVec &cmd) { void do_tick_command(const StringVec &cmd) {
world_->run_scheduled_threads(util::strtoint(cmd[1], -1)); world_->invoke(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, ""));
} }
void do_quit_command(const StringVec &cmd) { void do_quit_command(const StringVec &cmd) {

View File

@@ -69,6 +69,9 @@ World::World(util::WorldType wt) {
source_db_.init(state()); source_db_.init(state());
std::string srcerrs = source_db_.rebuild(); std::string srcerrs = source_db_.rebuild();
// Clear the clock.
clock_ = 0;
// There shouldn't be any lua errors in the sourceDB at this // There shouldn't be any lua errors in the sourceDB at this
// point, since there's no lua code in the sourceDB. // point, since there's no lua code in the sourceDB.
assert(srcerrs == ""); assert(srcerrs == "");
@@ -402,6 +405,8 @@ void World::invoke(const Invocation &inv) {
case Invocation::KIND_FLUSH_PRINTS: case Invocation::KIND_FLUSH_PRINTS:
invoke_flush_prints(inv.actor(), inv.place(), inv.action(), inv.data()); invoke_flush_prints(inv.actor(), inv.place(), inv.action(), inv.data());
break; break;
case Invocation::KIND_TICK:
invoke_tick(inv.actor(), inv.place(), inv.action(), inv.data());
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
@@ -576,18 +581,25 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a
// Push the thread's ID into the runnable thread queue, // Push the thread's ID into the runnable thread queue,
// then run the thread queue. // then run the thread queue.
thread_sched_.add(0, tid, place_id); thread_sched_.add(0, tid, place_id);
run_scheduled_threads(0); run_scheduled_threads();
assert(stack_is_clear()); assert(stack_is_clear());
} }
void World::run_scheduled_threads(int64_t clk) { void World::invoke_tick(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
if (util::world_type_authoritative(world_type_)) {
clock_ += 1;
run_scheduled_threads();
}
}
void World::run_scheduled_threads() {
assert(stack_is_clear()); assert(stack_is_clear());
lua_State *L = state(); lua_State *L = state();
LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread; LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread;
LuaStack LS(L, 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"); LS.rawget(tangibles, LuaRegistry, "tangibles");
while (thread_sched_.ready(clk)) { while (thread_sched_.ready(clock_)) {
SchedEntry sched = thread_sched_.pop(); SchedEntry sched = thread_sched_.pop();
LS.rawget(place, tangibles, sched.place_id()); LS.rawget(place, tangibles, sched.place_id());
if (!LS.istable(place)) { if (!LS.istable(place)) {
@@ -632,7 +644,7 @@ void World::run_scheduled_threads(int64_t clk) {
if (status == LUA_YIELD) { if (status == LUA_YIELD) {
// If there's nothing on the stack, infer that tangible.nopredict yielded. // If there's nothing on the stack, infer that tangible.nopredict yielded.
if (lua_gettop(CO) == 0) { if (lua_gettop(CO) == 0) {
std::cerr << "Thread killed self using tangible.nopredict" << std::endl; // std::cerr << "Thread killed self using tangible.nopredict" << std::endl;
LS.rawset(threads, sched.thread_id(), LuaNil); LS.rawset(threads, sched.thread_id(), LuaNil);
} }
// If there's a single number on the stack, infer that 'wait' yielded. // If there's a single number on the stack, infer that 'wait' yielded.
@@ -641,9 +653,9 @@ void World::run_scheduled_threads(int64_t clk) {
lua_settop(CO, 0); lua_settop(CO, 0);
LS.rawset(thinfo, "isnew", false); LS.rawset(thinfo, "isnew", false);
LS.rawset(thinfo, "useppool", false); LS.rawset(thinfo, "useppool", false);
std::cerr << "Thread wait = " << delay << std::endl; // std::cerr << "Thread wait = " << delay << std::endl;
thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id()); thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id());
std::cerr << "Added to schedule." << std::endl; // std::cerr << "Added to schedule." << std::endl;
} }
// In any other case, generate an error and kill the coroutine. // In any other case, generate an error and kill the coroutine.
else { else {
@@ -652,12 +664,12 @@ void World::run_scheduled_threads(int64_t clk) {
} }
} else if (status == LUA_OK) { } else if (status == LUA_OK) {
// Successfully ran to completion. Remove from thread table. // Successfully ran to completion. Remove from thread table.
std::cerr << "Thread ran to completion." << std::endl; // std::cerr << "Thread ran to completion." << std::endl;
LS.rawset(threads, sched.thread_id(), LuaNil); LS.rawset(threads, sched.thread_id(), LuaNil);
} else { } else {
// Generated an error. Add a traceback, print, and kill the coroutine. // Generated an error. Add a traceback, print, and kill the coroutine.
traceback_coroutine(CO); traceback_coroutine(CO);
std::cerr << lua_tostring(CO, -1); // std::cerr << lua_tostring(CO, -1);
LS.rawset(threads, sched.thread_id(), LuaNil); LS.rawset(threads, sched.thread_id(), LuaNil);
} }
} }
@@ -733,6 +745,7 @@ void World::serialize(StreamBuffer *sb) {
// int64_t wc0 = sb->total_writes(); // int64_t wc0 = sb->total_writes();
lua_snap_.serialize(sb); lua_snap_.serialize(sb);
id_global_pool_.serialize(sb); id_global_pool_.serialize(sb);
sb->write_int64(clock_);
thread_sched_.serialize(sb); thread_sched_.serialize(sb);
sb->write_uint32(tangibles_.size()); sb->write_uint32(tangibles_.size());
for (const auto &p : tangibles_) { for (const auto &p : tangibles_) {
@@ -749,6 +762,7 @@ void World::deserialize(StreamBuffer *sb) {
redirects_.clear(); redirects_.clear();
lua_snap_.deserialize(sb); lua_snap_.deserialize(sb);
id_global_pool_.deserialize(sb); id_global_pool_.deserialize(sb);
clock_ = sb->read_int64();
thread_sched_.deserialize(sb); thread_sched_.deserialize(sb);
// Mark all tangibles for deletion by setting ID to zero. // Mark all tangibles for deletion by setting ID to zero.
for (const auto &p : tangibles_) { for (const auto &p : tangibles_) {

View File

@@ -66,8 +66,9 @@ public:
// Print Buffer // Print Buffer
// //
// In non-actors, this is a null pointer. Stores the console // Stores the console output for this actor until it can be
// output for this actor until it can be probed by the client. // probed by the client. Most tangibles have empty printbuffers,
// which are stored as just a null pointer internally.
// //
PrintBuffer print_buffer_; PrintBuffer print_buffer_;
@@ -124,9 +125,9 @@ public:
// Make a tangible. // Make a tangible.
// //
// If the ID is zero, allocates an ID using the thread's ID allocator. If // You must provide a valid previously-unused ID. If pushdb is true, pushes
// pushdb is true, pushes the tangible's database onto the lua stack. // the tangible's database onto the lua stack. Otherwise, leaves the lua
// Otherwise, leaves the lua stack untouched. // stack untouched.
// //
Tangible *tangible_make(lua_State *L, int64_t id, const std::string &plane, bool pushdb); Tangible *tangible_make(lua_State *L, int64_t id, const std::string &plane, bool pushdb);
@@ -220,7 +221,7 @@ public:
// Run any threads which according to the scheduler queue are ready. // Run any threads which according to the scheduler queue are ready.
// //
void run_scheduled_threads(int64_t clk); void run_scheduled_threads();
// Check that the main thread has nothing on the stack // Check that the main thread has nothing on the stack
// //
@@ -272,6 +273,10 @@ private:
// //
void invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data); void invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
// Invoke the tick operation.
//
void invoke_tick(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
public: public:
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@@ -453,6 +458,9 @@ private:
// //
std::unordered_map<int64_t, UniqueTangible> tangibles_; std::unordered_map<int64_t, UniqueTangible> tangibles_;
// Current time.
int64_t clock_;
// Thread schedule: must include every thread, except // Thread schedule: must include every thread, except
// for the one currently-executing thread. // for the one currently-executing thread.
// //

View File

@@ -2,12 +2,21 @@ maketangible('login')
function login.interface(actor, place) function login.interface(actor, place)
gui.menu_item("becomeplayer", "Become a Player") gui.menu_item("becomeplayer", "Become a Player")
gui.menu_item("p123", "Print 1, 2, 3")
end end
function login.action.becomeplayer(actor, place, dialog) function login.action.becomeplayer(actor, place, dialog)
tangible.setclass(actor, player) tangible.setclass(actor, player)
end end
function login.action.p123(actor, place, dialog)
print(1)
wait(1)
print(2)
wait(1)
print(3)
end
function setfoo(n) function setfoo(n)
tangible.nopredict() tangible.nopredict()
tangible.actor().inventory.foo = n tangible.actor().inventory.foo = n
@@ -16,4 +25,3 @@ end
function buildq() function buildq()
return tangible.build{class="login", x=10, y=0, z=0, plane="nowhere", graphic="what"} return tangible.build{class="login", x=10, y=0, z=0, plane="nowhere", graphic="what"}
end end