Skip to content

Python API

Scripts run inside splintertree-world against an embedded Python runtime (PyO3). The splintertree module exposes typed handles (Creature, Player, GameObject, Item, Unit, MapInstance, World, …) and ~22 base classes covering the AzerothCore hook surface.

Base classes

Class Purpose
CreatureScript NPC / mob behaviour
PlayerScript Player-lifecycle hooks
SpellScript Spell pre / post effects
ItemScript Use / equip / quest-item hooks
GameObjectScript World-object interactions
WorldScript World-level lifecycle
InstanceMapScript Per-instance state
DungeonScript, RaidScript Encounter logic
BattlegroundScript, ArenaScript PvP map control
GlobalScript Cross-cutting hooks
GuildScript, GroupScript, ArenaTeamScript Social entities
AuctionHouseScript, MailScript Economy hooks
EventListenerScript Generic event subscriber

Hooks

Each base class binds the matching AC-style hook surface: on_spawn, on_enter_combat, on_death, on_update, on_gossip_hello / select, on_spell_hit, on_kill_player, on_damage_taken, on_heal_received, on_before_melee_damage, on_before_spell_damage, on_before_heal, …

from splintertree import (
    Creature, CreatureScript, MapInstance, Unit,
    on_enter_combat, on_spawn,
)

class Hogger(CreatureScript):
    entry = 448

    @on_spawn
    def greet(self, instance: MapInstance, creature: Creature) -> None:
        print(f"hogger up at ({creature.x:.1f},{creature.y:.1f})")

    @on_enter_combat
    def engage(
        self,
        instance: MapInstance,
        creature: Creature,
        attacker: Unit | None,
    ) -> None:
        creature.cast(spell_id=12555, target=attacker)  # Pummel

Server-pushed UI (AIO)

Mods talk to the client through instance.* methods on MapInstance. The Splintertree client addon ships in every realm's auto-built .rcmod (no per-player install) and dispatches incoming frames by channel name:

from splintertree import (
    AIOReplyScript, on_aio_reply, on_login,
)

class TradeReplies(AIOReplyScript):
    @on_aio_reply("mymod.trade.accept")
    def accept(self, instance, player, payload):
        ...

@on_login
def offer_trade(self, instance, player):
    instance.show_confirm(
        player.char_id,
        message=instance.localize(player.char_id, "mymod.trade.prompt"),
        on_ok="mymod.trade.accept",
        on_cancel="mymod.trade.reject",
    )

Built-in primitives

show_confirm, show_toast, show_message, show_input, show_picker, show_wizard. For arbitrary client work the unsandboxed instance.send_code(player, lua_text, compress=True) pushes raw Lua against the full _G.

A capability handshake (STAIOHS on PLAYER_LOGIN) populates instance.player_has_aio(char_id) so mods can degrade gracefully on addon-less clients.

Compression

Static-dictionary compression (~30% reduction) for large payloads, multi-frame reassembly for >219-byte bodies. The wire format is opaque to the script author — pass any JSON-serialisable Python value to send_ui and the runtime handles the rest.

Localisation

text = instance.localize(player.char_id, "mymod.greeting", player.name)

Strings live in cluster.i18n_strings keyed by mod name + key + locale. Per-character last_locale is the fallback for offline players.

Custom DB tables

stctl orm build lets mods declare typed cluster.* tables in YAML; the builder emits SQL migrations and matching Python accessors.

from splintertree.orm import MyModBalances
balances = MyModBalances.get_or_create(char_id=player.char_id)
balances.gold += 100
balances.save()

Live reload

.script reload python

Re-imports every scripts/*.py and re-wires hooks without restarting the world worker. Pair it with splintertree-test-harness for an inner loop that runs in seconds.