Rename files in Docs, and add new Doc about print statements.
This commit is contained in:
142
Docs/Print-Statement-Handling.md
Normal file
142
Docs/Print-Statement-Handling.md
Normal file
@@ -0,0 +1,142 @@
|
||||
### 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
|
||||
|
||||
Inside the Luprex DLL, class PrintBuffer is used to store
|
||||
the contents of the GUI console. Every logged in player has
|
||||
a PrintBuffer as part of their character tangible.
|
||||
Difference transmission is capable of amending the contents
|
||||
of the PrintBuffer.
|
||||
|
||||
When the luprex thread scheduler starts executing a thread,
|
||||
it sets up an ostringstream to collect any print statements.
|
||||
When the thread pauses or ends, the contents of the ostringstream
|
||||
are copied into the PrintBuffer. The code to create the
|
||||
stringstream and to copy it into the PrintBuffer are both
|
||||
in the thread scheduling code in world-core.cpp.
|
||||
|
||||
The stringstream isn't *always* copied into the PrintBuffer.
|
||||
There are exceptions: for example, in a 'probe',
|
||||
print statements don't go into the PrintBuffer, they get
|
||||
rerouted to dprint instead.
|
||||
|
||||
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 flag 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
|
||||
AddConsoleOutput feeds new prints into the LuprexGameMode,
|
||||
those prints get added to the UlxConsoleOutput. This stores
|
||||
the contents of the console as one big string. From there,
|
||||
the string is copied into a text widget.
|
||||
Reference in New Issue
Block a user