Overhaul the lthread_prints_ buffer.
This commit is contained in:
@@ -83,23 +83,25 @@ it will get fixed by difference transmission.
|
|||||||
|
|
||||||
## Implementation of print
|
## Implementation of print
|
||||||
|
|
||||||
Inside the Luprex DLL, class PrintBuffer is used to store
|
The world model contains an ostringstream lthread_prints_
|
||||||
the contents of the GUI console. Every logged in player has
|
which is used to temporarily collect normal print statements.
|
||||||
a PrintBuffer as part of their character tangible.
|
Whenever a thread pauses or ends, the contents of the
|
||||||
Difference transmission is capable of amending the contents
|
ostringstream are transferred to where they belong.
|
||||||
of the PrintBuffer.
|
|
||||||
|
|
||||||
When the luprex thread scheduler starts executing a thread,
|
If the thread is executing normally, as part of an 'invoke',
|
||||||
it sets up an ostringstream to collect any print statements.
|
then the contents of the stringstream are transferred into
|
||||||
When the thread pauses or ends, the contents of the ostringstream
|
a "PrintBuffer." Class PrintBuffer is a class that the
|
||||||
are copied into the PrintBuffer. The code to create the
|
Luprex DLL uses to store the contents of a player's GUI
|
||||||
stringstream and to copy it into the PrintBuffer are both
|
console. The PrintBuffer is part of the world model: every
|
||||||
in the thread scheduling code in world-core.cpp.
|
logged in player has a PrintBuffer as part of their
|
||||||
|
character tangible. Difference transmission is capable of
|
||||||
|
amending the contents of the PrintBuffer.
|
||||||
|
|
||||||
The stringstream isn't *always* copied into the PrintBuffer.
|
The stringstream doesn't always get transferred to a
|
||||||
There are exceptions: for example, in a 'probe',
|
PrintBuffer. If the thread is a 'probe', then the stringstream
|
||||||
print statements don't go into the PrintBuffer, they get
|
is sent to 'dprint'. If the thread is running under the HTTP
|
||||||
rerouted to dprint instead.
|
server, then the stringstream may be sent back as part of
|
||||||
|
the HTTP response.
|
||||||
|
|
||||||
The difference transmitter handles PrintBuffers specially.
|
The difference transmitter handles PrintBuffers specially.
|
||||||
It doesn't just fix the contents of the PrintBuffer. It also
|
It doesn't just fix the contents of the PrintBuffer. It also
|
||||||
@@ -121,7 +123,7 @@ print statements. If so, they will set a flag called "have_prints"
|
|||||||
in the DrivenEngine. Unreal periodically polls this flag using
|
in the DrivenEngine. Unreal periodically polls this flag using
|
||||||
EngineWrapper::get_have_prints.
|
EngineWrapper::get_have_prints.
|
||||||
|
|
||||||
If the flag is set, Unreal then calls
|
If the have_prints is set, Unreal then calls
|
||||||
EngineWrapper::play_access(... CHANNEL_PRINTS ...).
|
EngineWrapper::play_access(... CHANNEL_PRINTS ...).
|
||||||
This asks the PrintChanneler to fetch all new authoritative prints.
|
This asks the PrintChanneler to fetch all new authoritative prints.
|
||||||
Then, CHANNEL_PRINTS will send an invoke via the standard
|
Then, CHANNEL_PRINTS will send an invoke via the standard
|
||||||
@@ -136,7 +138,8 @@ That will probably change at some point.
|
|||||||
|
|
||||||
The LuprexGameMode maintains an object of class UlxConsoleOutput
|
The LuprexGameMode maintains an object of class UlxConsoleOutput
|
||||||
which keeps a record of what's in the GUI Console. When
|
which keeps a record of what's in the GUI Console. When
|
||||||
AddConsoleOutput feeds new prints into the LuprexGameMode,
|
the LuprexGameMode receives new strings via AddConsoleOutput,
|
||||||
those prints get added to the UlxConsoleOutput. This stores
|
it adds those string to the UlxConsoleOutput. The UlxConsoleOutput
|
||||||
the contents of the console as one big string. From there,
|
turns those individual lines into one big string. From there,
|
||||||
the string is copied into a text widget.
|
the LuprexGameMode copies the entire string into the body of
|
||||||
|
a UMG text widget.
|
||||||
|
|||||||
@@ -387,29 +387,24 @@ eng::string World::probe_lua_expr(int64_t actor_id, std::string_view lua) {
|
|||||||
// Call the closure.
|
// Call the closure.
|
||||||
int top = lua_gettop(L);
|
int top = lua_gettop(L);
|
||||||
lua_pushvalue(L, closure.index());
|
lua_pushvalue(L, closure.index());
|
||||||
open_lthread_state(actor_id, actor_id, 0, false, true);
|
open_lthread_state(actor_id, actor_id, 0, false);
|
||||||
eng::string msg = traceback_pcall(L, 0, LUA_MULTRET);
|
eng::string msg = traceback_pcall(L, 0, LUA_MULTRET);
|
||||||
|
|
||||||
// If there's an error message, print it.
|
// If there's an error message, print it.
|
||||||
// Otherwise, pretty-print the results.
|
// Otherwise, pretty-print the results.
|
||||||
std::ostream *ostream = lthread_print_stream();
|
|
||||||
if (msg.empty()) {
|
if (msg.empty()) {
|
||||||
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
for (int i = top + 1; i <= lua_gettop(L); i++) {
|
||||||
LuaSpecial root(i);
|
LuaSpecial root(i);
|
||||||
pprint(LS, root, PrettyPrintOptions(), ostream);
|
pprint(LS, root, PrettyPrintOptions(), <hread_prints_);
|
||||||
// TODO: this endl is unnecessary if we just printed a newline.
|
// TODO: this endl is unnecessary if we just printed a newline.
|
||||||
(*ostream) << std::endl;
|
lthread_prints_ << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(*ostream) << msg << std::endl;
|
lthread_prints_ << msg << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the lthread_prints (and also make sure they
|
eng::string result = lthread_prints_.str();
|
||||||
// don't go into the printbuffer).
|
clear_lthread_state();
|
||||||
eng::string result = lthread_prints_->str();
|
|
||||||
lthread_prints_.reset();
|
|
||||||
|
|
||||||
close_lthread_state();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,15 +473,10 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
open_lthread_state(actor_id, place_id, 0, false, true);
|
open_lthread_state(actor_id, place_id, 0, false);
|
||||||
eng::string msg = traceback_pcall(L, nargs, LUA_MULTRET);
|
eng::string msg = traceback_pcall(L, nargs, LUA_MULTRET);
|
||||||
LuaExtraArgs returnvalues(calltop + 1, lua_gettop(L) - calltop);
|
LuaExtraArgs returnvalues(calltop + 1, lua_gettop(L) - calltop);
|
||||||
|
|
||||||
// Send any prints to the console.
|
|
||||||
eng::string prints = lthread_prints_->str();
|
|
||||||
lthread_prints_.reset();
|
|
||||||
util::dprint(prints);
|
|
||||||
|
|
||||||
// If a probe generates a lua error, we're not supposed to dprint it.
|
// If a probe generates a lua error, we're not supposed to dprint it.
|
||||||
// Instead, we're supposed to send it back to unreal as the first
|
// Instead, we're supposed to send it back to unreal as the first
|
||||||
// return value of the function.
|
// return value of the function.
|
||||||
@@ -526,7 +516,7 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close_lthread_state();
|
clear_lthread_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -545,11 +535,10 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view
|
|||||||
bool World::rebuild_sourcedb() {
|
bool World::rebuild_sourcedb() {
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
for (const eng::string &mod: source_db_.modules()) {
|
for (const eng::string &mod: source_db_.modules()) {
|
||||||
open_lthread_state(0, 0, 0, false, true);
|
open_lthread_state(0, 0, 0, false);
|
||||||
eng::string err = source_db_.rebuild_module(mod);
|
eng::string err = source_db_.rebuild_module(mod);
|
||||||
eng::string prints = lthread_prints_->str();
|
eng::string prints = lthread_prints_.str();
|
||||||
lthread_prints_.reset();
|
clear_lthread_state();
|
||||||
close_lthread_state();
|
|
||||||
if (!err.empty()) ok = false;
|
if (!err.empty()) ok = false;
|
||||||
if (!err.empty() || !prints.empty()) {
|
if (!err.empty() || !prints.empty()) {
|
||||||
util::dprint("Loading Module ", mod);
|
util::dprint("Loading Module ", mod);
|
||||||
@@ -705,9 +694,11 @@ HttpServerResponse World::http_serve(const HttpParser &request) {
|
|||||||
int oldtop = lua_gettop(L);
|
int oldtop = lua_gettop(L);
|
||||||
lua_pushvalue(L, func.index());
|
lua_pushvalue(L, func.index());
|
||||||
lua_pushvalue(L, reqtab.index());
|
lua_pushvalue(L, reqtab.index());
|
||||||
open_lthread_state(0, 0, 0, false, false);
|
open_lthread_state(0, 0, 0, false);
|
||||||
eng::string msg = traceback_pcall(L, 1, LUA_MULTRET);
|
eng::string msg = traceback_pcall(L, 1, LUA_MULTRET);
|
||||||
close_lthread_state();
|
if (!msg.empty()) lthread_prints_ << msg << std::endl;
|
||||||
|
lthread_prints_to_dprint();
|
||||||
|
clear_lthread_state();
|
||||||
|
|
||||||
// If the call threw an error, return
|
// If the call threw an error, return
|
||||||
// a 500 Internal Server Error to the client.
|
// a 500 Internal Server Error to the client.
|
||||||
@@ -1073,10 +1064,9 @@ void World::run_scheduled_threads() {
|
|||||||
|
|
||||||
// Resume the coroutine.
|
// Resume the coroutine.
|
||||||
lua_State *CO = LS.ckthread(thread);
|
lua_State *CO = LS.ckthread(thread);
|
||||||
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), sched.thread_id(), LS.ckboolean(useppool), true);
|
open_lthread_state(LS.ckinteger(actorid), sched.place_id(), sched.thread_id(), LS.ckboolean(useppool));
|
||||||
int nargs = LS.ckboolean(isnew) ? (lua_gettop(CO) - 1) : lua_gettop(CO);
|
int nargs = LS.ckboolean(isnew) ? (lua_gettop(CO) - 1) : lua_gettop(CO);
|
||||||
int status = lua_resume(CO, nullptr, nargs);
|
int status = lua_resume(CO, nullptr, nargs);
|
||||||
std::ostream *ostream = lthread_print_stream();
|
|
||||||
|
|
||||||
if (status == LUA_OK) {
|
if (status == LUA_OK) {
|
||||||
// Successfully ran to completion. Print any return values.
|
// Successfully ran to completion. Print any return values.
|
||||||
@@ -1086,8 +1076,8 @@ void World::run_scheduled_threads() {
|
|||||||
LuaCoreStack LSCO(CO);
|
LuaCoreStack LSCO(CO);
|
||||||
if (LS.ckboolean(print)) {
|
if (LS.ckboolean(print)) {
|
||||||
for (int i = 1; i <= lua_gettop(CO); i++) {
|
for (int i = 1; i <= lua_gettop(CO); i++) {
|
||||||
pprint(LSCO, LuaSpecial(i), PrettyPrintOptions(), ostream);
|
pprint(LSCO, LuaSpecial(i), PrettyPrintOptions(), <hread_prints_);
|
||||||
(*ostream) << std::endl;
|
lthread_prints_ << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (status == LUA_YIELD) {
|
} else if (status == LUA_YIELD) {
|
||||||
@@ -1106,11 +1096,12 @@ void World::run_scheduled_threads() {
|
|||||||
// Currently, the error is sent to the actor. That seems... not right in the long run.
|
// Currently, the error is sent to the actor. That seems... not right in the long run.
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
traceback_coroutine(CO);
|
traceback_coroutine(CO);
|
||||||
(*ostream) << lua_tostring(CO, -1);
|
lthread_prints_ << lua_tostring(CO, -1);
|
||||||
}
|
}
|
||||||
LS.rawset(threads, sched.thread_id(), LuaNil);
|
LS.rawset(threads, sched.thread_id(), LuaNil);
|
||||||
}
|
}
|
||||||
close_lthread_state();
|
lthread_prints_to_printbuffer();
|
||||||
|
clear_lthread_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1140,14 +1131,15 @@ void World::clear_lthread_state() {
|
|||||||
LS.rawset(globals, "actor", LuaNil);
|
LS.rawset(globals, "actor", LuaNil);
|
||||||
LS.rawset(globals, "place", LuaNil);
|
LS.rawset(globals, "place", LuaNil);
|
||||||
|
|
||||||
lthread_prints_.reset();
|
|
||||||
lthread_actor_id_ = 0;
|
lthread_actor_id_ = 0;
|
||||||
lthread_place_id_ = 0;
|
lthread_place_id_ = 0;
|
||||||
lthread_thread_id_ = 0;
|
lthread_thread_id_ = 0;
|
||||||
lthread_use_ppool_ = false;
|
lthread_use_ppool_ = false;
|
||||||
|
lthread_prints_.str();
|
||||||
|
lthread_prints_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, bool ppool, bool prints) {
|
void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, bool ppool) {
|
||||||
// Store actor and place in global variables.
|
// Store actor and place in global variables.
|
||||||
LuaVar lactor, lplace, tangibles, globals;
|
LuaVar lactor, lplace, tangibles, globals;
|
||||||
LuaExtStack LS(state(), lactor, lplace, tangibles, globals);
|
LuaExtStack LS(state(), lactor, lplace, tangibles, globals);
|
||||||
@@ -1170,18 +1162,13 @@ void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, boo
|
|||||||
lthread_place_id_ = place;
|
lthread_place_id_ = place;
|
||||||
lthread_thread_id_ = thread;
|
lthread_thread_id_ = thread;
|
||||||
lthread_use_ppool_ = ppool;
|
lthread_use_ppool_ = ppool;
|
||||||
if (prints) {
|
lthread_prints_.str();
|
||||||
lthread_prints_.reset(new eng::ostringstream);
|
lthread_prints_.clear();
|
||||||
} else {
|
|
||||||
lthread_prints_.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::close_lthread_state() {
|
void World::lthread_prints_to_printbuffer()
|
||||||
// Copy prints from lthread_prints_ stringstream into
|
{
|
||||||
// the appropriate actor's PrintBuffer.
|
const eng::string &output = lthread_prints_.str();
|
||||||
if (lthread_prints_ != nullptr) {
|
|
||||||
const eng::string &output = lthread_prints_->str();
|
|
||||||
if (output.size() > 0) {
|
if (output.size() > 0) {
|
||||||
Tangible *actor = tangible_get(lthread_actor_id_);
|
Tangible *actor = tangible_get(lthread_actor_id_);
|
||||||
if (actor != nullptr) {
|
if (actor != nullptr) {
|
||||||
@@ -1189,17 +1176,15 @@ void World::close_lthread_state() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now clean up everything.
|
|
||||||
clear_lthread_state();
|
void World::lthread_prints_to_dprint()
|
||||||
|
{
|
||||||
|
const eng::string &output = lthread_prints_.str();
|
||||||
|
if (output.size() > 0) {
|
||||||
|
util::dprintview(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream *World::lthread_print_stream() const {
|
|
||||||
if (lthread_prints_ != nullptr) {
|
|
||||||
return lthread_prints_.get();
|
|
||||||
} else {
|
|
||||||
return &std::cerr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void World::set_global(const eng::string &gvar, std::string_view value) {
|
void World::set_global(const eng::string &gvar, std::string_view value) {
|
||||||
// Store the serialized blob.
|
// Store the serialized blob.
|
||||||
|
|||||||
@@ -358,10 +358,12 @@ public:
|
|||||||
// cleared.
|
// cleared.
|
||||||
//
|
//
|
||||||
void clear_lthread_state();
|
void clear_lthread_state();
|
||||||
void open_lthread_state(int64_t actor_id, int64_t place_id, int64_t thread_id, bool ppool, bool prints);
|
void open_lthread_state(int64_t actor_id, int64_t place_id, int64_t thread_id, bool ppool);
|
||||||
void close_lthread_state();
|
|
||||||
|
|
||||||
std::ostream *lthread_print_stream() const;
|
std::ostream *lthread_print_stream() { return <hread_prints_; }
|
||||||
|
|
||||||
|
void lthread_prints_to_printbuffer();
|
||||||
|
void lthread_prints_to_dprint();
|
||||||
|
|
||||||
// Set a lua global variable.
|
// Set a lua global variable.
|
||||||
//
|
//
|
||||||
@@ -670,7 +672,7 @@ private:
|
|||||||
int64_t lthread_place_id_;
|
int64_t lthread_place_id_;
|
||||||
int64_t lthread_thread_id_;
|
int64_t lthread_thread_id_;
|
||||||
int64_t lthread_use_ppool_;
|
int64_t lthread_use_ppool_;
|
||||||
std::unique_ptr<eng::ostringstream> lthread_prints_;
|
eng::ostringstream lthread_prints_;
|
||||||
|
|
||||||
friend class Tangible;
|
friend class Tangible;
|
||||||
friend int lfn_tangible_animate(lua_State *L);
|
friend int lfn_tangible_animate(lua_State *L);
|
||||||
|
|||||||
Reference in New Issue
Block a user