Skip to content

Bind print, and stop the log_* wrappers from crashing when called with a colon - #434

Open
Remleo wants to merge 2 commits into
praydog:masterfrom
Remleo:lua-logging-fixes
Open

Remleo wants to merge 2 commits into
praydog:masterfrom
Remleo:lua-logging-fixes

Conversation

@Remleo

@Remleo Remleo commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Two things that bite anyone writing Lua for UEVR. One file, one commit each, and they are
independent if you only want one of them.

print did nothing

There is no binding for it, so the stock implementation runs and writes to a stdout the game
does not have. Nothing reaches log.txt and there is no error either. It just looks like the
script never ran, which is an easy hour to lose.

lua-api/examples/hello_world.lua is built almost entirely on print, so the example that
ships with the repo currently produces no output at all.

ScriptContext::log already does the right thing (OutputDebugStringA, stderr, and
API::log_info with a %s format), it was only used internally. print now goes through
it. Arguments are stringified by Lua's own tostring, so any type works and arguments are
tab separated, same as stock print.

log_info crashes if you call it the usual way

The three fields are declared in API.h as C variadics:

void (*log_info)(const char* format, ...);

They were bound straight through, so this kills the process:

uevr.params.functions:log_info("hello")

These are struct fields, not methods, so sol uses the object only to fetch the pointer and
then forwards whatever Lua passed. The colon adds the object as an extra first argument, so
the format parameter gets something that is not a format string and printf reads it as one.
It is an access violation rather than a Lua error, so pcall does not help and the log ends
mid-line. With a dot it works, but the message itself is the format string, so any % in it
is a landmine.

The wrappers now take a ready string and pass it as an argument to "%s". Percent signs in
the text are harmless and the colon syntax keeps working.

Also adds a global log table with info, warn and error, since the warn and error
levels had no safe route at all. If a global is too much, say so and I will move it under
uevr.

Tested

Release build, The Outer Worlds 2 (UE5). Before: functions:log_info("...") took the game
down while the script was still loading, and no print output ever appeared in log.txt.
After: both write to log.txt, and a % in the message no longer matters. Been running on
it daily for a couple of weeks.

Remleo added 2 commits August 30, 2026 16:39
log_error, log_warn and log_info are declared in API.h as C variadic functions. Bound straight through, calling one from Lua with a colon passes self as the first argument, so the struct pointer lands in the format parameter and printf starts reading it as a format string. That is an access violation, not a Lua error, so pcall does not catch it. On a live game uevr.params.functions:log_info("...") killed the process while the script was still loading.

The wrappers now take a ready string and pass it as an argument to a %s format, so percent signs in the text are harmless and the colon syntax keeps working.
print was never bound, so the stock implementation ran and wrote to a stdout the game does not have. No script output ever reached log.txt, and there was no error either, which is easy to mistake for the script not running at all.

ScriptContext::log already writes to OutputDebugStringA, stderr and API::log_info with a safe %s format, it was only used internally. print goes through it now. Arguments are stringified by Lua's own tostring, so any type works and arguments are tab separated, same as stock print.

Also adds a global log table with info, warn and error, since the warn and error levels had no safe route from a script.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant