Python Limon API Usage
Limon Engine exposes the same extension API to Python that it does to C++. Python extensions are built on pybind11 and have full parity with the C++ API - the same GenericParameter contract, the same enums, and automatic type conversion. This page covers how Python scripting is wired up: where scripts live, how the engine discovers them, and the conventions an extension must follow. For the method-by-method API listing see the Python API reference; for the per-extension-point how-tos see How to Implement an Action, How to Implement a Player Extension, How to Implement an AI Actor, and How to implement a camera attachment.
Note
Python and C++ extensions are not mutually exclusive. A C++ Action and a Python Player Extension can run in the same world; they share the same engine API instance but execute in separate contexts. See Multi-Interpreter Python.
Where scripts live
Scripts are loaded from two directories, following the engine-wide convention that Engine/ holds built-ins and Data/ holds user content:
./Engine/Scripts- built-in: the base interface classes, helper types, and the bundled samples. Treat this as engine-owned; don’t put your game’s scripts here../Data/Scripts- your scripts: drop your own extension.pyfiles here. This directory is optional - if it doesn’t exist it’s simply skipped.
Engine/Scripts/ # built-in (engine-owned)
__init__.py # package marker (not loaded as a script)
trigger_interface.py # base class
actor_interface.py # base class
camera_extension_interface.py # base class
player_extension_interface.py # base class
generic_parameter.py # helper type
vec3.py # helper type
limonimp.py # sample trigger (MyTrigger)
simple_guard_actor.py # sample actor (SimpleGuardActor)
python_orthographic_camera_rig.py # sample camera attachment (PythonOrthographicCameraRig)
python_player_extension.py # sample player extension (PythonPlayerExtension)
Data/Scripts/ # your game's scripts go here
my_trigger.py
my_actor.py
Both directories are on the Python import path (Engine/Scripts first), so a script in Data/Scripts can freely import the built-in base classes and helpers - from trigger_interface import TriggerInterface works from either location.
Warning
Don’t give a user script the same filename as a built-in module (trigger_interface.py, actor_interface.py, camera_extension_interface.py, player_extension_interface.py, generic_parameter.py, vec3.py). Python resolves a module name to the built-in copy (it is first on the path), so a same-named file in Data/Scripts is skipped with a warning and never loads. Pick a distinct filename.
How discovery works
When a world is loaded, the engine creates an isolated Python sub-interpreter for it and scans both script directories (./Engine/Scripts then ./Data/Scripts). Every .py file except __init__.py is imported as a module, and each top-level class in that module is inspected:
A class that subclasses TriggerInterface is registered as an Action.
A class that subclasses PlayerExtensionInterface is registered as a Player Extension.
A class that subclasses ActorInterface is registered as an AI Actor.
A class that subclasses CameraExtensionInterface is registered as a Camera Attachment.
Registration is automatic - there is no Python equivalent of the C++ registerAsTrigger entry point. The name the extension is registered under, and the name a map designer picks from in the editor, is the Python class name itself.
Note
Registered camera attachments (CameraExtensionInterface) are auto-discovered the same way - a class subclassing it is registered as a Camera Attachment.
Warning
Because every class in every module is inspected, keep one extension class per file (the samples follow this convention). Helper classes that do not subclass one of the extension interfaces are ignored, so they are safe to keep alongside.
The limon module and the base classes
Inside each sub-interpreter the engine injects a built-in limon module. It exposes value types and enums you use at runtime (Vec4, Inputs, LogSubsystem, and so on); the LimonAPI instance is handed to your extension’s constructor as limon_api.
Your extension class must subclass the matching pure-Python base class shipped in Engine/Scripts:
from trigger_interface import TriggerInterface
class MyTrigger(TriggerInterface):
...
from player_extension_interface import PlayerExtensionInterface
class MyExtension(PlayerExtensionInterface):
...
Discovery matches by class identity against these base classes (TriggerInterface in trigger_interface.py, PlayerExtensionInterface in player_extension_interface.py, ActorInterface in actor_interface.py).
Warning
Do not use dir(limon) to discover extension base classes. The limon module exposes internal C++ binding types such as limon.TriggerInterface that are not intended for user extension — subclassing them will silently fail to register, and can trigger an interpreter-shutdown crash. Only use the documented pure-Python base classes from Engine/Scripts. You still import limon when you need its value types and enums.
Required methods per extension type
The engine calls a fixed set of methods on each extension instance. The base classes in Engine/Scripts document the full contract of each method in their docstrings; the minimum set the engine expects is:
Extension type |
Methods the engine calls |
|---|---|
|
|
|
|
|
|
|
|
All method names are snake_case to match the Python API binding. See How to implement a camera attachment for camera attachments.
Parameters
Python extensions use the same unified parameter contract as C++. get_parameters returns a list of GenericParameter objects - each carrying both its descriptor (request type, description, value type) and its value - and set_parameters receives the edited or loaded values back. The defaults you return from get_parameters at construction are what the editor shows; the configured values are persisted with the map and editable in the editor exactly as for C++ extensions.
The RequestParameterType and ValueType enums and the GenericParameter and Vec3 helper types are documented in the Python API reference.
World-scoped interpreters
Each world gets its own Python sub-interpreter, so scripts in one world cannot access or interfere with scripts in another. When a world is unloaded its interpreter is torn down and any module-level state is released; when a world is loaded a fresh interpreter starts. Scripts do not carry state across world loads unless they persist it externally (for example through the engine’s set_variable / get_variable API). For the full behaviour and the caveats around module-level singletons, see Multi-Interpreter Python.
Next steps
Browse the Sample Scripts and copy the one closest to what you need.
Read the per-extension-point how-tos: How to Implement an Action, How to Implement a Player Extension, How to Implement an AI Actor, How to implement a camera attachment.
Look up individual methods in the Python API reference.
Review engine behaviours that cut across extension types in API Fundamentals.