149 lines
6.3 KiB
Markdown
149 lines
6.3 KiB
Markdown
### Handling Print Statements
|
|
|
|
This document describes how "print" statements are handled within
|
|
Luprex. It documents the full path by which print statements
|
|
originate with Lua, and gradually travel to the Unreal virtual
|
|
console and debug log. There are two types of print statements:
|
|
|
|
- *dprint*, for messages that go to the debug logs.
|
|
- *print*, for messages that go to a window in the user's GUI.
|
|
|
|
The following sections explain the differences, and how each
|
|
of these is implemented.
|
|
|
|
## dprint, for messages that go to the debug logs
|
|
|
|
The lua code can use 'dprint' to print a message into Unreal's
|
|
debugging logs. A 'dprint' goes to the same places
|
|
that a debugging message within Unreal goes. That includes:
|
|
|
|
- Visual studio's debug message window.
|
|
- The Unreal Editor's debug message window.
|
|
- Unreal's debug message log file.
|
|
|
|
In the lua server, currently, dprints just go to stderr. We're
|
|
probably going to enhance that by adding a log file as well.
|
|
|
|
The unreal logs are not managed by predictive reexecution.
|
|
To put it differently, once a message goes into the unreal logs,
|
|
it can't be corrected by difference transmission. Because of
|
|
this, if a dprint is reexecuted, you will get multiple possibly
|
|
conflicting messages in the unreal logs.
|
|
|
|
## Implementation of dprint
|
|
|
|
The luprex DLL contains a function util::dprintview, along with
|
|
a couple of convenience wrappers like util::dprintf. This
|
|
function is used to make a debugging print. It is used in a
|
|
number of places throughout Luprex. It can also be called from
|
|
lua, via the 'dprint' function.
|
|
|
|
The Luprex DLL exports an API: EngineWrapper::hook_dprint. This
|
|
function accepts a pointer to a C callback function, which takes
|
|
a string as a parameter. The driver is expected to call
|
|
hook_dprint early in 'main', to set the dprint callback. The
|
|
pointer to the callback function is stored in the global
|
|
variable util::dprint_hook. The function util::dprintview
|
|
breaks the string into lines, and calls util::dprint_hook
|
|
once per line.
|
|
|
|
In the Unreal client, the dprint callback is set to a function
|
|
that calls UE_LOG, the unreal error logging macro, with a log
|
|
category of LogLuprex. So therefore, in the Unreal client,
|
|
calling dprint is equivalent to calling UE_LOG.
|
|
|
|
In the server, the dprint callback is set to a function that
|
|
outputs the string to the console (ie, stdout).
|
|
|
|
|
|
The print-callback is the *only* place where the Luprex DLL actually
|
|
calls into the driver (via a callback). Normally, the driver is
|
|
supposed to call into the Luprex DLL, but not the other way around.
|
|
This one exception is necessary to handle the case where the
|
|
Luprex DLL is about to crash and wants to print a message before it
|
|
does.
|
|
|
|
## print, for messages that go to a window in the user's GUI
|
|
|
|
The client is expected to have a GUI of some sort
|
|
that includes a text console, where messages can be
|
|
displayed. The print statement puts a message into this GUI
|
|
console.
|
|
|
|
Every person who is logged in has their own GUI text console.
|
|
Therefore, it is possible to print a message onto any one
|
|
of those. When the lua code executes a print statement,
|
|
it sends its output to the GUI console of the *actor*.
|
|
|
|
The contents of the GUI text console is part of the world model.
|
|
Therefore, it can be corrected by difference transmission.
|
|
If a print-statement is predicted incorrectly, the user's
|
|
GUI text console will temporarily contain the wrong text, but
|
|
it will get fixed by difference transmission.
|
|
|
|
## Implementation of print
|
|
|
|
The world model contains an ostringstream lthread_prints_
|
|
which is used to temporarily collect normal print statements.
|
|
Whenever a thread pauses or ends, the contents of the
|
|
ostringstream are transferred to where they belong.
|
|
|
|
If the thread is executing normally, as part of an 'invoke',
|
|
then the contents of the stringstream are transferred into
|
|
a "PrintBuffer." Class PrintBuffer is a class that the
|
|
Luprex DLL uses to store the contents of a player's GUI
|
|
console. The PrintBuffer is part of the world model: every
|
|
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 doesn't always get transferred to a
|
|
PrintBuffer. If the thread is a 'probe', then the stringstream
|
|
is sent to 'dprint'. If the thread is running under the HTTP
|
|
server, then the stringstream may be sent back as part of
|
|
the HTTP response.
|
|
|
|
The difference transmitter handles PrintBuffers specially.
|
|
It doesn't just fix the contents of the PrintBuffer. It also
|
|
leaves an 'authoritative' bit inside the PrintBuffer to
|
|
indicate which print statements are confirmed as authoritative,
|
|
and conversely, to indicate which ones are still just
|
|
predictions. Because of this, the PrintBuffer knows the
|
|
difference between a "final" print and a "tentative" print.
|
|
|
|
The client and server in lpxclient.cpp and lpxserver.cpp both
|
|
contain objects of class PrintChanneler. This class is designed
|
|
to monitor a PrintBuffer to see if anything has changed.
|
|
Specifically, it is capable of answering the question: does
|
|
the PrintBuffer contain any new authoritative print statements?
|
|
|
|
Periodically, lpxserver.cpp and lpxclient.cpp will check
|
|
their PrintChannelers to see if there are any new authoritative
|
|
print statements. If so, they will set a flag called "have_prints"
|
|
in the DrivenEngine. Unreal periodically polls this flag using
|
|
EngineWrapper::get_have_prints.
|
|
|
|
If the have_prints is set, Unreal then calls
|
|
EngineWrapper::play_access(... CHANNEL_PRINTS ...).
|
|
This asks the PrintChanneler to fetch all new authoritative prints.
|
|
Then, CHANNEL_PRINTS will send an invoke via the standard
|
|
predictive reexecution channels to FLUSH_PRINTS - ie, to forget
|
|
the print statements that it just channeled.
|
|
|
|
When the PrintChanneler returns prints to Unreal, Unreal
|
|
passes those prints to the LuprexGameMode blueprint by calling
|
|
LuprexGameMode::AddConsoleOutput. The LuprexGameMode is
|
|
currently responsible for implementing the GUI text console.
|
|
That will probably change at some point.
|
|
|
|
The LuprexGameMode maintains an object of class UlxConsoleOutput
|
|
which keeps a record of what's in the GUI Console. When
|
|
the LuprexGameMode receives new strings via AddConsoleOutput,
|
|
it adds those string to the UlxConsoleOutput. The UlxConsoleOutput
|
|
turns those individual lines into one big string. From there,
|
|
the LuprexGameMode copies the entire string into the body of
|
|
a UMG text widget.
|
|
|
|
## A Possible new lua print function
|
|
|