fix(unreal): Unreal layout alignment fix and other small things (#13028) b20256f368 * fixed other align types * merge build changes * now copy over gm headers as well * fixed gm crash with new canvas gms * Fixed nested view models not getting the owning artboard set. Started getting nested artboard defaults. Fixed getting obj ptr values. * typo * always add artboard default * list index now doesnt create a variable. instead you get the value via an async task * better display name * undef * command server now supports passing luaua output through * now get the artboard name for artboard defaults * new getting index property correctly * downgraded unsettle log * revert no longer needed changes * missed ptr * Potential fix for pull request finding * Potential fix for pull request finding * removed uneeded elseif, removed commented code * removed duplicate call * several issues found in PR * Potential fix for pull request finding * Potential fix for pull request finding * some more small cleanup * uneeded change * missed some files * removed uneeded comments * guard function input * better guard * better set owning artboard model * dont subscrive when its not needed. * removed uneeded comment and added copyright Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jonathon Copeland <jcopela4@gmail.com>
diff --git a/.rive_head b/.rive_head index 892e121..786a329 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -25b5033ad80758aeb3ad9acdf160a72ac35ef96b +b20256f36800e04af741647860cb9013d148c76e
diff --git a/include/rive/command_queue.hpp b/include/rive/command_queue.hpp index 8abc809..dee88b7 100644 --- a/include/rive/command_queue.hpp +++ b/include/rive/command_queue.hpp
@@ -14,6 +14,7 @@ #include <condition_variable> #include <cstdint> #include <functional> +#include <memory> #include <mutex> #include <string> #include <vector> @@ -41,6 +42,7 @@ class ArtboardInstance; class StateMachineInstance; class CommandServer; +class ScriptingContext; RIVE_DEFINE_HANDLE(FontHandle); RIVE_DEFINE_HANDLE(FileHandle); @@ -55,6 +57,15 @@ using CommandServerCallback = std::function<void(CommandServer*)>; using CommandServerDrawCallback = std::function<void(DrawKey, CommandServer*)>; +// Creates the ScriptingContext used for a loaded file's Lua VM. Invoked on the +// command server thread with the server's Factory so the context is built with +// the correct factory on the correct thread. Return nullptr (or leave the +// factory empty) to fall back to the default CPPRuntimeScriptingContext. Used +// by hosts (e.g. Unreal) to redirect Lua console/error output to their own +// logging. +using ScriptingContextFactory = + std::function<std::unique_ptr<ScriptingContext>(Factory*)>; + struct ViewModelEnum { std::string name; @@ -351,9 +362,15 @@ CommandQueue(); ~CommandQueue(); - FileHandle loadFile(std::vector<uint8_t> rivBytes, - FileListener* listener = nullptr, - uint64_t requestId = 0); + FileHandle loadFile( + std::vector<uint8_t> rivBytes, + FileListener* listener = nullptr, + uint64_t requestId = 0 +#ifdef WITH_RIVE_SCRIPTING + , + ScriptingContextFactory scriptingContextFactory = nullptr +#endif + ); void deleteFile(FileHandle, uint64_t requestId = 0); @@ -1008,6 +1025,9 @@ ObjectStream<rcp<AudioSource>> m_externalAudioSources; ObjectStream<rcp<Font>> m_externalFonts; ObjectStream<std::vector<uint8_t>> m_byteVectors; +#ifdef WITH_RIVE_SCRIPTING + ObjectStream<ScriptingContextFactory> m_scriptingContextFactories; +#endif ObjectStream<PointerEvent> m_pointerEvents; ObjectStream<std::string> m_names; ObjectStream<CommandServerCallback> m_callbacks;
diff --git a/include/rive/logging_scripting_context.hpp b/include/rive/logging_scripting_context.hpp new file mode 100644 index 0000000..e8a5d9e --- /dev/null +++ b/include/rive/logging_scripting_context.hpp
@@ -0,0 +1,38 @@ +/* + * Copyright 2025 Rive + */ + +#ifndef _RIVE_LOGGING_SCRIPTING_CONTEXT_HPP_ +#define _RIVE_LOGGING_SCRIPTING_CONTEXT_HPP_ + +#include "rive/command_queue.hpp" + +#include <cstddef> +#include <functional> + +// A lua-free bridge for routing a script's console/error output to a host +// logger. + +namespace rive +{ +enum class ScriptingLogLevel +{ + info, + warn, + error, +}; + +// Receives a single fully-assembled UTF-8 log line from a script. The line has +// no trailing newline. Invoked on the command server thread. `data` is only +// valid for the duration of the call (copy it if you need to retain it). +using ScriptingLogSink = std::function< + void(ScriptingLogLevel level, const char* data, size_t length)>; + +// Builds a ScriptingContextFactory whose contexts forward all script console +// output as ScriptingLogLevel::info lines and runtime/Lua errors as +// ScriptingLogLevel::error lines to `sink`. +ScriptingContextFactory makeLoggingScriptingContextFactory( + ScriptingLogSink sink); +} // namespace rive + +#endif
diff --git a/src/command_queue.cpp b/src/command_queue.cpp index 25d2148..c9df381 100644 --- a/src/command_queue.cpp +++ b/src/command_queue.cpp
@@ -35,9 +35,15 @@ CommandQueue::~CommandQueue() {} -FileHandle CommandQueue::loadFile(std::vector<uint8_t> rivBytes, - FileListener* listener, - uint64_t requestId) +FileHandle CommandQueue::loadFile( + std::vector<uint8_t> rivBytes, + FileListener* listener, + uint64_t requestId +#ifdef WITH_RIVE_SCRIPTING + , + ScriptingContextFactory scriptingContextFactory +#endif +) { auto handle = reinterpret_cast<FileHandle>(++m_currentFileHandleIdx); @@ -54,6 +60,9 @@ m_commandStream << handle; m_commandStream << requestId; m_byteVectors << std::move(rivBytes); +#ifdef WITH_RIVE_SCRIPTING + m_scriptingContextFactories << std::move(scriptingContextFactory); +#endif return handle; }
diff --git a/src/command_server.cpp b/src/command_server.cpp index cd88272..3903520 100644 --- a/src/command_server.cpp +++ b/src/command_server.cpp
@@ -676,11 +676,25 @@ commandStream >> handle; commandStream >> requestId; m_commandQueue->m_byteVectors >> rivBytes; +#ifdef WITH_RIVE_SCRIPTING + ScriptingContextFactory scriptingContextFactory; + m_commandQueue->m_scriptingContextFactories >> + scriptingContextFactory; +#endif lock.unlock(); #ifdef WITH_RIVE_SCRIPTING std::cout << "Rive: Command Server Scripting Enabled.\n"; - auto scriptingContext = - std::make_unique<CPPRuntimeScriptingContext>(m_factory); + // Use the host-provided scripting context when supplied (e.g. + // Unreal routes console/error output to UE_LOG); otherwise fall + // back to the default CPP runtime context. + std::unique_ptr<ScriptingContext> scriptingContext = + scriptingContextFactory ? scriptingContextFactory(m_factory) + : nullptr; + if (scriptingContext == nullptr) + { + scriptingContext = + std::make_unique<CPPRuntimeScriptingContext>(m_factory); + } scriptingContext->setRenderContext(m_factory); auto vm = make_rcp<ScriptingVM>(std::move(scriptingContext)); rcp<rive::File> file = rive::File::import(rivBytes,
diff --git a/src/lua/logging_scripting_context.cpp b/src/lua/logging_scripting_context.cpp new file mode 100644 index 0000000..41d6d36 --- /dev/null +++ b/src/lua/logging_scripting_context.cpp
@@ -0,0 +1,80 @@ +/* + * Copyright 2025 Rive + */ + +#include "rive/logging_scripting_context.hpp" + +#ifdef WITH_RIVE_SCRIPTING +#include "rive/lua/rive_lua_libs.hpp" + +#include <cstring> +#include <string> +#include <utility> + +namespace rive +{ +namespace +{ +// Routes a script's console output and errors to a host-provided sink instead +// of stdout/stderr. print() may be called several times per line, so text is +// accumulated between printBeginLine()/printEndLine() and flushed as one line. +class LoggingScriptingContext : public CPPRuntimeScriptingContext +{ +public: + LoggingScriptingContext(Factory* factory, ScriptingLogSink sink) : + CPPRuntimeScriptingContext(factory), m_sink(std::move(sink)) + {} + + void printBeginLine(lua_State*) override { m_line.clear(); } + + void print(Span<const char> data) override + { + m_line.append(data.data(), data.size()); + } + + void printEndLine() override + { + m_sink(ScriptingLogLevel::info, m_line.c_str(), m_line.size()); + m_line.clear(); + } + + void printError(lua_State* state) override + { + const char* error = lua_tostring(state, -1); + if (error != nullptr) + { + m_sink(ScriptingLogLevel::error, error, std::strlen(error)); + } + } + +private: + ScriptingLogSink m_sink; + std::string m_line; +}; +} // namespace + +ScriptingContextFactory makeLoggingScriptingContextFactory( + ScriptingLogSink sink) +{ + if (!sink) + { + return nullptr; + } + return [sink = std::move(sink)]( + Factory* factory) -> std::unique_ptr<ScriptingContext> { + return std::make_unique<LoggingScriptingContext>(factory, sink); + }; +} +} // namespace rive + +#else // !WITH_RIVE_SCRIPTING + +namespace rive +{ +ScriptingContextFactory makeLoggingScriptingContextFactory(ScriptingLogSink) +{ + return nullptr; +} +} // namespace rive + +#endif