KenshiPy is a DLL extension for Kenshi that embeds a Python runtime and exposes selected portions of KenshiLib to Python via SWIG-generated bindings.
The goal is to provide a Python scripting workflow to mod Kenshi and lowers the barrier to entry for utilizing KenshiLib through rapid iteration and scripting.
Loaded at runtime through RE_Kenshi, KenshiPy exposes selected portions of KenshiLib through automatically generated Python bindings, enabling rapid development, experimentation, and mod creation without requiring direct interaction with the underlying C++ APIs.
- Runtime integration with Kenshi through RE_Kenshi
- Access to KenshiLib functionality through generated bindings
- A foundation for scripting and gameplay extensions
- Faster iteration compared to traditional C++ mod development
- Support for Python through generated bindings
It's safe to assume that basically all functions that use standard types (ints, floats, bools, etc) should be usable.
- For example, C++ functions that return void and use standard data types should be available to call from Python.
class GameWorld
{
void userPause(bool p);
}should be useable like so
# Remeber to import the KenshiPy Python module
import KenshiPy
# Get pointer to global GameWorld object
world = getGameWorld()
world.userPause(True)
# Game should now be pausedThe parts of KenshiLib that explicitly do not work:
- Ogre, MyGui, and Boost libraries have not been wrapped and so any parts of KenshiLib requiring the use of them should be assumed non-functional.
- KenshiLib's use of MinHook for hooking functionality. Hooks need to be defined within the KenshiPy as part of a callback system in order for Python to use them. See Callbacks below for what is currently available.
- Support for lektor, ogre_unordered_set, and ogre_unordered_map types
- Bindings allowing MyGUI widgets to be created from Python
- More callbacks
- Ensure RE_Kenshi v0.3.3 or later is installed.
- Download the latest release of KenshiPy and extract the archive, placing the contents in Kenshi's
modsdirectory. - Activate the mod using the game's launcher like you would any other mod.
Pressing Ctrl + ` in-game will open KenshiPy's Script Editor GUI. From here you can load, edit, save, and run scripts using the buttons on the Script Editor's toolbar.
When the game starts, KenshiPy will check whether any active mods contain a file, KenshiPy.json, and if found will load the Python scripts declared within.
Place KenshiPy.json within a given mod's directory
.\Kenshi\mods\<modname>\KenshiPy.json
KenshiPy.json should be formatted like so
{
"Scripts": ["example.py"]
}Assuming the file example.py exists at this location.
.\Kenshi\mods\<modname>\example.py
Example.py
import KenshiPy
KenshiPy.DebugLog("Hello from Python")Quick Guide
- Use SWIG 3.0.12 to generate the Python binding wrapper,
GeneratedWrappper.cpp, and KenshiPy Python module,KenshiPy.py, from KenshiLib's headers and KenshiPy's interface files - Compile the generated wrapper alongside the rest of KenshiPy's source into
KenshiPy.dll - Place the
KenshiPy.dllandKenshiPy.pyalong withpython34.dll,Kenshi_ScriptEditor_EditBox.xml,RE_Kenshi.jsoninto a mod folder namedKenshiPython
- Visual Studio 2010 Professional/Ultimate
- Visual Studio 2022 Community
- SWIG 3.0.12
- Python 3.4
- Kenshi
The project's include paths, library paths, and build events use environment variables to account for varying installation locations, avoiding the need to hardcode paths in the project file. You will need to set the variables listed below before building.
- SWIG 3.0.12 — set environment variable
SWIG_INSTALL_DIRto SWIG's root directory (e.g.C:\swigwin-3.0.12\) - Python 3.4 — set environment variable
PYTHON34_INSTALL_DIRto Python 3.4's root directory (e.g.C:\Python34\) - Kenshi — set environment variable
KENSHI_INSTALL_DIRto Kenshi's root directory- Steam:
C:\Program Files (x86)\Steam\steamapps\common\Kenshi\ - GOG:
C:\Program Files (x86)\GOG Galaxy\Games\Kenshi\
- Steam:
KenshiPy.vcxprojcan be built using Visual Studio's standard build option and includes two custom build events to help streamline the process. These events are disabled by default. If you choose to enable them, you can find them described below. If you prefer not to use them, manual alternatives are provided.
-
When this event is enabled, SWIG parses KenshiLib's headers and KenshiPy's interface files in order to generate the Python bindings, writing the output to
.\src\GeneratedWrapper.cppand$(SolutionDir)bin\$(Configuration)\KenshiPython\KenshiPy.py -
When this event is disabled, either use a pre-existing copy of
GeneratedWrapper.cppfrom the source repository and move on, or run SWIG manually to generate the required files:
set SWIG_INSTALL_DIR=C:\swigwin-3.0.12\
set PYTHON_INSTALL_DIR=C:\Python34\
set OUTPUT_DIR=%SolutionDir%bin\%Configuration%\KenshiPython
"%SWIG_INSTALL_DIR%swig.exe" -Wall -v -c++ -python -threads -I"%SolutionDir%include" -I"%SolutionDir%interfaces" -I"%SolutionDir%extern\KenshiLib\Include" -outdir "%OUTPUT_DIR%" -o "%SolutionDir%src\GeneratedWrapper.cpp" "%SolutionDir%interfaces\KenshiPy.i"
NOTE: The large size of GeneratedWrapper.cpp and therefore GenerateWrapper.obj leads to long build times. In my experience they can be upwards of of around 1 hour when building with Optimizations enabled (which they are by default).
-
This event copies
python34.dll,Kenshi_ScriptEditor_EditBox.xml,RE_Kenshi.jsoninto the build output directory alongsideKenshiPy.dll, then copies the entire output directory into Kenshi's mods folder (<KENSHI_INSTALL_DIR>\mods\KenshiPython\), making the mod immediately available to test in-game. -
Choosing not to enable the Post_Build Event requires manually copying the build output,
python34.dll,Kenshi_ScriptEditor_EditBox.xml, andRE_Kensh.jsoninto Kenshi's mods folder.
- The game and KenshiLib are built against MSVC2010-era assumptions, including C++ ABI layout, runtime library behavior, and Python embedding conventions. KenshiPy must match these constraints to ensure stable integration.
- SWIG 3.0.12 is used because newer versions may generate bindings incompatible with MSVC2010-era C++ ABI expectations.
- Python 3.4 is required to maintain compatibility with the embedded interpreter ABI and avoid breakage introduced in later Python versions.
KenshiPy exposes a set of callbacks that allow Python scripts to respond to in-game events. Each callback type has a corresponding Register and Unregister function.
Fired every time a key is pressed. The callback receives the key code as an int.
def on_key_down(key_code):
KenshiPy.DebugLog("Key pressed: " + str(key_code))
KenshiPy.RegisterKeyDownCallback(on_key_down)Fired once per rendered frame via Ogre's frame listener. The callback receives the time since the last frame as a float (in seconds).
def on_frame(delta_time):
pass # called every frame
KenshiPy.RegisterFrameCallback(on_frame)Fired when a Character speaks a line of dialogue. The callback receives the Character and the message as a str.
def on_character_say(character, message):
KenshiPy.DebugLog(message)
KenshiPy.RegisterCharacterSayCallback(on_character_say)Fired when the player selects a Character. The callback receives the Character.
def on_character_select(character):
pass
KenshiPy.RegisterCharacterSelectCallback(on_character_select)Fired when the player deselects a Character. The callback receives the Character.
def on_character_unselect(character):
pass
KenshiPy.RegisterCharacterUnselectCallback(on_character_unselect)Fired when a Character is declared dead. The callback receives the Character.
def on_character_declare_dead(character):
KenshiPy.DebugLog("A character has died.")
KenshiPy.RegisterCharacterDeclareDeadCallback(on_character_declare_dead)