More work on console I/O and minor fixes
This commit is contained in:
@@ -21,6 +21,7 @@ public:
|
||||
KIND_INVALID,
|
||||
KIND_PLAN,
|
||||
KIND_LUA,
|
||||
KIND_FLUSH_PRINTS,
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
|
||||
makeclass("unittests")
|
||||
|
||||
function cmt()
|
||||
for i=1,100000 do
|
||||
local t = {1,2,3,4,5,a=1,b=2,c=3,d=4,e=5}
|
||||
table.clear(t)
|
||||
end
|
||||
end
|
||||
|
||||
function unittests.tables()
|
||||
-- check table.count
|
||||
@@ -16,7 +10,7 @@ function unittests.tables()
|
||||
|
||||
-- check table.clear
|
||||
local t = { a = 1, b = 2 }
|
||||
table.clear(t)
|
||||
table.clear(t, true)
|
||||
assert(t.a == nil)
|
||||
assert(t.b == nil)
|
||||
assert(table.count(t) == 0)
|
||||
|
||||
Reference in New Issue
Block a user