Python bindings for unrealsdk, and embedded interpreter.
There are two ways of using the python sdk: via console command, or via the initialization script.
The python sdk registers two custom console commands which you can use to execute python code.
py
lets you run small snippets of python. By default, it executes one line at a time, stripping
any leading whitespace.
py print(unrealsdk.find_all("PlayerController", exact=False))
You can also use heredoc-like syntax to execute multiline queries. This happens if the first two
non-whitespace characters are <<
(which is invalid python syntax for a single line).
py << EOF
obj = unrealsdk.find_object("WillowGameEngine", "Transient.WillowGameEngine_0")
if obj:
print("Found engine:", obj)
else:
print("Couldn't find engine!")
EOF
pyexec
is useful for more complex scripts - it executes an entire file (relative to the game cwd).
Note that this is not running a python script in the traditional sense, it's instead more similar
to something like eval(open(file).read())
. The interpreter is not restarted, and there's no way to
accept arguments into sys.argv
.
If you want to make more permanent mods, you'll want to use the initialization script. By default
this is __main__.py
in the game's cwd, though you can overwrite this with the
PYUNREALSDK_INIT_SCRIPT
environment variable. The initialization script is automatically run after
sdk initialization, so you can use it to import other files and generally perform all your setup.
Once you've got code running, you probably want to setup some hooks - the sdk can run callbacks whenever an unreal function is called, allowing you to interact with it's args, and mess with it's execution.
def on_main_menu(
obj: unrealsdk.unreal.UObject,
args: unrealsdk.unreal.WrappedStruct,
func: unrealsdk.unreal.BoundFunction
) -> None:
print("Reached main menu!")
unrealsdk.hooks.add_hook(
"WillowGame.FrontendGFxMovie:Start",
unrealsdk.hooks.Type.PRE,
"main_menu_hook",
on_main_menu
)
Alternatively, if you're simply using pyexec
scripts, you might be able to find the objects you
want directly using find_all
and/or find_object
.
Once you have some unreal objects, you can access unreal properties through regular python attribute access. This gets dynamically resolved to the relevant unreal property.
paused = args.StartPaused
obj.MessagesOfTheDay[obj.MessageOfTheDayIdx].Body = "No MOTD today"
op_string = obj.BuildOverpowerPromptString(1, 10)
After you start writing some more complicated scripts, you'll probably want to get a debugger working. To do this, the sdk has some integrations with debugpy.
To use it:
-
Download and extract debugpy somewhere importable.
-
At the start of your initialization script, add the following:
import debugpy debugpy.listen(("localhost", 5678), in_process_debug_adapter=True)
-
Define the environment variable
PYUNREALSDK_DEBUGPY
. -
Attach using remote debugging with the debugger of your choice.
Note that the sdk disables the integration if it's unable to import debugpy on first use, meaning
you may not get away with manipulating sys.path
. Instead, consider using ._pth
files.
-
Download the relevant release.
If you don't know which compiler's version to get, we recommend MSVC (so functions log messages include namespaces).
-
Install some game specific plugin loader. The released dlls are not set up to alias any system dlls, you can't just call it
d3d9.dll
and assume your game will load fine.If you know a specific dll name is fine to use without aliasing, rename
pyunrealsdk.dll
. -
Extract all files to somewhere in your game's dll search path. Your plugin loader's plugins folder may work, otherwise you can fall back to the same directory as the executable.
To build:
-
Clone the repo (including submodules).
git clone --recursive https://github.com/bl-sdk/pyunrealsdk.git
-
Make sure you have Python with requests on your PATH. This doesn't need to be the same version as what the SDK uses, it's just used by the script which downloads the correct one.
pip install requests python -c 'import requests'
If not running on Windows, make sure
msiextract
is also on your PATH. This is typically part of anmsitools
package.apt install msitools # Or equivalent msiextract --version
See the explicit python readme for a few extra details.
-
(OPTIONAL) Copy
postbuild.template
, and edit it to copy files to your game install directories. -
Choose a preset, and run CMake. Most IDEs will be able to do this for you,
cmake . --preset msvc-ue4-x64-debug cmake --build out/build/msvc-ue4-x64-debug
-
(OPTIONAL) Copy the python runtime files to the game's directory. At a minimum, you probably want these:
python3.dll python3<version>.dll python3<version>.zip
A CMake install will copy these files, as well as several other useful libraries, to the install dir for you.
cmake --build out/build/msvc-ue4-x64-debug --target install
As an alternative to this and step 3, you could point the CMake install dir directly at your game, so everything's automatically copied. This however will only work with one game at a time.
-
(OPTIONAL) If you're debugging a game on Steam, add a
steam_appid.txt
in the same folder as the executable, containing the game's Steam App Id.Normally, games compiled with Steamworks will call
SteamAPI_RestartAppIfNecessary
, which will drop your debugger session when launching the exe directly - adding this file prevents that. Not only does this let you debug from entry, it also unlocks some really useful debugger features which you can't access from just an attach (i.e. Visual Studio's Edit and Continue).
The sdk provides some helpers to let you build extensions as python modules.
The sdk compiles to a shared module. As with unrealsdk itself, a few functions are exported to let you interact with it's internal state from other modules (i.e. python extensions). This is done transparently, you can just call the available C++ functions and they will automatically call into the dll. Note that a lot of sdk code relates to hosting the interpreter, which is not exported.
The sdk also provides the pyunrealsdk_add_module
CMake function, which creates a new target
linking against it, and building to a pyd. From this you also have full access to the libraries it
links against, so you can also call into any unrealsdk functions.
cmake_minimum_required(VERSION 3.24)
project(my_project)
add_subdirectory(pyunrealsdk EXCLUDE_FROM_ALL)
pyunrealsdk_add_module(my_module my_module.cpp)