Repository navigation
Plugin basics
A plugin is a folder in ProLeak/plugins/ holding a plugin.yml and Python files. The folder
name is the plugin id. Your first plugin makes one step by step; this page
describes each part.
ProLeak/plugins/my.wave-toast/
plugin.yml # the manifest
main.py # the entry file
waves.py # more modules, imported with relative imports
vendor/ # pure-Python dependencies (optional)Contents
# ProLeak/plugins/my.wave-toast/plugin.yml
id: my.wave-toast
name: Wave toast
version: 1.2.0
author: me
description: Says what each wave is and how your value compares.
proleak: ">=0.1,<0.2"
entry: main.py
depends:
- my.utils >=1.0
update:
github: me/ltd2-wave-toast
settings:
show_toast: {type: bool, default: true, label: Toast each wave}| Key | Required | Meaning |
|---|---|---|
id |
yes | Lowercase letters and digits, with single . or - between them; at most 64 characters. |
name |
yes | Display name. |
version |
yes | Semantic version of the plugin. |
author |
yes | Author name. |
description |
no | One line for the Mods menu. |
proleak |
yes | Range of ProLeak versions the plugin works with. Outside it the plugin is unavailable. |
entry |
no | Python file to import, main.py by default. |
depends |
no | List of <id> <range>. Dependencies load first. |
update |
no |
github: owner/repo, where releases are published. A registry install checks it against the registry entry (see Publishing plugins). |
settings |
no | Settings declared in the manifest, as an alternative to settings.add (see Plugin settings). |
ProLeak validates the manifest before running any Python. An unknown key, a missing required key or a bad value refuses the plugin, and the Mods menu shows the key and the reason.
Note
Two ids, one module
Two plugin ids can map to the same Python module name (a.b and a-b both become
proleak_plugins.a_b). The plugin later in load order is then refused, with a reason naming
the other one.
ProLeak imports the entry file inside the package proleak_plugins.<id> (with . and - in
the id turned into _), so files in the plugin folder import each other with relative imports
(from .waves import describe). A vendor/ folder inside the plugin goes on sys.path for
pure-Python dependencies.
Two optional functions in the entry file run as the plugin loads and unloads:
def on_load(ctx):
ctx.log.info(f"loaded from {ctx.dir}")
def on_unload():
pass
ctx member |
Meaning |
|---|---|
id |
The plugin id. |
dir |
The plugin folder. |
settings |
The plugin's settings, read as attributes (ctx.settings.show_toast) or with get, set and as_dict. |
log |
The plugin's logger. |
data_dir |
A folder the plugin may write to: ProLeak/data/<id>. |
On unload ProLeak calls on_unload(), then removes everything the plugin registered, newest
first: handlers, chat commands, timers, threads, UI and patches, so on_unload only has to
close what the plugin opened itself, such as files. Unloading a plugin unloads its dependents
first.
The Mods menu reloads, enables and disables plugins. proleak.plugins.reload() reloads the
calling plugin at the next frame boundary; the handler that called it finishes first. In dev
mode (dev: true in Settings) a plugin also reloads when a file in its folder
changes.
Handlers, UI callbacks and timers run on the game's main thread. Game APIs, proleak.on,
chat and ui work there only, and raise WrongThread elsewhere. log and
tasks.call_soon_main work from any thread.
Long work goes to a worker thread with tasks.run_in_thread (see Chat, camera, input and
more). Sleeping, waiting or blocking on a socket in a main-thread handler
raises BlockingCallOnMainThread.
| Code | Budget |
|---|---|
| Handlers, UI callbacks, timers | 50 ms |
on_load, on_unload
|
2 s |
Past the budget ProLeak raises PluginTimeout inside the handler, lets the game continue with
the event unchanged, and unloads the plugin as faulted. A player can raise one plugin's
budgets in Settings.
An exception in a handler is logged with its traceback, and the event goes on as if that
handler had done nothing. Five exceptions from one plugin within 10 s unload it as faulted.
Warning
What ProLeak cannot contain
A crash inside a C extension takes the game down with it. A loop that catches
BaseException swallows PluginTimeout, and a call blocked inside native code sees the
timeout only when it returns.
log.debug, log.info, log.warning and log.error write as the calling plugin to the
BepInEx console, ProLeak/logs/<id>.log and the Logs tab of the Mods page. print and the
logging module inside a plugin go to the same place.
from proleak import log
log.info("ready")Using ProLeak
Features
Tutorial
- Your first plugin
- Plugin basics
- Handling events
- Plugin settings
- User interface
- Players, units and game data
- Purchases, signals and pings
- Chat, camera, input and more
- Cosmetics
- Game modes
- Game updates
- Publishing plugins
Reference