Skip to content

Migrating from v4 to v5

Archie_UwU edited this page Mar 2, 2026 · 1 revision

YYToolkit v5 is a major update to YYToolkit. It's the first release that utilizes new features added in Aurie v2.0.0, such as register-preserving hooks and the new DbgPrint API. It also brings first-class C# support via AurieSharp.

Major changes

Due to severe changes happening under the hood, mods made for v4 and below do not run on v5 without changes. The sections below highlight changes made, some of which might impact your mods.

Debug prints

Explanation

Until version 5, YYToolkit has been responsible for creating a console window (hereafter referred to as the "YYToolkit Log"). Due to this, YYToolkit's interface contained undocumented Print, PrintInfo, PrintWarning and PrintError methods. These offered a way to print to the YYToolkit Log window.

Due to limitations imposed by YYToolkit, it was previously impossible to log mod output to a file and print to the log window in a single function call. This has led to mods creating their own logging systems, which leads to duplicate code, and multiple mod log files being created.

In an effort to unite logging in one place, the console window is no longer owned by YYToolkit, and is instead owned by Aurie Core. All mods now have access to the DbgPrint and DbgPrintEx functions. These functions allow mods to specify a severity, alongside a string. Both of these parameters are then output to a centralized location, the aurie.log file created in the executable's working directory.

The string supplied by the mod, alongside the severity, is logged, and may be viewed by the user even after unloading the framework or closing the game. This log file is flushed on each launch of the game.

Consequences

Using the new DbgPrint interface requires an update of Aurie's shared headers, and an update of YYToolkit's headers. Failure to meet this requirement may cause undefined behavior after using YYToolkit's interface.

Interface lookup changes

Explanation

To prevent crashes due to changes made to the interface, the interface has been renamed.

Consequences

Mods using ObGetInterface to find the YYTK_Main interface no longer work. Instead of calling ObGetInterface, mods using the YYToolkit interface should now use the YYTK::GetInterface function.

This function may return nullptr if YYToolkit is not loaded. Otherwise, it returns a typed pointer to the interface.

YYTK::RValue changes

Explanation

The YYTK::RValue structure represents a variable used by GameMaker. Until YYToolkit v5, most mods have chosen to access the fields (prefixed by m_) directly, using the m_Kind member to determine the type of data stored inside the object.

Accessing these members from mod code bypasses the GameMaker type system, as is therefore considered unsafe. Mods should use explicit conversions provided by v5 to convert data types according to GameMaker's internal rules. All conversions to and from native types by the YYTK::RValue class now use internal GameMaker handlers. This allows YYToolkit to properly type-cast across multiple types.

Consequences

In order for support to be given, mods must refrain from accessing the member variables of YYTK::RValue objects directly, and must treat it instead as an opaque object. The member variables may be changed at any time without a major version bump. Mods must use the provided functions for all type checking and conversions.

GetMethodParameter function added

Explanation

A new function has been added to the interface. For additional details, read the documentation.

Consequences

Using the new interface function requires an update of Aurie's shared headers, and an update of YYToolkit's headers. Failure to meet this requirement may cause undefined behavior after using YYToolkit's interface.

Changes to RValue indexing

Explanation

Indexing YYTK::RValue using operator[] had an edge case, where if the accessed member variable of the target object didn't exist, a shadow variable would be implicitely created, holding the same value (or memory address) as the parent object. Mod code has no way of knowing that this happened. Starting with v5, using operator[] to access a non-existent member variable now returns an empty RValue, or a null pointer if the referential indexer is used.

Consequences

Mods that do not properly validate accesses to in-game objects may display undefined behavior due to accessing invalid memory locations. Mods relying on this undocumented behavior may also display undocumented behavior.

Stack tracing changes

Explanation

Upon encountering a YYError call, YYToolkit chooses to save debug information into the log file in order to help developers hunt down bugs in their mods. It was previously assumed that this call unconditionally leads to a fatal game error, and as such performance was not prioritized. In recent runners, the YYError function is called on each exception, even if handled by a catch block. This may lead to performance degradation in games that heavily rely on exceptions to alter control flow.

Starting with v5, stacktrace details are no longer built by the std::stacktrace_entry::description function, and are instead built internally by YYToolkit. To improve debug output, the engine stacktrace output by debug_get_callstack is now also saved to the log.

Consequences

Automated tools parsing the YYToolkit log may require changes.

Three-stage init system

Explanation

Until v5, YYToolkit used a two-stage init system. The early initialization ran in ModulePreinitialize, and the late initialization ran in a module operation callback prior to the first ModuleInitialize call. This led to scripts that YYToolkit was unable to hook - for example, the GlobalScripts. These run once at the start of the game, prior to any ModuleInitialize calls - those only occur after the game creates a visible window.

Due to YYToolkit creating it's interface in ModulePreinitialize, mods could not safely register hooks prior to game code execution, as access to the interface was not guaranteed until after ModulePreinitialize finished.

To solve all of these issues, YYToolkit v5 brings forth a three-phase system. The interface creation is now done in the ModuleEntrypoint function, which runs prior to all ModulePreinitialize calls. As such, it is now safe to access YYToolkit's interface from ModulePreinitialize. Note that some functions may not be available and may block until the runner fully initializes.

To prevent such cases, a RUNNER_INIT callback has been implemented. Mods may register a callback handler using CreateCallback inside of YYToolkit's interface. Upon the callback's invocation, all runner structures have been initialized, but no game code, including GlobalScripts, has yet been run. This allows mods to intercept any executed code entry, no matter how early it may run.

The third phase of initialization runs after the game creates the window, replicating behavior of the old "late initialization".

Clone this wiki locally