Egregoria Python Mod API

Stable API version 1 for Egregoria Android 0.6.1-android.1. Mods are ordinary ZIP archives containing mod.json and UTF-8 Python source executed by embedded RustPython 0.5.

Stable releaseHello sampleDependency/action sample

Trust boundary: enabled Python mods execute inside the game process. Archive validation protects paths/storage and explicit approval protects code replacement; it does not sandbox trusted Python code or impose a hard CPU/memory quota.

Install and lifecycle

  1. Create the ZIP using the structure below.
  2. In Egregoria, open Main Menu → Python Mods → Import ZIP.
  3. Select the ZIP with Android's system picker.
  4. Review the discovered manifest, then explicitly enable it.
  5. Restart Egregoria. Enabled mods execute once during startup in deterministic dependency order.

Import never executes a selected archive. Approval is bound to (manifest ID, archive SHA-256); changing even one archive byte disables it until approved again. Duplicate installed manifest IDs invalidate every conflicting archive.

ZIP layout

my-mod.zip
├── mod.json
├── main.py
└── assets/
    └── optional.json

The manifest may instead live inside exactly one wrapper directory; entrypoint and asset paths are relative to that manifest.

Required Python entrypoint

def on_load(context):
    context.log("Loaded " + context.mod_name)
    count = int(context.get_data("count", "0")) + 1
    context.set_data("count", count)

on_load(context) must exist and be callable. There are no per-frame callbacks in API 1.

mod.json

{
  "api": 1,
  "id": "starter-funds",
  "name": "Starter Funds",
  "version": "1.0.0",
  "description": "Adds money once.",
  "compatible_game_version": "0.6.1",
  "entrypoint": "main.py",
  "dependencies": ["hello-egregoria"],
  "load_after": [],
  "load_before": [],
  "assets": []
}
FieldRequired/defaultContract
apiRequiredInteger; must be 1.
idRequired1–64 lowercase ASCII letters, digits, dot, underscore, or hyphen. Stable storage/dependency identity.
nameRequiredHuman-readable, 1–80 characters.
versionRequired1–32 characters; author-defined.
description""Up to 500 characters.
compatible_game_version"*"* or exact game version, up to 32 characters.
entrypoint"main.py"Safe relative path to UTF-8 source.
dependencies[]Required enabled mod IDs. Missing, disabled, cyclic, or runtime-failed dependencies prevent execution.
load_after[]Ordering edges for enabled installed IDs.
load_before[]Inverse ordering edges for enabled installed IDs.
assets[]Up to 128 safe relative paths that must exist. API 1 does not expose an asset-reading context method.

Dependency/order arrays allow up to 64 unique valid IDs each. Ordering is deterministic and lexical when several nodes are otherwise ready.

Context metadata

AttributeTypeMeaning
context.api_versionintAlways 1.
context.mod_idstrManifest ID.
context.mod_namestrManifest name.
context.mod_versionstrManifest version.
context.game_versionstrCurrent Egregoria base version.

Context methods

context.log(message)

Adds a diagnostic line shown in Python Mods. Values are converted with str(). Maximum 128 lines per load; individual messages are truncated after 1,024 characters.

context.get_data(key, default=None)

Reads this mod's private persistent string state. The key is converted to a string.

context.set_data(key, value)

Writes private persistent state after successful execution. Key/value are strings. Limits: key 128 characters, value 4,096 characters, 256 entries. U+001F is forbidden. If execution fails, returned actions/state are not committed.

context.add_money(amount)

Queues a signed whole-bucks startup delta. Allowed range: -1,000,000,000 through 1,000,000,000. Final host arithmetic is saturating.

context.set_time_warp(multiplier)

Queues startup simulation speed. Allowed values: 0, 1, 2, 4, 8.

At most 128 total actions may be queued in one execution. The Rust host validates every action again after Python returns.

Complete examples

Persistent load counter

def on_load(context):
    count = int(context.get_data("load_count", "0")) + 1
    context.set_data("load_count", count)
    context.log("Python runtime active; load count = " + str(count))

One-time starter funds

def on_load(context):
    if context.get_data("granted", "no") != "yes":
        context.add_money(50000)
        context.set_data("granted", "yes")
        context.log("Granted 50,000 starter funds")

Archive validation

Runtime/storage details

files/mods/                              installed ZIPs
files/config/python_mods.json            ID → approved SHA-256
files/config/python_mod_cache/ID/SHA256/ validated extraction cache
files/config/python_mod_state/ID.json    private persistent string state

RustPython is embedded without its standard library. Native CPython extensions and packages requiring CPython are unsupported. Each mod gets a fresh interpreter/scope and isolated diagnostics, but all code remains trusted in-process code.

Compatibility policy

API 1 is the contract documented on this page. Internal globals and implementation details beginning with underscores are not API and may change without notice. Use only the public context attributes and methods above.

Inspect corresponding source · GPL-3.0

← Egregoria release · AnimeFu home