Unreal Engine 4/5 Scripting System

WARNING: This is the dev version of the UE4SS docs. The API and features are subject to change at any time. If you are developing mods for UE4SS, you should reference the latest release instead.

Lua scripting system platform, C++ Modding API, SDK generator, blueprint mod loader, live property editor and other dumping utilities for UE4/5 games.

Major features

Targeting UE Versions: From 4.12 To 5.5

The goal of UE4SS is not to be a plug-n-play solution that always works with every game. The goal is to have an underlying system that works for most games. You may need to update AOBs on your own, and there's a guide for that below.

Basic Installation

The easiest installation is via downloading the non-dev version of the latest non-experimental build from Releases and extracting the zip content to {game directory}/GameName/Binaries/Win64/.

If your game is in the custom config list, extract the contents from the relevant folder to Win64 as well.

If you are planning on doing mod development using UE4SS, you can do the same as above but download the zDEV version instead.

Full installation guide

Fixing compatibility problems

Lua API - Overview

Generating UHT compatible headers

Custom Game Configs

Creating Compatible Blueprint Mods

UE4SS Discord Server Invite

Unreal Engine Modding Discord Server Invite

Build requirements

  • A computer running Windows.
    • Linux support might happen at some point but not soon.
  • A version of MSVC that supports C++23:
    • MSVC toolset version >= 14.39.0
    • MSVC version >= 19.39.0
    • Visual Studio version >= 17.9
    • More compilers will hopefully be supported in the future.
  • Rust toolchain >= 1.73.0
  • xmake >= 2.9.3

Build instructions

  1. Clone the repo.
  2. Execute this command: git submodule update --init --recursive Make sure your Github account is linked to your Epic Games account for UE source access. Do not use the --remote option because that will force third-party dependencies to update to the latest commit, and that can break things. You will need your github account to be linked to an Epic games account to pull the Unreal pseudo code submodule.

There are several different ways you can build UE4SS.

Building from cli

Configuration settings

xmake allows you to flexibly configure some build options to suit your specific needs. The following is a non-comprehensive list of configuration settings you might find useful.

important

All configuration changes are made by using the xmake config command. You can also use xmake f as an alias for config.

After configuring xmake with any of the following options, you can build the project with xmake or xmake build.

Mode

The build modes are structured as follows: <Target>__<Config>__<Platform>

Currently supported options for these are:

  • Target

    • Game - for regular games on UE versions greater than UE 4.21
    • LessEqual421 - for regular games on UE versions less than or equal to UE 4.21
    • CasePreserving - for games built with case preserving enabled
  • Config

    • Dev - development build
    • Debug - debug build
    • Shipping - shipping(release) build
    • Test - build for tests
  • Platform

    • Win64 - 64-bit windows

tip

Configure the project using this command: xmake f -m "<BuildMode>". -m is an alias for --mode=<BuildMode>.

Patternsleuth (Experimental)

By default, the patternsleuth tool installs itself as an xmake package. If you do not intend on modifying the patternsleuth source code, then you don't have to configure anything special. If you want to be able to modify the patternsleuth source code, you have to supply the --patternsleuth=local option to xmake config in order to recompile patternsleuth as part of the UE4SS build.

Proxy Path

By default, UE4SS generates a proxy based on C:\Windows\System32\dwmapi.dll. If you want to change this for any reason, you can supply the --ue4ssProxyPath=<path proxy dll> to the xmake config command..

Profiler Flavor

By default, UE4SS uses Tracy for profiling. You can pass --profilerFlavor=<profiler> to the xmake config command to set the profiler flavor. The currently supported flavors are Tracy, Superluminal, and None.

Version Check

By default, xmake will check if you have the minimum required version of Rust or MSVC installed (if you are using the MSVC toolchain). If you do not, it will throw an error on the configure step. If you want to ignore this check, you can pass --versionCheck=n to the xmake config command.

Once you set the flag, the option value be set until you specify otherwise.

Therefore, to not check versions when running xmake project -k vsxmake2022, you must first run the xmake config --versionCheck=n command, then run the xmake project -k vsxmake2022 command.

Helpful xmake commands

You may encounter use for the some of the more advanced xmake commands. A non-comprehensive list of some useful commands is included below.

SyntaxAliasesUses
xmake <command> --yesxmake <command> -yAutomatically confirm any user prompts.
xmake --verbose <command>xmake -v <command>Enable verbose level logging.
xmake --Diagnostic <command>xmake -D <command>Enable diagnostic level logging.
xmake --verbose --Diagnostic --yes <command>xmake -vDy <command>You can combine most flags into a single -flagCombo.
xmake configxmake fConfigure xmake with any of these options.
xmake clean --allxmake c --allCleans binaries and intermediate output of all targets.
xmake clean <target>xmake c <target>Cleans binaries and intermediates of a specific target.
xmake buildxmake bIncrementally builds UE4SS using input file detection.
xmake build --rebuildxmake b -rForces a full rebuild of UE4SS.
xmake build <target>xmake b <target>Incrementally builds a specific target.
xmake showShows xmake info and current project info.
xmake show --target=<target>xmake show -t <target>Prints lots of information about a target. Useful for debugging scripts, compiler flags, dependency tree, etc.
xmake require --cleanxmake q -cClears all package caches and uninstalls all not-referenced packages.
xmake require --forcexmake q -fForce re-installs all dependency packages.
xmake require --listxmake q -lLists all packages that are needed for the project.
xmake project --kind=vsxmake2022 --modes="Game__Shipping__Win64"xmake project -k vsxmake2022 -m "Game__Shipping__Win64"Generates a Visual Studio project based on your current xmake configuration. You can specify multiple modes to generate by supplying -m "Comma,Separated,Modes". If you do not supply any modes, the VS project will generate all permutations of modes.

Opening in an IDE

Visual Studio / Rider

To generate Visual Studio project files, run the xmake project -k vsxmake2022 -m "Game__Shipping__Win64" command.

Afterwards open the generated .sln file inside of the vsxmake2022 directory

Note that you should also commit & push the submodules that you've updated if the reason why you updated was not because someone else pushed an update, and you're just catching up to it.

warning

The vs. build plugin performs the compile operation by directly calling the xmake command under vs, and also supports intellisense and definition jumps, as well as breakpoint debugging. This means that modifying the project properties within Visual Studio will not affect which flags are passed to the build when VS executes xmake. XMake provides some configurable project settings which can be found in VS under the Project Properties -> Configuration Properties -> Xmake menu.

caution

If you have multiple Visual Studio versions installed, run xmake f --vs=2022, otherwise you may encounter issues with the project generation.

Configuring additional modes

tip

Additional modes can be generated by running xmake project -k vsxmake2022 -m "Game__Shipping__Win64,Game__Debug__Win64". Further explanation can be found in the xmake command table.

Regenerating solution best practices

caution

If you change your configuration with xmake config, you may need to regenerate your Visual Studio solution to pick up on changes to your configuration. You can simply re-run the xmake project -k vsxmake2022 -m "<modes>" command to regenerate the solution.

Building Windows binaries on Linux

We only officially support msvc-wine for cross-compiling.
Make sure you have winbind (libwbclient & samba on Arch) installed.

caution

You must use xmake v2.9.7 or later, and as of early December 2024, this version is not yet released which means you must install the dev version of xmake.

You need to install the x86_64-pc-windows-msvc target (not the windows-gnu target) with rustup.
When invoking xmake f, you must set --plat, --arch, and --sdk.
You must also use --ue4ssCross=msvc-wine, and disable the version check.
The following projects are not supported when cross-compiling and are automatically disabled:

proxy
proxy_generator
UVTD

When invoking the xmake build command, patternsleuth will automatically be built without xmake.
The binary files are available in deps/first/patternsleuth_bind/target/x86_64-pc-windows-msvc.
They are automatically used by xmake when --ue4ssCross is set to msvc-wine.
Here's an example of a full command that will build Windows binaries on a Linux machine:

xmake f -m "Game__Shipping__Win64" -p windows -a x64 --sdk=/home/<username>/my_msvc/opt/msvc --versionCheck=n --ue4ssCross=msvc-wine

Debugging under wine

Debugging can be done using winedbg.
You can also debug minidumps:

winedbg crash_2024_12_26_07_39_15.dmp

Keep in mind that debugging symbols are not stored in the dmp file, and you must have the exact same symbol file (PDB) that your UE4SS.dll was built with.
The easiest way to make sure that you have the correct symbols is to build the exact commit that the dmp file was generated from.

Updating git submodules

If you want to update git submodules, you do so one of three ways:

  1. You can execute git submodule update --init --recursive to update all submodules.
  2. You can also choose to update submodules one by one, by executing git submodule update --init --recursive deps/<first-or-third>/<Repo>. Do not use the --remote option unless you actually want to update to the latest commit.
  3. If you would rather pick a specific commit or branch to update a submodule to then cd into the submodule directory for that dependency and execute git checkout <branch name or commit>. The main dependency you might want to update from time to time is deps/first/Unreal.

Credits

All contributors since the project became open source: https://github.com/UE4SS-RE/RE-UE4SS/graphs/contributors

  • Original Creator The original creator no longer wishes to be involved in or connected to this project. Please respect their wishes, and avoid using their past usernames in connection with this project.
  • Archengius
    • UHT compatible header generator
  • CasualGamer
    • Injector code & aob scanner is heavily based on his work, 90% of that code is his.
  • SunBeam
    • Extra signature for function 'GetFullName' for UE4.25.
    • Regex to check for proper signature format when loaded from ini.
    • Lots and lots of work on signatures
  • tomsa
    • const char* to vector<int> converter
      • tomsa: Idea & most of the code
      • Original Creator: Nibblet support
  • boop / usize
    • New UFunction hook method
  • RussellJ
    • Blueprint Modloader inspiration
  • Narknon
    • Certain features and maintenance/rehosting of the project
  • DeadMor0z
    • Certain features and Lua updates/maintenance
  • OutTheShade
    • Unreal Mappings (USMAP) Generator
  • DmgVol
    • Inspiration for map dumper
  • Buckminsterfullerene
    • Rewriting the documentation, various fixes
  • trumank
    • Lua bindings generator, various fixes, automation & improvements
  • localcc
    • C++ API

Thanks to everyone who helped with testing

  • GreenHouse
  • Otis_Inf
  • SunBeam
  • Motoson
  • hooter
  • Synopis
  • Buckminsterfullerene

Installation

Core structure concept

There are four concepts you need to know about.

  1. The root directory.
    • This directory contains the UE4SS dll file.
  2. The working directory.
    • This directory contains configuration and mod files and is located inside the root directory.
  3. The game directory.
    • This directory usually contains a small executable with the name of your game and a folder with the same name.
    • This executable is not your actual game but instead it's just a small wrapper that starts any 3rd party launcher such as Steam or if there is none then it launches the real executable.
    • Example of a game directory: D:\Games\Epic Games\SatisfactoryEarlyAccess\
  4. The game executable directory.
    • This directory contains the real executable file for your game and is not part of the UE4SS directory structure.
    • You can also recognize it as the game executable located there is usually the largest (much larger than the wrapper above) and is the one running as the child process of the wrapper when the game is running.
    • Example of a game executable directory: D:\Games\Epic Games\SatisfactoryEarlyAccess\FactoryGame\Binaries\Win64\

Choosing an installation method

You can install UE4SS in a couple of different ways.

The goal is to have the *.dll of UE4SS to be loaded by the target game one way or another, and have the configuration files and \Mods\ directory in the correct place for UE4SS to find them.

Method #1 - Basic Install

Using method #1 will mean that the root directory and working directory are treated as one single directory that happens to also be the same directory as your game executable directory.

The basic install is intended for end-users who are using UE4SS for mods that use it and don't need to do any development work. There should be no extra windows visible when using this method.

The preferred and most straightforward way to install UE4SS is to choose the UE4SS_v{version_number} download (e.g. UE4SS_v3.0.0) and then just drag & drop all the necessary files into the game executable directory.

Now all you need to do is start your game and UE4SS will automatically be injected.

Method #2 - Developer Install

Using method #2 will mean that the root directory and working directory are treated as one single directory that happens to also be the same directory as your game executable directory.

The developer install is intended for mod developers or users who wish to use UE4SS for debugging, dumping or other utilities. The difference between this version and the basic install version is that there are some extra files included, and slightly different default settings in the UE4SS-settings.ini file, such as the console and GUI console being visible.

The preferred and most straightforward way to install UE4SS is to choose the zDEV-UE4SS_v{version_number} download (e.g. zDEV-UE4SS_v3.0.0) and then just drag & drop all the necessary files into the game executable directory.

Now all you need to do is start your game and UE4SS will automatically be injected.

Experimental Install

If you want the latest and greatest features and don't mind the potential for more bugs than the main release, you can visit the experimental part of releases which is automatically updated for each commit to the main branch.

There are a lot of older files in the experimental releases, so you will need to look for the latest downloads. You can tell which are the latest by looking at the date of the release.

There are two main packages you need to look for: basic, and dev. They are in a similar formats as above, but with -commit number-commit hash appended to the end of the version number. For example, a basic install might look like UE4SS_v3.0.0-5-ga5e818e.zip and a dev install might look like zDEV-UE4SS_v3.0.0-5-ga5e818e.zip.

Note: If you are using the experimental version for development, you should be using the dev version of the docs, which you can get to by appending docs.ue4ss.com with /dev (e.g. this page would be https://docs.ue4ss.com/dev/installation-guide).

Overriding Install Location

This method allows you to override the location of the root directory while proxy injection still works.

In your game executable directory alongside the dwmapi.dll, create a file called override.txt and inside it you can write either an absolute path or a relative path to your new UE4SS.dll.

For example, possible paths could be:

  • C:/ue4ss/
  • ../../Content/Paks

Manual Injection

Using manual injection will mean that the root directory and working directory are treated as one single directory that happens to also be the same directory as your game executable directory, but any directory may be used.

Following the download of basic or dev methods (stable or experimental) and delete dwmapi.dll. Afterwards, launch the game and manually inject UE4SS.dll using your injector of choice.

Central Install Location via Manual Injection

This method is a way to install UE4SS in one place for all your games. Simply extract the zip file of your choice (basic or dev) in any directory outside the game directory, this is what's known as the root directory.

You will then create a folder inside with the name of your game and drag UE4SS-settings.ini in to it, this is what's known as the working directory.

If the path to your game executable is

D:\Games\Epic Games\SatisfactoryEarlyAccess\FactoryGame\Binaries\Win64\FactoryGame-Win64-Shipping.exe

Then the name of your working directory should be SatisfactoryEarlyAccess.
This directory will be automatically found and used by UE4SS if it exists.

The following files & folders exist inside the working directory:

  • Mods
    • Mod folders
    • mods.txt
  • UE4SS-settings.ini
  • UE4SS.log
  • UE4SS.dll
  • ...and some other auxiliary, optional files that are not required for UE4SS to function.

While dwmapi.dll (can have a name of any DLL that is loaded by the game engine or by its dependencies) is in the game executable directory.

Now all you need to do is start your game and point your injector of choice to <root directory>/UE4SS.dll.

If you use this method, if you keep a copy of UE4SS-settings.ini inside the root directory then this file will act as a default for all the games that don't have a working directory as long as you still point your injector to the root directory.

This way you can use this method for most of your games and at the same time you can use method #1 or method #2 for other games.

How to verify that UE4SS is running successfully?

Try any of the following:

  • Press any of the default keyboard shortcuts, such as @ or F10 that open the in-game console (using built-in ConsoleEnablerMod)
  • Check that the log file UE4SS.log is created in the same folder as the UE4SS main DLL, and that the log file contains fresh timestamps and no errors.
  • Enable the GUI console in UE4SS-settings.ini and check that it appears as a separate window (rendered with OpenGL by default).
  • (For developers, if the game is confirmed to be safely debuggable) Check that the UE4SS library is being loaded in a debugger and has its threads spawned in the target game's process and in a reasonable state.

Contributing to RE-UE4SS

Thank you for taking the time to contribute!

All types of contributions are encouraged and valued. This guide outlines the process and guidelines for contributing to the project.

Table of Contents

Reporting Issues

Before you ask a question, it is best to search for existing issues and read the available documentation.

If you still need clarification:

  • Open an issue using the correct template
  • Provide as much context as you can about what you're experiencing
  • Provide project and platform versions (OS, etc.)
  • If the issue is build related, provide toolchain information (compiler version, etc.)
  • Provide UE4SS.log whenever it's available, even if it doesn't seem useful

Security Issues: Never report security-related issues, vulnerabilities or bugs including sensitive information to the issue tracker. Instead, please email sensitive bugs to security@ue4ss.com.

Pull Request Guidelines

General Requirements

  1. Before submitting a PR, please ensure your changes have been tested on:

    • At least one Unreal Engine version between 4.11 and 4.25 (the version where UProperty was replaced by FProperty)
    • At least one version after 4.25, preferably UE 5.1 or newer
  2. All PRs must include:

    • A clear description of the changes
    • Steps to reproduce the issue being fixed (if applicable)
    • Code to reproduce the problem it fixes or feature it adds
    • Any relevant test cases or examples
    • Updates to the Changelog.md file with relevant changes
  3. Code should be well-documented and follow the existing style conventions of the repository.

Development Workflow

  1. Fork the repository
  2. Create a new branch for your feature or bug fix
  3. Implement your changes
  4. Run the appropriate tests for your changes
  5. Update the documentation if necessary
  6. Submit a pull request

Code Style Guidelines

UEPseudo Code

  • Code in UEPseudo should match Unreal Engine's formatting and style conventions
  • When implementing code directly from Unreal Engine, maintain the original style
  • Any deviations or fixes from the original UE code should be enclosed between these comments:
    // RE-UE4SS FIX (Contributor initials or username): [explanation of why the fix is needed]
    // Modified code goes here
    // RE-UE4SS FIX END
    

Main Repository Code

  • All code in the main repository should have clang-format run against it before submission
  • Follow the existing code style of the repository for consistency
  • Use meaningful variable and function names
  • Include comments for complex logic or non-obvious implementations

Commit Message Guidelines

  • We favor Conventional Commits format for all commit messages.
    <type>[optional scope]: <description>
    
    [optional body]
    
    [optional footer(s)]
    
  • Common types include: feat, fix, docs, style, refactor, test, chore
  • Reference issue numbers in your commit messages when applicable
  • Keep commits focused on a single logical change

Upgrade Guidelines

We maintain an upgrade guide to track breaking changes and help users migrate between versions.

For Contributors

When making breaking changes to the API:

  1. You must document all breaking changes in /docs/upgrade-guide.md, including:

    • A clear description of what changed
    • The reason for the change
    • Code examples showing the old way vs. the new way
    • Any special migration steps
  2. Breaking changes should follow these principles:

    • Minimize the impact on existing code
    • Provide deprecation warnings where possible before removal
    • Consider backward compatibility or transitional APIs
    • Clearly communicate the change through version numbering (follow semver)
  3. When submitting a PR with breaking changes:

    • Explicitly tag it with breaking-change in the PR title
    • Update the Changelog with a "Breaking Changes" subsection
    • Reference the PR in the upgrade guide

For API Users

  • The /docs/upgrade-guide.md file contains version-by-version migration instructions
  • Each major and minor version has its own section detailing changes
  • Always review the upgrade guide before updating to a new version
  • The guide includes code examples for adapting to API changes

The upgrade guide is organized chronologically with the most recent version at the top, making it easy to follow incremental upgrades.

License

RE-UE4SS License

By contributing to RE-UE4SS, you agree that your contributions will be licensed under the MIT licence

UEPseudo Code Licensing

UEPseudo code is subject to Epic Games' licensing terms.

Thank you for helping improve RE-UE4SS!

Attribution

This guide is based on the contributing.md!

RE-UE4SS Upgrade Guide

This document provides detailed guidance for upgrading between versions of RE-UE4SS. Each section covers a specific version upgrade and describes breaking changes, deprecations, and the steps required to migrate your code successfully.

Version 3.x to 4.x

Released: [TBD]
PR: [Link to PR when available]

Breaking Changes

TObjectPtr Implementation Change

What Changed:
The TObjectPtr<> class has been enhanced to function as a proper smart pointer instead of a simple wrapper.

Before (v3.x):

// Simple class that makes everything compile.
template<typename UnderlyingType>
class TObjectPtr
{
public:
    UnderlyingType* UnderlyingObjectPointer;
};

After (v4.x):

template<typename T>
class TObjectPtr
{
public:
    using ElementType = T;
    // Constructors
    TObjectPtr() : Ptr(nullptr) {}
    TObjectPtr(TYPE_OF_NULLPTR) : Ptr(nullptr) {}
    TObjectPtr(const TObjectPtr& Other) : Ptr(Other.Ptr) {}
    explicit TObjectPtr(ElementType* InPtr) : Ptr(InPtr) {}
    
    // Assignment operators
    TObjectPtr& operator=(const TObjectPtr& Other) { Ptr = Other.Ptr; return *this; }
    TObjectPtr& operator=(TYPE_OF_NULLPTR) { Ptr = nullptr; return *this; }
    TObjectPtr& operator=(ElementType* InPtr) { Ptr = InPtr; return *this; }
    
    // Pointer operators
    ElementType& operator*() const { return *Ptr; }
    ElementType* operator->() const { return Ptr; }
    
    // Conversion operator
    operator ElementType*() const { return Ptr; }
    
    // Comparison operators
    bool operator==(const TObjectPtr& Other) const { return Ptr == Other.Ptr; }
    bool operator!=(const TObjectPtr& Other) const { return Ptr != Other.Ptr; }
    bool operator==(const ElementType* InPtr) const { return Ptr == InPtr; }
    bool operator!=(const ElementType* InPtr) const { return Ptr != InPtr; }
    bool operator==(TYPE_OF_NULLPTR) const { return Ptr == nullptr; }
    bool operator!=(TYPE_OF_NULLPTR) const { return Ptr != nullptr; }
    
    // Additional API compatibility
    bool operator!() const { return Ptr == nullptr; }
    explicit operator bool() const { return Ptr != nullptr; }
    ElementType* Get() const { return Ptr; }
    
private:
    ElementType* Ptr;
};

Migration Steps:

  1. For direct pointer access (previously accessed via UnderlyingObjectPointer):

    // Old
    TObjectPtr<UClass> ClassPtr;
    UClass* RawPtr = ClassPtr.UnderlyingObjectPointer;
    
    // New
    TObjectPtr<UClass> ClassPtr;
    UClass* RawPtr = ClassPtr; // implicit conversion
    // or
    UClass* RawPtr = ClassPtr.Get(); // explicit access
    
  2. For address-of operations on TObjectPtr contents:

    // Old
    &ObjectPtr.UnderlyingObjectPointer
    
    // New
    &ObjectPtr.Get()
    // or simply
    &ObjectPtr
    

FString API Changes

GetCharArray() Behavior Change

What Changed:
The GetCharArray() method now returns the actual array instead of a pointer to characters.

Before (v3.x):

// Returned a character pointer
auto GetCharArray() const -> const CharType*;

After (v4.x):

// Returns the array itself
FORCEINLINE DataType& GetCharArray() { return Data; }
FORCEINLINE const DataType& GetCharArray() const { return Data; }

// Use operator* for character pointer access
FORCEINLINE const TCHAR* operator*() const { return Data.Num() ? Data.GetData() : TEXT(""); }

Migration Steps:

  1. For character pointer access (previously GetCharArray()):

    // Old
    const TCHAR* ptr = myString.GetCharArray();
    
    // New
    const TCHAR* ptr = *myString;
    
  2. For array access (previously GetCharTArray()):

    // Old
    TArray<TCHAR>& arr = myString.GetCharTArray();
    
    // New
    TArray<TCHAR>& arr = myString.GetCharArray();
    

Deprecations

  • GetCharTArray() is now deprecated but still available for compatibility. It forwards to GetCharArray() and will be removed in a future version.

Reporting Migration Issues

If you encounter problems while upgrading, please:

  1. Check the open issues for similar reports
  2. Create a new upgrade problem issue if your issue hasn't been reported

Blueprint Modloader

As our BP system is based on RussellJ's, this tutorial video is applicable for creating a blueprint mod for UE4SS:

Live Viewer

The Live Viewer is a tool that allows you to search, view, edit & watch the properties of every object making it very powerful for debugging mods or figuring out how values are changed during runtime. Note however that it cannot show unreflected data.

In order to see it, you must make sure that the following configuration settings are set to 1:

  • GuiConsoleEnabled
  • GuiConsoleVisible

If you are having any issues seeing the window, for example if it opens as blanked out/white, you can change the GraphicsAPI setting, for example to dx11.

Sometimes the font is too small to easily see (particularly on larger resolutions). You can edit the GuiConsoleFontScaling setting to change this.

live-viewer

You can right-click the search bar to show options.

live-viewer search options

SettingExplanation
Refresh searchRefreshes the search results.
Save filtersSaves the current search filters to working directory/liveview/filters.meta.json. Saved filters are loaded when the game is next launched.
Apply when not searchingApplies the filters while not searching for any objects by name.
Include inheritanceIncludes any child objects of any search results.
Instances onlyOnly includes object instances. These are objects present in the level as part of actors (actors themselves, widgets, components etc.), and their properties are a reflection of the real-time values. Examples include <package name>_C or <package name>_C_<some instance id>.
Non-instances onlyOnly includes the default state of object packages, which are loaded in memory but are not present in the level. You cannot change the values of these properties.
CDOs onlyOnly includes the class default objects (CDOs), which are the reflected properties inherited by non-instances from a UClass object. Examples include <full path>_GEN_VARIABLE or <package path>.Default__<package name>.
Include CDOsIncludes CDOs in any of your search criteria.
Use Regex for searchAllows you to use regex to make more specific searches.
Exclude class namesAllows you to exclude specific class names, e.g., CanvasPanelSlot or StaticMeshComponent. Comma seperated list, e.g., CanvasPanelSlot, StaticMeshComponent,Package , Function.
Include class namesAccessible by exclude class names dropdown. Allows you to include only specific class names, e.g., CanvasPanelSlot or StaticMeshComponent. Comma seperated list, e.g., CanvasPanelSlot, StaticMeshComponent,Package , Function.
Has propertyFinds objects only if they have a property of a specific name, e.g., Player Name. This setting is applied only if you have entered any value in the search bar.
Has property typeFinds objects only if they have a property of a specific type, such as BoolProperty or MulticastInlineDelegateProperty.
Function parameter flagsClicking on this checkbox opens a new popup. Allows you to select any number of flags to filter functions by and whether or not you want it to use your selected flags to check for the return flag

In the property viewer pane at the bottom, there are three sub-controls:

  • << which goes backwards through your history.
  • >> which goes forwards through your history.
  • Find functions which opens a window to search for and call any functions associated with this object and any of its children. You can use this to test calling functions in-game without having to write any code.

property view controls find functions

Watches

You can right click most property types, and functions, to add a watch.

add watch

To view your watches, go into the watches tab.

Click the plus buttons on the left of each watch to expand the values box.

expand watch values

On the left side of the watch, there are two checkboxes:

  • Enable/disable watch
  • Write the watch to a file. If this is enabled, when you close the game, the values from your watche will be saved as a text file in working directory/watches/.

On the right side of the watch, there is an option to save the watch which will be automatically re-added when you restart the game. This data is stored inside of working directory/watches/watches.meta.json

save watch method of watch re-aquisition

Dumpers

C++ Header Generator

The C++ dumper is a tool that generates C++ headers from UE4 classes and blueprints.

The keybind to generate headers is by default CTRL + H, and it can be changed in Mods/Keybinds/Scripts/main.lua.

It generates a .hpp file for each blueprint (including animation blueprint and widget blueprint), and then all of the base classes inside of <ProjectName>.hpp or <EngineModule>.hpp. All classes are at the top of the files, followed by all structs. Enums are seperated into files named the same as their class, but with _enums appended to the end.

Configurations

  • DumpOffsetsAndSizes (bool)

    • Whether to property offsets and sizes
    • Default: 1
  • KeepMemoryLayout (bool)

    • Whether memory layouts of classes and structs should be accurate
    • This must be set to 1, if you want to use the generated headers in an actual C++ project
    • When set to 0, padding member variables will not be generated
    • Default: 0

    Warning: A value of 1 has no purpose yet as memory value is not accurate either way!

  • LoadAllAssetsBeforeGeneratingCXXHeaders (bool)

    • Whether to force all assets to be loaded before generating headers
    • Default: 0

    Warning: Can require multiple gigabytes of extra memory, is not stable & will crash the game if you load past the main menu after dumping

Unreal Header Tool (UHT) Dumper

Generates Unreal Header Tool compatible C++ headers for creating a mirror .uproject for your game. The guide for using these headers can be found here.

The keybind to generate headers is by default CTRL + Numpad 9, and it can be changed in Mods/Keybinds/Scripts/main.lua.

Configurations

  • IgnoreAllCoreEngineModules (bool)

    • Whether to skip generating packages that belong to the engine, particularly useful for any games that make alterations to the engine
    • Default: 0
  • IgnoreEngineAndCoreUObject (bool)

    • Whether to skip generating the Engine and CoreUObject packages
    • Default: 1
  • MakeAllFunctionsBlueprintCallable (bool)

    • Whether to force all UFUNCTION macros to have BlueprintCallable
    • Default: 1

    Warning: This will cause some errors in the generated headers that you will need to manually fix

  • MakeAllPropertyBlueprintsReadWrite (bool)

    • Whether to force all UPROPERTY macros to have BlueprintReadWrite
    • Also forces all UPROPERTY macros to have meta=(AllowPrivateAccess=true)
    • Default: 1
  • MakeEnumClassesBlueprintType (bool)

    • Whether to force UENUM macros on enums to have BlueprintType if the underlying type was implicit or uint8
    • Default: 1

    Warning: This also forces the underlying type to be uint8 where the type would otherwise be implicit

  • MakeAllConfigsEngineConfig (bool)

    • Whether to force Config = Engine on all UCLASS macros that use either one of: DefaultConfig, GlobalUserConfig or ProjectUserConfig
    • Default: 1

Object Dumper

Dumps all loaded objects to the file UE4SS_ObjectDump.txt (you can turn on force loading for all assets).

The keybind to dump objects is by default CTRL + J, and can be changed in Mods/Keybinds/Scripts/main.lua.

Example output:

[000002A70F57E5C0] Function /Game/UI/Art/WidgetParts/Basic_ButtonScalable2.Basic_ButtonScalable2_C:BndEvt__Button_0_K2Node_ComponentBoundEvent_0_OnButtonClickedEvent__DelegateSignature [n: 5343AA] [c: 000002A727993A00] [or: 000002A708466980]
[000002A70F57E4E0] Function /Game/UI/Art/WidgetParts/Basic_ButtonScalable2.Basic_ButtonScalable2_C:PreConstruct [n: 4057B] [c: 000002A727993A00] [or: 000002A708466980]
[000002A70F876600] BoolProperty /Game/UI/Art/WidgetParts/Basic_ButtonScalable2.Basic_ButtonScalable2_C:PreConstruct:IsDesignTime [o: 0] [n: 4D63DB] [c: 00007FF683722CC0] [owr: 000002A70F57E4E0]

There are multiple sets of opening & closing square brackets and each set has a different meaning and the letters in this table explains what they mean.
Within the first set of brackets is the location in memory where the object or property is stored.

LettersMeaningUE Member Variable
nName of an object/propertyNamePrivate
cClass of the object/property/enum valueClassPrivate
orOuter of the objectOuterPrivate
oOffset of a property value in an objectOffset_Internal
owrOwner of an FField, 4.25+ onlyOwner
kpKey property of an FMapPropertyKeyProp
vpValue property of an FMapPropertyValueProp
mcClass that this FClassProperty refers toMetaClass
dfFunction that this FDelegateProperty refers toFunctionSignature
pcClass that this FObjectProperty/FFieldPathProperty refers toPropertyClass
icClass that this FInterfaceProperty refers toInterfaceClass
ssStruct that this FStructProperty refers toStruct
emEnum that this FEnumProperty refers toEnum
fmField mask that this FBoolProperty refers toFieldMask
bmByte mask that this FBoolProperty refers toByteMask
vValue corresponding to this enum keyN/A
spsSuperStruct of this UClassSuperStruct
aiProperty that this FArrayProperty storesInner

Configurations

  • LoadAllAssetsBeforeDumpingObjects (bool)
    • Whether to force all assets to be loaded before dumping objects
    • Default: 0

    Warning: Can require multiple gigabytes of extra memory, is not stable & will crash the game if you load past the main menu after dumping

.usmap Dumper

Generate .usmap mapping files for unversioned properties.

The keybind to dump mappings is by default Ctrl + Numpad 6, and can be changed in Mods/Keybinds/Scripts/main.lua.

Thanks to OutTheShade for the original implementation.

.umap Recreation Dumper

Dump all loaded actors to the file ue4ss_static_mesh_data.csv to generate .umaps in-editor.

Two prerequisites are required to load the dumped actors in-editor to reconstruct the .umap:

  • All dumped actors (static meshes, their materials/textures) must be reconstructed in the editor
  • Download zMapGenBP.zip from the Releases page and follow the instructions in the Readme file inside of it

The keybind to dump mappings is by default Ctrl + Numpad 7, and can be changed in Mods/Keybinds/Scripts/main.lua.

Lua API

These are the Lua API functions available in UE4SS, on top of the standard libraries that Lua comes with by defualt.

For version: 3.1.0

Current status: mostly complete

Full API Overview

This is an overall list of API definitions available in UE4SS. For more readable information, see the individual API definition pages in the collapsible sections 4.1, 4.2 and 4.3.

Warning: This API list is not updated since 2.5.2, so it out of date. Please refer to the individual API definition pages for the most up-to-date information.

Table Definitions

  • The definitions appear as: FieldName | FieldValueType
  • Fields that only have numeric field names have '#' as their name in this definition for clarity
  • All fields are required unless otherwise specified
    ModifierKeys
        # | string (Microsoft Virtual Key-Code)

    PropertyTypes
        ObjectProperty      | internal_value
        ObjectPtrProperty   | internal_value
        Int8Property        | internal_value
        Int16Property       | internal_value
        IntProperty         | internal_value
        Int64Property       | internal_value
        NameProperty        | internal_value
        FloatProperty       | internal_value
        StrProperty         | internal_value
        ByteProperty        | internal_value
        UInt16Property      | internal_value
        UIntProperty        | internal_value
        UInt64Property      | internal_value
        BoolProperty        | internal_value
        ArrayProperty       | internal_value
        MapProperty         | internal_value
        StructProperty      | internal_value
        ClassProperty       | internal_value
        WeakObjectProperty  | internal_value
        EnumProperty        | internal_value
        TextProperty        | internal_value

    OffsetInternalInfo
        Property        | string (Name of the property to use as relative start instead of base)
        RelativeOffset  | integer (Offset from relative start to this property)

    ArrayPropertyInfo
        Type | table (PropertyTypes)

    CustomPropertyInfo
        Name            | string (Name to use with the __index metamethod)
        Type            | table (PropertyTypes)
        BelongsToClass  | string (Full class name without type that this property belongs to)
        OffsetInternal  | integer or table (if table: OffsetInternalInfo, otherwise: offset from base to this property)
        ArrayProperty   | table (Optional, ArrayPropertyInfo)

    EObjectFlags
        - A table of object flags that can be or'd together by using |.
        RF_NoFlags                       | 0x00000000
        RF_Public                        | 0x00000001
        RF_Standalone                    | 0x00000002
        RF_MarkAsNative                  | 0x00000004
        RF_Transactional                 | 0x00000008
        RF_ClassDefaultObject            | 0x00000010
        RF_ArchetypeObject               | 0x00000020
        RF_Transient                     | 0x00000040
        RF_MarkAsRootSet                 | 0x00000080
        RF_TagGarbageTemp                | 0x00000100
        RF_NeedInitialization            | 0x00000200
        RF_NeedLoad                      | 0x00000400
        RF_KeepForCooker                 | 0x00000800
        RF_NeedPostLoad                  | 0x00001000
        RF_NeedPostLoadSubobjects        | 0x00002000
        RF_NewerVersionExists            | 0x00004000
        RF_BeginDestroyed                | 0x00008000
        RF_FinishDestroyed               | 0x00010000
        RF_BeingRegenerated              | 0x00020000
        RF_DefaultSubObject              | 0x00040000
        RF_WasLoaded                     | 0x00080000
        RF_TextExportTransient           | 0x00100000
        RF_LoadCompleted                 | 0x00200000
        RF_InheritableComponentTemplate  | 0x00400000
        RF_DuplicateTransient            | 0x00800000
        RF_StrongRefOnFrame              | 0x01000000
        RF_NonPIEDuplicateTransient      | 0x01000000
        RF_Dynamic                       | 0x02000000
        RF_WillBeLoaded                  | 0x04000000
        RF_HasExternalPackage            | 0x08000000
        RF_AllFlags                      | 0x0FFFFFFF

    EInternalObjectFlags
        - A table of internal object flags that can be or'd together by using |.
        ReachableInCluster               | 0x00800000
        ClusterRoot                      | 0x01000000
        Native                           | 0x02000000
        Async                            | 0x04000000
        AsyncLoading                     | 0x08000000
        Unreachable                      | 0x10000000
        PendingKill                      | 0x20000000
        RootSet                          | 0x40000000
        GarbageCollectionKeepFlags       | 0x0E000000
        AllFlags                         | 0x7F800000

Global Functions

    print(any... Message)
        - Does not have the capability to format. Use 'string.format' if you require formatting.
    
    CreateInvalidObject() -> UObject
        - Creates an object with an IsValid function that always returns false

    StaticFindObject(string ObjectName) -> { UObject | AActor | nil }
    StaticFindObject(UClass Class=nil, UObject InOuter=nil, string ObjectName, bool ExactClass=false)
        - Maps to https://docs.unrealengine.com/4.26/en-US/API/Runtime/CoreUObject/UObject/StaticFindObject/
    
    FindFirstOf(string ShortClassName) -> { UObject | AActor | nil }
        - Find the first non-default instance of the supplied class name
        - Param 'ShortClassName': Should only contains the class name itself without path info
    
    FindAllOf(string ShortClassName) -> table -> { UObject | AActor } | nil
        - Find all non-default instances of the supplied class name
        - Param 'ShortClassName': Should only contains the class name itself without path info
    
    RegisterKeyBind(integer Key, function Callback)
    RegisterKeyBind(integer Key, table ModifierKeys, function callback)
        - Registers a callback for a key-bind
        - Callbacks can only be triggered while the game or debug console is on focus

    IsKeyBindRegistered(integer Key)
    IsKeyBindRegistered(integer Key, table ModifierKeys)
        - Checks if, at the time of the invocation, the supplied keys have been registered
    
    RegisterHook(string UFunctionName, function Callback) -> integer, integer
        - Registers a callback for a UFunction
        - Callbacks are triggered when a UFunction is executed
        - The callback params are: UObject self, UFunctionParams...
        - Returns two ids, both of which must be passed to 'UnregisterHook' if you want to unregister the hook.

    UnregisterHook(string UFunctionName, integer PreId, integer PostId)
        - Unregisters a hook.
		
    ExecuteInGameThread(function Callback)
        - Execute code inside the game thread using ProcessEvent.
        - Will execute as soon as the game has time to execute.

    FName(string Name) -> FName
    FName(integer ComparisonIndex) -> FName
        - Returns the FName for this string/ComparisonIndex or the FName for "None" if the name doesn't exist

    FText(string Text) -> FText
        - Returns the FText representation of this string

    StaticConstructObject(UClass Class,
                          UObject Outer,
                          FName Name, #Optional
                          EObjectFlags Flags, #Optional
                          EInternalObjectFlags InternalSetFlags, #Optional
                          bool CopyTransientsFromClassDefaults, #Optional
                          bool AssumeTemplateIsArchetype, #Optional
                          UObject Template, #Optional
                          FObjectInstancingGraph InstanceGraph, #Optional
                          UPackage ExternalPackage, #Optional
                          void SubobjectOverrides #Optional) -> UObject
        - Attempts to construct a UObject of the passed UClass
        - (>=4.26) Maps to https://docs.unrealengine.com/4.27/en-US/API/Runtime/CoreUObject/UObject/StaticConstructObject_Internal/1/
        - (<4.25) Maps to https://docs.unrealengine.com/4.27/en-US/API/Runtime/CoreUObject/UObject/StaticConstructObject_Internal/2/

    RegisterCustomProperty(table CustomPropertyInfo)
        - Registers a custom property to be used automatically with 'UObject.__index'

    ForEachUObject(function Callback)
        - Execute the callback function for each UObject in GUObjectArray
        - The callback params are: UObject object, integer ChunkIndex, integer ObjectIndex

    NotifyOnNewObject(string UClassName, function Callback)
        - Executes the provided Lua function whenever an instance of the provided class is constructed.
        - Inheritance is taken into account, so if you provide "/Script/Engine.Actor" as the class then it will execute your
        - Lua function when any object is constructed that's either an AActor or is derived from AActor.

    RegisterCustomEvent(string EventName, function Callback)
        - Registers a callback that will get called when a BP function/event is called with the name 'EventName'.
    
    RegisterLoadMapPreHook(function Callback)
        - Registers a callback that will get called before UEngine::LoadMap is called.
        - The callback params are: UEngine Engine, struct FWorldContext& WorldContext, FURL URL, class UPendingNetGame* PendingGame, FString& Error
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        
    RegisterLoadMapPostHook(function Callback)
        - Registers a callback that will get called after UEngine::LoadMap is called.
        - The callback params are: UEngine Enigne, struct FWorldContext& WorldContext, FURL URL, class UPendingNetGame* PendingGame, FString& Error
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.

    RegisterInitGameStatePreHook(function Callback)
        - Registers a callback that will get called before AGameModeBase::InitGameState is called.
        - The callback params are: AGameModeBase Context
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.

    RegisterInitGameStatePostHook(function Callback)
        - Registers a callback that will get called after AGameModeBase::InitGameState is called.
        - The callback params are: AGameModeBase Context
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.

    RegisterBeginPlayPreHook(function Callback)
        - Registers a callback that will get called before AActor::BeginPlay is called.
        - The callback params are: AActor Context
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.

    RegisterBeginPlayPostHook(function Callback)
        - Registers a callback that will get called after AActor::BeginPlay is called.
        - The callback params are: AActor Context
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.

    RegisterProcessConsoleExecPreHook(function Callback)
        - Registers a callback that will get called before UObject::ProcessConsoleExec is called.
        - The callback params are: UObject Context, string Cmd, table CommandParts, FOutputDevice Ar, UObject Executor
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - If the callback returns nothing (or nil), the original return value of ProcessConsoleExec will be used.
        - If the callback returns true or false, the supplied value will override the original return value of ProcessConsoleExec.

    RegisterProcessConsoleExecPostHook(function Callback)
        - Registers a callback that will get called after UObject::ProcessConsoleExec is called.
        - The callback params are: UObject Context, string Cmd, table CommandParts, FOutputDevice Ar, UObject Executor
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - If the callback returns nothing (or nil), the original return value of ProcessConsoleExec will be used.
        - If the callback returns true or false, the supplied value will override the original return value of ProcessConsoleExec.

    RegisterCallFunctionByNameWithArgumentsPreHook(function Callback)
        - Registers a callback that will be called before UObject::CallFunctionByNameWithArguments is called.
        - The callback params are: UObject Context, string Str, FOutputDevice Ar, UObject Executor, bool bForceCallWithNonExec
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - If the callback returns nothing (or nil), the original return value of CallFunctionByNameWithArguments will be used.
        - If the callback returns true or false, the supplied value will override the original return value of CallFunctionByNameWithArguments.

    RegisterCallFunctionByNameWithArgumentsPostHook(function Callback)
        - Registers a callback that will be called after UObject::CallFunctionByNameWithArguments is called.
        - The callback params are: UObject Context, string Str, FOutputDevice Ar, UObject Executor, bool bForceCallWithNonExec
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - If the callback returns nothing (or nil), the original return value of CallFunctionByNameWithArguments will be used.
        - If the callback returns true or false, the supplied value will override the original return value of CallFunctionByNameWithArguments.

    RegisterULocalPlayerExecPreHook(function Callback)
        - Registers a callback that will be called before ULocalPlayer::Exec is called.
        - The callback params are: ULocalPlayer Context, UWorld InWorld, string Cmd, FOutputDevice Ar
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - The callback can have two return values.
        - If the first return value is nothing (or nil), the original return value of Exec will be used.
        - If the first return value is true or false, the supplied value will override the original return value of Exec.
        - The second return value controls whether the original Exec will execute.
        - If the second return value is nil or true, the orginal Exec will execute.
        - If the second return value is false, the original Exec will not execute.

    RegisterULocalPlayerExecPostHook(function Callback)
        - Registers a callback that will be called after ULocalPlayer::Exec is called.
        - The callback params are: ULocalPlayer Context, UWorld InWorld, string Cmd, FOutputDevice Ar
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - The callback can have two return values.
        - If the first return value is nothing (or nil), the original return value of Exec will be used.
        - If the first return value is true or false, the supplied value will override the original return value of Exec.
        - The second return value controls whether the original Exec will execute.
        - If the second return value is nil or true, the orginal Exec will execute.
        - If the second return value is false, the original Exec will not execute.

    RegisterConsoleCommandHandler(string CommandName, function Callback)
        - Registers a callback for a custom console commands.
        - The callback only runs in the context of UGameViewportClient.
        - The callback params are: string Cmd, table CommandParts, FOutputDevice Ar
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - The callback must return either true or false.
        - If the callback returns true, no further handlers will be called for this command.

    RegisterConsoleCommandGlobalHandler(string CommandName, function Callback)
        - Registers a callback for a custom console command.
        - Unlike 'RegisterConsoleCommandHandler', this global variant runs the callback for all contexts.
        - The callback params are: string Cmd, table CommandParts, FOutputDevice Ar
        - Params (except strings & bools & FOutputDevice) must be retrieved via 'Param:Get()' and set via 'Param:Set()'.
        - The callback must return either true or false.
        - If the callback returns true, no further handlers will be called for this command.

    ExecuteAsync(function Callback)
        - Asynchronously executes the specified function

    ExecuteWithDelay(integer DelayInMilliseconds, function Callback)
        - Asynchronously executes the specified function after the specified delay

    RegisterConsoleCommandHandler(string CommandName, function Callback)
        - Executes the provided Lua function whenever the CommandName is entered into the UE console.
        - The parameters for the callback are the full command (string),
        - and the parameters (table, containing the full command split by spaces), and FOutputDevice.
        - In the callback, return true to prevent other handlers from handling the command, or false to allow other handlers.

    LoadAsset(string AssetPathAndName)
        - Loads an asset by name.
        - Must only be called from within the game thread.
        - For example, from within a UFunction hook or RegisterConsoleCommandHandler callback.

    FindObject(string|FName|nil ClassName, string|FName|nil ObjectShortName, EObjectFlags RequiredFlags, EObjectFlags BannedFlags) -> UObject derivative
        - Finds an object by either class name or short object name.
        - ClassName or ObjectShortName can be nil, but not both.
        - Returns a UObject of a derivative of UObject.

    FindObject(UClass InClass, UObject InOuter, string Name, bool ExactClass)
        - Finds an object. Works the same way as the function by the same name in the UE source.

    FindObjects(integer NumObjectsToFind, string|FName|nil ClassName, string|FName|nil ObjectShortName, EObjectFlags RequiredFlags, EObjectFlags BannedFlags, bool bExactClass) -> table -> { UObject derivative }
        - Finds the first specified number of objects by class name or short object name.
        - To find all objects that match your criteria, set NumObjectsToFind to 0 or nil.
        - Returns a table of UObject derivatives

    LoopAsync(integer DelayInMilliseconds, function Callback)
        - Starts a loop that sleeps for the supplied number of milliseconds and stops when the callback returns true.
    
    IterateGameDirectories() -> table
        - Returns a table of all game directories.
        - An example of an absolute path to Win64: Q:\SteamLibrary\steamapps\common\Deep Rock Galactic\FSD\Binaries\Win64
        - To get to the same directory, do: IterateGameDirectories().Game.Binaries.Win64
        - Note that the game name is replaced by 'Game' to keep things generic.
        - You can use '.__name' and '.__absolute_path' to retrieve values.
        - You can use '.__files' to retrieve a table containing all files in this directory.
        - You also use '.__name' and '.__absolute_path' for files.

    DumpAllObjects()
    GenerateSDK()
    GenerateLuaTypes()
    GenerateUHTCompatibleHeaders()
    DumpStaticMeshes()
    DumpAllActors()
    DumpUSMAP()

Classes

    RemoteObject
        Inheritance:
        - The first of two base objects that all other objects inherits from
        - Contains a pointer to a C/C++ object that's typically owned by the game
        Methods
            IsValid() -> bool
                - Returns whether this object is valid or not

    LocalObject
        Inheritance:
        - The second of two base objects that all other objects inherits from
        - Contains an inlined object which is fully owned by Lua
        Methods

    UnrealVersion
        Inheritance:
        Methods
            GetMajor() -> integer
            GetMinor() -> integer
            IsEqual(number MajorVersion, number MinorVersion) -> bool
            IsAtLeast(number MajorVersion, number MinorVersion) -> bool
            IsAtMost(number MajorVersion, number MinorVersion) -> bool
            IsBelow(number MajorVersion, number MinorVersion) -> bool
            IsAbove(number MajorVersion, number MinorVersion) -> bool

    UE4SS
        Inheritance:
        - Class for interacting with UE4SS metadata
        Methods
            GetVersion() -> 3x integer
                - Returns major, minor and hotfix version numbers
                - To detect version 1.0 or below, check if "UE4SS" or "UE4SS.GetVersion" is nil

    Mod
        Inheritance: RemoteObject
        - Class for interacting with the local mod object
        Methods
            SetSharedVariable(string VariableName, any Value)
                - Sets a variable that can be accessed by any mod.
                - The second parameter (Value) can only be one of the following types: nil, string, number, bool, UObject(+derivatives), lightuserdata.
                - These variables do not get reset when hot-reloading.

            GetSharedVariable(string VariableName) -> any
                - Gets a variable that could've been set from another mod.
                - The return value can only be one of the following types: nil, string, number, bool, UObject(+derivatives), lightuserdata.
        
            type() -> string
                - Returns "ModRef"

    UObject
        Inheritance: RemoteObject
        - This is the base class that most other Unreal Engine game objects inherit from
        Methods
            __index(string MemberVariableName) -> auto
                - Attempts to return either a member variable or a callable UFunction
                - Can return any type, you can use the 'type()' function on the returned value to figure out what Lua class it's using (if non-trivial type)
            
            __newindex(string MemberVariableName, auto NewValue)
                - Attempts to set the value of a member variable
            
            GetFullName() -> string
                - Returns the full name & path info for a UObject & its derivatives
            
            GetFName() -> FName
                - Returns the FName of this object by copy
                - All FNames returned by '__index' are returned by reference
            
            GetAddress() -> integer
                - Returns the memory address of this object
            
            GetClass() -> UClass
                - Returns the class of this object, this is equivalent to 'UObject->ClassPrivate' in Unreal

            GetOuter() -> UObject
                - Returns the Outer of this object

            IsAnyClass() -> bool
                - Returns true if this UObject is a UClass or a derivative of UClass

            Reflection() -> UObjectReflection
                - Returns a reflection object

            GetPropertyValue(string MemberVariableName) -> auto
                - Identical to __index

            SetPropertyValue(string MemberVariableName auto NewValue)
                - Identical to __newindex

            IsClass() -> bool
                - Returns whether this object is a UClass or UClass derivative

            GetWorld() -> UWorld
                - Returns the UWorld that this UObject is contained within.

            CallFunction(UFunction function, auto Params...)
                - Calls the supplied UFunction on this UObject.

            IsA(UClass Class) -> bool
            IsA(string FullClassName) -> bool
                - Returns whether this object is of the specified class.

            HasAllFlags(EObjectFlags FlagsToCheck)
                - Returns whether the object has all of the specified flags.

            HasAnyFlags(EObjectFlags FlagsToCheck)
                - Returns whether the object has any of the specified flags.
            
            HasAnyInternalFlags(EInternalObjectFlags InternalFlagsToCheck)
                - Return whether the object has any of the specified internal flags.

            ProcessConsoleExec(string Cmd, nil Reserved, UObject Executor)
                - Calls UObject::ProcessConsoleExec with the supplied params.

            type() -> string
                - Returns the type of this object as known by UE4SS
                - This does not return the type as known by Unreal

    UStruct
        Inheritance: UObject
        Methods
            GetSuperStruct() -> UClass
                - Returns the SuperStruct of this struct (can be invalid).

            ForEachFunction(function Callback)
                - Iterates every UFunction that belongs to this struct.
                - The callback has one param: UFunction Function.
                - Return true in the callback to stop iterating.

            ForEachProperty(function Callback)
                - Iterates every Property that belongs to this struct.
                - The callback has one param: Property Property.
                - Return true in the callback to stop iterating.

    UClass
        Inheritance: UClass
        Methods
            GetCDO() -> UClass
                - Returns the ClassDefaultObject of a UClass.

            IsChildOf(UClass Class) -> bool
                - Returns whether or not the class is a child of another class.

    AActor
        Inheritance: UObject
        Methods
            GetWorld() -> UObject | nil
                - Returns the UWorld that this actor belongs to
            
            GetLevel() -> UObject | nil
                - Returns the ULevel that this actor belongs to
    
    FName
        Inheritance: LocalObject
        Methods
            ToString() -> string
                - Returns the string for this FName
            
            GetComparisonIndex() -> integer
                - Returns the ComparisonIndex for this FName (index into global names array)
    
    TArray
        Inheritance: RemoteObject
        Methods
            __index(integer ArrayIndex)
                - Attempts to retrieve the value at the specified offset in the array
                - Can return any type, you can use the 'type()' function on the returned value to figure out what Lua class it's using (if non-trivial type)
            
            __newindex(integer ArrayIndex, auto NewValue)
                - Attempts to set the value at the specified offset in the array
            
            GetArrayAddress() -> integer
                - Returns the address in memory where the TArray struct is located
            
            GetArrayNum() -> integer
                - Returns the number of current elements in the array
            
            GetArrayMax() -> integer
                - Returns the maximum number of elements allowed in this array (aka capacity)
            
            GetArrayDataAddress -> integer
                - Returns the address in memory where the data for this array is stored
            
            ForEach(function Callback)
                - Iterates the entire TArray and calls the callback function for each element in the array
                - The callback params are: integer index, RemoteUnrealParam | LocalUnrealParam elem
                - Use 'elem:get()' and 'elem:set()' to access/mutate an array element

    UEnum
        Inheritance: RemoteObject
        Methods
            GetNameByValue(integer Value) -> FName
                - Returns the FName that corresponds to the specified value.
            ForEachName(LuaFunction Callback) -> FName
                - Iterates every FName/Value combination that belongs to this enum.
                - The callback has two params: FName Name, integer Value.
                - Return true in the callback to stop iterating.

    RemoteUnrealParam | LocalUnrealParam
        Inheritance: RemoteObject | LocalObject
            - This is a dynamic wrapper for any and all types & classes
            - Whether the Remote or Local variant is used depends on the requirements of the data but the usage is identical with either param types
        Methods
            get() -> auto
                - Returns the underlying value for this param
            
            set(auto NewValue)
                - Sets the underlying value for this param
            
            type() -> string
                - Returns "RemoteUnrealParam" or "LocalUnrealParam"
    
    UScriptStruct
        Inheritance: LocalObject
        Methods
            __index(string StructMemberVarName) -> auto
                - Attempts to return the value for the supplied variable
                - Can return any type, you can use the 'type()' function on the returned value to figure out what Lua class it's using (if non-trivial type)
            
            __newindex(string StructMemberVarName, auto NewValue)
                - Attempts to set the value for the supplied variable
            
            GetBaseAddress() -> integer
                - Returns the address in memory where the UObject that this UScriptStruct belongs to is located
            
            GetStructAddress() -> integer
                - Returns the address in memory where this UScriptStruct is located
            
            GetPropertyAddress() -> integer
                - Returns the address in memory where the corresponding U/FProperty is located

            IsValid() -> bool
                - Returns whether the struct is valid

            IsMappedToObject() -> bool
                - Returns whether the base object is valid

            IsMappedToProperty() -> bool
                - Returns whether the property is valid
            
            type() -> string
                - Returns "UScriptStruct"
    
    UFunction
        Inheritance: UObject
        Methods
            __call(UFunctionParams...)
                - Attempts to call the UFunction

            GetFunctionFlags() -> integer
                - Returns the flags for the UFunction.

            SetFunctionFlags(integer Flags)
                Sets the flags for the UFuction.

    FString
        Inheritance: RemoteObject
            - A TArray of characters
        Methods
            ToString()
                - Returns a string that Lua can understand

            Clear()
                - Clears the string by setting the number of elements in the TArray to 0

    FieldClass
        Inheritance: LocalObject
        Methods
            GetFName()
                - Returns the FName of this class by copy.

    Property
        Inheritance: RemoteObject
        Methods
            GetFullName() -> string
                - Returns the full name & path for this property.

            GetFName() -> FName
                - Returns the FName of this property by copy.
                - All FNames returned by '__index' are returned by reference.

            IsA(PropertyTypes PropertyType) -> bool
                - Returns true if the property is of type PropertyType.

            GetClass() -> PropertyClass

            ContainerPtrToValuePtr(UObjectDerivative Container, integer ArrayIndex) -> LightUserdata
                - Equivalent to FProperty::ContainerPtrToValuePtr<uint8> in UE.

            ImportText(string Buffer, LightUserdata Data, integer PortFlags, UObject OwnerObject)
                - Equivalent to FProperty::ImportText in UE, except without the 'ErrorText' param.

    ObjectProperty
        Inheritance: Property
        Methods
            GetPropertyClass() -> UClass
                - Returns the class that this property holds.

    BoolProperty
        Inheritance: Property
        Methods
            GetByteMask() -> integer
            GetByteOffset() -> integer
            GetFieldMask() -> integer
            GetFieldSize() -> integer

    StructProperty
        Inheritance: Property
        Methods
            GetStruct() -> UScriptStruct
                - Returns the UScriptStruct that's mapped to this property.

    ArrayProperty
        Inheritance: Property
        Methods
            GetInner() -> Property
                - Returns the inner property of the array.

    UObjectReflection
        Inheritance:
        Methods
            GetProperty(string PropertyName) -> Property
                - Returns a property meta-data object

    FOutputDevice
        Inheritance: RemoteObject
        Methods
            Log(string Message)
                - Logs a message to the output device (i.e: the in-game console)

    FWeakObjectPtr
        Inheritance: LocalObject
        Methods
            Get() -> UObjectDerivative
                - Returns the pointed to UObject or UObject derivative (can be invalid, so call UObject:IsValid after calling Get).

Key

The Key table contains Microsoft virtual key-code strings.

This table is automatically populated with data. Do not modify the data inside this table.

Key-code strings

LEFT_MOUSE_BUTTON
RIGHT_MOUSE_BUTTON
CANCEL
MIDDLE_MOUSE_BUTTON
XBUTTON_ONE
XBUTTON_TWO
BACKSPACE
TAB
CLEAR
RETURN
PAUSE
CAPS_LOCK
IME_KANA
IME_HANGUEL
IME_HANGUL
IME_ON
IME_JUNJA
IME_FINAL
IME_HANJA
IME_KANJI
IME_OFF
ESCAPE
IME_CONVERT
IME_NONCONVERT
IME_ACCEPT
IME_MODECHANGE
SPACE
PAGE_UP
PAGE_DOWN
END
HOME
LEFT_ARROW
UP_ARROW
RIGHT_ARROW
DOWN_ARROW
SELECT
PRINT
EXECUTE
PRINT_SCREEN
INS
DEL
HELP
ZERO
ONE
TWO
THREE
FOUR
FIVE
SIX
SEVEN
EIGHT
NINE
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
LEFT_WIN
RIGHT_WIN
APPS
SLEEP
NUM_ZERO
NUM_ONE
NUM_TWO
NUM_THREE
NUM_FOUR
NUM_FIVE
NUM_SIX
NUM_SEVEN
NUM_EIGHT
NUM_NINE
MULTIPLY
ADD
SEPARATOR
SUBTRACT
DECIMAL
DIVIDE
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
F17
F18
F19
F20
F21
F22
F23
F24
NUM_LOCK
SCROLL_LOCK
BROWSER_BACK
BROWSER_FORWARD
BROWSER_REFRESH
BROWSER_STOP
BROWSER_SEARCH
BROWSER_FAVORITES
BROWSER_HOME
VOLUME_MUTE
VOLUME_DOWN
VOLUME_UP
MEDIA_NEXT_TRACK
MEDIA_PREV_TRACK
MEDIA_STOP
MEDIA_PLAY_PAUSE
LAUNCH_MAIL
LAUNCH_MEDIA_SELECT
LAUNCH_APP1
LAUNCH_APP2
OEM_ONE
OEM_PLUS
OEM_COMMA
OEM_MINUS
OEM_PERIOD
OEM_TWO
OEM_THREE
OEM_FOUR
OEM_FIVE
OEM_SIX
OEM_SEVEN
OEM_EIGHT
OEM_102
IME_PROCESS
PACKET
ATTN
CRSEL
EXSEL
EREOF
PLAY
ZOOM
PA1
OEM_CLEAR

Example

local enter_key = Key.RETURN

ModifierKey

The ModifierKey table contains Microsoft virtual key-code strings that are meant to be modifier keys such as CONTROL and ALT.

This table is automatically populated with data.
Do not modify the data inside this table.

Modifier key-code strings

SHIFT
CONTROL
ALT

Example

local CTRL_Key = ModifierKey.CONTROL

PropertyTypes

The PropertyTypes table contains type information for Unreal Engine properties.
This is primarily used with the RegisterCustomProperty Lua function.

This table is automatically populated with data.
Do not modify the data inside this table.

Structure

KeyValue
ObjectPropertyinternal_value
Int8Propertyinternal_value
Int16Propertyinternal_value
IntPropertyinternal_value
Int64Propertyinternal_value
NamePropertyinternal_value
FloatPropertyinternal_value
StrPropertyinternal_value
BytePropertyinternal_value
BoolPropertyinternal_value
ArrayPropertyinternal_value
MapPropertyinternal_value
StructPropertyinternal_value
ClassPropertyinternal_value
WeakObjectPropertyinternal_value
EnumPropertyinternal_value
TextPropertyinternal_value

Example

local PropertyType = PropertyTypes.ObjectProperty

OffsetInternalInfo

The OffsetInternalInfo table contains information related to a custom property.

You must supply data yourself when using this table.

Structure

KeyValue TypeInformation
PropertystringName of the property to use as relative start instead of base
RelativeOffsetintegerOffset from relative start to this property

Example

local PropertyInfo = {
    ["Property"] = "HistoryBuffer",
    ["RelativeOffset"] = 0x10
}

ArrayPropertyInfo

The ArrayPropertyInfo table contains type information for custom ArrayProperty properties.

You must supply data yourself when using this table.

Structure

KeyValue TypeSub Type
TypetablePropertyTypes

Example

local ArrayPropertyInfo = {
    ["Type"] = PropertyTypes.IntProperty
}

CustomPropertyInfo

The CustomPropertyInfo table contains information about a custom property.

You must supply data yourself when using this table.

Structure

KeyValue TypeSub TypeInformation
NamestringName to use with the __index metamethod
TypetablePropertyTypes
BelongsToClassstringFull class name without type, that this property belongs to
OffsetInternalinteger or tableOffsetInternalInfoSub Type only valid if Type is table
ArrayPropertytableArrayPropertyInfoOnly use when 'Type' is PropertyTypes.ArrayProperty

Simple Example

Creates a custom property with the name MySimpleCustomProperty that accesses whatever data is at offset 0xF40 in any instance of class Character as if it was an IntProperty.

local CustomPropertyInfo = {
    ["Name"] = "MySimpleCustomProperty",
    ["Type"] = PropertyTypes.IntProperty,
    ["BelongsToClass"] = "/Script/Engine.Character"
    ["OffsetInternal"] = 0xF40
}

Advanced Example

Creates a custom property with the name MyAdvancedCustomProperty that accesses whatever data is at offset 0xF48 in any instance of class Character as if it was an ArrayProperty with an inner type of IntProperty.

local CustomPropertyInfo = {
    ["Name"] = "MyAdvancedCustomProperty",
    ["Type"] = PropertyTypes.ArrayProperty,
    ["BelongsToClass"] = "/Script/Engine.Character"
    ["OffsetInternal"] = 0xF48,
    ["ArrayProperty"] = {
        ["Type"] = PropertyTypes.IntProperty
    }
}

EObjectFlags

A table of object flags that can be or'd together by using |

Field NameField Value Type
RF_NoFlags0x00000000
RF_Public0x00000001
RF_Standalone0x00000002
RF_MarkAsNative0x00000004
RF_Transactional0x00000008
RF_ClassDefaultObject0x00000010
RF_ArchetypeObject0x00000020
RF_Transient0x00000040
RF_MarkAsRootSet0x00000080
RF_TagGarbageTemp0x00000100
RF_NeedInitialization0x00000200
RF_NeedLoad0x00000400
RF_KeepForCooker0x00000800
RF_NeedPostLoad0x00001000
RF_NeedPostLoadSubobjects0x00002000
RF_NewerVersionExists0x00004000
RF_BeginDestroyed0x00008000
RF_FinishDestroyed0x00010000
RF_BeingRegenerated0x00020000
RF_DefaultSubObject0x00040000
RF_WasLoaded0x00080000
RF_TextExportTransient0x00100000
RF_LoadCompleted0x00200000
RF_InheritableComponentTemplate0x00400000
RF_DuplicateTransient0x00800000
RF_StrongRefOnFrame0x01000000
RF_NonPIEDuplicateTransient0x01000000
RF_Dynamic0x02000000
RF_WillBeLoaded0x04000000
RF_HasExternalPackage0x08000000
RF_AllFlags0x0FFFFFFF

EInternalObjectFlags

A table of internal object flags that can be or'd together by using |

Field NameField Value Type
ReachableInCluster0x00800000
ClusterRoot0x01000000
Native0x02000000
Async0x04000000
AsyncLoading0x08000000
Unreachable0x10000000
PendingKill0x20000000
RootSet0x40000000
GarbageCollectionKeepFlags0x0E000000
AllFlags0x7F800000

EFindName

Field NameField Value Type
FNAME_Find0
FNAME_Add1

RemoteObject

The RemoteObject class is the first of two base objects that all other objects inherits from, the other one being LocalObject.

It contains a pointer to a C++ object that is typically owned by the game.

Inheritance

None

Methods

IsValid()

  • Return type: bool
  • Returns: whether this object is valid or not

Example

-- 'StaticFindObject' returns a UObject which inherits from RemoteObject.
local Object = StaticFindObject("/Script/CoreUObject.Object")
if Object:IsValid() then
    print("Object is valid\n")
else
    print("Object is NOT valid\n")
end

LocalObject

The LocalObject class is the second of two base objects that all other objects inherits from, the other one being RemoteObject.

It contains an inlined C++ object that is owned by Lua.

Inheritance

None

Methods

None

UnrealVersion

The UnrealVersion class contains helper functions for retrieving which version of Unreal Engine that is being used.

Inheritance

None

Methods

GetMajor()

  • Return type: integer

GetMinor()

  • Return type: integer

IsEqual(number MajorVersion, number MinorVersion)

  • Return type: bool

IsAtLeast(number MajorVersion, number MinorVersion)

  • Return type: bool

IsAtMost(number MajorVersion, number MinorVersion)

  • Return type: bool

IsBelow(number MajorVersion, number MinorVersion)

  • Return type: bool

IsAbove(number MajorVersion, number MinorVersion)

  • Return type: bool

Examples

local Major = UnrealVersion.GetMajor()
local Minor = UnrealVersion.GetMinor()
print(string.format("Version: %s.%s\n", Major, Minor))

if UnrealVersion.IsEqual(5, 0) then print("Version is 5.0\n") end
if UnrealVersion.IsAtLeast(5, 0) then print("Version is >=5.0\n") end
if UnrealVersion.IsAtMost(5, 0) then print("Version is <=5.0\n") end
if UnrealVersion.IsBelow(5, 0) then print("Version is <5.0\n") end
if UnrealVersion.IsAbove(5, 0) then print("Version is >5.0\n") end

UE4SS

The UE4SS class is for interacting with UE4SS metadata.

Inheritance

None

Methods

GetVersion()

Returns: the current version of UE4SS that is being used.
Return Value:

#TypeInformation
1integerMajor version
2integerMinor version
3integerHotfix version

Example #1

Warning: This only works in UE4SS 1.1+. See example #2 for UE4SS <=1.0.

local Major, Minor, Hotfix = UE4SS.GetVersion()
print(string.format("UE4SS v%d.%d.%d\n", Major, Minor, Hotfix))

Example #2

This example shows how to distinguish between UE4SS <=1.0, which didn't have the UE4SS class, and UE4SS >=1.1.

if UE4SS == nil then
    print("Running UE4SS <=1.0\n")
end

Mod

The Mod class is responsible for interacting with the local mod object.

Inheritance

RemoteObject

Methods

SetSharedVariable(string VariableName, any Value)

  • Sets a variable that can be accessed by any mod.
  • The second parameter Value can only be one of the following types: nil, string, number, bool, UObject (+derivatives), lightuserdata.

Warning: These variables do not get reset when hot-reloading.

Example

-- When sharing a UObject, make absolutely sure that it's a UObject that doesn't cease to exist before it's used again.
-- It's a very bad idea to share transient objects like actors as they might die and stop existing.
local StaticObject = StaticFindObject("/Script/Engine.Default__GameplayStatics")

-- The 'ModRef' variable is a global variable that's automatically created and is the instance of the current mod.
ModRef:SetSharedVariable("MyVariable", StaticObject)

GetSharedVariable(string VariableName)

  • Return type: any
  • Returns: a variable that could've been set from another mod.
  • The return value can only be one of the following types: nil, string, number, bool, UObject(+derivatives), lightuserdata.

Example

-- Assuming that the example script for 'SetSharedVariable' has been executed.
local SharedObject = ModRef:GetSharedVariable("MyVariable")

-- 'GetSharedVariable' may return anything that its able to store.
-- Any mod is able to override the value for any shared variable.
if SharedObject and type(SharedObject) == "userdata" and SharedObject:type() == "UObject" and SharedObject:IsValid() then
    print(string.format("SharedObject '%s' is valid.\n", SharedObject:GetFullName()))
else
    print("SharedObject was nil, not userdata, not a UObject, or an invalid UObject")
end

type()

  • Return type: string
  • Returns: "ModRef"

UObject

The UObject class is the base class that most other Unreal Engine game objects inherit from.

Inheritance

RemoteObject

Metamethods

__index

  • Usage: UObject["ObjectMemberName"] or UObject.ObjectMemberName

  • Returns either a member variable (reflected property or custom property) or a UFunction.

  • This method can return any type, and you can use the UObject-specific type() function on the returned value to figure out the type if the type is non-trivial.

  • If the type is trivial, use the regular type() Lua function.

  • Return Value:

#TypeInformation
1UObject or UFunctionIf the type is UObject, then the actual type may be any class that inherits from UObject.
  • Example:
    local Character = FindFirstOf("Character")
    
    -- Retrieve a non-trivial type
    local MovementComponent = Character.CharacterMovement
    
    -- Retrieve a trivial type
    local JumpMaxCount = Character.JumpMaxCount
    
    -- Call a UFunction member on the object
    -- Remember to use a colon (:) for calls
    local CanCharacterJump = Character:CanJump()
    
    

__newindex

  • Usage: UObject["ObjectMemberName"] = NewValue or UObject.ObjectMemberName = NewValue

  • Sets the value of a member variable to NewValue.

  • Example: Sets the value of MaxParticleResize in the first instance of class UEngine in memory.

    local Engine = FindFirstOf("Engine")
    Engine.MaxParticleResize = 4
    

Methods

GetFullName()

  • Returns: the full name & path info for a UObject & its derivatives

  • Return Value:

#TypeInformation
1stringFull name and path of the UObject
  • Example:
    local Engine = FindFirstOf("Engine")
    print(string.format("Engine Name: %s", Engine:GetFullName()))
    
    -- Output
    -- Engine Name: FGGameEngine /Engine/Transient.FGGameEngine_2147482618
    

GetFName()

  • Returns: the FName of the UObject. This is equivalent to Object->NamePrivate in Unreal.

Warning: All FNames returned by __index are returned by reference.

  • Return Value:
#TypeInformation
1FNameFName of the UObject
  • Example:
    local Character = FindFirstOf("Character")
    if Character:IsValid() then
        local CharacterName = Character:GetFName()
        print(string.format("ComparisonIndex: 0x%X\n", CharacterName:GetComparisonIndex()))
    end
    

GetAddress()

  • Returns: where in memory the UObject is located.

  • Return Value:

#TypeInformation
1integer64-bit integer, address of the UObject
  • Example:
    local Character = FindFirstOf("Character")
    if Character:IsValid() then
        print(string.format("Character: 0x%X\n", Character:GetAddress()))
    end
    

GetClass()

  • Returns: the class of this object. This is equivalent to UObject->ClassPrivate in Unreal.

  • Return Value:

#TypeInformation
1UClassThe class of the UObject
  • Example:
    local Character = FindFirstOf("Character")
    if Character:IsValid() then
        print(string.format("Character Class: 0x%X\n", Character:GetClass():GetAddress()))
    end
    

GetOuter()

  • Returns: the outer of the UObject. This is equivalent to Object->OuterPrivate in Unreal.

  • Return Value:

#TypeInformation
1UObjectThe outer UObject of this UObject
  • Example:
    local Character = FindFirstOf("Character")
    if Character:IsValid() then
        print(string.format("Character Outer: 0x%X\n", Character:GetOuter():GetAddress()))
    end
    

IsAnyClass()

  • Return type: bool
  • Returns: true if this UObject is a UClass or a derivative of UClass

Reflection()

  • Return type: UObjectReflection
  • Returns: a reflection object

GetPropertyValue(string MemberVariableName)

  • Return type: auto
  • Identical to __index metamethod (doing UObject["ObjectMemberName"])

SetPropertyValue(string MemberVariableName, auto NewValue)

  • Identical to __newindex metamethod (doing UObject["ObjectMemberName"] = NewValue)

IsClass()

  • Return type: bool
  • Returns: whether this object is a UClass or UClass derivative

GetWorld()

  • Return type: UWorld
  • Returns: the UWorld that this UObject is contained within.

IsA(UClass Class)

  • Return type: bool
  • Returns: whether this object is of the specified UClass.

IsA(string FullClassName)

  • Return type: bool
  • Returns: whether this object is of the specified class name.

HasAllFlags(EObjectFlags FlagsToCheck)

  • Return type: bool
  • Returns: whether the object has all of the specified flags.

HasAnyFlags(EObjectFlags FlagsToCheck)

  • Return type: bool
  • Returns: whether the object has any of the specified flags.

HasAnyInternalFlags(EInternalObjectFlags InternalFlagsToCheck)

  • Return type: bool
  • Returns: whether the object has any of the specified internal flags.

CallFunction(UFunction Function, auto Params...)

  • Calls the supplied UFunction on this UObject.

ProcessConsoleExec(string Cmd, nil Reserved, UObject Executor)

  • Calls UObject::ProcessConsoleExec with the supplied params.

type()

  • Return type: string
  • Returns: the type of this object as known by UE4SS
  • This does not return the type as known by Unreal
  • Not equivalent to doing type(UObject), which returns the type as known by Lua (a 'userdata')

UStruct

Inheritance

UObject

Methods

GetSuperStruct()

  • Return type: UClass
  • Returns: the SuperStruct of this struct (can be invalid).

ForEachFunction(function Callback)

  • Iterates every UFunction that belongs to this struct.
  • The callback has one param: UFunction Function.
  • Return true in the callback to stop iterating.

ForEachProperty(function Callback)

  • Iterates every Property that belongs to this struct.
  • The callback has one param: Property Property.
  • Return true in the callback to stop iterating.

UScriptStruct

Inheritance

LocalObject

Metamethods

__index

  • Usage: UScriptStruct["StructMemberName"] or UScriptStruct.StructMemberName

  • Return type: auto

  • Returns the value for the supplied member name.

  • Can return any type, you can use the type() function on the returned value to figure out what Lua class it's using (if non-trivial type).

  • Example:

local scriptStruct = FindFirstOf('_UI_Items_C')

-- Either of the following can be used:
local item = scriptStruct['Item']
local item = scriptStruct.Item

__newindex

  • Usage: UScriptStruct["StructMemberName"] = NewValue or UScriptStruct.StructMemberName = NewValue

  • Attempts to set the value for the supplied member name to NewValue.

  • Example:

local scriptStruct = FindFirstOf('_UI_Items_C')

-- Either of the following can be used:
scriptStruct['Item'] = 5
scriptStruct.Item = 5

Methods

GetBaseAddress()

  • Return type: integer
  • Returns: the address in memory where the UObject that this UScriptStruct belongs to is located

GetStructAddress()

  • Return type: integer
  • Returns: the address in memory where this UScriptStruct is located

GetPropertyAddress()

  • Return type: integer
  • Returns: the address in memory where the corresponding UProperty/FProperty is located

IsValid()

  • Return type: bool
  • Returns: whether the struct is valid

IsMappedToObject()

  • Return type: bool
  • Returns: whether the base object is valid

IsMappedToProperty()

  • Return type: bool
  • Returns: whether the property is valid

type()

  • Return type: string
  • Returns: "UScriptStruct"

UClass

Inheritance

UStruct

Methods

GetCDO()

  • Return type: UClass
  • Returns: the ClassDefaultObject of a UClass.

IsChildOf(UClass Class)

  • Return type: bool
  • Returns: whether or not the class is a child of another class.

UFunction

Inheritance

UObject

Metamethods

__call

  • Usage: UFunction(UFunctionParams...)
  • Return type: auto
  • Attempts to call the UFunction and returns the result, if any.
  • If the UFunction is obtained without a context (e.g. from StaticFindObject), a UObject context must be passed as the first parameter.

Methods

GetFunctionFlags()

  • Return type: integer
  • Returns: the flags for the UFunction.

SetFunctionFlags(integer Flags)

  • Sets the flags for the UFunction.

UEnum

Inheritance

RemoteObject

Methods

GetNameByValue(integer Value)

  • Return type: FName
  • Returns: the FName that corresponds to the specified value.

ForEachName(LuaFunction Callback)

  • Iterates every FName/Value combination that belongs to this enum.
  • The callback has two params: FName Name, integer Value.
  • Return true in the callback to stop iterating.

GetEnumNameByIndex(integer Index)

  • Return types: FName, Integer
  • Returns: the FName that coresponds the given Index.
  • Returns: the Integer value that coresponds the given Index.

InsertIntoNames(string Name, integer Value, integer Index, boolean ShiftValues = true)

  • Inserts a FName/Value combination into a a UEnum at the given Index.
  • If ShiftValues = true, will shift all enum values greater than inserted value by one.

EditNameAt(integer Index, string NewName)

  • At a given Index, will modify the found element in the UEnum and replace its Name with the given NewName.

EditValueAt(integer Index, integer NewValue)

  • At a given Index, will modify the found element in the UEnum and replace its value with the given NewValue.

RemoveFromNamesAt(integer Index, integer Count = 1, boolean AllowShrinking = true)

  • Will remove Count element(s) at the given Index from a UEnum.
  • If AllowShrinkning = true, will shrink the enum array when removing elements.

AActor

Inheritance

UObject

Methods

GetWorld()

  • Return types: UObject
  • Returns: the UWorld that this actor belongs to or an invalid UObject

GetLevel()

  • Return type: UObject
  • Returns: the ULevel that this actor belongs to or an invalid UObject

FString

FString is a TArray of characters.

Inheritance

RemoteObject

Methods

ToString()

  • Return type: string
  • Returns: a string that Lua can understand.

Clear()

  • Clears the string by setting the number of elements in the TArray to 0.

FName

Inheritance

LocalObject

Methods

ToString()

  • Return type: string
  • Returns: the string for this FName.

GetComparisonIndex()

  • Return type: integer
  • Returns: the ComparisonIndex for this FName (index into global names array).

FText

Inheritance

LocalObject

Methods

ToString()

  • Return type: string
  • Returns: the string representation of this FText.

FieldClass

Inheritance

LocalObject

Methods

GetFName()

  • Return type: FName
  • Returns: the FName of this class by copy.

TArray

Inheritance

RemoteObject

Metamethods

__index

  • Usage: TArray[ArrayIndex]
  • Return type: auto
  • Attempts to retrieve the value at the specified integer offset ArrayIndex in the array.
  • Can return any type, you can use the type() function on the returned value to figure out what Lua class it's using (if non-trivial type).

__newindex

  • Usage: TArray[ArrayIndex] = NewValue
  • Attempts to set the value at the specified integer offset ArrayIndex in the array to NewValue.

__len

  • Usage: #TArray
  • Return type: integer
  • Returns the number of current elements in the array.

Methods

GetArrayAddress()

  • Return type: integer
  • Returns: the address in memory where the TArray struct is located.

GetArrayNum()

  • Return type: integer
  • Returns: the number of current elements in the array.

GetArrayMax()

  • Return type: integer
  • Returns: the maximum number of elements allowed in this array (aka capacity).

GetArrayDataAddress()

  • Return type: integer
  • Returns: the address in memory where the data for this array is stored.

Empty()

  • Clears the array.

ForEach(function Callback)

  • Iterates the entire TArray and calls the callback function for each element in the array.
  • The callback params are: integer index, RemoteUnrealParam elem | LocalUnrealParam elem.
  • Use elem:get() and elem:set() to access/mutate an array element.

TMap

Inheritance

RemoteObject

Metamethods

__len

  • Usage: #TMap
  • Return type: integer
  • Returns the number of current pairs in the map.

Methods

Find(key)

  • Return type: RemoteUnrealParam | LocalUnrealParam
  • Returns: the element found in the map
  • Throws: if an element was not found in the map
  • Finds the specified key in the map

Add(key, value)

  • Inserts a key/value pair into the map. If the key already exists in the map, replaces the value.

Contains(key)

  • Return type: bool
  • Returns: if the element exists in the map.
  • Checks if a key exists inside of the map.

Remove(key)

  • Removes an element from the map. If an element doesn't exist, does nothing.

Empty()

  • Clears the map.

ForEach(function Callback)

  • Iterates the entire TMap and calls the callback function for each element in the array.
  • The callback params are: RemoteUnrealParam key, RemoteUnrealParam value | LocalUnrealParam value.
  • Use elem:get() and elem:set() to access/mutate the value.
  • Mutating the key is undefined behavior.

RemoteUnrealParam

This is a dynamic wrapper for any and all types & classes.

Whether the Remote or Local variant is used depends on the requirements of the data but the usage is identical with either param types.

Inheritance

RemoteObject

Methods

get()

  • Return type: auto
  • Returns: the underlying value for this param.

set(auto NewValue)

  • Sets the underlying value for this param.

type()

  • Return type: string
  • Returns: "RemoteUnrealParam".

LocalUnrealParam

This is a dynamic wrapper for any and all types & classes.

Whether the Remote or Local variant is used depends on the requirements of the data but the usage is identical with either param types.

Inheritance

LocalObject

Methods

get()

  • Return type: auto
  • Returns: the underlying value for this param.

set(auto NewValue)

  • Sets the underlying value for this param.

type()

  • Return type: string
  • Returns: "LocalUnrealParam".

Property

Inheritance

RemoteObject

Methods

GetFullName()

  • Return type: string
  • Returns: the full name & path for this property.

GetFName()

  • Return type: FName
  • Returns: the FName of this property by copy.

All FNames returned by __index are returned by reference.

IsA(PropertyTypes PropertyType)

  • Return type: bool
  • Returns: true if the property is of type PropertyType.

GetClass()

  • Return type: PropertyClass

ContainerPtrToValuePtr(UObjectDerivative Container, integer ArrayIndex)

  • Return type: LightUserdata
  • Equivalent to FProperty::ContainerPtrToValuePtr<uint8> in UE.

ImportText(string Buffer, LightUserdata Data, integer PortFlags, UObject OwnerObject)

  • Equivalent to FProperty::ImportText in UE, except without the ErrorText param.

ObjectProperty

Inheritance

Property

Methods

GetPropertyClass()

  • Return type: UClass
  • Returns: the class that this property holds.

StructProperty

Inheritance

Property

Methods

GetStruct()

  • Return type: UScriptStruct
  • Returns: the UScriptStruct that's mapped to this property.

BoolProperty

Inheritance

Property

Methods

GetByteMask()

  • Return type: integer

GetByteOffset()

  • Return type: integer

GetFieldMask()

  • Return type: integer

GetFieldSize()

  • Return type: integer

ArrayProperty

Inheritance

Property

Methods

GetInner()

  • Return type: Property
  • Returns: the inner property of the array.

UObjectReflection

Inheritance

None

Methods

GetProperty(string PropertyName)

  • Return type: Property
  • Returns: a property meta-data object.

FOutputDevice

Inheritance

RemoteObject

Methods

Log(string Message)

  • Logs a message to the output device (i.e: the in-game console).

FWeakObjectPtr

Inheritance

LocalObject

Methods

Get()

  • Return type: UObjectDerivative
  • Returns: the pointed to UObject or UObject derivative.

The return can be invalid, so call UObject:IsValid after calling this function.

UWorld

Inheritance

RemoteObject

Methods

SpawnActor(UClass ActorClass, FVector Location, FRotator Rotation)

This function uses UGameplayStatics:BeginDeferredActorSpawnFromClass and UGameplayStatics:FinishSpawningActor to spawn an actor.

  • Return type: AActor
  • Returns: Spawned actor object or an invalid object.

print

The print function is used for debugging and outputs a string to the debug console.

This function cannot be used to format strings, please use string.format for string formatting purposes.

New lines are not automatically appended, so make sure to use \n whenever you want a new line.

Parameters

#TypeInformation
...anyString or variable to output

Example

print("Hello Debug Console\n")

CreateInvalidObject

The function CreateInvalidObject always returns an object with an IsValid function that returns flase.

The sole purpose of the function is to ensure that the mod's Lua code adheres to UE4SS code conventions, where all functions return an invalid UObject instead of nil.

Example

The example code below ensures that you never need to check if EngineCache is nil, and the same applies to the return value of GetEngine().

local EngineCache = CreateInvalidObject() ---@cast EngineCache UEngine
---Returns instance of UEngine
---@return UEngine
function GetEngine()
    if EngineCache:IsValid() then return EngineCache end

    EngineCache = FindFirstOf("Engine") ---@type UEngine
    return EngineCache
end

FName

The FName function is used to get an FName representation of a string or integer.

Parameters (overload #1)

This overload mimics FName::FName with the FindType param set to EFindName::FName_Add.

#TypeInformation
1stringString that you'd like to get an FName representation of
2EFindNameFinding or adding name type. It can be either FNAME_Find or FNAME_Add. Default is FNAME_Add if not explicitly supplied

Parameters (overload #2)

#TypeInformation
1integer64-bit integer representing the ComparisonIndex part that you'd like to get an FName representation of
2EFindNameFinding or adding name type. It can be either FNAME_Find or FNAME_Add. Default is FNAME_Add if not explicitly supplied

Return Value

#TypeInformation
1FNameFName corresponding to the string or ComparisonIndex, if one exists, or the "None" FName if one doesn't exist. If FNAME_Add is supplied then it adds the name if it doesn't exist

Example

local name = FName("MyName")

print(name) -- MyName

FText

The FText function is used to create an FText object from a string.

Useful when you have to interact with UserWidget-related classes for the UI of your mods, and call their SetText(FText("My New Text")) methods.

Parameters (overload #1)

This overload uses FText::FText( FString&& InSourceString ) to create a new FText object.

| Type | Information

---|----------|------------- 1 | string | Content with which FText will to be created

Return Value

| Type | Information

---|-------|------------- 1 | FText | FText object that contains the passed string

Example

Code:

local my_text = FText("My Text")
print(string.format("Lua type: %s\n", type(my_text)))
print(string.format("Object type: %s\n", my_text:type()))
print(string.format("Content: %s\n", my_text:ToString()))

Output:

[Lua] Lua type: userdata
[Lua] Object type: FText
[Lua] Content: My Text

IterateGameDirectories

Returns a table of all game directories.

An example of an absolute path to Win64: Q:\SteamLibrary\steamapps\common\Deep Rock Galactic\FSD\Binaries\Win64.

To get to the same directory, do IterateGameDirectories().<Game Name>.Binaries.Win64.

  • You can use .__name and .__absolute_path to retrieve values.
  • You can use .__files to retrieve a table containing all files in this directory.
  • You also use .__name and .__absolute_path for files.

Return Value

#TypeInformation
1tableThe game directories table

Example

for _, GameDirectory in pairs(IterateGameDirectories()) do
    print(GameDirectory.__name)
    print(GameDirectory.__absolute_path)
end

FindObject

FindObject is a function that finds an object.

Overload #1 finds by either class name or short object name.

Overload #2 works the same way as FindObject in the UE source.

Parameters (overload #1)

#TypeInformation
1string|FName|UClass|nilThe short name of the object's class or the UClass itself
2string|FName|nilThe short name of the object itself
3EObjectFlagsAny flags that the object cannot have. Uses | as a seperator
4EObjectFlagsAny flags that the object must have. Uses | as a seperator

Param 1 or Param 2 can be nil, but not both.

Parameters (overload #2)

#TypeInformation
1UClassThe class to find
2UObject|UClassThe outer to look inside. If this is null then param 3 should start with a package name
3stringThe object path to search for an object, relative to param 2
4boolWhether to require an exact match with the UClass parameter

Return Value (overload #1 & #2)

#TypeInformation
1UObjectThe derivative of the UObject

Example (overload #1)

-- SceneComponent instance called TransformComponent0
local Object = FindObject("SceneComponent", "TransformComponent0")
-- FirstPersonCharacter_C instance called FirstPersonCharacter_C_0
local Object = FindObject("FirstPersonCharacter_C", "FirstPersonCharacter_C_0", EObjectFlags.RF_NoFlags, EObjectFlags.RF_ClassDefaultObject)

Alternatively, you can use a UClass object:

local Object = FindObject(SceneComponentClass, "TransformComponent0")

Example (overload #2)

local Object = FindObject(UClass, World, "Character", true)

FindObjects

Finds the first specified number of objects by class name or short object name.

To find all objects that match your criteria, set param 1 to 0 or nil.

Parameters

#TypeInformation
1integerThe number of objects to find
1string|FName|nilThe short name of the class of the object
2string|FName|nilThe short name of the object itself
3EObjectFlagsAny flags that the object cannot have. Uses | as a seperator
4EObjectFlagsAny flags that the object must have. Uses | as a seperator
6boolWhether to require an exact match with the UClass parameter

Return Value

#TypeSub TypeInformation
1tableUObjectThe derivative of the UObject

Example

local Object = FindObjects(4, "SceneComponent", "TransformComponent0", EObjectFlags.RF_NoFlags, EObjectFlags.RF_ClassDefaultObject, true)

for _, Object in pairs(Objects) do
    -- Do something with Object
end

StaticFindObject

The StaticFindObject function is used to find any object that inherits from UObject that currently exists in memory.

This function is the recommended way of retrieving non-instance objects such as objects of type UClass or UFunction.

Parameters (overload #1)

#TypeInformation
1stringFull name of the object to find, without the type prefix

Parameters (overload #2)

The parameters for this overload mimics the StaticFindObject function from UE4.
For more information see: Unreal Engine API -> StaticFindObject

#TypeInformation
1UClassThe class of the object to find, can be nil.
2UObjectThe outer to look inside. All packages are searched if nil.
3stringName of the object to find
4boolWhether to require an exact match with the UClass parameter

Return Value (overload #1 & #2)

#TypeInformation
1UObject, UClass, or AActorObject is only valid if an instance was found

Example (overload #1)

local CharacterInstance = StaticFindObject("/Script/Engine.Character")
if not CharacterInstance:IsValid() then
    print("No instance of class 'Character' was found.")
end

FindFirstOf

The FindFirstOf function will find the first non-default instance of the supplied class name.

This function cannot be used to find non-instances or default instances.

Parameters

#TypeInformation
1stringShort name of the class to find an instance of

Return Value

#TypeInformation
1UObject, UClass, or AActorObject is only valid if an instance was found

Example

local CharacterInstance = FindFirstOf("Character")
if not CharacterInstance:IsValid() then
    print("No instance of class 'Character' was found.")
end

FindAllOf

The FindAllOf function will find all non-default instances of the supplied class name.

This function cannot be used to find non-instances or default instances.

Parameters

#TypeInformation
1stringShort name of the class to find instances of

Return Value

#TypeSub TypeInformation
1nil or tableUObject, UClass, or AActornil if no instances were found, otherwise a numerically indexed table of all instances

Example

Outputs the name of all objects that inherit from the Actor class.

local ActorInstances = FindAllOf("Actor")
if not ActorInstances then
    print("No instances of 'Actor' were found\n")
else
    for Index, ActorInstance in pairs(ActorInstances) do
        print(string.format("[%d] %s\n", Index, ActorInstance:GetFullName()))
    end
end

StaticConstructObject

The StaticConstructObject function attempts to construct a UE4 object of some type.

This function mimics the function StaticConstructObject_Internal.

Parameters (overload #1)

#TypeInformation
1UClassThe class of the object to construct
2UObjectThe outer to construct the object inside
3FNameOptional
4integerOptional, 64 bit integer
5integerOptional, 64 bit integer
6boolOptional
7boolOptional
8UObjectOptional
9integerOptional, 64 bit integer
10integerOptional, 64 bit integer
11integerOptional, 64 bit integer

Parameters (overload #2)

#TypeInformation
1UClassThe class of the object to construct
2UObjectThe outer to construct the object inside
3integerOptional, 64 bit integer representation (ComparisonIndex & Number) of an FName
4integerOptional, 64 bit integer
5integerOptional, 64 bit integer
6boolOptional
7boolOptional
8UObjectOptional
9integerOptional, 64 bit integer
10integerOptional, 64 bit integer
11integerOptional, 64 bit integer

Return Value

#TypeInformation
1UObjectObject is only valid if an object was successfully constructed

Example

This example constructs a UConsole object.

local Engine = FindFirstOf("Engine")
local ConsoleClass = Engine.ConsoleClass
local GameViewport = Engine.GameViewport

if not ConsoleClass:IsValid() or not GameViewport:IsValid() then
    print("Was unable to construct UConsole because the console class didn't exist\n")
else
    local CreatedConsole = StaticConstructObject(ConsoleClass, GameViewport, 0, 0, 0, nil, false, false, nil)

    if CreatedConsole:IsValid() then
        print(string.format("CreatedConsole: %s\n", CreatedConsole:GetFullName()))
    else
        print("Was unable to construct UConsole\n")
    end
end

ForEachUObject

The ForEachUObject function iterates every UObject that currently exists in GUObjectArray.

The GUObjectArray UE4 variable is a large chunked array that contains UObjects.

The structure of this array has changed over the years and the ForEachUObject function is designed to work identically across all engine versions.

Parameters

#TypeInformation
1functionCallback to execute for every UObject in GUObjectArray

Callback Parameters

#TypeInformation
1UObjectThe UObject
2integerThe chunk index of the UObject
3integerThe object index of the UObject

Example

-- Warning: This will take quite a while to finish executing due to all of the 'print' calls
ForEachUObject(function(Object, ChunkIndex, ObjectIndex)
    print(string.format("Chunk: %X | Object: %X | Name: %s\n", ChunkIndex, ObjectIndex, Object:GetFullName()))
end)

NotifyOnNewObject

The NotifyOnNewObject function executes a callback whenever an instance of the supplied class is constructed via StaticConstructObject_Internal by UE4.

Inheritance is taken into account, so if you provide "/Script/Engine.Actor" as the class then it will execute the callback when any object is constructed that's either an AActor or is derived from AActor.

The callback can return true to remove (unregister) that specific NotifyOnNewObject callback.

The provided class must exist before this calling this function.

Parameters

#TypeInformation
1stringFull name of the class to get instance construction notifications for, without the type prefix
2functionThe callback to execute when an instance of the supplied class is constructed

Callback Parameters

#TypeInformation
1UObjectThe constructed object

Callback Return Value

#TypeInformation
1booleanIf true, remove the current callback from listening to any more notifications

Example

NotifyOnNewObject("/Script/Engine.World", function(ConstructedObject)
    print(string.format("World Constructed: %s\n", ConstructedObject:GetFullName()))
end)

NotifyOnNewObject("/Script/Engine.Actor", function(ConstructedObject)
    print(string.format("Actor Constructed: %s\n", ConstructedObject:GetFullName()))

    -- Unregister this callback after the first actor is found,
    -- meaning this current function will only be called exactly once
    return true
end)

What NOT to do

Please don't duplicate the NotifyOnNewObject call for the same class multiple times, as it could cause performance issues if multiple mods are doing it (which has been seen in the wild).

For example, this:

NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.bShowMouseCursor = true
end)
NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.bForceFeedbackEnabled = false
end)
NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.InputYawScale = 2.5
end)
NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.InputPitchScale = -2.5
end)
NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.InputRollScale = 1.0
end)

should just be this:

NotifyOnNewObject("/Script/Engine.PlayerController", function(PlayerController)
    PlayerController.bShowMouseCursor = true
    PlayerController.bForceFeedbackEnabled = false
    PlayerController.InputYawScale = 2.5
    PlayerController.InputPitchScale = -2.5
    PlayerController.InputRollScale = 1.0
end)

ExecuteWithDelay

The ExecuteWithDelay function asynchronously executes the supplied callback after the supplied delay is over.

Parameters

#TypeInformation
1integerDelay, in milliseconds, to wait before executing the supplied callback
2functionThe callback to execute after the supplied delay is over

Example

ExecuteWithDelay(2000, function()
    print("Executed asynchronously after a 2 second delay\n")
end)

ExecuteInGameThread

ExecuteInGameThread is a function that allows you to execute code using ProcessEvent.

It will execute as soon as the game has time to execute it.

Parameters

#TypeInformation
1functionCallback to execute when the game has time

Example

ExecuteInGameThread(function()
    print("Hello from the game thread!\n")
end)

ExecuteAsync

The ExecuteAsync function asynchronously executes the supplied callback.

It works in a similar manner to ExecuteWithDelay, except that there is no delay beyond the cost of registering the callback.

Parameters

#TypeInformation
1functionThe callback to execute

Example

ExecuteAsync(function()
    print("Executed asynchronously\n")
end)

LoopAsync

Starts a loop that sleeps for the supplied number of milliseconds and stops when the callback returns true.

Parameters

#TypeInformation
1integerThe number of milliseconds to sleep
2functionThe callback function

Example

LoopAsync(1000, function()
    print("Hello World!")
    return false -- Loops forever
end)

LoadAsset

The LoadAsset function loads an asset by name.

It must only be called from within the game thread. For example, from within a UFunction hook or RegisterConsoleCommandHandler callback.

Parameters

#TypeInformation
1stringPath and name of the asset

Example

RegisterConsoleCommandHandler("summon", function(FullCommand, Parameters)
    if #Parameters < 1 then return false end
    
    -- Parameters[1] example: /Game/LevelElements/Refinery/Pipeline/BP_Pipeline_Start
    LoadAsset(Parameters[1])

    return false
end)

RegisterKeyBind

The RegisterKeyBind function is used to bind a key on the keyboard to a Lua function.

Callbacks registered with this function are only executed when either the game or the debug console is in focus.

Parameters (overload #1)

#TypeSub TypeInformation
1tableKeyKey to bind
2functionCallback to execute when the key is hit on the keyboard

Parameters (overload #2)

#TypeSub TypeInformation
1integerKey to bind, use the 'Key' table
2tableModifierKeysModifier keys required alongside the 'Key' parameter
3functionCallback to execute when the key is hit on the keyboard

Example (overload #1)

RegisterKeyBind(Key.O, function()
    print("Key 'O' hit.\n")
end)

Example (overload #2)

RegisterKeyBind(Key.O, {ModifierKey.CONTROL, ModifierKey.ALT}, function()
    print("Key 'CTRL + ALT + O' hit.\n")
end)

Advanced Example (overload #1)

This registers a key bind with a callback that does nothing unless there are no widgets currently open

local AnyWidgetsOpen = false

RegisterHook("/Script/UMG.UserWidget:Construct", function()
    AnyWidgetsOpen = true
end)

RegisterHook("/Script/UMG.UserWidget:Destruct", function()
    AnyWidgetsOpen = false
end)

RegisterKeyBind(Key.B, function()
    if AnyWidgetsOpen then return end
    print("Key 'B' hit, while no widgets are open.\n")
end)

IsKeyBindRegistered

The IsKeyBindRegistered checks if, at the time of the invocation, the supplied keys have been registered

Parameters (overload #1)

#TypeSub TypeInformation
1integerKeyKey to check

Parameters (overload #2)

#TypeSub TypeInformation
1integerKeyKey to bind, use the 'Key' table
2tableModifierKeysModifier keys to check alongside the 'Key' parameter

RegisterHook

The RegisterHook registers a callback for a UFunction

Callbacks are triggered when a UFunction is executed.

The callback params are: UObject self, UFunctionParams...

Returns two ids, both of which must be passed to UnregisterHook if you want to unregister the hook.

Any UFunction that you attempt to register with RegisterHook must already exist in memory when you register it.

RegisterHook doesn't support delegate functions!

RegisterHook Parameters

#TypeInformation
1stringFull name of the UFunction to hook. Type prefix has no effect.
2functionIf UFunction path starts with /Script/: Callback to execute before the UFunction is executed.
Otherwise: Callback to execute after the UFunction is executed.
3function(optional)
If UFunction path starts with /Script/: Callback to execute after the UFunction is executed
Otherwise: Param does nothing.

RegisterHook Return Values

#TypeInformation
1integerThe PreId of the hook
2integerThe PostId of the hook

Callback Parameters

#TypeInformation
1RemoteUnrealParamObject representation of the "this"-pointer ("self" in lua) of the function, also known as "Context". It contains the object wrapped as RemoteUnrealParam that called the function.
2..NRemoteUnrealParam...All function parameters wrapped as RemoteUnrealParam

Callback Return Values

#TypeInformation
1anyAn attempt will be made to automatically convert this value to a UE value, and the value will override the original function return value.
A value of nil (or no return statement) will leave the original return value unchanged.

Example

PreId, PostId = RegisterHook("/Script/Engine.PlayerController:ClientRestart", function(Context, NewPawn)
    local PlayerController = Context:get()
    local Pawn = NewPawn:get()

    print(string.format("PlayerController FullName: %s\n", PlayerController:GetFullName()))
    if Pawn:IsValid() then
        print(string.format("NewPawn FullName: %s\n", Pawn:GetFullName()))
    end

    if PreId then
        -- Unhook once the function has been called
        UnregisterHook("/Script/Engine.PlayerController:ClientRestart", PreId, PostId)
    end
end)

UnregisterHook

The UnregisterHook unregisters a callback for a UFunction.

Parameters

#TypeInformation
1stringFull name of the UFunction to hook. Type prefix has no effect.
2integerThe PreId of the hook
3integerThe PostId of the hook

Example

local preId, postId = RegisterHook("/Script/Engine.PlayerController:ClientRestart", function()
    print("PlayerController restarted\n")
end)

UnregisterHook("/Script/Engine.PlayerController:ClientRestart", preId, postId)

RegisterCustomProperty

The RegisterCustomProperty function is used to register custom properties for use just as if it were a reflected native or BP property.

This is an advanced function that's used to add support for non-reflected properties in the __index metamethod in multiple metatables.

Parameters

#TypeSub TypeInformation
1tableCustomPropertyInfoA table containing all of the required information for registering a custom property

Example

Registers a custom property with the name MySimpleCustomProperty that accesses whatever data is at offset 0xF40 in any instance of class Character as if it was an IntProperty.

It then grabs that value of the first instance of the class Character as an example of how the system works.

RegisterCustomProperty({
    ["Name"] = "MySimpleCustomProperty",
    ["Type"] = PropertyTypes.IntProperty,
    ["BelongsToClass"] = "/Script/Engine.Character"
    ["OffsetInternal"] = 0xF40
})

local CharacterInstance = FindFirstOf("Character")
if CharacterInstance:IsValid() then
    local MySimplePropertyValue = CharacterInstance.MySimpleCustomProperty
end

RegisterCustomEvent

This registers a callback that will get called when a blueprint function or event is called with the name EventName.

Parameters

#TypeInformation
1stringName of the event to hook.
2functionThe callback to call when the event is called.

Example

RegisterCustomEvent("MyCustomEvent", function()
    print("MyCustomEvent was called\n")
end)

RegisterInitGameStatePreHook

This registers a callback that will get called before AGameModeBase::InitGameState is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1AGameStateBaseThe game state context

Example

RegisterInitGameStatePreHook(function(GameState)
    print("InitGameStatePreHook")
end)

RegisterInitGameStatePostHook

This registers a callback that will get called after AGameModeBase::InitGameState is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1AGameStateBaseThe game state context

Example

RegisterInitGameStatePostHook(function(GameState)
    print("InitGameStatePostHook")
end)

RegisterBeginPlayPreHook

This registers a callback that will get called before AActor::BeginPlay is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1AActorThe actor context

Example

RegisterBeginPlayPreHook(function(Actor)
    print("BeginPlayPreHook")
end)

RegisterBeginPlayPostHook

This registers a callback that will get called after AActor::BeginPlay is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1AActorThe actor context

Example

RegisterBeginPlayPostHook(function(Actor)
    print("BeginPlayPostHook")
end)

RegisterProcessConsoleExecPreHook

This registers a callback that will get called before UObject::ProcessConsoleExec is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

If the callback returns nothing (or nil), the original return value of ProcessConsoleExec will be used.

If the callback returns true or false, the supplied value will override the original return value of ProcessConsoleExec.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1UObjectThe object context
2stringFull command string
3tableAll command parts, including the command name
4FOutputDeviceThe AR
5UObjectThe executor

Callback Return Value

#TypeInformation
1boolWhether to override the original return value of ProcessConsoleExec

Example

RegisterProcessConsoleExecPreHook(function(Context, FullCommand, CommandParts, Ar, Executor)
    print("RegisterProcessConsoleExecPreHook:\n")
    local ContextObject = Context:get()
    local ExecutorObject = Executor:get()

    print(string.format("Context FullName: %s\n", ContextObject:GetFullName()))
    if ExecutorObject:IsValid() then
        print(string.format("Executor FullName: %s\n", ExecutorObject:GetFullName()))
    end
    print(string.format("Command: %s\n", FullCommand))
    print(string.format("Number of command parts: %i\n", #CommandParts))
    
    if #CommandParts > 0 then
        print(string.format("Command Name: %s\n", CommandParts[1]))
        for PartNumber, CommandPart in ipairs(CommandParts) do
            print(string.format("CommandPart: #%i -> '%s'\n", PartNumber, CommandPart))
        end
    end

    Ar:Log("Write something to game console")

    return true
end)

-- Entered into console: CommandExample param1 "param 2" 3
-- Output
--[[
RegisterProcessConsoleExecPreHook:
Context FullName: GameState /Game/Maps/Map_MainMenu.Map_MainMenu:PersistentLevel.GameState_2147479851
Executor FullName: BP_MainMenuPawn_C /Game/Maps/Map_MainMenu.Map_MainMenu:PersistentLevel.BP_MainMenuPawn_C_2147479061
Command: CommandExample param1 "param 2" 3
Number of command parts: 4
Command Name: CommandExample
CommandPart: #1 -> 'CommandExample'
CommandPart: #2 -> 'param1'
CommandPart: #3 -> 'param 2'
CommandPart: #4 -> '3'
--]]

RegisterProcessConsoleExecPostHook

This registers a callback that will get called after UObject::ProcessConsoleExec is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

If the callback returns nothing (or nil), the original return value of ProcessConsoleExec will be used.

If the callback returns true or false, the supplied value will override the original return value of ProcessConsoleExec.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1UObjectThe object context
2stringFull command string
3tableAll command parts, including the command name
4FOutputDeviceThe AR
5UObjectThe executor

Callback Return Value

#TypeInformation
1boolWhether to override the original return value of ProcessConsoleExec

Example

RegisterProcessConsoleExecPostHook(function(Context, FullCommand, CommandParts, Ar, Executor)
    print("RegisterProcessConsoleExecPostHook:\n")
    local ContextObject = Context:get()
    local ExecutorObject = Executor:get()

    print(string.format("Context FullName: %s\n", ContextObject:GetFullName()))
    if ExecutorObject:IsValid() then
        print(string.format("Executor FullName: %s\n", ExecutorObject:GetFullName()))
    end
    print(string.format("Command: %s\n", FullCommand))
    print(string.format("Number of command parts: %i\n", #CommandParts))
    
    if #CommandParts > 0 then
        print(string.format("Command Name: %s\n", CommandParts[1]))
        for PartNumber, CommandPart in ipairs(CommandParts) do
            print(string.format("CommandPart: #%i -> '%s'\n", PartNumber, CommandPart))
        end
    end

    Ar:Log("Write something to game console")

    return true
end)

-- Entered into console: CommandExample param1 "param 2" 3
-- Output
--[[
RegisterProcessConsoleExecPostHook:
Context FullName: GameState /Game/Maps/Map_MainMenu.Map_MainMenu:PersistentLevel.GameState_2147479851
Executor FullName: BP_MainMenuPawn_C /Game/Maps/Map_MainMenu.Map_MainMenu:PersistentLevel.BP_MainMenuPawn_C_2147479061
Command: CommandExample param1 "param 2" 3
Number of command parts: 4
Command Name: CommandExample
CommandPart: #1 -> 'CommandExample'
CommandPart: #2 -> 'param1'
CommandPart: #3 -> 'param 2'
CommandPart: #4 -> '3'
--]]

RegisterCallFunctionByNameWithArgumentsPreHook

This registers a callback that will get called before UObject::CallFunctionByNameWithArguments is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

If the callback returns nothing (or nil), the original return value of CallFunctionByNameWithArguments will be used.

If the callback returns true or false, the supplied value will override the original return value of CallFunctionByNameWithArguments.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1UObjectThe object context
2stringThe string
3FOutputDeviceThe AR
4UObjectThe executor
5boolThe bForceCallWithNonExec value

Callback Return Value

#TypeInformation
1boolWhether to override the original return value of CallFunctionByNameWithArguments

Example

local function MyCallback(Context, Str, Ar, Executor, bForceCallWithNonExec)
    -- Do something with the parameters
    -- Return nil to use the original return value of CallFunctionByNameWithArguments
    -- Return true or false to override the original return value of CallFunctionByNameWithArguments

    return nil
end

RegisterCallFunctionByNameWithArgumentsPreHook(MyCallback)

RegisterCallFunctionByNameWithArgumentsPostHook

This registers a callback that will get called after UObject::CallFunctionByNameWithArguments is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

If the callback returns nothing (or nil), the original return value of CallFunctionByNameWithArguments will be used.

If the callback returns true or false, the supplied value will override the original return value of CallFunctionByNameWithArguments.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1UObjectThe object context
2stringThe string
3FOutputDeviceThe AR
4UObjectThe executor
5boolThe bForceCallWithNonExec value

Callback Return Value

#TypeInformation
1boolWhether to override the original return value of CallFunctionByNameWithArguments

Example

local function MyCallback(Context, Str, Ar, Executor, bForceCallWithNonExec)
    -- Do something with the parameters
    -- Return nil to use the original return value of CallFunctionByNameWithArguments
    -- Return true or false to override the original return value of CallFunctionByNameWithArguments

    return nil
end

RegisterCallFunctionByNameWithArgumentsPreHook(MyCallback)

RegisterULocalPlayerExecPreHook

This registers a callback that will get called before ULocalPlayer::Exec is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

The callback can have two return values.

  • If the first return value is nothing (or nil), the original return value of Exec will be used.
  • If the first return value is true or false, the supplied value will override the original return value of Exec.
  • The second return value controls whether the original Exec will execute.
  • If the second return value is nil or true, the orginal Exec will execute.
  • If the second return value is false, the original Exec will not execute.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1ULocalPlayerThe local player context
2UWorldThe world
3stringThe command
4FOutputDeviceThe AR

Callback Return Values

#TypeInformation
1boolWhether to override the original return value of Exec
2boolWhether to execute the original Exec

Example

local function MyCallback(Context, InWorld, Command, Ar)
    -- Do something with the parameters
    -- Return true or false to override the original return value of Exec
    -- Return false to prevent the original Exec from executing

    return nil, true
end

RegisterULocalPlayerExecPreHook(MyCallback)

RegisterULocalPlayerExecPostHook

This registers a callback that will get called after ULocalPlayer::Exec is called.

Parameters (except strings & bools & FOutputDevice) must be retrieved via Param:Get() and set via Param:Set().

The callback can have two return values.

  • If the first return value is nothing (or nil), the original return value of Exec will be used.
  • If the first return value is true or false, the supplied value will override the original return value of Exec.
  • The second return value controls whether the original Exec will execute.
  • If the second return value is nil or true, the orginal Exec will execute.
  • If the second return value is false, the original Exec will not execute.

Parameters

#TypeInformation
1functionThe callback to register

Callback Parameters

#TypeInformation
1ULocalPlayerThe local player context
2UWorldThe world
3stringThe command
4FOutputDeviceThe AR

Callback Return Values

#TypeInformation
1boolWhether to override the original return value of Exec
2boolWhether to execute the original Exec

Example

local function MyCallback(Context, InWorld, Command, Ar)
    -- Do something with the parameters
    -- Return true or false to override the original return value of Exec
    -- Return false to prevent the original Exec from executing

    return nil, true
end

RegisterULocalPlayerExecPostHook(MyCallback)

RegisterConsoleCommandHandler

The RegisterConsoleCommandHandler function executes the provided Lua function whenever the supplied custom command is entered into the UE console.

Parameters

#TypeInformation
1stringThe name of the custom command
2functionThe callback to execute when the custom command is entered into the UE console

Callback Parameters

#TypeInformation
1stringFull command string
2tableTable with parameters (without the command name)
3FOutputDeviceThe output device to write to

Callback Return Value

#TypeInformation
1boolWhether to prevent other handlers from handling this command

Example

RegisterConsoleCommandHandler("CommandExample", function(FullCommand, Parameters, OutputDevice)
    print("RegisterConsoleCommandHandler:\n")

    print(string.format("Command: %s\n", FullCommand))
    print(string.format("Number of parameters: %i\n", #Parameters))
    
    for ParameterNumber, Parameter in ipairs(Parameters) do
        print(string.format("Parameter #%i -> '%s'\n", ParameterNumber, Parameter))
    end

    OutputDevice:Log("Write something to game console")

    return false
end)

-- Entered into console: CommandExample param1 "param 2" 3
-- Output
--[[
RegisterConsoleCommandHandler:
Command: CommandExample param1 "param 2" 3
Number of parameters: 3
Parameter #1 -> 'param1'
Parameter #2 -> 'param 2'
Parameter #3 -> '3'
--]]

RegisterConsoleCommandGlobalHandler

The RegisterConsoleCommandGlobalHandler function executes the provided Lua function whenever the supplied custom command is entered into the UE console.

Unlike RegisterConsoleCommandHandler, this global variant runs the callback for all contexts.

Parameters

#TypeInformation
1stringThe name of the custom command
2functionThe callback to execute when the custom command is entered into the UE console

Callback Parameters

#TypeInformation
1stringFull command string
2tableTable with parameters (without the command name)
3FOutputDeviceThe output device to write to

Callback Return Value

#TypeInformation
1boolWhether to prevent other handlers from handling this command

Example

RegisterConsoleCommandGlobalHandler("CommandExample", function(FullCommand, Parameters, OutputDevice)
    print("RegisterConsoleCommandGlobalHandler:\n")

    print(string.format("Command: %s\n", FullCommand))
    print(string.format("Number of parameters: %i\n", #Parameters))
    
    for ParameterNumber, Parameter in ipairs(Parameters) do
        print(string.format("Parameter #%i -> '%s'\n", ParameterNumber, Parameter))
    end

    OutputDevice:Log("Write something to game console")

    return false
end)

-- Entered into console: CommandExample param1 "param 2" 3
-- Output
--[[
RegisterConsoleCommandGlobalHandler:
Command: CommandExample param1 "param 2" 3
Number of parameters: 3
Parameter #1 -> 'param1'
Parameter #2 -> 'param 2'
Parameter #3 -> '3'
--]]

DumpAllObjects

Dumps all objects from memory to the file UE4SS_ObjectDump.txt.

The function does the same as the Dump Objects & Properties button in the UE4SS Debugging Tools aka. the GUI Console.

Example

RegisterKeyBind(Key.F1, function()
    DumpAllObjects()
end)

GenerateSDK

Generates C++ Headers in the CXXHeaderDump directory.

The function does the same as the Dump CXX Headers button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    GenerateSDK()
end)

GenerateLuaTypes

Generates Lua types for Custom Lua Bindings in the Mods/shared/types directory.

The function does the same as the Generate Lua Types button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    GenerateLuaTypes()
end)

GenerateUHTCompatibleHeaders

Generates Unreal Header Tool Headers in the UHTHeaderDump directory.

The function does the same as the Generate UHT Compatible Headers button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    GenerateUHTCompatibleHeaders()
end)

DumpStaticMeshes

Dumps all static actor meshes from memory to the file (timestamp here)-ue4ss_static_mesh_data.csv.

The function does the same as the Dump all static actor meshes to file button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    DumpStaticMeshes()
end)

DumpAllActors

Dumps all actors from memory to the file (timestamp here)-ue4ss_actor_data.csv.

The function does the same as the Dump all actors to file button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    DumpAllActors()
end)

DumpUSMAP

Generates an Unreal Mapping file Mappings.usmap.

The function does the same as the Generate .usmap file UnrealMappingsDumper by OutTheShade button in the UE4SS Debugging Tools aka. the GUI Console under the Dumpers tab.

Example

RegisterKeyBind(Key.F1, function()
    DumpUSMAP()
end)

Examples

  • Check the code snippets at the bottom of the individual pages in the Lua API section and tutorials in this repository.
  • Hogwarts Legacy modding uses UE4SS' Lua API for its primary logic mods. This website contains example code for some of the mods.
    • You can search for interesting code in that page, the collapsed sections of the webpage will auto-expand when the text is found.
  • The Palworld modding wiki has some decent docs and examples on how to develop Lua mods for new learners.
  • Search GitHub for any Lua code calling reasonably uniquely-named UE4SS API functions, excluding the actual UE4SS repository from the search:
    • https://github.com/search?q=language%3ALua+StaticFindObject+NOT+repo%3AUE4SS-RE%2FRE-UE4SS&type=code
    • https://github.com/search?q=language%3ALua+FindFirstOf+NOT+repo%3AUE4SS-RE%2FRE-UE4SS&type=code
    • and so on, as long as language:Lua is specified in the query

Creating a Lua mod

Before you start

To create a Lua mod in UE4SS, you should first:

  • know how to install UE4SS in your target game and make sure it is running OK;
  • be able to write basic Lua code (see the official book Programming in Lua and its later editions, or any other recommended tutorial online);
  • have an understanding of the object model of the Unreal Engine and the basics of game modding.

How does a minimal Lua mod look like

A Lua mod in UE4SS is a set of Lua scripts placed in a folder inside the Mods/ folder of UE4SS installation. Let's call it MyLuaMod for the purpose of this example.

In order to be loaded and executed:

  1. The mod folder must have a scripts subfolder and a main.lua file inside, so it looks like:
Mods\
    ...
    MyLuaMod\
        scripts\
            main.lua
    ...
  1. The Mods\MyLuaMod\scripts\main.lua file has some Lua code inside it, e.g.:
print("[MyLuaMod] Mod loaded\n")
  1. The mod must be added and enabled in Mods\mods.txt with a new line containing the name of your mod folder (name of your mod) and 1 for enabling or 0 for disabling the mod:
...
MyLuaMod : 1
...

Your custom functionality goes inside main.lua, from which you can include other Lua files if needed, including creating your own Lua modules or importing various libraries.

What can you do in a Lua mod

The API provided by UE4SS and available to you in Lua is documented in sub-sections of chapter "Lua API" here. Using those functions and classes, you find and manipulate the instances of Unreal Engine objects in memory, creating new objects or modifying existing ones, calling their methods and accessing their fields.

Basically, you are doing the exact same thing that an Unreal Engine game developer does in their code, but using UE4SS to locate the necessary objects and guessing a bit, while the developers already knew where and what they are (because they have their source code).

Creating simple data types

If you need to create an object of a structure-like class, e.g. FVector, in order to pass it into a Unreal Engine function, UE4SS allows you to pass a Lua table with the fields of the class like {X=1.0, Y=2.0, Z=3.0} instead.

Using Lua C libraries

If you ever need to load Lua C libraries, that have native code (i.e. with DLLs on Windows), you can place these DLLs directly inside the same \scripts\ folder.

Setting up a Lua mod development environment

It is much easier to write mods if your code editor or IDE is properly configured for Lua development and knows about UE4SS API.

  1. Configure your code editor/IDE to support Lua syntax highlighting and code completion. If you use VSCode, see here in Using Custom Lua Bindings.

  2. Make sure that your build of UE4SS contains Mods\shared\Types.lua (a development build from Github releases contains it). This will load the UE4SS API definitions in your IDE.

  3. (Optional) Dump the Lua Bindings fromm UE4SS Gui console, and follow the recommendations to load them here.

Then open the Mods/ folder of your UE4SS installation in your IDE, and create or modify your mod inside it.

Applying code changes

The main benefit of developing Lua mods is that you can quickly edit Lua sources without recompiling/rebuilding the C++ mod library as is always the case with C++ mods, and retry without restarting the game.

You can either:

  • reload all mods from the UE4SS GUI Console with the "Restart All Mods" button on the "Console" tab, or,
  • enable "Hot reload" in UE4SS-settings.ini and use the assigned hotkey (Ctrl+R by default) to do the same.

Your first mod

In the main.lua file of your mod, write some code that will try to access the objects of Unreal Engine inside your target game and do something that you can observe in the UE4SS console.

You can start by trying just

print("[MyLuaMod] Mod loaded\n")

and once you have verified that it runs OK, you can start implementing some actual functionality.

The example code below is fairly generic and should work for many games supported by UE4SS.

It registers a hotkey Ctrl+F1 and when pressed, it reads the player coordinates and calculates how far the player has moved since the last time the hotkey was pressed.

Note that the logging print calls include the name of the mod in square brackets, as it helps you find your mod's output among other log strings in the console.

The player coordinates are retrieved in the following way:

  1. Gets the player controller using UE4SS UEHelpers class.
  2. Get the Pawn, which represents the actual "physical" entity that the player can control in Unreal Engine.
  3. Call the appropriate Unreal Engine method K2_GetActorLocation that returns a Pawn's location (by accessing its parent Actor class).
  4. The location is a 3-component vector of Unreal Engine type FVector, having X, Y and Z as its fields.
local UEHelpers = require("UEHelpers")

print("[MyLuaMod] Mod loaded\n")

local lastLocation = nil

function ReadPlayerLocation()
    local FirstPlayerController = UEHelpers:GetPlayerController()
    local Pawn = FirstPlayerController.Pawn
    local Location = Pawn:K2_GetActorLocation()
    print(string.format("[MyLuaMod] Player location: {X=%.3f, Y=%.3f, Z=%.3f}\n", Location.X, Location.Y, Location.Z))
    if lastLocation then
        print(string.format("[MyLuaMod] Player moved: {delta_X=%.3f, delta_Y=%.3f, delta_Z=%.3f}\n",
            Location.X - lastLocation.X,
            Location.Y - lastLocation.Y,
            Location.Z - lastLocation.Z)
        )
    end
    lastLocation = Location
end

RegisterKeyBind(Key.F1, { ModifierKey.CONTROL }, function()
    print("[MyLuaMod] Key pressed\n")
    ExecuteInGameThread(function()
        ReadPlayerLocation()
    end)
end)

When you load the game until you can move the character, press the hotkey, move the player, press it again, the mod will generate a following output or something very similar:

...
[2024-01-09 19:37:27] Starting Lua mod 'MyLuaMod'
[2024-01-09 19:37:27] [Lua] [MyLuaMod] Mod loaded
...
[2024-01-09 19:37:32] [Lua] [MyLuaMod] Key pressed
[2024-01-09 19:37:32] [Lua] [MyLuaMod] Player location: {X=-63.133, Y=4.372, Z=90.000}
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Key pressed
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Player location: {X=788.232, Y=-639.627, Z=90.000}
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Player moved: {delta_X=851.364, delta_Y=-643.999, delta_Z=0.000}
...

Using Custom Lua Bindings

To make development of Lua mods easier, we've added the ability to dump custom Lua bindings from your game. We also have a shared types file that contains default UE types and the API functions/classes/objects that are available to you.

Dumping Custom Lua Bindings

Simply open the Dumpers tab in the GUI console window and hit the "Dump Lua Bindings" button.

The generator will place the files into the Mods/shared/types folder.

Warning: Do not include any of the generated files in your Lua scripts. If they are included, any globals set by UE4SS will be overridden and things will break.

To Use Bindings

I recommend using Visual Studio Code to do your Lua development. You can install the extension just called "Lua" by sumneko.

Open the Mods folder as a workspace. You can also save this workspace so you don't have to do this every time you open VS Code.

When developing your Lua mods, the language server should automatically parse all the types files and give you intellisense.

Warning: For many games the number of types is so large that the language server will fail to parse everything. In this case, you can add a file called .luarc.json into the root of your workspace and add the following:

{
    "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
    "workspace.maxPreload": 50000,
    "workspace.preloadFileSize": 5000
}

How to use your mod's directory as workspace

As alternative you can open just your mod's root directory as workspace.
In this case you need to add a .luarc.json with "workspace.library" entries containing a path to the "shared" folder and the "Scripts" directory of your mod.
Both paths can be relative.
Example .luarc.json:

{
    "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
    "workspace.maxPreload": 50000,
    "workspace.preloadFileSize": 5000,
    "workspace.library": ["../shared", "Scripts"]
}

Annotating

To get context sensitive information about the custom game types, you need to annotate your code (alternative documentation). This is done by adding a comment above the function/class/object that you want to annotate.

Example

---@class ITM_MisSel_Biome_C
local biome = FindFirstOf("ITM_MisSel_Biome_C")

---@type int
local numMissions = biome.NumMissions

---@type FVector
local soundCoords = { 420.5, 69.0, 3.1 }
biome:SetSoundCoordinate(soundCoords)

C++ API

These are the C++ API functions available in UE4SS, on top of the standard libraries that C++ comes with by default and the reflected functions available in Unreal Engine.

You are expected to have a basic understanding of C++ and Unreal Engine's C++ API before using these functions.

You may need to read code in the UEPsuedo repository (more specifically, the include/Unreal directory) to understand how to use these functions.

For version: 3.1.0

Current status: incomplete

Blueprint Macros

The following macros are used to manipulate blueprint functions from C++.

Note: Param names for wrappers must be identical to the names used for the function in UE, and they should then be passed to macros with a PropertyName param as shown in AActor.cpp.

This does not apply to macros with the _CUSTOM suffix.

With those macros you have to supply both the UE property name as well as the name of your C++ param.

These _CUSTOM suffixed macros are useful when the UE property name contains spaces or other characters that aren't valid for a C++ variable.

Regular macros:

Intended for normal use by modders.

UE_BEGIN_SCRIPT_FUNCTION_BODY:

Finds non-native (meaning BP) UFunction by its full name without the type prefixed, throws if not found.

UE_BEGIN_NATIVE_FUNCTION_BODY:

Same as above except for native, meaning non-BP UFunctions.

See: AActor::K2_DestroyActor

UE_SET_STATIC_SELF:

Used for static functions, and should be the CDO to the class that the UFunction belongs to.

See: UKismetNodeHelperLibrary::GetEnumeratorUserFriendlyName.

UE_COPY_PROPERTY:

Copies the property of the supplied name into the already allocated params struct.

  • Param 1: The name, without quotes, of a property that exists for this UFunction.
  • Param 2: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.

UE_COPY_PROPERTY_CUSTOM:

Copies the property of the supplied name into the already allocated params struct.

  • Param 1: The name, without quotes, of a property that exists for this UFunction.
  • Param 2: A C++ compatible variable name for the property.
  • Param 3: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.

UE_COPY_STRUCT_PROPERTY_BEGIN:

Begins the process of copying an entire struct.

  • Param 1: The name, without quotes, of an FStructProperty that exists for this UFunction.

UE_COPY_STRUCT_PROPERTY_CUSTOM_BEGIN:

Begins the process of copying an entire struct.

  • Param 1: The name, without quotes, of an FStructProperty that exists for this UFunction.
  • Param 2: A C++ compatible variable name for the property.

UE_COPY_STRUCT_INNER_PROPERTY:

Copies a property from within an FStructProperty into the already allocated params struct.

  • Param 1: The name, without quotes, of the FStructProperty supplied to UE_COPY_STRUCT_PROPERTY_BEGIN.
  • Param 2: The name, without quotes, of a property that exists in the supplied FStructProperty.
  • Param 3: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.
  • Param 4: The name of the C++ variable that you're copying.

See: AActor::K2_SetActorRotation

UE_COPY_STRUCT_INNER_PROPERTY_CUSTOM:

  • Param 1: The name, without quotes, of the FStructProperty supplied to UE_COPY_STRUCT_PROPERTY_BEGIN.
  • Param 2: The name, without quotes, of a property that exists in the supplied FStructProperty.
  • Param 3: A C++ compatible variable name for the property.
  • Param 4: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.
  • Param 5: The name of the C++ variable that you're copying.

UE_COPY_OUT_PROPERTY:

Copies the out property of the supplied name from the params struct into the supplied C++ variable.

This means the wrapper param (which is named the same as the property supplied) must be a reference, meaning suffixed with a "&".

  • Param 1: The name, without quotes, of a property that exists for this UFunction.
  • Param 2: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.

See: UGameplayStatics::FindNearestActor

UE_COPY_OUT_PROPERTY_CUSTOM:

Copies the out property of the supplied name from the params struct into the supplied C++ variable.

  • Param 1: The name, without quotes, of a property that exists for this UFunction.
  • Param 2: A C++ compatible variable name for the property.
  • Param 3: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.

This means the wrapper param (which is named the same as the property supplied) must be a reference, meaning suffixed with a "&".

UE_COPY_VECTOR:

Helper for copying an FVector. Must use UE_COPY_STRUCT_PROPERTY_BEGIN first.

  • Param 1: The C++ name, without quotes, of the FVector to copy from.
  • Param 2: The name, without quotes, of the FVector, same as supplied to UE_COPY_STRUCT_PROPERTY_BEGIN.

UE_COPY_STL_VECTOR_AS_TARRAY:

Helper for copying a TArray.

  • Param 1: The name, without quotes, of an FArrayProperty that exists for this UFunction.
  • Param 2: The C++ type, without quotes, that the TArray holds. For example, without quotes, "float", for FFloatProperty.
  • Param 3: The C++ that the contents of the TArray will be copied into.

UE_CALL_FUNCTION:

Performs a non-static function call. All non-out params must be copied ahead of this.

UE_CALL_STATIC_FUNCTION:

Performs a static function call, using the CDO provided by UE_SET_STATIC_SELF as the static instance. All non-out params must be copied ahead of this.

UE_RETURN_PROPERTY:

Copies the underlying value that the UFunction returned and returns it.

  • Param 1: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.

UE_RETURN_PROPERTY_CUSTOM:

  • Param 1: The type that you want the underlying value to be copied as. For example, without quotes, "float" for FFloatProperty.
  • Param 2: The name, without quotes, for the property of this function where the return value will be copied from.

UE_RETURN_VECTOR:

Helper for returning an FVector.

UE_RETURN_STRING:

Helper for returning an FStrProperty. Converts to StringType.

UE_RETURN_STRING_CUSTOM:

Helper for returning an FStrProperty. Converts to StringType.

  • Param 1: The name, without quotes, for the FStrProperty of this function where the return value will be copied from.

WITH_OUTER:

Used for templated C++ types passed to macros, like TArray or TMap.

For example, pass, without quotes, WITH_OUTER(TMap, FName, int) instead of TMap<FName, int> to all macros.

Internal macros

These are only used by other macros, or by users of our C++ API if they properly understand the internals of the macros, and this requires preexisting knowledge around how UFunctions work, and you'll likely have to BPMacros.hpp to understand how to use them properly.

UE_BEGIN_FUNCTION_BODY_INTERNAL:

Throws if the UFunction doesn't exist, and allocates enough space (on the stack when possible, otherwise the heap) for the params and return value(s).

UE_COPY_PROPERTY_INTERNAL:

Finds the property, and throws if not found.

UE_COPY_PROPERTY_CUSTOM_INTERNAL:

Finds the property with the supplied name, and throws if not found.

UE_RETURN_PROPERTY_INTERNAL:

Finds the property to be used for the return value, throws if not found.

C++ Examples

Template repository for making UE4SS C++ mods: UE4SSCPPTemplate - note that if you are developing on latest main branch, you should use the dev branch of this template repo if there are any changes to the template that are not yet merged into main.

Search GitHub for any Lua code calling reasonably uniquely-named UE4SS API functions, excluding the actual UE4SS repository from the search:

https://github.com/search?q=language%3Acpp++%22+%3A+public+CppUserModBase%22+NOT+repo%3AUE4SS-RE%2FRE-UE4SS+NOT+repo%3AEpicGames%2FUnrealEngine&type=code

Creating a C++ mod

This guide will help you create a C++ mod using UE4SS.
It's split up into four parts.
Part one goes over the prerequisites.
Part two goes over creating the most basic C++ mod possible.
Part three will show you how to interact with UE4SS and UE itself (via UE4SS).
Part four will cover installation of the mod.

The guide requires having a working C++ development environment with xmake and git, preferably similar to the one required to build UE4SS itself from sources.

Part 1

Make sure you have downloaded all the build requirements mentioned in the README before following these steps!

  1. Make an Epic account and link it to your GitHub account
  2. Check your email and accept the invitation to the @EpicGames GitHub organization for Unreal source access.
  3. Setup SSH keys on your GitHub account which will let git access the Unreal source you got access for in 2 and 3.
  4. Make a directory somewhere on your computer, the name doesn't matter but I named mine MyMods.
  5. Clone the RE-UE4SS repo so that you end up with MyMods/RE-UE4SS.
  6. Open CMD and cd into RE-UE4SS and execute: git submodule update --init --recursive
  7. Go back to the MyMods directory and create a new directory, this directory will contain your mod source files. I named mine MyAwesomeMod.
  8. Create a file called xmake.lua inside MyMods and put this inside it:
includes("RE-UE4SS")
includes("MyAwesomeMod")

Part #2

  1. Create a file called xmake.lua inside MyMods/MyAwesomeMod and put this inside it:
local projectName = "MyAwesomeMod"

target(projectName)
    add_rules("ue4ss.mod")
    add_includedirs(".")
    add_files("dllmain.cpp")
  1. Make a file called dllmain.cpp in MyMods/MyAwesomeMod and put this inside it:
#include <stdio.h>
#include <Mod/CppUserModBase.hpp>

class MyAwesomeMod : public RC::CppUserModBase
{
public:
    MyAwesomeMod() : CppUserModBase()
    {
        ModName = STR("MyAwesomeMod");
        ModVersion = STR("1.0");
        ModDescription = STR("This is my awesome mod");
        ModAuthors = STR("UE4SS Team");
        // Do not change this unless you want to target a UE4SS version
        // other than the one you're currently building with somehow.
        //ModIntendedSDKVersion = STR("2.6");
        
        printf("MyAwesomeMod says hello\n");
    }

    ~MyAwesomeMod() override
    {
    }

    auto on_update() -> void override
    {
    }
};

#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
    MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
    {
        return new MyAwesomeMod();
    }

    MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
    {
        delete mod;
    }
}
  1. In the command prompt, in the MyMods directory, execute either: A.
xmake f -m "Game__Shipping__Win64"
xmake

or B.

xmake project -k vsxmake2022

If you chose option B, the VS solution will be in the vsxmake2022 directory.

  1. Open MyMods/vsxmake2022/MyMods.sln
  2. Make sure that you're set to the Game___Shipping__Win64 configuration unless you want to debug.
  3. Find your project (in my case: MyAwesomeMod) in the solution explorer and right click it and hit Build.

Part #3

In this part, we're going to learn how to log to file, and both consoles, as well as find a UObject by name, and log that name.

  1. Add #include <DynamicOutput/DynamicOutput.hpp> under #include <Mod/CppUserModBase.hpp>.
    You can now also remove #include <stdio.h> because we'll be removing the use of printf which was the only thing that required it.
  2. To save some time and annoyance and make the code look a bit better, add this line below all the includes:
using namespace RC;
  1. Replace the call to printf in the body of the MyAwesomeMod constructor with:
Output::send<LogLevel::Verbose>(STR("MyAwesomeMod says hello\n"));

It's longer than a call to printf, but in return the message gets propagated to the log file and both the regular console and the GUI console.
We also get some support for colors via the LogLevel enum.

  1. Add this below the DynamicOutput include:
#include <Unreal/UObjectGlobals.hpp>
#include <Unreal/UObject.hpp>
  1. Let's again utilize the using namespace shortcut by adding this below the first one: using namespace RC::Unreal;
  2. Add this function in your mod class:
auto on_unreal_init() -> void override
{
    // You are allowed to use the 'Unreal' namespace in this function and anywhere else after this function has fired.
    auto Object = UObjectGlobals::StaticFindObject<UObject*>(nullptr, nullptr, STR("/Script/CoreUObject.Object"));
    Output::send<LogLevel::Verbose>(STR("Object Name: {}\n"), Object->GetFullName());
}

Note that Output::send doesn't require a LogLevel and that we're using {} in the format string instead of %s.
The Output::send function uses std::format in the back-end so you should do some research around std::format or libfmt if you want to know more about it.

  1. Right click your project and hit Build.

Part #4

Click to go to guide for installing a C++ Mod

Installing a C++ Mod

  1. This part assumes you have UE4SS installed and working for your game already. If not, refer to the installation guide.

  2. After building, you will have the following file:

    • MyAwesomeMod.dll in MyMods\Binaries\<Configuration>\MyAwesomeMod
  3. Navigate over to your game's executable folder and open the Mods folder. Here we'll do a couple things:

    • Create a folder structure in Mods that looks like MyAwesomeMod\dlls.
    • Move MyAwesomeMod.dll inside the dlls folder and rename it to main.dll.

The result should look like:

Mods\
    MyAwesomeMod\
        dlls\
            main.dll
  1. To enable loading of your mod in-game you will have to edit the mods.txt located in the Mods folder. By default it looks something like this:
CheatManagerEnablerMod : 1
ActorDumperMod : 0
ConsoleCommandsMod : 1
ConsoleEnablerMod : 1
SplitScreenMod : 0
LineTraceMod : 1
BPModLoaderMod : 1
jsbLuaProfilerMod : 0



; Built-in keybinds, do not move up!
Keybinds : 1

Here you will want to add the line:

MyAwesomeMod : 1

above the keybinds to enable MyAwesomeMod.

Alternatively, place an empty text file named enabled.txt inside of the MyAwesomeMod folder. This method is not recommended because it does not allow load ordering
and bypasses mods.txt, but may allow for easier installation by end users.

  1. Launch your game and if everything was done correctly, you should see the text "MyAwesomeMod says hello" highlighted in blue somewhere at the top of UE4SS console (before all the scanning occurs), and if you used the on_unreal_init function, you should see "Object Name: /Script/CoreUObject.Object" highlighted in blue as well (right after the scanning finishes).

Automation

Now that you understand how the process works, you can use the UE4SS CPP Template repository that automates the process of creating a mod, building it, and installing it. Be aware that the new_mod_setup.bat script will checkout the commit at the latest release so that you can be sure that your mod is being built with the correct ABI as latest release.

NOTE: Any changes to the build system that affects the mod template is pushed to the dev branch, which is then merged into main when a new UE4SS release is created. This makes sure that the template is always in-sync with the latest UE4SS release.

Creating GUI tabs with a C++ mod

UE4SS already includes the ImGui library to render its console GUI, built from the UE4SS-RE/imgui repo. Refer to ImGui documentation in that repo on how to use ImGui-specific classes and methods for rendering actual buttons and textboxes and other window objects.

This guide will show how you create custom tabs for the GUI with a C++ mod, and the guide will take the form of comments in the code example below:

#include <Mod/CppUserModBase.hpp>
#include <UE4SSProgram.hpp>

class MyAwesomeMod : public RC::CppUserModBase
{
private:
    int m_private_number{33};
    std::shared_ptr<GUI::GUITab> m_less_safe_tab{};

public:
    MyAwesomeMod() : CppUserModBase()
    {
        ModName = STR("MyAwesomeMod");
        ModVersion = STR("1.0");
        ModDescription = STR("This is my awesome mod");
        ModAuthors = STR("UE4SS Team");
        
        // The 'register_tab' function will tell UE4SS to render a tab.
        // Tabs registered this way will be automatically cleaned up when this C++ mod is destructed.
        // The first param is the display name of your tab.
        // The second param is a callback that UE4SS will use to render the contents of the tab.
        // The param to the callback is a pointer to your mod.
        register_tab(STR("My Test Tab"), [](CppUserModBase* instance) {
            // In this callback, you can start rendering the contents of your tab with ImGui. 
            ImGui::Text("This is the contents of the tab");
            
            // You can access members of your mod class with the 'instance' param.
            auto mod = dynamic_cast<MyAwesomeMod*>(instance);
            if (!mod)
            {
                // Something went wrong that caused the 'instance' to not be correctly set.
                // Let's abort the rest of the function so that you don't access an invalid pointer.
                return;
            }
            
            // You can access both public and private members.
            mod->render_some_stuff(mod->m_private_number);
        });
        
        // The 'UE4SSProgram::add_gui_tab' function is another way to tell UE4SS to render a tab.
        // This way of registering a tab will make you responsible for cleaning up the tab when your mod destructs.
        // Failure to clean up the tab on mod destruction will result in a crash.
        // It's recommended that you use 'register_tab' instead of this function.
        m_less_safe_tab = std::make_shared<GUI::GUITab>(STR("My Less Safe Tab"), [](CppUserModBase* instance) {
            // This callback is identical to the one used with 'register_tab' except 'instance' is always nullptr.
            ImGui::Text("This is the contents of the less safe tab");
        });
        UE4SSProgram::get_program().add_gui_tab(m_less_safe_tab);
    }

    ~MyAwesomeMod() override
    {
        // Because you created a tab with 'UE4SSProgram::add_gui_tab', you must manually remove it.
        // Failure to remove the tab will result in a crash.
        UE4SSProgram::get_program().remove_gui_tab(m_less_safe_tab);
    }
    
    auto on_ui_init() -> void override
    {
        // It's critical that you enable ImGui if you intend to use ImGui within the context of UE4SS.
        // If you don't do this, a crash will occur as soon as ImGui tries to render anything, for example in your tab.
        UE4SS_ENABLE_IMGUI()
    }
    
    auto render_some_stuff(int Number) -> void
    {
        auto calculated_value = Number + 1;
        ImGui::Text(std::format("calculated_value: {}", calculated_value).c_str());
    }
};

#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
    MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
    {
        return new MyAwesomeMod();
    }

    MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
    {
        delete mod;
    }
}

Fixing missing AOBs

If UE4SS won't properly start because of missing AOBs, you can provide your own AOB and callback using Lua.

For this guide you'll need to know what a root directory and working directory is. A root directory is always the directory that contains ue4ss.dll. A working directory is either the directory that contains ue4ss.dll OR a game specific directory, for example <root directory>/SatisfactoryEarlyAccess.

How to find AOBs

caution

Finding AOBs for a game is no simple task and requires research into basic reverse engineering principles. It's not something for which we can just make an all-encompassing guide.

Since the process is quite complicated, here will just cover the general steps you need to take.

  1. Make a blank shipped game in your game's UE version, with PDBs
  2. Read game's memory using x64dbg
  3. Look for the signature you need - those can be found below
  4. Grab a copy of the bytes from that function, sometimes the header is enough. If it is not, it may be better to grab a call to the function and if it's not a virtual function, you can grab the RIP address there
  5. Open your game's memory in x64dbg and search it for the same block of bytes
  6. If you find it, you can use the swiss army knife tool to extract the AOB for it which you can use in a simple script such as example here

For more in-depth instructions, see the advanced guide.

How to setup your own AOB and callback

  1. Create the directory UE4SS_Signatures if it doesn't already exist in your working directory.
  2. Identify which AOBs are broken and need fixing.
  3. Make the following files inside UE4SS_Signatures, depending on which AOBs are broken:
    • GUObjectArray.lua
    • FName_ToString.lua
    • FName_Constructor.lua
    • FText_Constructor.lua (Optional)
    • StaticConstructObject.lua
    • GMalloc.lua
    • GUObjectHashTables.lua (Optional)
    • GNatives.lua (Optional)
    • ConsoleManager.lua (Optional)
    • ProcessLocalScriptFunction.lua
    • ProcessInternal.lua
    • CallFunctionByNameWithArguments.lua
  4. Inside the .lua file you need a global Register function with no params
    • Keep in mind that the names of functions in Lua files in the UE4SS_Signatures directory are case-senstive.
  5. The Register function must return the AOB that you want UE4SS to scan for.
    • The format is a list of nibbles, and every two forms a byte.
    • I like putting a space between each byte just for clarity but this is not a requirement.
    • An example of an AOB: 8B 51 04 85.
    • Another example of an AOB: 8B510485.
    • The AOB scanner supports wildcards for either nibble or the entire byte.
  6. Next you need to create a global OnMatchFound function.
    • This function has one param, MatchAddress, and this is the address of the match.
    • It's in this function that you'll place all your logic for calculating the final address.
    • The most simple way to do this is to make sure that your AOB leads directly to the start of the final address. That way you can simply return MatchAddress.
    • In the event that you're doing something more advanced (e.g. indirect aob scan), UE4SS makes available two global functions, DerefToInt32 which takes an address and returns, as a 32-bit integer, whatever data is located there OR nil if the address could not be dereferenced, and print for debugging purposes.

What 'OnMatchFound' must return for each AOB

  • GUObjectArray
    • Must return the exact address of the global variable named 'GUObjectArray'.
  • FName_ToString
    • Must return the exact address of the start of the function 'FName::ToString'.
      Function signature: public: void cdecl FName::ToString(class FString & ptr64)const __ptr64
  • FName_Constructor
    • Must return the exact address of the start of the function 'FName::FName'.
      This callback is likely to be called many times and we do a check behind the scenes to confirm if we found the right constructor.
      It doesn't matter if your AOB finds both 'char*' versions and 'wchar_t*' versions.
      Function signature: public: cdecl FName::FName(wchar_t const * ptr64,enum EFindName) __ptr64
  • FText_Constructor (Optional)
    • Must return the exact address of the start of the function 'FText::FText'.
      Function signature: public: cdecl FText::FText(class FString & ptr64)const __ptr64
  • StaticConstructObject
    • Must return the exact address of the start of the global function 'StaticConstructObject_Internal'.
      In UE4SS, we scan for a call in the middle of 'UUserWidget::InitializeInputComponent' and then resolve the call location.
      Function signature: class UObject * __ptr64 __cdecl StaticConstructObject_Internal(struct FStaticConstructObjectParameters const & __ptr64)
  • GMalloc
    • Must return the exact address of the global variable named 'GMalloc'.
      In UE4SS, we scan for FMemory::Free and then resolve the MOV instruction closest to the first CALL instruction.
  • GNatives (Optional)
    • Must return the exact address of the global variable named 'GNatives'.
  • GUObjectHashTables (WIP) (Optional)
  • ConsoleManager (WIP) (Optional)
  • ProcessLocalScriptFunction
    • This is not required 99% of the time.
    • Must return the exact address of ProcessLocalScriptFunction.
  • ProcessInternal
    • This is not required 99% of the time.
    • Must return the exact address of ProcessInternal.
  • CallFunctionByNameWithArguments
    • This is not required 99% of the time.
    • Must return the exact address of CallFunctionByNameWithArguments.

Example script (Simple, direct scan)

function Register()
    return "48 8B C4 57 48 83 EC 70 80 3D ?? ?? ?? ?? ?? 48 89"
end

function OnMatchFound(MatchAddress)
    return MatchAddress
end

Example script (Advanced, indirect scan)

function Register()
    return "41 B8 01 00 00 00 48 8D 15 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? E9"
end

function OnMatchFound(MatchAddress)
    local InstrSize = 0x05

    local JmpInstr = MatchAddress + 0x14
    local Offset = DerefToInt32(JmpInstr + 0x1)
    local Destination = JmpInstr + Offset + InstrSize

    return Destination
end

Fixing Missing AOBs (Advanced & In-Depth)

When UE4SS fails to properly launch due to missing AOBs (Array of Bytes signatures), you can provide custom AOBs and callback functions using Lua.
Doing so, however, requires a level of reverse engineering knowledge and tooling setup that may feel complex at first.
This guide expands upon the original instructions, providing more detail, some context and tips.

Prerequisites

  • Knowledge of Basic Reverse Engineering Concepts:
    You should have a general idea of what a signature (AOB) is, how to use a debugger, and how to navigate memory in x64dbg.

  • Familiarity with UE4SS Setup & Directories:
    Make sure you know where the UE4SS_Signatures folder should be created (it should be next to ue4ss.dll or in a game-specific working directory).

  • Preparation and Tools Installed:

    • Epic Games Launcher & Unreal Engine: For creating a “blank shipped game” environment with the correct engine version.
    • x64dbg: A debugger tool for Windows (https://x64dbg.com/).
    • (Optional) Baymax Tools: A plugin to help generate signatures easily.
    • (Optional) Swiss Army Knife (by Nukem9): For more easily extracting signatures with correct wildcards.

When is this needed, and why ?

Some games don't use Unreal Engine with its default configuration, and we only support the default configuration out of the box.
Anything that affects the code generated by the compiler, including the devs using Clang instead of MSVC, can make our built-in AOBs no longer be valid.
These AOBs are used to find functions and variables that are critical for UE4SS to work.

High-Level Overview

  1. Identify Which Signatures Are Missing: Determine which functions or variables UE4SS cannot find (e.g., GUObjectArray, GMalloc, FName or FText constructors).
  2. Set Up a Reference Environment: Create a blank Unreal Engine game (using the Shipping target) with debug files (PDBs) that uses the same Unreal Engine version as your game. This environment helps you identify function signatures cleanly.
  3. Reverse Engineer and Extract AOBs: Using x64dbg (and optional plugins), open the blank game and locate the desired function in memory. Copy out the unique bytes that form a reliable signature. This signature should be properly wildcarded, if it's not, it won't be found in your game.
  4. Apply Your Signatures to the Actual Game: Attach x64dbg to the target game, find the matching bytes, and confirm that the signature you extracted matches code in the game you want to mod.
  5. Create a Lua Script: Write a Lua file in UE4SS_Signatures to tell UE4SS what AOB pattern to search for (through Register) and what final address to return (through OnMatchFound).

Finding AOBs: A More Detailed Explanation

caution

Reverse engineering these signatures isn’t trivial. You may need to step outside the scope of this guide, read reverse-engineering tutorials, or ask for community support. The steps below are a starting point, not a complete guide on reverse engineering.

Step 1: Determine Your Game’s Unreal Engine Version

UE4SS tries to detect the engine version automatically. If you need to verify, the following steps usually work:

  • Right-click on the game’s .exe file (often in Binaries folder).
  • Select Properties -> Details tab.
  • Look for the “File Version” or “Product Version” field, which often correlates to the Unreal Engine version.

For example: If it says 5.3.2.0, it likely corresponds to UE 5.3.2.
In rare cases, the version will either be empty, or it will refer to the game version instead of the engine version.
Note that the last number doesn't usually matter, so if your game is using UE 5.3.2, your blank game can generally use any 5.3 version.

Step 2: Installing the Matching Unreal Engine Version

  • Create an Epic Games account and install the Epic Games Launcher.
  • In the launcher, go to Unreal Engine -> Library tab and install the engine version matching your game’s version (e.g., UE 5.3.2).

Step 3: Creating a Blank Shipped Game with PDBs

  1. Launch the installed Unreal Engine version.
  2. In the New Project window, select the Games tab -> Blank template.
    • Uncheck “Starter Content” because it's not needed, and unchecking this will save time and space.
    • Name your project and specify a directory.
  3. Once created, open Platforms -> Packaging Settings, and enable “Include Debug Files in Shipping Builds”.
  4. From Platforms -> Windows, select “Shipping” configuration (or whichever build matches your target game’s build type).
  5. Package Project -> Choose a folder.
  6. After packaging completes, verify that the output folder’s Binaries directory contains both a .exe and a .pdb. The .pdb file provides symbolic information for reverse engineering.

Step 4: Using x64dbg to Analyze the Blank Project

  1. Install x64dbg from https://x64dbg.com/.
  2. Run the .exe of your newly packaged blank project from its root directory.
  3. Open x64dbg.
    • Go to File -> Attach -> Select the blank project .exe.
    • Ensure you’re attaching to the shipped .exe located in Binaries or root (whichever works).

Step 5: Identifying the Function of Interest

You need to know which function or variable you’re trying to match in your target game. For example, if UE4SS fails on GMalloc, you must find FMemory::Free as a reference to locate GMalloc.

Optional Steps:

  • Connect your Epic Games account with GitHub to access the Unreal Engine source code.
    • Epic Games Website -> Manage Account -> Apps and Accounts -> GitHub
    • Accept invitation via email.
  • Browse the Unreal Engine source for the function you need. For FMemory::Free in UE5.3.2, you might look at:
    FMemory::Free in UE source

Step 6: Locating the Function in x64dbg

Note that there's a bug in x64dbg where navigating to code or memory from the symbols tab sometimes doesn't work properly.
If you're navigating and not seeing what you expect, it's worth restarting x64dbg and trying again.
You can also try copy the address from the symbols tab and manually navigate to it in the correct panel in the CPU tab.

  1. In x64dbg, switch to the Symbols tab.
  2. In the left pane, select the .exe.
  3. In the right pane, search for the function name (e.g., FMemory::Free).
  4. Double-click the found function to navigate back to the CPU view, positioning the instruction pointer at the start of the function in memory.

Step 7: Extracting a Signature

Once you’ve identified the start of the function, you need to copy a unique sequence of bytes:

  1. Consider installing Baymax Tools or Swiss Army Knife for x64dbg to ease signature extraction.
  2. Highlight a set of instructions at the start of the function.
    • Right-click -> Copy -> Selection (Bytes only) to get a raw byte sequence.
    • With Baymax Tools: Right-click -> Baymax Tools -> Copy Signature for a ready-made signature pattern.
  3. Save these bytes or patterns for later comparison. You may want to store them in a file to easily refer back.

Understanding Terminology

  • Signature: A carefully chosen sequence of bytes that uniquely identifies a function or code snippet.
  • Block of Bytes: A simple, possibly unstructured, segment of raw data without inherent uniqueness.
  • RIP (Instruction Pointer): The CPU register that holds the address of the next instruction to execute.

Step 8: Searching in the Actual Target Game

Now that you have a reference signature, you need to find it in your target game:

  1. Launch the target game .exe.
  2. Attach x64dbg as before: File -> Attach -> Select the game’s .exe.
  3. In x64dbg, search memory for the signature you extracted from the blank project.
    • If direct search fails, try partial sequences of bytes.
    • Try patterns generated by Baymax Tools.
    • Compare and contrast instructions between the blank project and the actual game to locate a similar code region.

If you find a match, you’ve identified the address that corresponds to the target function or variable in the actual game.
If you can’t find it, you may need to refine your signature, pick a different part of the function, or ask for community help (UE4SS Discord or GitHub Issues).

Step 9: Applying the Signature in UE4SS

Once you have a working AOB:

  1. Create the UE4SS_Signatures directory in your working directory (if it doesn’t already exist).
  2. Make a .lua file corresponding to the missing AOB (e.g., GMalloc.lua if you’re fixing GMalloc).
  3. Inside this .lua file, define the Register and OnMatchFound functions.

Register function: Returns your AOB signature as a string (spaces optional), e.g.:

function Register()
    return "48 8B D9 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 0C E8 ?? ?? ?? ??"
end

OnMatchFound function: Receives the match address and must return the exact memory address of the target function or variable. Use DerefToInt32 if needed to resolve relative addresses.

function OnMatchFound(MatchAddress)
    local MovInstr = MatchAddress + 0x03
    local Offset = DerefToInt32(MovInstr + 0x3)
    local RIP = MovInstr + 0x7
    local GMallocAddress = RIP + Offset
    return GMallocAddress
end

Verifying Your Work

  • Run the game with UE4SS again. If successful, UE4SS now uses your custom script to find the previously missing address.
  • If it fails silently, confirm:
    • That the Lua script is in the correct directory.
    • That your AOB is correct and unique.
    • That OnMatchFound returns the correct final address.

If still stuck, consider posting detailed steps, logs, and code snippets to the UE4SS community channels.
The more detail you provide, the more likely someone can guide you to a solution.

What ‘OnMatchFound’ Should Return, and Example Scripts

See the regular guide.

Tips, Tricks, and Troubleshooting

  • Patience & Iteration: Extracting and refining AOBs can be trial-and-error. If a signature doesn’t work, try a different sequence of bytes or look elsewhere in the function.
  • Partial Signatures: If the full function signature isn’t found, try unique parts of it.
  • Community Help: If stuck, show your steps, scripts, and logs on the UE4SS Discord or GitHub Issues.
  • Check Offsets Carefully: Off-by-one or incorrect indexing is a common issue. Double-check your calculations.
  • Manual Verification: Sometimes running the blank project again in x64dbg and comparing with the target game’s memory can highlight discrepancies.

By following these expanded steps and leveraging the provided tools, you’ll have a more comprehensive understanding of how to fix missing AOBs with UE4SS.
Although still complex, this extended guide should help clarify the process and offer practical insights into the reverse engineering territory.

Generating UHT compatible headers

Supported versions

While the UHT header generator is only officially supported in 4.25+, it has worked for older game versions (tested on 4.18.3; 4.17 (has some default property issues that should be fixed soon)). It also works for 5.0+.

How to use

The key bind to generate headers is by default CTRL + Numpad 9, and it can be changed in Mods/Keybinds/Scripts/main.lua.

To utilize the generated headers to their full potential, see UE4GameProjectGenerator by Archengius (link to Buck's fork because of a couple fixes that Arch is too lazy to merge).

The project generator will only compile for UE versions 4.22 and higher. Engine customizations by developers may lead to unexpected results. If generating a project for an engine version older than 4.22, generate it by compiling the project generator for 4.22 or higher first.

Before compiling the projectgencommandlet, open GameProjectGenerator.uproject and your game's pluginmanifest or .uproject and add any default engine plugins used by the game or plugins that the game uses and you found open source or purchased (it is not recommended to include purchased plugins in a public uproject) to the commandlet's uproject file.

After compiling the commandlet and running it on your game files, simply change the engine version in the generated .uproject to the correct engine version for your game.

image

This commandlet (by Spuds) will enter the CLI commands for the project gen for you, and make a batch file to regenerate with the same settings (e.g., to regenerate after a major game update).

Possible inaccurate generation issues:

UE4SS has two different types of generators, a UHT compatible generator and what's called a CXX generator.

The UHT compatible generator is what's used when creating a .uproject file with the UE4GameProjectGenerator, and the CXX generator is a very shoddily made generator that doesn't generate UHT macros or proper #include statements but it does generate headers for core UE classes which the UHT generator doesn't.

Note the UE4SS CXX dumps do not currently have accurate padding. An SDK dump generated from another source may be a better source for determining the below corrections if it generates with correct padding, particularly for the bitfield checks.

Certain default properties may not generate correctly in older engine versions. For example, SoftObjectProperty was called AssetObjectProperty and SoftClassProperty was AssetClassProperty in <4.17. It is recommended to also generate an SDK/CXX dump to check for those properties and correct them in your project.

Bitfields will always generate as uint8. However, they may actually be declared as uint32 in the original source. You can try to determine the actual size based on the CXX/SDK dump to correct these. In a CXX dump the bitfields will show the same offset. If there are multiple bitfields at the same offset and the next property is 4 bytes after that offset, then the bitfield should be changed to uint32.

Instructions for possible errors you may encounter

These are some general instructions of how to generate a project and it also covers a few errors that you are likely to encounter.

The following errors & solutions is what was found when generating projects for various games.

Note that you can check here for solutions even if your game isn't listed below. Error lists compiled by Buckminsterfullerene, CheatingMuppet, Narknon & Blubb.

Inherited Virtuals

UE4SS is unable to generate inherited virtuals if they are unreflected. This is often the source of LNK2001: unresolved external symbol errors, particularly when a class inherits from an interface. The build log is often not helpful for determining which file needs these virtuals.

To determine the file that they need to be added to, search for the virtual function listed in the error or for the class of the function in the engine, e.g., Module.AkAudio.cpp.obj : error LNK2001: unresolved external symbol "public: virtual class FString const __cdecl UInterpTrack::GetEdHelperClassName(void)const you could search for GetEdHelperClassName or UInterpTrack. Find the parent function and then find any classes within your project that inherit from same. Ideally find a sample of another class that inherits those virtuals within the engine on which to base your fixes, and copy the implementations from same into your affected project files, being sure to change the class name to match the class in your project.

You typically will also want to delete the logic in the implementations to simply return the correct type of data or "null" without actually running any logic.

Game Target Generation

The project gen commandlet does not generate a game target file. Copy and duplicate your GameNameEditor.target.cs file in the same location. Remove Editor from the name. Open the file and delete "Editor" in the red crossed locations, and replace "Editor" with "Game" in the highlighted location.

image

Deep Rock Galactic

========================== First do: ==========================
Generate project using commandlet
Then open it in Rider/VS.

========================== Then do, in no particular order: ==========================
Find out what version of mod.io game currently uses. At time of writing it is https://github.com/modio/modio-ue4/releases/tag/v2.16.1792. Delete the existing 'Modio' folder first. Paste the 'Modio', 'ModioTests' and 'ThirdParty' folders from this into Plugins/Modio/Source, replacing the existing 'Modio' folder. Do not replace the .uplugin file. Delete the ModioEx section form the .uplugin file instead.

In:
- CharacterSightSensor.h, FCharacterSightSensorDelegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FCharacterSightSensorDelegate);
- FSDProjectileMovementComponent.h top delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnProjectilePenetrateDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnProjectileOutOfPropulsion);
Add the macro DECLARE_DYNAMIC_MULTICAST_DELEGATE(<\DelegateName>); above the UCLASS

In:
- SubHealthComponent.h, line 56
- HealthComponentBase.h, line 117
- HealthComponent.h, line 98
- EnemyHealthComponent.h, line 39
- FriendlyHealthComponent.h, line 33
Comment out UFUNCTION

Errors that look like this: "ActorFunctionLibrary.gen.cpp(153): [C2664] 'void UActorFunctionLibrary::DissolveMaterials(UObject *,const UMeshComponent *&,float)': cannot convert argument 2 from 'UMeshComponent *' to 'const UMeshComponent *&'":
Remove the const before the arguments that have the error (remember to also remove them in the definition stub too)
OR use this regex string (const) ((\w+)\*\&) and replace with $2

In "ShowroomStage.cpp" inside of the implementation of the constructor, comment out "this->SceneCapture = CreateDefaultSubobject<\USceneCaptureComponent2D>(TEXT("SceneCapture"));" 

Set supported platforms to windows

cyubeVR

Add the following 4 lines in the "Plugins" section in the generated "cyubeVR.uproject":
{
    "Name": "ChaosEditor",
    "Enabled": false
}

Copy and paste the cyubeVREditor.Target.cs file (inside Source folder) and name it cyubeVRGame.Target.cs. Then replace any mentions of "editor" and replace with "game" inside of this new file

Right click generated project and open with IDE (e.g. Rider)

Comment out UFUNCTION() in ReceiveLightActor.h
    - UseActorCustomLocation
    - GetActorCustomLocation

Set the "_MAX UMETA(Hidden)," to "_MAX = 0xFF UMETA(Hidden)," in:
    - EUGCMatchingUGCTypeBP.h
    - EItemPreviewTypeBP.h

Remove the constructor from IpNetDriverUWorks.h and cpp files.

Remove TEnumAsByte<> (but not the type inside of it) in:
    - OnInput inside VRGripInterface.h
    - OnEndPlay inside VRGripScriptBase.h and its _Implementation version in the .cpp file
    - SetMobilityAllEvent inside DeerCPP.h and its _Implementation version in the .cpp file

Then right click the .uproject and hit "regenerate solution files".

If you get the "failed to create version memory for PCH" errors when trying to build or pack, do it again.

Game 3

Error 1
In an Enum class:
System.ArgumentException - String cannot contain a minus sign if the base is not 10.

Fix:
Remove the BlueprintType meta tag and the uint8 override on the enum ': uint8'.


Error 2
Unable to find 'class', 'delegate', 'enum', or 'struct' with name 'XYZ', where XYZ is an FStruct used within a class with no separate UStruct declaration.

Fix:
DECLARE_DYNAMIC_MULTICAST_DELEGATE(XYZ); , close to the Top of header Files.


Error 3
"is not supported by blueprint."

Fix:
-> Remove BlueprintReadWrite
-> or Remove BlueprintCallable


Error 4
cannot instantiate abstract class

fix:

cpp looks like:

 UAbilitySystemComponent* AActorWithGAS::GetAbilitySystemComponent() const {
    return nullptr;
 }
 
 Go to Header File and add:
 
 UAbilitySystemComponent* GetAbilitySystemComponent() const override;


Error 5
modifiers not allowed on static member functions

Fix: 
Remove the modifier, like "const"

Example:
static TSoftObjectPtr<Test> SomeFunction(some args) const;  <- remove const

In both h and cpp File.


Error 6
'AAkAMbientSound' no appropriate default consturctor available.

Fix:
-------
Header File
-------
AkAmbientSound();

->

AkAmbientSound(const class FObjectInitializer& ObjectInitializer);

-------
CPP File
-------
AkAmbientSound::AkAmbientSound() {
    this->AkEvent = NULL;
}

->

AkAmbientSound::AkAmbientSound(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)  {
    this->AkEvent = NULL;
}

Astro Colony

========================== First do: ==========================
Generate project using commandlet
Then open it in Rider/VS.

========================== Then do, in no particular order: ==========================
Copy the EditorTarget file, rename it to AstroColonyGame.Target, and inside of it change target type to Game

In:
- VoxelPhysicsPartSpawner_VoxelWorlds.h, FConfigureVoxelWorld;
- TGNamedSlot.h, FOnNamedSlotAdded/Removed
- EHLogicObject.h, FOnSelectedResourcesChanged
- EHSignalObject.h, FOnResourcesSignalOutChanged/FOnSelectedDeviceChanged
- EHInteractableServiceObject, FOnAIInsideChanged
- EHModsBrowsedOptionViewModel, FOnInstalProgressChanged/FOnInstalCompleted
- EHSaveLoadListViewModel, FOnScenarioDetailsUpdated
- EHTrainingObject, FOnTrainedChanged
- EHSchoolObject, FOnAwaitingSpecialistTrainingsChange
- EHSignalReceiver, FOnSignalSendChanged
- EHModsListViewModel, FOnModsOptionSelected
- EHSignalNetwork, FOnSignalChanged
- AbilityAsync_WaitGameplayTagAdded, FAsyncWaitGameplayTagDelegate (put it inside of AbilityAsync_WaitGameplayTag)
Add the macro DECLARE_DYNAMIC_MULTICAST_DELEGATE(<\DelegateName>); above the UCLASS

In: 
- AbilityAsync_WaitGameplayTagRemoved.h
- AbilityAsync_WaitGameplayTagAdded.h
Remove the UAbilityAsync_WaitGameplayTag:: from the front of each member

In EHSummaryViewModel.h add #include "EHSaveLoadListViewModel.h"

In:
- MaterialExpressionBlendMaterialAttributesBarycentric.h (every property)
- MaterialExpressionUnpack.h (FExpressionInput Input)
- GameplayCueInterface.h (ForwardGameplayCueToParent)
remove BlueprintReadWrite/BlueprintCallable (where appropriate) flag from the 'UPROPERTY' macro.

In MaterialPackInput.h, add #include "MaterialExpressionIO.h" and remove BlueprintReadWrite flag from the 'UPROPERTY' macro for FExpressionInput Input;

In EAbilityTaskWaitState.h, add None = 0 to the enum

In:
- AbilityTask.h/.cpp
- UMovieSceneGameplayCueTriggerSection
- UMovieSceneGameplayCueSection
comment out the constructor/definition

In AbilitySystemComponent.h/.cpp, comment out:
- The constructor
- ServerSetReplicatedEventWithPayload
- ServerSetReplicatedEvent
- ClientSetReplicatedEvent

In EHBaseButtonWidget.h, add:
#include "Components/HorizontalBox.h"
#include "Components/BackgroundBlur.h"
#include "Components/SizeBox.h"
then remove the forward declarations for UHorizontalBox, UBackgroundBlur, USizeBox. 
Then comment out
	UFUNCTION(BlueprintImplementableEvent)
    void OnInputControllerChanged(TEnumAsByte<ETGInputControllerType> InputControllerType); 

In:
- EHPlanetoidDestructibleItem.h
- EHPlanetoidVisualItem.h (also remove array from SpawnDensity)
- EHGridComponent.h, BillboardTextures
- EHHUDGame.h, PopMenuClasses/HUDMenuClasses (also change GetPopMenuClass return type)
- EHScenarioParams.h, TerrainTypeSpawnChances/ShapeTypeSpawnChances
- EHDataProvider.h, every array
replace the array decleration with TArray<> and add BlueprintReadWrite+other normal flags to the 'UPROPERTY' macro. Then update the .cpp constructor.

In VoxelProceduralMeshComponent.h/.cpp, add the UPrimitiveComponent interface, i.e. like this:
VoxelProceduralMeshComponent.h:
#pragma once
#include "CoreMinimal.h"
#include "Components/ModelComponent.h"
#include "VoxelIntBox.h"
#include "VoxelProceduralMeshComponent.generated.h"

class UBodySetup;
class UStaticMeshComponent;
class AVoxelWorld;
class UModelComponent;

UCLASS(Blueprintable, ClassGroup=Custom, meta=(BlueprintSpawnableComponent))
class VOXEL_API UVoxelProceduralMeshComponent : public UModelComponent {
    GENERATED_BODY()
public:
private:
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Transient, meta=(AllowPrivateAccess=true))
    UBodySetup* BodySetup;
    
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Transient, meta=(AllowPrivateAccess=true))
    UBodySetup* BodySetupBeingCooked;
    
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Export, Transient, meta=(AllowPrivateAccess=true))
    UStaticMeshComponent* StaticMeshComponent;
    
public:
    UVoxelProceduralMeshComponent(const FObjectInitializer& ObjectInitializer);
    UFUNCTION(BlueprintCallable)
    static void SetVoxelCollisionsFrozen(const AVoxelWorld* VoxelWorld, bool bFrozen);
    
    UFUNCTION(BlueprintImplementableEvent)
    void InitChunk(uint8 ChunkLOD, FVoxelIntBox ChunkBounds);
    
    UFUNCTION(BlueprintCallable, BlueprintPure)
    static bool AreVoxelCollisionsFrozen(const AVoxelWorld* VoxelWorld);

//~ Begin UPrimitiveComponent Interface.
	virtual void CreateRenderState_Concurrent(FRegisterComponentContext* Context) override;
	virtual void DestroyRenderState_Concurrent() override;
	virtual bool GetLightMapResolution( int32& Width, int32& Height ) const override;
	virtual int32 GetStaticLightMapResolution() const override;
	virtual void GetLightAndShadowMapMemoryUsage( int32& LightMapMemoryUsage, int32& ShadowMapMemoryUsage ) const override;
	virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
	virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
	virtual bool ShouldRecreateProxyOnUpdateTransform() const override;
#if WITH_EDITOR
	virtual void GetStaticLightingInfo(FStaticLightingPrimitiveInfo& OutPrimitiveInfo,const TArray<ULightComponent*>& InRelevantLights,const FLightingBuildOptions& Options) override;
	virtual void AddMapBuildDataGUIDs(TSet<FGuid>& InGUIDs) const override;
#endif
	virtual ELightMapInteractionType GetStaticLightingType() const override	{ return LMIT_Texture;	}
	virtual void GetStreamingRenderAssetInfo(FStreamingTextureLevelContext& LevelContext, TArray<FStreamingRenderAssetPrimitiveInfo>& OutStreamingRenderAssets) const override;
	virtual void GetUsedMaterials(TArray<UMaterialInterface*>& OutMaterials, bool bGetDebugMaterials = false) const override;
	virtual class UBodySetup* GetBodySetup() override { return ModelBodySetup; };
	virtual int32 GetNumMaterials() const override;
	virtual UMaterialInterface* GetMaterial(int32 MaterialIndex) const override;
	virtual UMaterialInterface* GetMaterialFromCollisionFaceIndex(int32 FaceIndex, int32& SectionIndex) const override;
	virtual bool IsPrecomputedLightingValid() const override;
	//~ End UPrimitiveComponent Interface.

	//~ Begin UActorComponent Interface.
	virtual void InvalidateLightingCacheDetailed(bool bInvalidateBuildEnqueuedLighting, bool bTranslationOnly) override;
	virtual void PropagateLightingScenarioChange() override;
	//~ End UActorComponent Interface.

	//~ Begin UObject Interface.
	virtual void Serialize(FArchive& Ar) override;
	virtual void PostLoad() override;
	virtual bool IsNameStableForNetworking() const override;
#if WITH_EDITOR
	virtual void PostEditUndo() override;
#endif // WITH_EDITOR
	static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
	//~ End UObject Interface.

	//~ Begin Interface_CollisionDataProvider Interface
	virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override;
	virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override;
	virtual bool WantsNegXTriMesh() override { return false; }
	//~ End Interface_CollisionDataProvider Interface

//#if WITH_EDITOR
	/**
	 *	Generate the Elements array.
	 *
	 *	@param	bBuildRenderData	If true, build render data after generating the elements.
	 *
	 *	@return	bool				true if successful, false if not.
	 */
	virtual bool GenerateElements(bool bBuildRenderData);
//#endif // WITH_EDITOR  
};

VoxelProceduralMeshComponent.cpp:
#include "VoxelProceduralMeshComponent.h"

class AVoxelWorld;

void UVoxelProceduralMeshComponent::SetVoxelCollisionsFrozen(const AVoxelWorld* VoxelWorld, bool bFrozen) {

}

bool UVoxelProceduralMeshComponent::AreVoxelCollisionsFrozen(const AVoxelWorld* VoxelWorld) {
    return false;
}

UVoxelProceduralMeshComponent::UVoxelProceduralMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
    this->BodySetup = NULL;
    this->BodySetupBeingCooked = NULL;
    this->StaticMeshComponent = NULL;
}

void UVoxelProceduralMeshComponent::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
	/*UVoxelProceduralMeshComponent* This = CastChecked<UVoxelProceduralMeshComponent>(InThis);
	Collector.AddReferencedObject( This->StaticMeshComponent, This );
	AddReferencedObjects( This, Collector );*/
}

void UVoxelProceduralMeshComponent::Serialize(FArchive& Ar)
{
	/*Serialize(Ar);
	
	Ar << StaticMeshComponent;*/
}

void UVoxelProceduralMeshComponent::PostLoad()
{
	/*PostLoad();

	// Fix for old StaticMeshComponent components which weren't created with transactional flag.
	SetFlags( RF_Transactional );

	// BuildRenderData relies on the StaticMeshComponent having been post-loaded, so we ensure this by calling ConditionalPostLoad.
	check(StaticMeshComponent);
	StaticMeshComponent->ConditionalPostLoad();*/
	
}

bool UVoxelProceduralMeshComponent::IsNameStableForNetworking() const
{
	// UVoxelProceduralMeshComponent is always persistent for the duration of a game session, and so can be considered to have a stable name
	return true;
}

void UVoxelProceduralMeshComponent::GetUsedMaterials(TArray<UMaterialInterface*>& OutMaterials, bool bGetDebugMaterials) const
{
	
}

int32 UVoxelProceduralMeshComponent::GetNumMaterials() const
{
	return 0;
}

UMaterialInterface* UVoxelProceduralMeshComponent::GetMaterial(int32 MaterialIndex) const
{
	UMaterialInterface* Material = nullptr;
	
	return Material;
}

UMaterialInterface* UVoxelProceduralMeshComponent::GetMaterialFromCollisionFaceIndex(int32 FaceIndex, int32& SectionIndex) const
{
	UMaterialInterface* Result = nullptr;
	SectionIndex = 0;
	return Result;
}

bool UVoxelProceduralMeshComponent::IsPrecomputedLightingValid() const
{
	return false;
}

void UVoxelProceduralMeshComponent::GetStreamingRenderAssetInfo(FStreamingTextureLevelContext& LevelContext, TArray<FStreamingRenderAssetPrimitiveInfo>& OutStreamingRenderAssets) const
{
	
}

void UVoxelProceduralMeshComponent::CreateRenderState_Concurrent(FRegisterComponentContext* Context)
{

}

void UVoxelProceduralMeshComponent::DestroyRenderState_Concurrent()
{
	
}

FPrimitiveSceneProxy* UVoxelProceduralMeshComponent::CreateSceneProxy()
{
	return NULL;
}

bool UVoxelProceduralMeshComponent::ShouldRecreateProxyOnUpdateTransform() const
{
	return true;
}

FBoxSphereBounds UVoxelProceduralMeshComponent::CalcBounds(const FTransform& LocalToWorld) const
{
	return FBoxSphereBounds(LocalToWorld.GetLocation(), FVector::ZeroVector, 0.f);
}

void UVoxelProceduralMeshComponent::InvalidateLightingCacheDetailed(bool bInvalidateBuildEnqueuedLighting, bool bTranslationOnly)
{
	
}

void UVoxelProceduralMeshComponent::PropagateLightingScenarioChange()
{
	
}

bool UVoxelProceduralMeshComponent::GetLightMapResolution( int32& Width, int32& Height ) const
{
	return false;
}

int32 UVoxelProceduralMeshComponent::GetStaticLightMapResolution() const
{
	/*int32 Width;
	int32 Height;
	GetLightMapResolution(Width, Height);

	return FMath::Max<int32>(Width, Height);*/
	return NULL;
}

void UVoxelProceduralMeshComponent::GetLightAndShadowMapMemoryUsage( int32& LightMapMemoryUsage, int32& ShadowMapMemoryUsage ) const
{
	/*return;*/
}

#if WITH_EDITOR
void UVoxelProceduralMeshComponent::GetStaticLightingInfo(FStaticLightingPrimitiveInfo& OutPrimitiveInfo,const TArray<ULightComponent*>& InRelevantLights,const FLightingBuildOptions& Options)
{
	/*check(0);*/
}

void UVoxelProceduralMeshComponent::AddMapBuildDataGUIDs(TSet<FGuid>& InGUIDs) const
{
	
}

void UVoxelProceduralMeshComponent::PostEditUndo()
{
	/*PostEditUndo();*/
}
#endif // WITH_EDITOR

bool UVoxelProceduralMeshComponent::GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData)
{
	return false;
}

bool UVoxelProceduralMeshComponent::ContainsPhysicsTriMeshData(bool InUseAllTriData) const
{
	return false;
}

bool UVoxelProceduralMeshComponent::GenerateElements(bool bBuildRenderData)
{
	return false;
}

Set supported platforms to windows

Creating a C++ mod

This guide will help you create a C++ mod using UE4SS.
It's split up into four parts.
Part one goes over the prerequisites.
Part two goes over creating the most basic C++ mod possible.
Part three will show you how to interact with UE4SS and UE itself (via UE4SS).
Part four will cover installation of the mod.

The guide requires having a working C++ development environment with xmake and git, preferably similar to the one required to build UE4SS itself from sources.

Part 1

Make sure you have downloaded all the build requirements mentioned in the README before following these steps!

  1. Make an Epic account and link it to your GitHub account
  2. Check your email and accept the invitation to the @EpicGames GitHub organization for Unreal source access.
  3. Setup SSH keys on your GitHub account which will let git access the Unreal source you got access for in 2 and 3.
  4. Make a directory somewhere on your computer, the name doesn't matter but I named mine MyMods.
  5. Clone the RE-UE4SS repo so that you end up with MyMods/RE-UE4SS.
  6. Open CMD and cd into RE-UE4SS and execute: git submodule update --init --recursive
  7. Go back to the MyMods directory and create a new directory, this directory will contain your mod source files. I named mine MyAwesomeMod.
  8. Create a file called xmake.lua inside MyMods and put this inside it:
includes("RE-UE4SS")
includes("MyAwesomeMod")

Part #2

  1. Create a file called xmake.lua inside MyMods/MyAwesomeMod and put this inside it:
local projectName = "MyAwesomeMod"

target(projectName)
    add_rules("ue4ss.mod")
    add_includedirs(".")
    add_files("dllmain.cpp")
  1. Make a file called dllmain.cpp in MyMods/MyAwesomeMod and put this inside it:
#include <stdio.h>
#include <Mod/CppUserModBase.hpp>

class MyAwesomeMod : public RC::CppUserModBase
{
public:
    MyAwesomeMod() : CppUserModBase()
    {
        ModName = STR("MyAwesomeMod");
        ModVersion = STR("1.0");
        ModDescription = STR("This is my awesome mod");
        ModAuthors = STR("UE4SS Team");
        // Do not change this unless you want to target a UE4SS version
        // other than the one you're currently building with somehow.
        //ModIntendedSDKVersion = STR("2.6");
        
        printf("MyAwesomeMod says hello\n");
    }

    ~MyAwesomeMod() override
    {
    }

    auto on_update() -> void override
    {
    }
};

#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
    MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
    {
        return new MyAwesomeMod();
    }

    MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
    {
        delete mod;
    }
}
  1. In the command prompt, in the MyMods directory, execute either: A.
xmake f -m "Game__Shipping__Win64"
xmake

or B.

xmake project -k vsxmake2022

If you chose option B, the VS solution will be in the vsxmake2022 directory.

  1. Open MyMods/vsxmake2022/MyMods.sln
  2. Make sure that you're set to the Game___Shipping__Win64 configuration unless you want to debug.
  3. Find your project (in my case: MyAwesomeMod) in the solution explorer and right click it and hit Build.

Part #3

In this part, we're going to learn how to log to file, and both consoles, as well as find a UObject by name, and log that name.

  1. Add #include <DynamicOutput/DynamicOutput.hpp> under #include <Mod/CppUserModBase.hpp>.
    You can now also remove #include <stdio.h> because we'll be removing the use of printf which was the only thing that required it.
  2. To save some time and annoyance and make the code look a bit better, add this line below all the includes:
using namespace RC;
  1. Replace the call to printf in the body of the MyAwesomeMod constructor with:
Output::send<LogLevel::Verbose>(STR("MyAwesomeMod says hello\n"));

It's longer than a call to printf, but in return the message gets propagated to the log file and both the regular console and the GUI console.
We also get some support for colors via the LogLevel enum.

  1. Add this below the DynamicOutput include:
#include <Unreal/UObjectGlobals.hpp>
#include <Unreal/UObject.hpp>
  1. Let's again utilize the using namespace shortcut by adding this below the first one: using namespace RC::Unreal;
  2. Add this function in your mod class:
auto on_unreal_init() -> void override
{
    // You are allowed to use the 'Unreal' namespace in this function and anywhere else after this function has fired.
    auto Object = UObjectGlobals::StaticFindObject<UObject*>(nullptr, nullptr, STR("/Script/CoreUObject.Object"));
    Output::send<LogLevel::Verbose>(STR("Object Name: {}\n"), Object->GetFullName());
}

Note that Output::send doesn't require a LogLevel and that we're using {} in the format string instead of %s.
The Output::send function uses std::format in the back-end so you should do some research around std::format or libfmt if you want to know more about it.

  1. Right click your project and hit Build.

Part #4

Click to go to guide for installing a C++ Mod

Installing a C++ Mod

  1. This part assumes you have UE4SS installed and working for your game already. If not, refer to the installation guide.

  2. After building, you will have the following file:

    • MyAwesomeMod.dll in MyMods\Binaries\<Configuration>\MyAwesomeMod
  3. Navigate over to your game's executable folder and open the Mods folder. Here we'll do a couple things:

    • Create a folder structure in Mods that looks like MyAwesomeMod\dlls.
    • Move MyAwesomeMod.dll inside the dlls folder and rename it to main.dll.

The result should look like:

Mods\
    MyAwesomeMod\
        dlls\
            main.dll
  1. To enable loading of your mod in-game you will have to edit the mods.txt located in the Mods folder. By default it looks something like this:
CheatManagerEnablerMod : 1
ActorDumperMod : 0
ConsoleCommandsMod : 1
ConsoleEnablerMod : 1
SplitScreenMod : 0
LineTraceMod : 1
BPModLoaderMod : 1
jsbLuaProfilerMod : 0



; Built-in keybinds, do not move up!
Keybinds : 1

Here you will want to add the line:

MyAwesomeMod : 1

above the keybinds to enable MyAwesomeMod.

Alternatively, place an empty text file named enabled.txt inside of the MyAwesomeMod folder. This method is not recommended because it does not allow load ordering
and bypasses mods.txt, but may allow for easier installation by end users.

  1. Launch your game and if everything was done correctly, you should see the text "MyAwesomeMod says hello" highlighted in blue somewhere at the top of UE4SS console (before all the scanning occurs), and if you used the on_unreal_init function, you should see "Object Name: /Script/CoreUObject.Object" highlighted in blue as well (right after the scanning finishes).

Automation

Now that you understand how the process works, you can use the UE4SS CPP Template repository that automates the process of creating a mod, building it, and installing it. Be aware that the new_mod_setup.bat script will checkout the commit at the latest release so that you can be sure that your mod is being built with the correct ABI as latest release.

NOTE: Any changes to the build system that affects the mod template is pushed to the dev branch, which is then merged into main when a new UE4SS release is created. This makes sure that the template is always in-sync with the latest UE4SS release.

Creating GUI tabs with a C++ mod

UE4SS already includes the ImGui library to render its console GUI, built from the UE4SS-RE/imgui repo. Refer to ImGui documentation in that repo on how to use ImGui-specific classes and methods for rendering actual buttons and textboxes and other window objects.

This guide will show how you create custom tabs for the GUI with a C++ mod, and the guide will take the form of comments in the code example below:

#include <Mod/CppUserModBase.hpp>
#include <UE4SSProgram.hpp>

class MyAwesomeMod : public RC::CppUserModBase
{
private:
    int m_private_number{33};
    std::shared_ptr<GUI::GUITab> m_less_safe_tab{};

public:
    MyAwesomeMod() : CppUserModBase()
    {
        ModName = STR("MyAwesomeMod");
        ModVersion = STR("1.0");
        ModDescription = STR("This is my awesome mod");
        ModAuthors = STR("UE4SS Team");
        
        // The 'register_tab' function will tell UE4SS to render a tab.
        // Tabs registered this way will be automatically cleaned up when this C++ mod is destructed.
        // The first param is the display name of your tab.
        // The second param is a callback that UE4SS will use to render the contents of the tab.
        // The param to the callback is a pointer to your mod.
        register_tab(STR("My Test Tab"), [](CppUserModBase* instance) {
            // In this callback, you can start rendering the contents of your tab with ImGui. 
            ImGui::Text("This is the contents of the tab");
            
            // You can access members of your mod class with the 'instance' param.
            auto mod = dynamic_cast<MyAwesomeMod*>(instance);
            if (!mod)
            {
                // Something went wrong that caused the 'instance' to not be correctly set.
                // Let's abort the rest of the function so that you don't access an invalid pointer.
                return;
            }
            
            // You can access both public and private members.
            mod->render_some_stuff(mod->m_private_number);
        });
        
        // The 'UE4SSProgram::add_gui_tab' function is another way to tell UE4SS to render a tab.
        // This way of registering a tab will make you responsible for cleaning up the tab when your mod destructs.
        // Failure to clean up the tab on mod destruction will result in a crash.
        // It's recommended that you use 'register_tab' instead of this function.
        m_less_safe_tab = std::make_shared<GUI::GUITab>(STR("My Less Safe Tab"), [](CppUserModBase* instance) {
            // This callback is identical to the one used with 'register_tab' except 'instance' is always nullptr.
            ImGui::Text("This is the contents of the less safe tab");
        });
        UE4SSProgram::get_program().add_gui_tab(m_less_safe_tab);
    }

    ~MyAwesomeMod() override
    {
        // Because you created a tab with 'UE4SSProgram::add_gui_tab', you must manually remove it.
        // Failure to remove the tab will result in a crash.
        UE4SSProgram::get_program().remove_gui_tab(m_less_safe_tab);
    }
    
    auto on_ui_init() -> void override
    {
        // It's critical that you enable ImGui if you intend to use ImGui within the context of UE4SS.
        // If you don't do this, a crash will occur as soon as ImGui tries to render anything, for example in your tab.
        UE4SS_ENABLE_IMGUI()
    }
    
    auto render_some_stuff(int Number) -> void
    {
        auto calculated_value = Number + 1;
        ImGui::Text(std::format("calculated_value: {}", calculated_value).c_str());
    }
};

#define MY_AWESOME_MOD_API __declspec(dllexport)
extern "C"
{
    MY_AWESOME_MOD_API RC::CppUserModBase* start_mod()
    {
        return new MyAwesomeMod();
    }

    MY_AWESOME_MOD_API void uninstall_mod(RC::CppUserModBase* mod)
    {
        delete mod;
    }
}

Interacting with UE properties in C++

When making a mod, there's a very good chance that you're going to need to read or write a UE property.
The classic and not recommended methods to do this is to either set up your own structs that match the memory of the game, or manually add an offset to the base pointer.
Both these methods come with the downside of easily becoming out-of-date with the game from game or engine updates.
The better method to interact with properties is to use the APIs that we provide to C++ mods.
These APIs don't rely on a struct known at compile-time, or offsets that can easily become out of date.
It does this by using the metadata available in every UE game.

Templated APIs

We supply templated functions to make it easier to retrieve the type you want.
These are the easiest ways to access property values.

UObject::GetValuePtrByPropertyNameInChain

This function returns a pointer to the value for the property.
It searches the entire class hierarchy (inheritance) for 'PropertyName', and because of this, it's recommended over the non-InChain variant.

// For this exmaple, assume that this UObject* is correctly retrieved in some way, and isn't nullptr.
UObject* PlayerController{};
FVector* SpawnLocation = PlayerController->GetValuePtrByPropertyNameInChain<FVector>(STR("SpawnLocation"));
Output::send(STR("{} {} {}\n"), SpawnLocation->X(), SpawnLocation->Y(), SpawnLocation->Z());

UObject::GetValuePtrByPropertyName

Same as UObject::GetValuePtrByPropertyNameInChain, except it only checks the innermost class.
For example, for APlayerController, it will only look inside APlayerController, not AController or any other class that it inherits from.

Accessing UStruct values

It's not recommended to create your own C++ structs that mimic the game struct.
Instead, you should make use of UEs metadata to retrieve pointers to each member individually.
This is to prevent your code from becoming out-of-date with the game, and it's also easier because it doesn't require reverse-engineering.
We provide a few structs that don't become out-of-date, such as FVector and FRotator, but in most cases we don't.

Here's some example code showing how to retrieve a value from a struct inside a UObject.

// Retrieving: Engine->TinyFontName.SubPathString
// For this example, assume that this UObject* is correctly retrieved in some way, and isn't nullptr.
UObject* Engine{};
// 1. Retrieve the property for the TinyFontName variable.
//    The real type as far as the game is concerned is FSoftObjectPath.
//    We do provide an 'FSoftObjectPath' struct in UE4SS, but let's pretend that we don't.
auto TinyFontNameProperty = static_cast<FStructProperty*>(Engine->GetPropertyByNameInChain(STR("TinyFontName")));
// 2. Retrieve the UStruct that corresponds with the property.
UStruct* SoftObjectPath = TinyFontNameProperty->GetStruct();
// 3. Retrieve a pointer to the TinyFontName value inside the UEngine instance.
auto TinyFontName = TinyFontNameProperty->ContainerPtrToValuePtr<void>(Engine);
// 4. Retrieve the property for the SubPathString in the FSoftObjectPath UStruct.
FProperty* SubPathStringProperty = SoftObjectPath->GetPropertyByNameInChain(STR("SubPathString"));
// 5. Retrieve a pointer to the SubPathString value inside the FSoftObjectPath struct instance 'TinyFontName'.
//    Note that we're supplying a template parameter here because we know that it's an FString, and UE4SS has a safe FString implementation.
//    If you don't supply a template parameter, it will return a void* instead.
auto SubPathString = SubPathStringProperty->ContainerPtrToValuePtr<FString>(TinyFontName);
// 6. Output the value.
Output::send(STR("TinyFontName: {}\n"), SubPathString->GetCharArray());

You can take this any number of containers deep, meaning that structs in structs is not a problem.
For example:

// Retrieving: Engine->TinyFontName.AssetPath.AssetName
// Continuing from the above example.
// 7. Retrieve the property for the AssetPath variable in the FSoftObjectPath UStruct.
//    The real type as far as the game is concerned is FTopLevelAssetPath.
auto AssetPathProperty = static_cast<FStructProperty*>(SoftObjectPath->GetPropertyByNameInChain(STR("AssetPath")));
// 8. Retrieve the UStruct that corresponds with the property.
auto TopLevelAssetPath = AssetPathProperty->GetStruct();
// 9. Retrieve a pointer to the AssetPath value inside the FSoftObjectPath struct instance 'TinyFontName'.
auto AssetPath = AssetPathProperty->ContainerPtrToValuePtr<void>(TinyFontName);
// 10. Retrieve the property for the AssetName variable in the FTopLevelAssetPath UStruct.
auto AssetNameProperty = TopLevelAssetPath->GetPropertyByNameInChain(STR("AssetName"));
// 11. Retrieve a pointer to the AssetName value inside the FTopLevelAssetPath struct instance 'AssetPath'.
auto AssetName = AssetNameProperty->ContainerPtrToValuePtr<FName>(AssetPath);
// 12. Output the value.
Output::send(STR("AssetName: {}\n"), AssetName->ToString());

The ContainerPtrToValuePtr function always returns a pointer to T.
In the example above, T == FName, so the function returns FName*.
Because it always returns a pointer, changing property values is trivial:

// Continuing from the above example.
// 13. Set the value of 'AssetName' to "THIS IS A TEST" by dereferencing the pointer and setting the value to a new FName.
*AssetName = FName(STR("THIS IS A TEST"), FNAME_Add);
// 14. Output the new value.
Output::send(STR("AssetName: {}\n"), AssetName->ToString());

Creating a Lua mod

Before you start

To create a Lua mod in UE4SS, you should first:

  • know how to install UE4SS in your target game and make sure it is running OK;
  • be able to write basic Lua code (see the official book Programming in Lua and its later editions, or any other recommended tutorial online);
  • have an understanding of the object model of the Unreal Engine and the basics of game modding.

How does a minimal Lua mod look like

A Lua mod in UE4SS is a set of Lua scripts placed in a folder inside the Mods/ folder of UE4SS installation. Let's call it MyLuaMod for the purpose of this example.

In order to be loaded and executed:

  1. The mod folder must have a scripts subfolder and a main.lua file inside, so it looks like:
Mods\
    ...
    MyLuaMod\
        scripts\
            main.lua
    ...
  1. The Mods\MyLuaMod\scripts\main.lua file has some Lua code inside it, e.g.:
print("[MyLuaMod] Mod loaded\n")
  1. The mod must be added and enabled in Mods\mods.txt with a new line containing the name of your mod folder (name of your mod) and 1 for enabling or 0 for disabling the mod:
...
MyLuaMod : 1
...

Your custom functionality goes inside main.lua, from which you can include other Lua files if needed, including creating your own Lua modules or importing various libraries.

What can you do in a Lua mod

The API provided by UE4SS and available to you in Lua is documented in sub-sections of chapter "Lua API" here. Using those functions and classes, you find and manipulate the instances of Unreal Engine objects in memory, creating new objects or modifying existing ones, calling their methods and accessing their fields.

Basically, you are doing the exact same thing that an Unreal Engine game developer does in their code, but using UE4SS to locate the necessary objects and guessing a bit, while the developers already knew where and what they are (because they have their source code).

Creating simple data types

If you need to create an object of a structure-like class, e.g. FVector, in order to pass it into a Unreal Engine function, UE4SS allows you to pass a Lua table with the fields of the class like {X=1.0, Y=2.0, Z=3.0} instead.

Using Lua C libraries

If you ever need to load Lua C libraries, that have native code (i.e. with DLLs on Windows), you can place these DLLs directly inside the same \scripts\ folder.

Setting up a Lua mod development environment

It is much easier to write mods if your code editor or IDE is properly configured for Lua development and knows about UE4SS API.

  1. Configure your code editor/IDE to support Lua syntax highlighting and code completion. If you use VSCode, see here in Using Custom Lua Bindings.

  2. Make sure that your build of UE4SS contains Mods\shared\Types.lua (a development build from Github releases contains it). This will load the UE4SS API definitions in your IDE.

  3. (Optional) Dump the Lua Bindings fromm UE4SS Gui console, and follow the recommendations to load them here.

Then open the Mods/ folder of your UE4SS installation in your IDE, and create or modify your mod inside it.

Applying code changes

The main benefit of developing Lua mods is that you can quickly edit Lua sources without recompiling/rebuilding the C++ mod library as is always the case with C++ mods, and retry without restarting the game.

You can either:

  • reload all mods from the UE4SS GUI Console with the "Restart All Mods" button on the "Console" tab, or,
  • enable "Hot reload" in UE4SS-settings.ini and use the assigned hotkey (Ctrl+R by default) to do the same.

Your first mod

In the main.lua file of your mod, write some code that will try to access the objects of Unreal Engine inside your target game and do something that you can observe in the UE4SS console.

You can start by trying just

print("[MyLuaMod] Mod loaded\n")

and once you have verified that it runs OK, you can start implementing some actual functionality.

The example code below is fairly generic and should work for many games supported by UE4SS.

It registers a hotkey Ctrl+F1 and when pressed, it reads the player coordinates and calculates how far the player has moved since the last time the hotkey was pressed.

Note that the logging print calls include the name of the mod in square brackets, as it helps you find your mod's output among other log strings in the console.

The player coordinates are retrieved in the following way:

  1. Gets the player controller using UE4SS UEHelpers class.
  2. Get the Pawn, which represents the actual "physical" entity that the player can control in Unreal Engine.
  3. Call the appropriate Unreal Engine method K2_GetActorLocation that returns a Pawn's location (by accessing its parent Actor class).
  4. The location is a 3-component vector of Unreal Engine type FVector, having X, Y and Z as its fields.
local UEHelpers = require("UEHelpers")

print("[MyLuaMod] Mod loaded\n")

local lastLocation = nil

function ReadPlayerLocation()
    local FirstPlayerController = UEHelpers:GetPlayerController()
    local Pawn = FirstPlayerController.Pawn
    local Location = Pawn:K2_GetActorLocation()
    print(string.format("[MyLuaMod] Player location: {X=%.3f, Y=%.3f, Z=%.3f}\n", Location.X, Location.Y, Location.Z))
    if lastLocation then
        print(string.format("[MyLuaMod] Player moved: {delta_X=%.3f, delta_Y=%.3f, delta_Z=%.3f}\n",
            Location.X - lastLocation.X,
            Location.Y - lastLocation.Y,
            Location.Z - lastLocation.Z)
        )
    end
    lastLocation = Location
end

RegisterKeyBind(Key.F1, { ModifierKey.CONTROL }, function()
    print("[MyLuaMod] Key pressed\n")
    ExecuteInGameThread(function()
        ReadPlayerLocation()
    end)
end)

When you load the game until you can move the character, press the hotkey, move the player, press it again, the mod will generate a following output or something very similar:

...
[2024-01-09 19:37:27] Starting Lua mod 'MyLuaMod'
[2024-01-09 19:37:27] [Lua] [MyLuaMod] Mod loaded
...
[2024-01-09 19:37:32] [Lua] [MyLuaMod] Key pressed
[2024-01-09 19:37:32] [Lua] [MyLuaMod] Player location: {X=-63.133, Y=4.372, Z=90.000}
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Key pressed
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Player location: {X=788.232, Y=-639.627, Z=90.000}
[2024-01-09 19:37:39] [Lua] [MyLuaMod] Player moved: {delta_X=851.364, delta_Y=-643.999, delta_Z=0.000}
...

Using Custom Lua Bindings

To make development of Lua mods easier, we've added the ability to dump custom Lua bindings from your game. We also have a shared types file that contains default UE types and the API functions/classes/objects that are available to you.

Dumping Custom Lua Bindings

Simply open the Dumpers tab in the GUI console window and hit the "Dump Lua Bindings" button.

The generator will place the files into the Mods/shared/types folder.

Warning: Do not include any of the generated files in your Lua scripts. If they are included, any globals set by UE4SS will be overridden and things will break.

To Use Bindings

I recommend using Visual Studio Code to do your Lua development. You can install the extension just called "Lua" by sumneko.

Open the Mods folder as a workspace. You can also save this workspace so you don't have to do this every time you open VS Code.

When developing your Lua mods, the language server should automatically parse all the types files and give you intellisense.

Warning: For many games the number of types is so large that the language server will fail to parse everything. In this case, you can add a file called .luarc.json into the root of your workspace and add the following:

{
    "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
    "workspace.maxPreload": 50000,
    "workspace.preloadFileSize": 5000
}

How to use your mod's directory as workspace

As alternative you can open just your mod's root directory as workspace.
In this case you need to add a .luarc.json with "workspace.library" entries containing a path to the "shared" folder and the "Scripts" directory of your mod.
Both paths can be relative.
Example .luarc.json:

{
    "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
    "workspace.maxPreload": 50000,
    "workspace.preloadFileSize": 5000,
    "workspace.library": ["../shared", "Scripts"]
}

Annotating

To get context sensitive information about the custom game types, you need to annotate your code (alternative documentation). This is done by adding a comment above the function/class/object that you want to annotate.

Example

---@class ITM_MisSel_Biome_C
local biome = FindFirstOf("ITM_MisSel_Biome_C")

---@type int
local numMissions = biome.NumMissions

---@type FVector
local soundCoords = { 420.5, 69.0, 3.1 }
biome:SetSoundCoordinate(soundCoords)

Custom Game Configs

important

Some of these files may be out of date as the games/UE4SS updates. If you find that a game's custom game config is out of date, please open an issue on the UE4SS-RE/RE-UE4SS repository. Make sure that you first test if the game works without the custom game config, as it may have been fixed in the latest version of UE4SS.

These settings are for games that have altered the engine in ways that make UE4SS not work out of the box.

You need to download the files from each folder for your game and place them in the same folder in your UE4SS installation. For example, downloading the configs for Kingdom Hearts 3 should result in your files being in the following structure:

Binaries/Win64/
├── CustomGameConfigs/
│   └── Kingdom Hearts 3/
│       ├── UE4SS_Signatures/
│       │   ├── FName_Constructor.lua
│       │   ├── FName_ToString.lua
│       │   ├── StaticConstructObject.lua
│       ├── MemberVariableLayout.ini
│       ├── UE4SS-settings.ini
│       ├── VTableLayout.ini

... but obviously the file structure will change depending on the game's configs.

If you download the zDEV version, all these files are already included in the zip file.

You can find them here

UE4SS Game Support (AOB only)

This is a list of games that UE4SS will find AOBs for out of the box.

important

Full support is not implied just because all AOBs are found for a game.
There can be various other problems preventing UE4SS from working out of the box.

Inclusion of a game in this list does not guarantee support or imply endorsement of UE4SS usage on that game.
Users are advised to review and comply with the game's Terms of Service and any applicable agreements before using mods or third-party software.

There's a second list at the bottom that contains games that Patternsleuth is testing, but can't find AOBs for.
These lists are powered by Patternsleuth.

Games tested with all AOBs found by Patternsleuth

412S_Torn
413DX_Fastigium
413D_The Last Look
413X_Carlos III
413X_IKEA VR Experience
413X_IKEA VR Pancake Kitchen
413X_Kismet2D
413X_Kismet_Oculus_Debug
413X_Moon Landing VR
413X_Smackitball
413X_Tomorrow
413X_datavizVR
413_Dawn
413_DesertRideCoaster
413_Egg Time
413_Extra Terrestrial Perception
413_Fantasynth
413_Fantasynth One
413_GaryTheGull
413_Hello Neighbor Alpha 3
413_Kismet
413_Kismet2D
413_Kismet_Oculus
413_MONITOR The Game
413_MagixHome VR
413_Mouse Playhouse
413_Nick
413_Pumpkin SculptrVR
413_RacetronicVR
413_Roots of Insanity
413_SenzaPeso
413_Storm VR
413_Styx2
413_The Fall of Lazarus
413_Thread Studio
413_Tiny Toyfare
414S_TestProj
414X_Amigo VR
414X_Jaguar I-PACE Concept
414X_Orc Hunter VR
414X_Snowday
414X_Storm VR
414_Annual
414_Biodigital
414_Conarium
414_ECHO
414_Echoes of the Fey Episode 0 The Immolation
414_Freedom Locomotion VR
414_J.A.W.S
414_JUMPER SPEEDRUN
414_Kyklos Code
414_Magic Tavern
414_Medusa's Labyrinth
414_Oblivion Tesseract VR
414_Power War The First Men
414_President Erect VR
414_Snow Games VR
414_The Midnight Sanctuary Demo
414_The Wendigo
415S_Brookhaven
415X_A Buttload of Free Games
415X_Amigo VR
415X_Downward Demo
415X_ORE
415X_Space Needle VR
415X_VRFlush
415X_imos VR LOFT
415_Amigo Fishing
415_Archangel Hellfire - Enlist FREE
415_Clark HOOVA VR
415_Covert Syndrome
415_Danger Zone
415_Dark Frontier
415_Einar
415_Gangsta Underground The Poker
415_Hello Neighbor Alpha 4
415_Hollowed
415_Home - A VR Spacewalk
415_Kiitsu
415_Lucky Night Texas Hold'em VR
415_Lucky Night VR
415_No70 Eye of Basir
415_ORE
415_RideOp - VR Thrill Ride Experience
415_Shardbound
415_Subject A-119
415_The IOTA Project
415_The Muybridge Mausoleum
415_They have HORNS
415_Trinity
415_Waking the Glares - Chapters I and II
415_We Walked In Darkness
415_YoreVR
415_Zombie Hobby VR
415_imos LOFT
416X_Anti Gravity Warriors VR
416X_Captivus
416X_DontMakeLove
416X_Kaisuo
416X_Tesseract VR
416_A Walk in the Woods
416_Aven Colony
416_Beer'em Up
416_Bernackels' Shoggoth
416_Covert Syndrome
416_Crash Force
416_Don't Make Love
416_Driftland The Magic Revival Demo
416_Egg Time
416_Elemental Combat
416_Fuel Renegades
416_Gamble of Gods
416_Game-Ready Assets
416_Guns'n'Stories Preface VR
416_Hunter's Trial The fight never ends
416_Insanity VR Last Score
416_Kaisuo
416_Kukui
416_Logout
416_MSI Electric City Core Assault
416_Marie's Room
416_Nomads of Driftland
416_ProjectM Dream
416_Protonwar
416_Shardbound
416_Sink or Skim
416_The Red Stare
416_The Song of Terminus
416_Up in the Air
416_Virtual Foosball
416_Virtual Space
416_vBuilder
417D_Hellblade
417S_Ancestors Legacy Free Peasant Edition
417S_Readout
417X_Beat Boxers
417X_Light of Mine
417X_Rugon - Unfinished
417X_SCP022
417X_Shinrin-yoku Forest Meditation and Relaxation
417_Bridge Trek
417_Casual Spider Solitaire
417_Cures and Curios
417_Dark Frontier
417_Fly Destroyer
417_Grip
417_Junkyard Simulator First Car (Prologue 2)
417_Kinoko
417_Light Rider
417_MOTHERGUNSHIP Gun Crafting Range
417_MSI Electric City Core Assault
417_MSICoreAssault
417_Marius
417_Midair
417_Morgan lives in a Rocket House VR
417_Morgan lives in a Rocket House in VR
417_Mutant Year Zero Road to Eden Demo
417_North Stars
417_Regenesis Arcade Lite
417_Rocket Armor
417_Run Of Mydan
417_STARDROP Demo
417_Shardbound
417_Tekling 2
417_Wheel Riders Online OBT
417_Z Escape
418S_Absolver
418S_Conarium
418XS_Hang Up
418XS_SpellBreak
418XS_Test Subject 901
418X_Assault on Hyperion Base
418X_Burning Daylight
418X_Dragon Quest XI S Demo
418X_Excursion
418X_Fatal Velocity Physics Combat
418X_Mutation Mayhem
418X_Nefertari Journey to Eternity
418X_Renters Revenge
418X_Saloon VR
418X_Volcano Eruption
418_Air Raid Over Britain
418_And I Must Scream
418_Arboreal
418_Archangel Hellfire - Enlist FREE
418_Blackbox
418_Bloodstained - Ritual of the Night
418_Cold Space
418_Egg Time
418_FROSTBITE Deadly Climate
418_Fireflies
418_Fishy
418_Fury Strike
418_Gizmo
418_Hexen Hegemony
418_Hold 10 Seconds
418_Infinite Vector
418_J.A.W.S
418_Larkin building by Frank Lloyd Wright
418_Liminal
418_Meet the Miner - WDR VR Bergwerk
418_Notified
418_ORE
418_Octopath Traveler
418_Oculus_MontereySetup
418_RAW FOOTAGE
418_Rail Recon
418_Realities
418_Rig or Skill PC Brawl
418_Rocket Armor
418_Sketchbox
418_Slime CCG
418_The Fall of Lazarus
418_The Mammoth A Cave Painting
418_The Steadfast VR Challenge
418_To the Core
418_UNDEFEATED
418_UNHALLOWED THE CABIN
418_Virtual Space
418_Vision
418_Voyage Senki VR
418_Wormhole City
419S_Helium Rain
419S_The Occupation
419S_Tower_Of_Guns_2
419X_Air Raid Over Britain
419X_Aztec Tower
419X_Beer and Skittls VR
419X_El Hincha - El Videojuego
419X_Gobligeddon
419X_RideOp - VR Thrill Ride Experience
419_Across The Line
419_Annual
419_Ascend
419_Ashen
419_Ashes of the Ark
419_Aztec Tower
419_Beyond the Stars VR
419_Blanket Heavy With Nightmares
419_Blast the Past
419_Cyberdrome
419_Darksiders 3
419_DeathTolls Experience
419_Debit And Credit A Walk Through Accounting Hell
419_Distorted Illusions
419_Dream Golf VR
419_DreamFly
419_ExoTanks
419_Expedia Cenote VR
419_FPSBois
419_FrostRunner
419_Greetings
419_Grudge TV
419_Guns'n'Stories Preface VR
419_Guns'n'Stories Preface VR
419_Hotel Remorse
419_Islands of Nyne Battle Royale
419_It Runs Red
419_John Condemned
419_Jump Force
419_La Camila A VR Story
419_La Rana
419_Long Arm of the Law
419_Magika Land of Fantasy
419_Mobler
419_Muerte's Arena
419_My Friend is a Raven
419_Nutrients for Life
419_Once A Stray
419_Optimum Link
419_Project Skylab
419_Quanero 2 - System Release
419_RIDE 3
419_Raatihuone
419_Remnants
419_Sanguine Soul
419_Scamp High Hat Havoc
419_Self-knowledge VR
419_Sheaf - Together EP
419_Sketchbox
419_Solitaire Knights
419_Space Needle VR
419_Storm VR
419_Tails Noir Prologue
419_The Ball Encounter
419_The Mill Blackbird VR Experience
419_The Nest
419_The Occupation Demo
419_TheBeastInside
419_Thirdmage
419_Time
419_Turbo Tunnel
419_Voipas
419_Wacca
419_WellTown
420D_SQUIDS FROM SPACE
420S_Backyard Brawl
420S_Helium Rain
420S_HelloNeighbor
420X_Adagio
420X_The ScreaMaze
420_Abstract
420_Accel
420_Avalo Legends
420_BarnFinders The Pilot
420_BarnFindersDemo
420_BaseProject
420_Borderlands 3
420_CHALICE
420_Castle Kong
420_Circuit Slinger
420_Containment Corps
420_Dimension Of Gameth
420_Dino Delivery
420_Doctor Who The Runaway
420_Dummy!
420_Echoes of the Fey Episode 0 The Immolation
420_Egg Time
420_Empires Apart
420_FLS
420_Face Your Demons
420_Genesis
420_Hellbound Survival Mode_Classic
420_Ice Cream Killer
420_Islands of Nyne
420_Islands of Nyne Battle Royale
420_JermaSlots
420_Kogarashi
420_Kra-Ken
420_Kukui
420_MIDI RANGER
420_Meaty McSkinBones
420_Moo Moo Move
420_OFFSIDE
420_Oculus_Home2
420_Old Town Stories
420_Orange Cast Prologue
420_Perdition
420_Project Skylab 2
420_Project Skylab 3 A New Frontier
420_Rise of One
420_Saint Paul Pre-Alpha
420_Shenmue III
420_Steve's Pub - Soda on tap
420_Strange Encounter
420_Streets of Neotokio
420_Tell Me Why
420_The Alchemist's House
420_The Great Emu War
420_Time Break 2121
420_Tiny Tinas Wonderlands
420_Virtual Viking
420_What Never Was
420_Zeus' Battlegrounds
420_pla_toon
421D_JourneyToTheSavagePlanet
421D_MY HERO ONE'S JUSTICE 2 Demo
421D_StarWarsJediFallenOrder
421D_TheOuterWorlds
421S_Samurai Shodown
421S_Tay Son Dynasty
421X_Endless Combat
421X_Light Rider
421X_Mogh
421X_Shattered Lights
421X_Sky Residences at Ice District
421X_Trash Time
421X_Veritex
421X_Win with steadily
421_Air Raid Over Britain
421_Antarctic Heritage Trust
421_Avast Ye
421_Bone Voyage
421_Capsa
421_Captain Starshot
421_CityWarHeroes VR
421_CityofBrass
421_Core Of Darkness
421_Drug Dealer Simulator Demo
421_Drug Dealer Simulator Free Sample
421_E06-Anomaly
421_Earthshakers
421_Experience Colorblindness
421_Fork Knights
421_Ganghood Survival
421_Happy World
421_Hell To Raze
421_Hellbound Survival Mode
421_Hide and go boom
421_Ironsmith Medieval Simulator Prologue
421_Light Rider
421_Living Dead
421_Muscle Magic
421_Night shot
421_NineTrials
421_Obduction
421_Omno Prologue
421_PROZE Prologue
421_Papers Dolls Original
421_Realities
421_Shattered Lights
421_Sky Shepherd
421_Snowrifters VEX
421_Tailypo The Game
421_Tay Son Dynasty
421_Tekling 2 Overdrive
421_The Chasm - Mines Of Madness
421_The First Confrontation
421_The Marvellous Machine
421_Ties
421_Tobit
421_WELCOME
421_Wish World
421_Wormhole City
422DS_CARRUMBLE
422D_DarksidersGenesis
422D_RemnantFromTheAshes
422S_AAW Wrestle Lab
422S_City of Brass
422S_The Last Stand
422S_Zagu Espionage
422S_not my car
422XD_GothicRemake_Demo
422XS_WarriOrb Prologue
422X_Bombrigade Battlegrounds
422X_Gobligeddon
422X_Gothic Remake Demo
422X_GunBoxing
422X_Horizon Beyond
422X_Immortal Hero
422X_John's Wizard Dungeon
422X_Me and myself
422X_Ranger Danger
422X_SCP The Foundation
422X_Spartan Commander Realtime
422X_Tailypo The Game
422X_The Emperor's Own Alpha Teaser
422X_Town Defence
422_1001 Hugs
422_Aircar
422_Apollo's Adventure
422_Arrow
422_Ashi Wash
422_Avast Ye
422_Bee Simulator
422_Brutal Games
422_Chex Quest HD
422_Chromalition
422_Corpse disaster-survivors
422_Dark Deception
422_Dark Fracture Prologue
422_Destroy All Humans
422_Discrepant
422_ESCAPE POINT
422_Escape from Labyrinth
422_Funny Archery
422_Ginkgo
422_Golden Moon
422_Goofballs
422_HRDINA
422_Hello Guest Hello Neighbor 2 pre-alpha
422_Heroes of Fortunia
422_Homeland Lay to Rest
422_Hoverloop
422_Hypatia
422_IL DIVINO Michelangelo's Sistine Ceiling in VR
422_John's Wizard Dungeon
422_LAVALAMP
422_Little Helper
422_Magika Land of Fantasy
422_ManicMiners
422_Misfire
422_MissionX Beta
422_Mythic Ocean Prologue
422_NEKROTRONIC VR
422_Northern Lights
422_OpenVR Benchmark
422_Other Submarine
422_Panic Station VR
422_Pedal to the Metal
422_Quest 4 Papa Reloaded
422_Rhome
422_Robots, Death & Venice
422_Rockytown
422_Sacred Siren
422_Shepherd of Light
422_Sketchbox
422_Subverse
422_TRAHA Global
422_Tailypo The Game
422_Tech Support 2077
422_Tex-Mechs
422_The Call of Karen
422_The Mammoth
422_The Mammoth A Cave Painting
422_To Be Headed Or Not To Be
422_Total Seclusion
422_Touhou Multi Scroll Shooting
422_Trials of Mana Demo
422_Twin Mirror
422_Vectromirror 0
422_Vision
422_War Smith
422_Wet Dog Corp
422_Win with steadily
422_Z Escape
422_Zedfest
423D_LifeIsStrangeRemastered
423D_MotoGP2020
423S_Colory Engine
423S_Hit n' Rush
423S_King of Seas
423S_Paradise Lost
423S_Terror of the Seven Seas Demo
423S_The Legend of Karl
423S_Warhammer 40,000 Dakka Squadron - Flyboyz Edition Demo
423X_DEUS EX MACHINA Stage Zero
423X_Endless Combat
423X_Extraction Valley
423X_Hard, Fast, & Flashy
423X_Project Grove Prologue
423X_Retention A Love Story
423X_UNHALLOWED THE CABIN
423_Agent-00
423_Animal Rescuer Prologue
423_Another_World
423_Arcus
423_Beetle Hunter
423_Castle Fight
423_Chronicles of cyberpunk - Deep sleep
423_Clay Game
423_Disaster Report 4 Summer Memories Demo
423_Discrepant
423_Escapeworld Dilemma
423_FIT Food
423_Firing Vibes
423_Flicker of Hope
423_Galaxy in Turmoil
423_Genesis Alpha One
423_Hellbound Survival Mode
423_Hellbound Survival Mode_Remaster
423_Hypatia
423_IMPACT
423_Kari
423_Kill It With Fire HEATWAVE
423_Kill It With Fire Ignition
423_Knockout Daddy
423_Ludicrous Speed
423_Luxocraft
423_Magical Strings
423_Mysteria Occult Shadows
423_Netherverse
423_Orion The Eternal Punishment
423_P.A.I.N.T
423_Peasants War
423_Plan V Virtual Studio
423_Project Exhibited
423_Pulse Forge VR v0.544
423_Raji Prologue
423_Rollers
423_SKYE
423_Sanatorium Anthropocene Retreat
423_Santa's Visit
423_Saranity
423_SgyuinBaldo
423_Spider Fear
423_SpongeBob SquarePants - BfBBR
423_Sporadic Spire
423_Strange Creatures
423_Tahul
423_The Desert's Rose
423_The Mutineer
423_The Unexpected Quest Prologue
423_VR Jetpack Game
423_VirtualCop
423_Wall Force
423_We Went Back
423_We're All Going To Die
423_Zagan Must Be Rescued
423_Zoelie - SCAD Games Studio
423_Zolaris
424D_Stela
424D_TonyHawk1and2
424S_AAW Wrestle Lab
424S_Aces in the Dust
424XS_Drop In - VR F2P
424X_Binaural Odyssey
424X_Death World
424X_Delta-G
424X_Lost Robot
424X_Pots and Potions
424X_Sweeping the Ruins
424X_Town Defence
424X_the fairytale of DEATH
424_6th Dimension
424_Astria
424_Beyond The Diorama Caribou World
424_Boo's Balloons
424_Chaos
424_Chicken Duty
424_Childlike
424_Code Zero
424_Cold Space
424_Cragls
424_Dark Deception
424_Devolverland Expo
424_Devolverland_Expo
424_Doggone Hungry
424_DreamRZ
424_Empires of the Undergrowth
424_Fadeout Underground
424_Ghost of Tomorrow Chapter 1
424_Ghostrunner Alpha
424_Glitch Arena
424_Gorge
424_Heretic's Lot Prologue
424_Huan-Yuan Sword 7
424_Impulse, From Here
424_Kidgilantes
424_KillSteel
424_KurtzPel
424_Liquidators
424_Little Nightmares II Demo
424_Little Traveler
424_MIR4
424_Maelstrom
424_Mortal Shell
424_Mousebound
424_Neokaiju
424_Octarina
424_Operation Covid-19
424_Operencia The Stolen Sun Demo
424_Out Of The Shelter
424_Pejes Vs Zombies
424_PengoRoyale
424_Potionomics
424_Primordium - Day Zero
424_Refight The Last Warship
424_Reflect Horizons
424_Refrigerant Recovery Simulation
424_Rushmore
424_Samurai Cooking
424_San Diablos
424_So long as there is Mercy
424_Sun Warriors
424_The Longest Walk
424_The Unluckiest Man
424_TheInfected
424_Transcender
424_UldreVoid
424_Undead Run
424_Understanding, The Game
424_VELVETIST Prototype
424_Vaccine19
424_Virtual Exhibition
424_Volumetric History
424_Walking Simulator
424_Walking Simulator 2020
424_Waves 2
424_lightblue
424_pla_toon
425D_DeepRockGalactic
425D_GothamKnights
425D_Kena
425D_MotoGP2021
425D_Sackboy
425S_DRG
425S_Dungeon Swappers
425S_Guilty Gear Strive
425S_Hideout Face your fears
425S_Learning Life - Mysteeri 247
425S_Occupy White Walls
425S_The Medium
425S_UnfortunateSpacemen
425X_Dissolution
425X_DreamFly
425X_Learn Spanish VR
425X_Phoenix Squadron Azure Stars
425X_Solitairica_Debug
425X_The Orville - Interactive Fan Experience
425X_UnfortunateSpacemen_Server
425_A Night In Berlin
425_A Place, Forbidden
425_Agent of Chaos
425_Apocalypse 2.0 Edition
425_BP Only Project
425_BUGWORLDONLINE PRESENTS MITCH MAKER
425_Boom Boomerang
425_Bound Forest Alpha
425_Boundary Benchmark
425_Builders of Egypt Prologue
425_Castle Kong
425_Chesnakisnak
425_Corpse disaster-survivors
425_DeadIsland2
425_Defective Holiday
425_Experience Colorblindness
425_Fall of an Empire Demo
425_Farlight84
425_First Person Shooter Kit Showcase
425_Galaxy in Turmoil
425_Ghostrunner
425_Gordian Rooms A curious heritage Prologue
425_Hellblade Senuas Sacrifice
425_Hello Neighbor 2 Alpha 1
425_Hidden Protector ROADTRIP (Preface)
425_IMPACT
425_INFLUXIS
425_Infinadeck Stonehenge
425_John Condemned
425_Little Helper
425_Luxocraft
425_Made in Abyss Binary Star Falling into Darkness
425_Maglam Lord
425_Meditation VR
425_Milkyway Funland
425_Mist of the Undead
425_OUTRIDERS Demo
425_Oxygen First Breath
425_Pejes Vs Zombies
425_Planet
425_Primordium - Day Zero
425_Pulseball
425_Quest 4 Papa Reloaded
425_Robin Hood - Sherwood Builders
425_Rokka
425_Ruff Night At The Gallery
425_SUPER DRINK BROS
425_Singularity
425_Solar Ash
425_Solitairica
425_Soundscape VR 2017
425_The Episodic
425_The Last Show of Mr. Chardish Act I
425_The Orville - Interactive Fan Experience
425_The Tool
425_TrenchesWIP
425_Virtual Exhibition
425_Witches x Warlocks
426D_HotwheelsUnleashed
426D_StarWarsJediSurvivor
426D_TheAscent
426S_A Clever Label
426S_COG Back To The 80s
426S_Elspell
426S_Escape From School
426S_EvilWest
426S_JUST DEFENSE
426S_Kibbi Keeper
426S_Legend of the Outlaw Mage
426S_Let It Flow
426S_MechWarrior5
426S_NASCAR 21 Ignition
426S_Opus Castle
426S_Project Wunderwaffe Prologue
426S_Sifu
426S_Slappyball
426S_Space Rescue
426S_Strategic Mind Spirit of Liberty - Prologue 1939
426S_Super Raft Boat VR
426S_TinShift
426S_Trash Patrol - Academic Version
426S_Twin Stones The Journey of Bukka
426S_WW2 Rebuilder Germany Prologue
426XS_Let It Flow
426XS_TinShift
426X_BadBunch
426X_Dead Transmission
426X_Operation Airsoft Beta
426X_Phoenix Squadron Azure Stars
426X_Project Grove Prologue
426X_Tirta Demo
426_ALTF4
426_Apocalypse 2.0 Edition
426_Ars Fabulae Demo
426_BEACHED
426_BloodPit Skelton II
426_Bloodhunt
426_Breeders of the Nephelym Alpha
426_Bridge of Dawn
426_Builders of Egypt Prologue
426_CandleKnight
426_Defenders Dark Side Demo
426_DemonsAreCrazy
426_Dizzy Two
426_Dodge It
426_Dreams of Valhalla Demo
426_EnderLilies
426_Everlife
426_FIT Food
426_Face Your Demons
426_Fight or Flight
426_Fishy
426_Fist
426_Ghostrunner
426_Hanako Honor & Blade
426_Heroes of Agora
426_INFLUXIS
426_Karl BOOM
426_Kenopsia
426_MageRun
426_Milkyway Funland
426_Mirages Demo
426_MissionX Beta
426_Mousebound
426_Necromunda Hired Gun
426_Pirates of the Asteroid Belt
426_Poppy Playtime
426_Pulse Forge VR v0.544
426_Raji An Ancient Epic
426_Santa's Visit
426_Souls of the Wind
426_Station to Station Demo
426_Survival Horror 8,436
426_Tails Noir Prologue
426_The Desert's Rose
426_The Last Show of Mr. Chardish Act I
426_The Sand Dunes
426_Torchlight Infinite
426_Torchlight_Infinite1791001
426_Train Sim World 5
426_VRParadise
426_Vampire The Masquerade Swansong
427C_cyubeVR
427D_GundamEvolution
427D_Laysara Summit Kingdom Demo
427D_Stray
427Denuvo_Persona3Reload
427P_Video_Horror_Society
427S_ANDARA RISE FOR REBELLION
427S_ArcRunner
427S_Blackout Protocol Demo
427S_ColdSun
427S_Conjury Revell
427S_Cosmic Shake
427S_Dummy_SIG_AES
427S_Eleonor's Nightmares
427S_FNAF9 Security Breach
427S_FRAGROOM Defenders
427S_Firefly Studio
427S_Forever Skies
427S_GangsOfSherwood
427S_Ghostrunner 2 (EGS)
427S_Gord Demo
427S_Hex Rally Racers
427S_HighOnLife
427S_Iragon Prologue 18+
427S_Occupy White Walls
427S_Opus Castle
427S_Paragonian 3 The Warrior of Light
427S_Passed Out Prologue
427S_ProBee
427S_Scuffy Game
427S_Slimegeon
427S_Symbiotic
427S_The Lord of the Rings Return to Moria
427S_TheImmortalMayor
427S_Tintin Reporter - Cigars of the Pharaoh
427S_Tribe XR DJ Academy
427S_UnderTheWaves
427XS_DaVinCo
427XS_Tintin Reporter - Cigars of the Pharaoh
427X_Dark Deception
427X_Potato Arena Prologue
427X_The Orville - Interactive Fan Experience
427_1001 Hugs
427_AES_Secondary
427_AEWFF
427_Afterimage
427_AitD Prologue
427_Aliens Dark Descent
427_Aliens_Dark_Descent
427_Boltgun
427_Breeders of the Nephelym Alpha
427_CCFF7R
427_CPPFPS
427_Chaos Hogwarts Dummy Uproject
427_DRG
427_Daymare 1994 Sandcastle
427_Deadlink
427_Deceive Inc
427_Desynced
427_Echo Point Nova Demo
427_EntropyCentre
427_Escape_The_Backrooms
427_Everspace 2
427_FNAF Security Breach
427_Firmament
427_Ghostrunner2
427_Granblue_Fantasy_Versus_Rising
427_Granblue_Fantasy_Versus_Rising2
427_Grimlord
427_GroundBranch
427_Grounded
427_Gun Grave Gore
427_HAWKEN Reborn
427_Harry Potter Quidditch Champions
427_Harvestella
427_HelloNeighbor2
427_HogwartsLegacy
427_INSOMNIA Demo
427_Into the Radius VR
427_Island of Winds Demo
427_Karl BOOM
427_LiesOfPDemo
427_Luna Abyss Demo
427_MailTime
427_Marie's Room
427_Miasma Chronicles
427_Moon Mystery Demo
427_MotoGP23
427_Octopath Traveler 2
427_OtisTestGame
427_Pact of Joy Prologue
427_Peasants War
427_Phoenix Squadron Azure Stars
427_Project Zero 2 DEMO
427_Prominence
427_REDACTED
427_RENEGADE
427_Railgrade
427_Rise of Gun
427_Rivals Squad
427_RoundZ Demo
427_SUPERCHICOS
427_Sanatorium Anthropocene Retreat
427_SandofAura
427_Santa's Visit
427_Satgat Demo
427_Scribble It
427_ShameLegacy
427_Sherlock Holmes The Awakened Demo
427_Six Days in Fallujah
427_SlavicPunk Oldtimer Demo
427_Star Trek Resurgence
427_Stray Gods Demo
427_Strip 'Em
427_System Shock Demo
427_System Shock Full
427_TO4 Tactical Operations
427_Tagline Demo
427_Tails Noir Prologue
427_Texas Chain Saw Massacre
427_The Desert's Rose
427_The Invincible Demo
427_The Lord of the Rings Gollum
427_The Orville - Interactive Fan Experience
427_Trepang2
427_Trepang2 Demo
427_Trial Out
427_TriangleStrategy
427_Unholy Demo
427_Virtual Viking
427_Voidtrain
427_WRC 2023
427_Warstride Challenges
427_Wayfinder
427_Witchfire
500S_Cosmopoly
500S_Marionette Mates
500S_Odyssee numerique
500S_The Unfallens Awakening
500S_Velocity Rift
500XS_CETERIS Paribus
500XS_Circle Killer
500XS_ConquestReimagined
500XS_Cyber Attack VR board game
500XS_Misteri Rumah Pak RT
500XS_One More Wish
500XS_SCP Vacation
500X_A Little Time
500X_AimX
500X_CargoRun
500X_Class Five Memorial GAME
500X_Dream back
500X_Game Dev Arcade
500X_I Am Jesus Christ Prologue
500X_Jeff Logar
500X_Levels
500X_No Time For Caution
500X_Radiant Cell Demo
500X_Reality Simulator
500X_SCP Vacation
500X_Secrets of the Temple
500X_Shanubis
500X_Short Horror Story
500X_The Backroom - Lost and Found
500X_The Collector
500X_The Last Job
500X_The Tower
500X_Yerr Out
500_Arengard - Invasion
500_BRAiN VOMiTS GARDEN
500_BRIGHT TRACER
500_Blue Funk
500_Boot Camp Endless Runner
500_Bout
500_Casting Whispers Demo
500_Christmas Luge
500_Coronation
500_Dark and Darker Playtest
500_Degen Royale
500_Evil Nun The Broken Mask
500_EzBench Benchmark
500_Fracture City of Destruction
500_Handyman Corporation Prologue
500_Hunted Kalevala
500_Inner Abyss
500_Jam Above Jam Below
500_Kitty May Cry Demo
500_Last Meow Standing
500_Light Maze
500_MYRNEscapes
500_MeetYourMaker
500_MultiVerse
500_Operation Satoshi [Early Version Alpha]
500_Park Ranger Lost In The Woods
500_Peepo Island
500_Petals
500_Pirates
500_Playtime with Hoogie
500_Pool Game
500_Prime Horror II
500_Project Possession
500_Project Swap
500_Protocol Xeno
500_Raia Nova Demo
500_Rambell
500_Robo pocket 3d fighter with rollback
500_SCP Fragmented Minds Demo
500_Scare Girl
500_Soccer But Different
500_Solar Showdown Playtest
500_SpeedFreeks PlayTest
500_Stalker
500_StarAtlas
500_TRAFFIC
500_TVG (The Vox Games) Journey
500_The Hidden Room
500_The Horror Story Remastered
500_Ubaste
500_Visite virtuelle de l'Assemblee nationale
500_Wrath of Anias
500_ZPlague
501D_Jusant_Demo
501S_BlasterBeat
501S_COG Back To The 80s
501S_Coreborn Nations of the Ultracore Demo
501S_Dawn vs Dusk
501S_Dominator Idle
501S_Greedland Demo
501S_Let's Learn Lingit
501S_Potions and Emotions
501S_SeaFeud
501S_StackoBot
501S_Viking Hiking
501XS_AgreementZero
501XS_Dice
501X_ASSASSIN The First List Demo
501X_Apex Rush
501X_BMX Bastards
501X_Dragonspire Demo
501X_First Person Shooter Kit Showcase
501X_Garten of Banban
501X_Golf Party
501X_Karagon Demo
501X_Metaart
501X_Regalia
501X_SurvivalIsLand
501_ALMOST Demo
501_ALTF42
501_After School
501_American Arcadia Demo
501_Astral Shipwright
501_Coreborn Nations of the Ultracore Technical Beta
501_Creature Rumble
501_Crossing Frontier(DEMO)
501_DESORDRE Demo
501_Degen Royale
501_Demonologist
501_Dinodash
501_Dominator Idle
501_Duskborn
501_ENENRADemo
501_En Garde
501_Endure Island
501_Evergreen - Mountain Life Simulator PROLOGUE
501_EzBench Benchmark
501_Game Dev Arcade
501_Garten of BanBan
501_Hero Team
501_Immortals of Aveum
501_Inworld Origins
501_Jusant Demo
501_KARDS - The WW2 Card Game
501_Korstrid
501_Layers of Fear Demo
501_LyraExample
501_Monsters Domain Demo
501_My Dad Left Me
501_NightGate
501_Omega Strikers
501_Only Up
501_Palia
501_Phantom-OS
501_Phoenix Squadron Azure Stars
501_Scuffy Game
501_Sengoku Dynasty
501_Sol Last Light
501_Source of Survival Early Access
501_StraySoulsDemo
501_Subway Sim
501_SurvivorsWill Demo
501_Swarm Survivor Demo
501_Sword Art Online Fractured Daydream
501_The Backroom - Lost and Found
501_The Casting of Frank Stone Demo
501_The Complex Found Footage
501_The Devil's Face Demo
501_The Hidden Room
501_The Horror Story Remastered
501_The Kindeman Remedy Demo
501_Triad Ball
501_Tribe Playtest
501_Tribe:PrimitiveBuilder
501_Vesna
501_Voidwalkers - Soul Hunters
501_Voidwalkers The Gates Of Hell
501_WinterfestWonderland
501_World of Football
501_Yerr Out
501_ZPlague
501_idk
502D_Remnant2
502P_PreRemnant2
502P_Remnant2
502S_Islands of Insight Open Playtest
502S_Let's Learn Lingit
502S_Remnant2_UProject
502S_Remnant2_UProject_AES
502S_Roomvas
502S_Scyla
502S_StackOBot
502S_Top Jump Prologue
502X_Direct Contact
502X_Grand Emprise Prologue
502X_SwammysAOTGame
502_Arcfall
502_BadRally
502_Chinese Frontiers Prologue
502_Compass of the Destiny Istanbul Demo
502_Fort Solis
502_Frostpunk2
502_Greed of Man
502_HexBat
502_KARDS - The WW2 Card Game
502_LabyrInk
502_Little Rhapsody of Game
502_LyraExample
502_Marbles on Stream
502_Metro Awakening VR
502_Only Climb Better Together
502_PaxDei
502_PlanetSprite
502_Project Possession
502_Remnant2_031223
502_Remnant2_131223
502_Robocop Rogue City Demo
502_Scholar's Mate - First Move Demo
502_Street's Disciple
502_Super Retro Rec League Racing
502_Tempo Mayhem
502_The Backroom - Lost and Found
502_The Day Before
502_The Hidden Room
502_The Isle
502_The Talos Principle 2 Demo
502_Time Survivors Chapter 0
502_Time Survivors Prologue
502_Top Jump Prologue
502_UVRT2
502_World War 1
502_ia scatter city
503S_AbioticFactor
503S_Bellwright
503S_Bellwright_29_08_24
503S_Kiborg Demo
503S_NobodyWantstoDie
503S_StackOBot
503_171
503_BrothersATaleOf2SonsRemake
503_Dwarven Realms
503_Forklift Racer
503_LEGO Horizon Adventures
503_LOLLIPOP CHAINSAW RePOP
503_Le Dernier Don
503_MosterJamShowdown
503_Once Alive
503_Panicore
503_Rennsport
503_Until Dawn
503_WildAssult
503_WildLife
503_Zoochosis
503_inZOI_Demo
504S_Abiotic Factor
504_BeerFactory
504_Deep Space 7
504_Enotria The Last Song
504_FaaastPenguin
504_MorseHorse6
504_THE JOY OF CREATION Demo

Games tested but with AOBs not found by Patternsleuth

402_HIT

FNameToString: expected at least one value
FTextFString: expected at least one value
StaticConstructObjectInternal: expected at least one value

405S_ARK Survival Evolved

FNameCtorWchar: expected at least one value
FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value
StaticConstructObjectInternal: expected at least one value

407_Kholat

FTextFString: expected at least one value
GUObjectArray: expected at least one value
StaticConstructObjectInternal: expected at least one value

407_Mist of the Dark

FTextFString: expected at least one value
StaticConstructObjectInternal: expected at least one value

407_Shot In The Dark

FTextFString: expected at least one value
GUObjectArray: expected at least one value
StaticConstructObjectInternal: expected at least one value

407_The Lost Mythologies

FTextFString: expected at least one value
StaticConstructObjectInternal: expected at least one value

408S_Gemini - Heroes Reborn

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Caffeine Demo

FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Gravitas

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Hello Neighbor Pre-Alpha

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Medusa's Labyrinth

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Shot In The Dark

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_The Park

FTextFString: expected at least one value
GUObjectArray: expected at least one value

408_Trials on Tatooine

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409S_RisingThunder

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_Deep Below

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_Dual Gear

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_Fortified

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_IKEA VR Experience

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_PROXY

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_The Flame in the Flood

FTextFString: expected at least one value
GUObjectArray: expected at least one value

409_The Rose and I

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410D_GnomesAndGoblin

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410S_Amigdala

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410X_Charlotte

FNameCtorWchar: found 2 unique values [14029B520, 14029B4A0]
GUObjectArray: expected at least one value

410X_DreamLand

FNameCtorWchar: found 2 unique values [14029C430, 14029C3B0]
GUObjectArray: expected at least one value

410X_SOLRAVEN

FNameCtorWchar: found 2 unique values [14029C160, 14029C0E0]
GUObjectArray: expected at least one value

410X_Shot In The Dark

FNameCtorWchar: found 2 unique values [1404DD150, 1404DD0D0]
GUObjectArray: expected at least one value

410_Atom Universe

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Calm Down, Stalin

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Hatred

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Hello Neighbor Alpha 1

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_INVASION!

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_JonahsPath

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Kismet

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Ley Lines

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Oculus_Dreamdeck

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_Pesadelo

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_TRON RUNr

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_The Divergent Series Allegiant VR

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_The Rose and I

FTextFString: expected at least one value
GUObjectArray: expected at least one value

410_WinBigOrDie

FTextFString: expected at least one value
GUObjectArray: expected at least one value

411DX_DaysGone

FNameCtorWchar: found 2 unique values [7FF69F616060, 7FF69F615FE0]
FTextFString: expected at least one value

411DX_Gears 5

FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

411DX_GearsTactics

FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

411S_TestProject

FTextFString: expected at least one value

411XS_TestProject

FNameCtorWchar: found 2 unique values [1404CCA80, 1404CCA00]
FTextFString: expected at least one value

411X_Clown2Beat

FNameCtorWchar: found 2 unique values [1401E9F00, 1401E9F80]
FTextFString: expected at least one value

411X_Home - A VR Spacewalk

FNameCtorWchar: found 2 unique values [1401EDE90, 1401EDF10]
FTextFString: expected at least one value

411X_NVIDIA VR Funhouse_Debug

FTextFString: expected at least one value

411X_SYNCH

FNameCtorWchar: found 2 unique values [1401E9F00, 1401E9F80]
FTextFString: expected at least one value

411X_Vistascapes VR

FNameCtorWchar: found 2 unique values [1404A0FF0, 1404A0F70]
FTextFString: expected at least one value

411_Abe

FTextFString: expected at least one value

411_Across The Line

FTextFString: expected at least one value

411_ArkhamVR

FTextFString: expected at least one value

411_Casual Spider Solitaire

FTextFString: expected at least one value

411_Gears 5

FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

411_HALP

FTextFString: expected at least one value

411_NEKOPALIVE

FTextFString: expected at least one value

411_NOKBAK

FTextFString: expected at least one value

411_NVIDIA VR Funhouse

FTextFString: expected at least one value

411_Portal StoriesVR

FTextFString: expected at least one value

411_Quanero

FTextFString: expected at least one value

411_Storm VR

FTextFString: expected at least one value

411_Super Ultra Monster Smash!

FTextFString: expected at least one value

411_Surge

FTextFString: expected at least one value

411_Tuebor

FNameToString: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

412D_Hide and Shriek

FTextFString: expected at least one value

412D_Moto Racer 4

FTextFString: expected at least one value

412X_Hello Neighbor Alpha 2

FTextFString: expected at least one value

412X_MSI Electric City

FTextFString: expected at least one value

412X_SpellKnights

FTextFString: expected at least one value

412_ABZU

FTextFString: expected at least one value

412_Allumette

FTextFString: expected at least one value

412_BATTLECREW Space Pirates

FTextFString: expected at least one value

412_BellyBots

FTextFString: expected at least one value

412_CRAPPY ZOMBIE GAME

FTextFString: expected at least one value

412_GENeuro

FTextFString: expected at least one value

412_HitBox

FTextFString: expected at least one value

412_Inner Chains

FTextFString: expected at least one value

412_Let Hawaii Happen VR

FTextFString: expected at least one value

412_Oculus_TouchNUX

FTextFString: expected at least one value

412_Please State Your Name

FTextFString: expected at least one value

412_Public Enemy Revolution Simulator

FTextFString: expected at least one value

412_Rexodus A VR Story Experience

FTextFString: expected at least one value

412_Savage Resurrection

FTextFString: expected at least one value

412_School Girl Zombie Hunter

FTextFString: expected at least one value

412_Scrap

FTextFString: expected at least one value

412_Seven The Days Long Gone Demo

FTextFString: expected at least one value
GUObjectArray: expected at least one value

412_The Turing Test

FTextFString: expected at least one value
GUObjectArray: expected at least one value

412_TheRoseAndI

FTextFString: expected at least one value

412_WakeUp

FTextFString: expected at least one value

414_Space Hulk Deathwing Enhanced Edition

EngineVersion: expected at least one value

415_Reaping Rewards

FTextFString: expected at least one value
GUObjectArray: expected at least one value

415_Vampyr

EngineVersion: expected at least one value

416M_Robo Recall Core

FNameCtorWchar: expected at least one value
FNameToString: expected at least one value
GUObjectArray: expected at least one value
StaticConstructObjectInternal: expected at least one value

416_Captain Spirit

FTextFString: expected at least one value

417D_SoulcaliburIV

FTextFString: expected at least one value

417_Kingdom_Hearts_3

FNameCtorWchar: expected at least one value
GUObjectArray: expected at least one value

417_Sword Art Online - Fatal Bullet

FTextFString: expected at least one value

417_idk

FTextFString: expected at least one value
GUObjectArray: expected at least one value

418D_CodeVein

FTextFString: expected at least one value

418_CodeVein

FTextFString: expected at least one value

418_FF7R

GUObjectArray: expected at least one value

420D_ManOfMedan

FNameCtorWchar: expected at least one value
GUObjectArray: expected at least one value

420D_TalesOfArise

FNameCtorWchar: expected at least one value
GUObjectArray: expected at least one value

421X_Lost City of Vampires

EngineVersion: expected at least one value

421_Malediction

FNameCtorWchar: found 2 unique values [140560DF0, 140560E70]

421_Marble Combat

EngineVersion: expected at least one value

423D_Mechwarrior5

FNameCtorWchar: expected at least one value

423_BravelyDefaultII

FTextFString: found 2 unique values [140D1CFA0, 140D1CA60]

424D_LittleHope

FNameCtorWchar: expected at least one value

424X_Leave The Red

EngineVersion: expected at least one value

425D_DemonSlayer

GUObjectArray: expected at least one value

425M_ReturnalCore

FNameCtorWchar: expected at least one value
FNameToString: expected at least one value
GUObjectArray: expected at least one value
StaticConstructObjectInternal: expected at least one value

425_Chivalry 2

FTextFString: found 2 unique values [14191B830, 141860840]

426D_HouseOfAshes

StaticConstructObjectInternal: found 2 unique values [7FF71F222300, 7FF720659BD0]

426_FF7Rebirth

EngineVersion: expected at least one value
FNameCtorWchar: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

426_Protoball

FTextFString: found 2 unique values [1410ECA70, 1410A1210]

426_Refight The Last Warship

FTextFString: found 2 unique values [1415BA320, 14154D2E0]

426_The Walking Dead Saints & Sinners

StaticConstructObjectInternal: found 2 unique values [140D09DB0, 142216380]

426_Will To Live Online

StaticConstructObjectInternal: found 2 unique values [140C61F60, 141CC9860]

427CS_clangPayday3Project

FNameCtorWchar: found 2 unique values [140DC75C0, 140DC7970]
FTextFString: expected at least one value

427D_HiFIiRush-646829

StaticConstructObjectInternal: found 2 unique values [140E6EDE0, 14289C850]

427S_The Outlast Trials

EngineVersion: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

427_Astro Colony

EngineVersion: expected at least one value

427_Atomic Heart

EngineVersion: found 2 unique values [EngineVersion(4.26), EngineVersion(4.27)]
StaticConstructObjectInternal: found 2 unique values [140E9DB80, 142693D30]

427_Denuvo_Homeworld3

EngineVersion: expected at least one value

427_GhostWireTokyo

EngineVersion: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

427_HIFIRush

StaticConstructObjectInternal: found 2 unique values [140E6EDE0, 14289C3E0]

427_Homeworld3

EngineVersion: expected at least one value

427_LiesOfP

GUObjectArray: expected at least one value
StaticConstructObjectInternal: found 2 unique values [140E526F0, 1422DB2B0]

427_LiesofP_010524

StaticConstructObjectInternal: found 2 unique values [140E54BA0, 1422E7760]

427_Splitgate

FTextFString: found 2 unique values [14173DF80, 14173DA10]

427_TheCallistoProtocol

FTextFString: expected at least one value
GUObjectArray: expected at least one value

427_TheOutlastTrials_119781

EngineVersion: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

500D_OBSKUR

EngineVersion: expected at least one value
FTextFString: expected at least one value

500S_Low Light Combat

EngineVersion: expected at least one value

500X_Kandra The Moonwalker

EngineVersion: expected at least one value

500_Black Myth Wukong Release Denuvo

EngineVersion: expected at least one value

500_HEADACHE

EngineVersion: expected at least one value

500_Jumpy Haha

EngineVersion: expected at least one value

500_Lunar Odyssey

EngineVersion: expected at least one value

500_OBSKUR

EngineVersion: expected at least one value
FTextFString: expected at least one value

500_Sorcerer Standoff

EngineVersion: expected at least one value

500_Source of Survival Early Access

EngineVersion: expected at least one value

500_Superfuse

FTextFString: found 2 unique values [141B41450, 141B41270]

500_The Market of Light

EngineVersion: expected at least one value

500_Watch Your Back

EngineVersion: expected at least one value

501S_LordsOfTheFallen-Pre

FTextFString: found 2 unique values [142BC7A90, 1429FC6B0]

501S_Rawbots

EngineVersion: expected at least one value
FTextFString: expected at least one value

501_SILENT HILL 2

EngineVersion: expected at least one value

502S_ARK_Survival_Ascended

EngineVersion: expected at least one value
GUObjectArray: expected at least one value

502_DBD

FTextFString: found 2 unique values [145611090, 145610EB0]

502_Dune Awakening

FTextFString: found 2 unique values [144B659E0, 143A3C3F0]

502_LifeIsStrangeDoubleExposure

EngineVersion: expected at least one value

502_The First Descendant

EngineVersion: expected at least one value
GUObjectArray: expected at least one value

502_tamash_testgame

FTextFString: found 2 unique values [145607FC0, 145607DE0]

503S_Sumerian Six

FTextFString: found 2 unique values [140BA6BF0, 143CEA760]

504_Funko Fusion

EngineVersion: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

504_MechwarriorClans

EngineVersion: expected at least one value
FTextFString: expected at least one value
GUObjectArray: expected at least one value

Devlogs

This section will contain a list of development logs that have been written by contributors of UE4SS. These logs are intended to be a way for contributors to share their experiences and knowledge with the community, and to provide a way for the community to understand the development process of UE4SS.

DataTables in UE4SS - bitonality (2024-02-07)

DataTables in UE4SS

Background

DataTables are a data structure in Unreal Engine that allows for hashed key-value pairs to be loaded at runtime. Common use cases include storing loot tables, experience point requirements for leveling up, base health/armor for actors, etc...

DataTables are intended to be populating as part of game compilation and aren't technically supposed to be modified at runtime. The documentation from Unreal sometimes contradicts this statement, so it's a bit hard to parse what's intended versus what's possible. My goal is to allow for full read/write/update/delete/iterate operations at runtime from a C++ context without the use of blueprints.

Why not just create a blueprint mod that replaces a DataTable?

This technically works. The problem is that your mod is the only mod that can change this DataTable. This is obviously not ideal for clients that want to use multiple mods that want to modify the same DataTable. I rate this solution around a 2/10 from a extensibility perspective.

What is the structure of a DataTable?

DataTables are build by using TMap and TSet from native Unreal. If you are familiar with Java's HashMap or C#'s Dictionary then you'll understand the gist of the contracts/usage. Unreal DataTable has keys of FName and the value is a struct that inherits from FTableRowBase. More on this later...

So what needs to be done?

I will outline a couple of possibilities for the modification of DataTables. I will be evaluating the feasibility/stability of each proposed solution to give some perspective.

Solution 1 (TMap implementation)

A DataTable in Unreal Engine exposes a RowMap property that can be accessed:

// DataTable.h
virtual const TMap< FName, uint8 * > & GetRowMap() const 
virtual const TMap< FName, uint8 * > & GetRowMap()  

The GetRowMap() function is reflected and is easily callable by using the UVTD files. The problem is that UE4SS has a bare-bones implementation of TMap. The current TMap implementation in UE4SS can be leveraged in the following manner:

// DataTable row format is <FName, CoolStruct>
struct CoolStruct : FTableRowBase
{
    FString SomeString;
    int_32 SomeNumber;
    bool SomeBoolean;
}

TMap<FName, unsigned char*> rowMap = dataTable->GetRowMap();
auto ptrElem = rowMap.GetElementsPtr();
for(int32_t i = 0; i < rowMap.Num(); i++)
{
    auto pair = &ptrElem[i];
    pair->Key;
    pair->Value;
    CoolStruct* row = reinterpret_cast<CoolStruct*>(pair->Value);
}

So what's the big deal?

UE4SS's TMap does not like when the underlying data is changed. This way of accessing data works reasonably well for DataTable reads/iterators, but after we call dt->AddRow() or dt->RemoveRow(), the underlying .GetElementsPtr() is inaccurate. If you look at the UE4SS implementation of TMap, you can see that it's fairly fragile unless you intend to read only.

Note that the current .Num() function in UE4SS TMap does not actually perform calculations on the TMap. The Num property is just set when we construct a TMap in UE4SS, so we don't get updates when the underlying size changes.

I suppose this solution is reasonable for reading a DataTable if that's all you want to do.

So how can we make this work?

Theoretically we can implement TMap in UE4SS with mirrored functionality to UE native. UE4SS has done a similar approach with TArray. The potential downsides are that if TMap underlying logic/structures have changed between UE versions, then we would need multiple implementations that represent the state of UE TMaps at different versions. Either that, or, we could have #if UE5_1 etc. to keep things consolidated in a single TMap.hpp/cpp file.

Will implementing TMap in UE4SS work for modifying DataTables? I haven't completed a thorough investigation, but my gut says... probably?

Why can't we use FindRow/GetRow on the DataTable object?

The only useful reflected functions we get from UDataTable dump is GetRowMap(), RemoveRow(), and AddRow(). Not too shabby, but unfortunate that we can't get a row directly or use a UE4SS TMap to get a row.

Solution 2 (Kismet DataTable Helper Library)

This approach leverages a blueprint DataTable helper class built into Unreal Engine. The reflected functions from this blueprint helper are:

static bool DoesDataTableRowExist
(
    UDataTable * Table,
    FName RowName
) 

static void GetDataTableRowNames
(
    UDataTable * Table,
    TArray< FName > & OutRowNames
) 

static bool GetDataTableRowFromName
(
    UDataTable * Table,
    FName RowName,
    FTableRowBase & OutRow
) 

If you've been paying attention, then a light bulb might be going off in your head. Seems like we could accomplish full DataTable support by utilizing

// DataTable reflected functions
AddRow();
RemoveRow();
Empty();

// DataTableFunctionLibrary reflected functions
DoesDataTableRowExist();
GetDataTableRowNames();
GetDataTableRowFromName();

But there's always a catch...

GetDataTableRowFromName(); is an especially cursed function. The TLDR is that it's probably usable, but will require some further experimentation.

This next section benefits from somewhat of an intimate knowledge of how Kismet/blueprints/FFrame and the blueprint scripting stack works. I'll include some pre-reads to familiarize yourself.

GetDataTableRowFromName() has the specifiers CustomThunk and CustomStructureParam.

CustomThunk:
The UnrealHeaderTool code generator will not produce a thunk for this function; it is up to the user to provide one with the DECLARE_FUNCTION or DEFINE_FUNCTION macros.

CustomStructureParam:
The listed parameters are all treated as wildcards. This specifier requires the UFUNCTION-level specifier, CustomThunk, which will require the user to provide a custom exec function. In this function, the parameter types can be checked and the appropriate function calls can be made based on those parameter types. The base UFUNCTION should never be called, and should assert or log an error if it is. 

Under the hood, the GetDataTableRowFromName() UFunction is just a stub. The DataTableFunctionLibrary provides the actual behavior with a DEFINE_FUNCTION(execGetDataTableRowFromName) macro. Let's take a look at what the defined function is:

// DataTableFunctionLibrary.h
 /** Based on UDataTableFunctionLibrary::GetDataTableRow */
 DECLARE_FUNCTION(execGetDataTableRowFromName)
 {
     P_GET_OBJECT(UDataTable, Table);
     P_GET_PROPERTY(FNameProperty, RowName);
     
     Stack.StepCompiledIn<FStructProperty>(NULL);
     void* OutRowPtr = Stack.MostRecentPropertyAddress;

		P_FINISH;
		bool bSuccess = false;
		// The following line fails to find the StructProp. See notes below this code block for the specifics.
		FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);
		if (!Table)
		{
FBlueprintExceptionInfo ExceptionInfo(
	EBlueprintExceptionType::AccessViolation,
	NSLOCTEXT("GetDataTableRow", "MissingTableInput", "Failed to resolve the table input. Be sure the DataTable is valid.")
);
FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
		}
		else if(StructProp && OutRowPtr)
		{
UScriptStruct* OutputType = StructProp->Struct;
const UScriptStruct* TableType  = Table->GetRowStruct();
		
const bool bCompatible = (OutputType == TableType) || 
	(OutputType->IsChildOf(TableType) && FStructUtils::TheSameLayout(OutputType, TableType));
if (bCompatible)
{
	P_NATIVE_BEGIN;
	bSuccess = Generic_GetDataTableRowFromName(Table, RowName, OutRowPtr);
	P_NATIVE_END;
}
else
{
	FBlueprintExceptionInfo ExceptionInfo(
		EBlueprintExceptionType::AccessViolation,
		NSLOCTEXT("GetDataTableRow", "IncompatibleProperty", "Incompatible output parameter; the data table's type is not the same as the return type.")
		);
	FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
}
		}
		else
		{
FBlueprintExceptionInfo ExceptionInfo(
	EBlueprintExceptionType::AccessViolation,
	NSLOCTEXT("GetDataTableRow", "MissingOutputProperty", "Failed to resolve the output parameter for GetDataTableRow.")
);
FBlueprintCoreDelegates::ThrowScriptException(P_THIS, Stack, ExceptionInfo);
		}
		*(bool*)RESULT_PARAM = bSuccess;
 }

The issue is that the Stack.MostRecentProperty does not get populated when we call the GetDataTableRowFromName() from a C++ context. This specifics of this have been documented at by the following GitHub issues:

Under the hood:

static bool GetDataTableRowFromName
(
    UDataTable * Table,
    FName RowName,
    FTableRowBase & OutRow
)

// Does some property reading, type checking, etc,
// Then internally it calls

static bool Generic_GetDataTableRowFromName
(
    const UDataTable * Table,
    FName RowName,
    void * OutRowPtr
) 

It would be suitable for us to use a void* for the OutRow instead of a ref FTableRowBase, but as fate would have it, this Generic_GetDataTableRowFromName() is not accessible via reflection.

The core of the problem is that the execGetDataTableRowFromName() is particularly aggressive at typechecking and ensuring that the function will work or gracefully exit. This is expected since this function is a blueprint node and needs to be a robust function to work within the blueprint framework. The specific way that Stack.MostRecentProperty is used is to determine the target type of Struct that we expect to retrieve from the DataTable. In the blueprint caller context, this property would be populated as part of the Kismet FFrame/Stack pipeline.

Anything we can do?

I am currently playing with manually setting the Stack.MostRecentProperty to trick the GetDataTableRowFromName() into thinking that we're calling the function as part of a legal blueprint function and not directly from C++ code. Like solution 1, I rate this solution as a probably? in the functionality department.

One final wrench in the machine...

There's also further research needed about how DataTable row structs are stored in memory. It appears some games might have compiler packing, but the extent of this is still unknown. Furthermore, some games have reasonably laid out struct members for memory footprint/alignment/padding purposes, and other games have their struct members in a way that makes sense from a readability standpoint, but not from a memory optimization standpoint.

// NameTypes.hpp (UE4SS)

// TODO:   Figure out what's going on here
//         It shouldn't be required to use 'alignas' here to make sure it's aligned properly in containers (like TArray)
//         I've never seen an FName not be 8-byte aligned in memory,
//         but it is 4-byte aligned in the source so hopefully this doesn't cause any problems
// UPDATE: This matters in the UE VM, when ElementSize is 0xC in memory for case-preserving games, it must be aligned by 0x4 in that case
#pragma warning(disable: 4324) // Suppressing warning about struct alignment
#ifdef WITH_CASE_PRESERVING_NAME
    struct alignas(4) RC_UE_API FName
#else
    struct alignas(8) RC_UE_API FName // FNames in DataTable rows seem to only work with alignas(4)

The above code is a TODO: that's still in UE4SS. The investigation of alignment will likely have benefits across other non-DataTable parts! We'll need to understand the full extent of alignment/padding regardless of which solution we use (TMap or Blueprint Library or Other).

Disclaimer

While I feel that I have a good understanding of the factors at play, I have no doubt that I've missed some of the nuance and have misunderstood parts of the underlying systems. Please let me know if you think something operates differently than is currently documented. I would really appreciate the help!

Got any ideas?

Please reach out in the UE4SS Discord to brainstorm/share any ideas you might have. While I am currently in the role as feature lead for DataTables, I appreciate all the help I can get.

Other Resources

Credits

Special thanks to localcc for being a wonderful mentor. Shout out to all early adopters of the DataTable branches (special thanks to El for being our first early adopter).

Thanks for your continued patience.

-- bitonality