More work on console I/O and minor fixes

This commit is contained in:
2021-10-25 14:47:37 -04:00
parent b5d62d3991
commit 9a02f408b0
13 changed files with 122 additions and 78 deletions

View File

@@ -21,6 +21,7 @@ public:
KIND_INVALID,
KIND_PLAN,
KIND_LUA,
KIND_FLUSH_PRINTS,
};
private:

View File

@@ -315,7 +315,7 @@ void LuaStack::makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const {
}
}
void LuaStack::cleartable(LuaSlot tab) const {
void LuaStack::cleartable(LuaSlot tab, bool clearmeta) const {
checktable(tab);
lua_pushnil(L_);
while (lua_next(L_, tab.index()) != 0) {
@@ -324,6 +324,10 @@ void LuaStack::cleartable(LuaSlot tab) const {
lua_pushnil(L_); // Push the new value.
lua_rawset(L_, tab.index());
}
if (clearmeta) {
lua_pushnil(L_);
lua_setmetatable(L_, tab.index());
}
}
int LuaStack::rawlen(LuaSlot obj) const {

View File

@@ -380,7 +380,7 @@ public:
lua_State *newthread(LuaSlot target) const;
void getglobaltable(LuaSlot gltab) const;
void makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const;
void cleartable(LuaSlot tab) const;
void cleartable(LuaSlot tab, bool clearmeta) const;
int rawlen(LuaSlot val) const;

View File

@@ -46,9 +46,6 @@ void PrintBuffer::discard_upto(int n) {
lines_.pop_front();
first_line_ += 1;
}
if (first_line_ < n) {
first_line_ = n;
}
if (first_unchecked_ < first_line_) {
first_unchecked_ = first_line_;
}
@@ -95,6 +92,9 @@ void PrintBuffer::patch(StreamBuffer *sb) {
}
first_unchecked_ = first_line_ + lines_.size();
discard_upto(auth_first);
if (first_line_ < auth_first) {
first_line_ = auth_first;
}
}
LuaDefine(unittests_printbuffer, "c") {
@@ -110,7 +110,7 @@ LuaDefine(unittests_printbuffer, "c") {
pbm.discard_upto(2);
LuaAssertStrEq(L, pbm.debug_string(), "2,6:baz;a;b;c;");
pbm.discard_upto(8);
LuaAssertStrEq(L, pbm.debug_string(), "8,8:");
LuaAssertStrEq(L, pbm.debug_string(), "6,6:");
LuaAssertStrEq(L, pbs.debug_string(), "0,0:");
pbs.add_string("foo\nbar\nbaz\n");
@@ -120,7 +120,7 @@ LuaDefine(unittests_printbuffer, "c") {
pbs.discard_upto(2);
LuaAssertStrEq(L, pbs.debug_string(), "2,2:baz;a;b;c;");
pbs.discard_upto(8);
LuaAssertStrEq(L, pbs.debug_string(), "8,8:");
LuaAssertStrEq(L, pbs.debug_string(), "6,6:");
pbm.clear();
pbs.clear();

View File

@@ -172,7 +172,7 @@ void SourceDB::diff(const SourceDB &auth, StreamBuffer *sb) {
SLS.result();
}
void SourceDB::patch(StreamBuffer *sb) {
bool SourceDB::patch(StreamBuffer *sb) {
lua_State *L = lua_state_;
LuaVar db, info;
LuaStack LS(L, db, info);
@@ -195,7 +195,7 @@ void SourceDB::patch(StreamBuffer *sb) {
}
}
LS.result();
if (nupdates > 0) rebuild(false);
return (nupdates > 0);
}
void SourceDB::set(const std::string &fn, const std::string &code, int sequence) {
@@ -250,7 +250,7 @@ void SourceDB::update(const util::LuaSourceVec &source) {
LS.newtable(sourcedb);
LS.rawset(LuaRegistry, "sourcedb", sourcedb);
}
LS.cleartable(sourcedb);
LS.cleartable(sourcedb, true);
for (int i = 0; i < int(source.size()); i++) {
const std::string &file = source[i].first;
@@ -278,8 +278,10 @@ static void source_clear_globals(lua_State *L) {
LS.rawset(globtab, "_G", LuaNil);
LS.set(classname, LuaNil);
while (LS.next(globtab, classname, classtab) != 0) {
if (LS.istable(classtab)) {
table_clear(LS, classtab);
if (LS.rawequal(globtab, classtab)) {
LS.rawset(globtab, classname, LuaNil);
} else if (LS.istable(classtab)) {
LS.cleartable(classtab, true);
} else {
LS.rawset(globtab, classname, LuaNil);
}
@@ -292,6 +294,8 @@ static void source_clear_globals(lua_State *L) {
// Load all the 'LuaDefine' C functions into the lua state.
//
static void source_load_cfunctions(lua_State *L) {
LuaVar classobj;
LuaStack LS(L, classobj);
auto regs = LuaFunctionReg::all();
for (const LuaFunctionReg *r : regs) {
const std::string &name = r->get_name();
@@ -307,24 +311,22 @@ static void source_load_cfunctions(lua_State *L) {
lua_CFunction func = r->get_func();
std::string mode = r->get_mode();
if (mode.find('c') != std::string::npos) { // Insert into class
lua_pushlstring(L, classname.c_str(), classname.size());
lfn_source_makeclass(L);
lua_pushcfunction(L, func);
lua_setfield(L, -2, funcname.c_str());
LS.makeclass(classobj, classname);
LS.rawset(classobj, funcname, func);
}
if (mode.find('f') != std::string::npos) { // Make global function
lua_pushcfunction(L, func);
lua_setglobal(L, funcname.c_str());
LS.getglobaltable(classobj);
LS.rawset(classobj, funcname, func);
}
}
LS.result();
}
// Run all the closures from the source database.
//
static void source_load_lfunctions(lua_State *L, bool print_errors) {
LuaRet errors;
static std::string source_load_lfunctions(lua_State *L) {
LuaVar sourcedb, key, info, seq, closure, err;
LuaStack LS(L, sourcedb, errors, key, info, seq, closure, err);
LuaStack LS(L, sourcedb, key, info, seq, closure, err);
// Get the source database.
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
@@ -349,9 +351,7 @@ static void source_load_lfunctions(lua_State *L, bool print_errors) {
// If there's already an error in the sourcedb, collect it.
if (!LS.isfunction(closure)) {
if (print_errors) {
errss << LS.ckstring(closure) << "\n";
}
errss << LS.ckstring(closure) << "\n";
continue;
}
@@ -362,22 +362,17 @@ static void source_load_lfunctions(lua_State *L, bool print_errors) {
errss << LS.ckstring(err);
}
}
LS.set(errors, errss.str());
LS.result();
return errss.str();
}
void SourceDB::rebuild(bool print_errors) {
std::string SourceDB::rebuild() {
lua_State *L = lua_state_;
LuaVar errs;
LuaStack LS(L, errs);
source_clear_globals(L);
source_install_builtins(L);
source_load_cfunctions(L);
source_load_lfunctions(L, print_errors);
lua_replace(L, errs.index());
std::string errstr = LS.ckstring(errs);
std::cerr << errstr;
LS.result();
std::string errs = source_load_lfunctions(L);
return errs;
}
void SourceDB::run_unittests() {

View File

@@ -141,18 +141,21 @@ public:
//
// Rebuild the lua environment: clear it out, then reinstall all the
// functions that should be there. See above for more information.
// If an error exists in any of the source files, or when loading any
// of the closures, the error can optionally be printed or ignored.
//
void rebuild(bool print_errors);
// Any error messages will be collected into a single long string
// containing one error per line, and returned. If the return value
// is the empty string, there were no errors.
//
std::string rebuild();
// Difference transmission.
//
// Note: The patch routine applies the differences to the source
// database, and if there are any changes, it does a source rebuild.
// The patch routine returns true if anything was modified.
//
void diff(const SourceDB &auth, StreamBuffer *sb);
void patch(StreamBuffer *sb);
bool patch(StreamBuffer *sb);
// run_unittests
//
@@ -170,19 +173,6 @@ public:
std::string get(const std::string &fn);
};
// The Lua 'makeclass' operator.
//
// Creates a table in the global environment with the specified name.
// Adds a __class field and an __index field, and an action subtable.
// If there's already a table with this name in the global environment,
// leaves it there, and repairs the __class and __index fields.
//
int lfn_source_makeclass(lua_State *L);
// Return true if the specified table is a class created by 'makeclass'
//
int lfn_source_isclass(lua_State *L);
#endif // SOURCE_HPP

View File

@@ -128,14 +128,23 @@ LuaDefine(table_count, "c") {
return 1;
}
void table_clear(LuaStack &LS0, LuaSlot table) {
LS0.cleartable(table);
}
LuaDefine(table_clear, "c") {
LuaArg tab;
LuaStack LS(L, tab);
table_clear(LS, tab);
LuaArg tab, clearmeta;
LuaVar metatable, metafield;
LuaStack LS(L, tab, clearmeta, metatable, metafield);
if (LS.ckboolean(clearmeta)) {
LS.getmetatable(metatable, tab);
if (LS.istable(metatable)) {
LS.rawget(metafield, metatable, "__metatable");
if (!LS.isnil(metafield)) {
luaL_error(L, "Cannot clear metatable.");
return LS.result();
}
}
LS.cleartable(tab, true);
} else {
LS.cleartable(tab, false);
}
return LS.result();
}

View File

@@ -12,13 +12,6 @@
#include "luastack.hpp"
// table_clear
//
// Remove all key/value pairs from the table. Does not remove
// the metatable.
//
void table_clear(LuaStack &LS0, LuaSlot tab);
// table_equal
//
// True if two tables contain the same key/value pairs.

View File

@@ -180,6 +180,11 @@ void TextGame::channel_printbuffer() {
std::cerr << "* " << printbuffer->nth(printbuffer_line_) << std::endl;
printbuffer_line_ += 1;
}
if (printbuffer_line_ > printbuffer->first_line()) {
InvocationData data;
Invocation inv(Invocation::KIND_FLUSH_PRINTS, actor_id_, actor_id_, std::to_string(printbuffer_line_), data);
world_->invoke(inv);
}
}

View File

@@ -67,7 +67,11 @@ World::World(util::WorldType wt) {
// Initialize the SourceDB. At this stage, the sourcedb is
// empty, so it's just populating the lua builtins.
source_db_.init(state());
source_db_.rebuild(true);
std::string srcerrs = source_db_.rebuild();
// There shouldn't be any lua errors in the sourceDB at this
// point, since there's no lua code in the sourceDB.
assert(srcerrs == "");
LS.result();
assert (stack_is_clear());
@@ -212,8 +216,7 @@ void World::tangible_delete(int64_t id) {
assert(LS.istable(database));
// Clear out the database.
LS.clearmetatable(database);
LS.cleartable(database);
LS.cleartable(database, true);
LS.settabletype(database, LUA_TT_DEADTANGIBLE);
// Remove the lua portion from the tangibles table.
@@ -252,6 +255,7 @@ World::Redirects World::fetch_redirects() {
}
int64_t World::create_login_actor() {
assert(stack_is_clear());
int64_t id = id_global_pool_.get_one();
Tangible *tan = tangible_make(state(), id, "nowhere", true);
LuaArg database;
@@ -323,10 +327,23 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
}
void World::update_source(util::LuaSourcePtr source) {
assert(stack_is_clear());
if (source != nullptr) {
source_db_.update(*source);
source_db_.rebuild(true);
assert(stack_is_clear());
std::string errs = source_db_.rebuild();
// I don't have a good place to send the error messages right
// now. The engine needs a catch-all place to send errors that
// occur at unexpected times.
std::cerr << errs;
}
assert(stack_is_clear());
}
void World::run_unittests() {
assert(stack_is_clear());
source_db_.run_unittests();
assert(stack_is_clear());
}
void World::invoke(const Invocation &inv) {
@@ -337,6 +354,9 @@ void World::invoke(const Invocation &inv) {
case Invocation::KIND_LUA:
invoke_lua(inv.actor(), inv.place(), inv.action(), inv.data());
break;
case Invocation::KIND_FLUSH_PRINTS:
invoke_flush_prints(inv.actor(), inv.place(), inv.action(), inv.data());
break;
default:
// Do nothing. Standard behavior for any invalid command is to
// simply do nothing at all. Perhaps eventually we may add a flag
@@ -347,6 +367,28 @@ void World::invoke(const Invocation &inv) {
}
}
void World::invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
assert(stack_is_clear());
// Check argument sanity.
if (actor_id != place_id) {
return;
}
int line = util::strtoint(action, -1);
if (line < 0) {
return;
}
Tangible *tactor = tangible_get(actor_id);
if (tactor == nullptr) {
return;
}
if (tactor->print_buffer_ == nullptr) {
return;
}
tactor->print_buffer_->discard_upto(line);
assert(stack_is_clear());
}
void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) {
assert(stack_is_clear());

View File

@@ -1,4 +1,5 @@
#include "world.hpp"
#include <iostream>
util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
return util::sort_union_id_vectors(
@@ -263,7 +264,13 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
}
void World::patch_source(StreamBuffer *sb) {
source_db_.patch(sb);
bool modified = source_db_.patch(sb);
if (modified) {
std::string errs = source_db_.rebuild();
// TODO: I don't currently have any good place to send the
// error messages. This is a stopgap.
std::cerr << errs;
}
}
void World::diff_source(World *master, StreamBuffer *sb) {

View File

@@ -194,7 +194,7 @@ public:
// Run all unit tests.
//
void run_unittests() { source_db_.run_unittests(); }
void run_unittests();
// fetch_global_pointer
//
@@ -263,6 +263,10 @@ private:
//
void invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
// Invoke the flush-prints operation.
//
void invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data);
public:
////////////////////////////////////////////////////////////////////////////
//