Each example focuses on one mechanism; read its ownership and lifecycle limits before adapting it. Confirm imports and client symbols for the game build you target.

How to use these recipes

Copy the ownership pattern

Keep callback IDs, subscriptions, workers, and original callables in mod-owned state.

Replace only verified symbols

Imports and client method names are build-dependent. Confirm them in current source and at runtime.

Test every transition

Exercise repeated initialization, account changes, battle entry, battle exit, and shutdown.

1. Cancellable delayed action

This illustration retains one callback handle and admits the delayed action when its captured generation still equals the current value. Connect real context transitions to invalidation and check feature readiness separately before adapting it; the snippet supplies no lifecycle wiring.

Verification scope: authored Python control flow, compared with current CallbackDelayer and CallbacksSetByID ownership paths. Their keyed scheduling and repetition contracts differ from this recipe. This comparison does not establish native callback timing, cancellation acknowledgement or thread safety; see runtime and callback guidance.

import BigWorld

_callback_id = None
_generation = 0


def schedule(delay, action):
    global _callback_id
    cancel()
    token = _generation

    def run():
        global _callback_id
        _callback_id = None
        if token != _generation:
            return
        action()

    _callback_id = BigWorld.callback(delay, run)


def cancel():
    global _callback_id
    if _callback_id is not None:
        BigWorld.cancelCallback(_callback_id)
        _callback_id = None


def invalidate_context():
    global _generation
    _generation += 1
    cancel()

schedule cancels the retained handle first, then captures the generation and stores the result of BigWorld.callback. It passes the delay through unchanged and returns no handle or completion result. With no retained ID, cancel does nothing. If native cancellation raises for a retained ID, that ID remains and scheduling stops before replacement. If scheduling raises after cancellation returned, the old ID is already cleared and no new result is stored. These are Python ordering facts, not a native failure guarantee.

run clears the shared handle before checking the token. An unequal generation returns; equality calls the retained action() with no arguments. It reads no account, arena, vehicle, view or active/readiness state. The callable and the state it reads are not copied into a snapshot. Its return value is discarded; an exception escapes after the handle was cleared. Clearing first allows an action to schedule a successor without a later cleanup assignment overwriting that handle. There is no automatic repetition, retry, rollback or successful-action receipt, and no further guard once the action has entered.

cancel and replacement scheduling do not advance the generation or permanently disable the owner. A normal cancellation return means the optional native call returned and the handle was cleared; it does not independently prove that an action never ran or cannot still enter. If an older cancelled or replaced closure were invoked in the same generation, this check would admit it. This is a conditional code path, not an observed native cancellation result or a latest-request-wins promise.

invalidate_context increments the generation before attempting cancellation. That increment survives a cancellation exception, while the old ID remains stored. An unequal older token would then be rejected if invoked; new scheduling is still permitted and captures the advanced generation. Invalidation registers no events and restores no feature resources.

Every run, including one rejected for an old generation, clears the current shared slot without comparing its own callback ID. If an old closure were delivered after a newer ID was stored, it would erase tracking of that ID before the token check. One stored slot does not prove at most one pending native callback, request identity, delivery order or synchronized ownership. Verify the supported delivery and cancellation boundary in the feature that owns the action.

The literal _generation name is also used by recipe 5; pasting both into one module shares a counter rather than creating independent channels. The loader shell describes the two increments when both invalidators finish. These fragments contain no mod version or exception reporting. A shipped adaptation needs version attribution and traceback-bearing diagnostics for caught unexpected failures. This review executed no recipe, scheduler stub, callback, cancellation or game operation; existing historical observations keep their original scope.

2. Composable method wrapper

This illustrative pattern owns one ordinary instance-method patch. Each installation captures its predecessor in a closure. Teardown disables its added behavior and restores the slot only while it still owns it; a later wrapper can safely keep calling the disabled wrapper.

Verification scope: Python wrapper composition, not a verified client hook. Choose the target from current callers and confirm its signature and lifetime. See retained callables and source verification before adapting it.

import logging

_LOGGER = logging.getLogger(__name__)
_patch = None


def install(target_class, method_name):
    global _patch
    if _patch is not None:
        return

    original = getattr(target_class, method_name)
    state = {'active': True}

    def wrapped(self, *args, **kwargs):
        result = original(self, *args, **kwargs)
        if state['active']:
            try:
                on_after_call(self, result)
            except Exception:
                _LOGGER.exception('Post-hook behavior failed')
        return result

    setattr(target_class, method_name, wrapped)
    _patch = (target_class, method_name, original, wrapped, state)


def uninstall():
    global _patch
    if _patch is None:
        return
    target_class, method_name, original, wrapped, state = _patch
    state['active'] = False
    if vars(target_class).get(method_name) is wrapped:
        setattr(target_class, method_name, original)
    _patch = None


def on_after_call(instance, result):
    pass

_patch is one module-wide slot: while it is non-None, install returns without examining even a different requested target. Otherwise it captures the current attribute, creates a fresh active-state dictionary and wrapper, assigns the wrapper, and only then stores its tuple receipt. Lookup failure stores nothing. Assignment failure skips that receipt; a custom setter may already have had side effects. There is no rollback, concurrent-install protection or per-target registry.

Each wrapper calls its captured predecessor once along that invocation path, passing self, positional arguments and keyword arguments through without copying their referenced objects. A predecessor failure escapes before the post hook. After a normal return, the active flag is checked, so teardown during the predecessor can suppress that post hook. The current global on_after_call(self, result) receives only the instance and result; it does not receive the original argument list. Its return is discarded and its pass body implements no feature.

The wrapper catches Exception only around the admitted post hook and records its active traceback. It returns the predecessor's result object if that path and logging return normally. A predecessor error, an exception outside Exception, or a logging failure escapes. There is no recursion, readiness or context guard and no cancellation of work an adapted post hook may start.

uninstall marks the saved wrapper inactive before examining the live class dictionary. It restores the predecessor only when that raw dictionary value is the saved wrapper object. A failed lookup or restoration leaves the tuple present and inactive, suppressing a new install. After a normal path, the tuple is cleared even when an interposed wrapper prevented restoration. This does not wait for in-flight calls or recall a post hook that already passed its active check.

Call uninstall() from the owner’s teardown. Reinstallation creates a fresh active wrapper; any retained old wrapper remains pass-through. This cannot remove an old wrapper from another mod’s chain, so avoid repeated patch cycling when a process-lifetime hook is sufficient. The ownership check reads the class dictionary because Python 2 class access creates an unbound-method object. This raw-identity check does not unwrap an unbound method another owner restored into the dictionary, inspect inherited slots or handle arbitrary descriptors. Clearing the tuple does not release predecessors still retained by old closures. Static methods, class methods and other descriptors need their own binding analysis. Before shipping an adaptation, provide version attribution and traceback-bearing diagnostics for caught unexpected failures. The post-hook catch does not diagnose installation or teardown failures on its own. No authored wrapper, hook target or client scenario was run for this review.

3. Explicit event ownership

This illustration guards calls with one module-wide flag. It does not retain the actual event or subscribed delegate. Repeated startup and teardown are locally guarded only while callers preserve that pair and the event operations return as assumed. The unspecified events.onChanged is not constructed or assigned an event type here.

_subscribed = False


def start(events):
    global _subscribed
    if _subscribed:
        return
    events.onChanged += _on_changed
    _subscribed = True


def stop(events):
    global _subscribed
    if not _subscribed:
        return
    events.onChanged -= _on_changed
    _subscribed = False


def _on_changed(*args, **kwargs):
    handle_change(args, kwargs)


def handle_change(args, kwargs):
    pass

start sets the flag only after events.onChanged += _on_changed returns, including assignment of the augmented result back to the attribute. A true flag suppresses startup even for another container. stop reads the supplied container's current event and the current global handler, subtracts it, then clears the flag. Neither function saves the original pair. Replacing the event attribute or rebinding the handler between calls can therefore leave the original listener attached.

For current ordinary Event.Event, addition checks callability and normal membership/equality; a normal return need not add a listener. Removing an absent listener returns normally. Thus start(A) followed by stop(B) on distinct ordinary events can reset the flag while leaving A subscribed. External clearing can cause the opposite drift: a true flag with no listener. The flag does not verify membership or acknowledge release of the original registration.

An add or attribute-write failure leaves the previous false flag; a removal or attribute-write failure leaves the previous true flag. Side effects may already have occurred before the failure, and there is no rollback or membership reconciliation. Retain or otherwise guarantee the actual event and compatible delegate through teardown; follow the event ownership guidance for the event type and lifecycle in use.

_on_changed calls handle_change(args, kwargs) with two positional arguments: the positional-argument tuple and keyword-argument dictionary. It does not expand them again or deep-copy referenced payload objects. The return value is discarded and pass implements no feature. Replacing that placeholder with a raising handler lets the error escape. Ordinary Event logs and re-raises listener failures, stopping later listeners in that dispatch; SafeEvent logs and continues.

Ordinary Event dispatches a snapshot, so a listener removed during dispatch may still be called from that snapshot. This handler has no active, context or readiness guard. Unsubscribing does not cancel timers, workers or requests a real handler may start. No per-owner registry, synchronization or complete lifecycle cleanup is provided. Before shipping an adaptation, add version attribution and preserve traceback-bearing diagnostics for caught unexpected failures. These are source-reviewed conditional paths; no authored handler or event scenario was run.

4. Backup-assisted JSON settings

This Illustrative example reads defaults, a primary file or a backup, then stages writes before rotating the active file. It demonstrates a recovery policy; schema validation, diagnostics and serialized ownership must be supplied by the integrating mod.

import json
import os

DEFAULTS = {'enabled': True, 'schemaVersion': 1}


def load_settings(path):
    data = dict(DEFAULTS)
    try:
        with open(path, 'rb') as stream:
            loaded = json.load(stream)
    except Exception:
        backup = path + '.bak'
        if not os.path.exists(backup):
            return data
        with open(backup, 'rb') as stream:
            loaded = json.load(stream)
    if isinstance(loaded, dict):
        data.update(loaded)
    return data


def _json_file_is_valid(path):
    with open(path, 'rb') as stream:
        try:
            json.load(stream)
        except ValueError:
            return False
    return True


def save_settings(path, data):
    directory = os.path.dirname(path)
    if directory and not os.path.isdir(directory):
        os.makedirs(directory)

    temporary = path + '.tmp'
    backup = path + '.bak'
    try:
        with open(temporary, 'wb') as stream:
            payload = json.dumps(data, indent=2, sort_keys=True)
            stream.write(payload.encode('utf-8'))
            stream.flush()
            os.fsync(stream.fileno())

        if os.path.exists(path) and _json_file_is_valid(path):
            if os.path.exists(backup):
                os.remove(backup)
            os.rename(path, backup)
        elif os.path.exists(path):
            os.remove(path)

        try:
            os.rename(temporary, path)
        except Exception:
            if not os.path.exists(path) and os.path.exists(backup):
                os.rename(backup, path)
            raise
    finally:
        if os.path.exists(temporary):
            os.remove(temporary)

Read selection and schema: load_settings shallowly copies the current DEFAULTS before its handler. The shown defaults are immutable scalars; future nested defaults would remain shared. Any Exception from primary opening, reading/parsing or closing silently selects the backup branch. This covers more than missing files or malformed JSON and emits no diagnostic reason. Failures during the defaults copy, backup-name construction, backup probing/opening/parsing/closing or later merge propagate; exceptions outside Exception are not caught.

A false backup-existence probe returns defaults; that probe is not proof of absence when filesystem access fails. A readable backup is parsed once, without another fallback. A parse-successful primary wins even if it is an array, scalar, null or unsupported schema: non-dictionaries leave defaults unchanged without trying the backup; dictionaries shallowly overlay every key, including unknown keys and schemaVersion, with nested loaded values inserted by reference. There is no deep copy/merge, type/range/schema validation or migration. Loading never repairs the active path, saves defaults or recovers a temporary file. The returned dictionary does not identify its source or certify persistence.

Parse admission: _json_file_is_valid catches only a ValueError raised during json.load. That type alone does not prove malformed bytes. Opening, other read/parse failures and context-manager exit failures propagate; closing can override a pending false return. Any decoded JSON value passes, not just a supported settings dictionary. The normal Python 2.7 JSON defaults also admit non-finite numbers. The decoded value is discarded. Saving uses this check on the old active file, with no validation/readback of the new payload.

Staging: Parent-path setup, directory creation and temporary/backup name construction precede the outer cleanup block; setup failures propagate without that cleanup. The fixed temporary file is opened/truncated before json.dumps serializes the supplied object. Serialization uses normal defaults with sorted keys and indentation, then forms UTF-8 bytes in memory. It does not validate settings or snapshot mutable input. Writing, flushing, fsync and closing all precede old-file rotation; any of those stages can fail after truncation. The full read/string/bytes allocations have no size, memory or latency bound, and there is no new-payload comparison.

Rotation: If the old active file parses, an existing backup is removed before moving that active file to the backup name, even if its schema is unusable. If parsing returns false and a second active-existence probe succeeds, the active file is removed while any older backup is retained. A raised validation error does not enter this removal branch. Probes and mutations are separate operations. Backup deletion, active removal and the active-to-backup rename occur outside the final-promotion handler; their failures propagate through cleanup without that handler's restore attempt, and earlier side effects have no general rollback.

Restore and cleanup: Only a final-promotion Exception attempts restore, and only when the active probe is false and the backup probe true. The backup is not parsed or tied to this write; it may be older data retained after invalid-active removal. Successful restore consumes the backup name and still re-raises the promotion failure. Failed probing/restoration can replace that failure; an unmet restore condition re-raises without repair. The unguarded finally then removes any remaining temporary file, including after failed serialization, rotation or restore. A cleanup error can replace a pending exception or surface after promotion; remaining staged data may be deleted. Normal completion returns None, with no separate status, retry, memory rollback or post-promotion verification.

Ownership and diagnostics: One serialized writer must own the active, backup and fixed temporary names for the full read-modify-write policy. Directory creation, existence probes and unique temporary names alone would not coordinate that shared pair; this example has no lock, generation guard or concurrent-reader/external-edit coordination. The active name is absent between the two renames. Temporary-file flush/fsync does not synchronize directory changes or make the sequence one atomic replacement; exception-driven restore does not handle process crashes or establish power-loss durability. See the storage and replacement boundaries.

The silent primary fallback is a diagnostic limitation. A shipped mod must distinguish expected recovery from unexpected failures, preserve traceback-bearing records inside its exception handlers and provide its module version for optional error attribution. This fragment supplies no logger/version or reporter integration; its writer/helper propagate failures for an integrating caller to report. The standard Python 2.7 exception rules explain the cleanup precedence, not the embedded client's native file behavior. This review executed no authored helper, thread, filesystem-failure fixture or game client, and establishes no disk durability or real filesystem-failure result.

5. Bounded background request

This Illustrative helper limits admission of one returned body and checks a generation before calling the result handler. It is not a complete request owner: worker admission, lifecycle invalidation, cancellation and delivery receipts remain separate.

import logging
from contextlib import closing
import BigWorld
import threading
import urllib2

_LOGGER = logging.getLogger(__name__)

_generation = 0
_MAX_RESPONSE_BYTES = 262144


def request(url, on_result):
    token = _generation

    def worker():
        result = None
        error = None
        try:
            with closing(urllib2.urlopen(url, timeout=5.0)) as response:
                body = response.read(_MAX_RESPONSE_BYTES + 1)
                if len(body) > _MAX_RESPONSE_BYTES:
                    raise ValueError('Response exceeds size limit')
            result = body
        except Exception as exc:
            _LOGGER.exception('Background request failed.')
            error = str(exc)

        def deliver():
            if token == _generation:
                on_result(result, error)

        BigWorld.callback(0.0, deliver)

    thread = threading.Thread(target=worker)
    thread.daemon = True
    thread.start()


def invalidate_requests():
    global _generation
    _generation += 1

Record unexpected failures inside the original exception handler before forwarding an error value or raising later. A later error message alone cannot reconstruct the original failing stack. Keep completion signals, context checks and delivery policy unchanged; see traceback-bearing logs. The diagnostic branches were checked with inert scheduling and in-memory logging substitutes, without real threads, waits, network requests or game-thread execution.

The worker requests a prefix of _MAX_RESPONSE_BYTES + 1 and rejects a returned body longer than 262144 bytes. Reading only the limit could accept a truncated prefix silently. An empty or short body is still accepted as raw data; this check does not validate framing, completeness, schema, decompressed size or total transport/process memory.

The acquired response is owned by contextlib.closing: entry returns that object and exit attempts its close without swallowing exceptions. Success is assigned after block exit, so open, read, excess-byte or close failures leave result=None. A close attempt is not proof of successful cleanup; a close failure during another error does not promise to preserve both failures.

Static inspection separately confirmed closing's close-on-exit behavior in CPython 2.7.18; the linked current reference does not verify the embedded runtime. Selected worker checks used fake openers, returned responses, thread launchers and scheduling with recorded read/close operations. They cover the size boundary and failure delivery, without real requests, sockets, threads or waits. The size policy does not validate HTTP framing, schema or transport completeness, and no live resource leak was diagnosed.

Every request starts a separate daemon and implicitly returns None. There is no concurrency cap, busy flag, worker/request handle or join/stop path; construction and start failures propagate to the caller. URL and handler references are retained without validation or copying, and no URL scheme or endpoint policy is supplied. Concurrent requests have no aggregate memory bound.

invalidate_requests only increments the generation and is not connected to an account, arena, view or destruction event here. Older deliveries silently skip the handler: invalidation does not stop workers, close responses, retract callbacks or report cancellation. All calls within the same generation can deliver, with no latest-request-wins or ordering guarantee. The check runs only at the start of delivery; the owner must define current identity and the lifecycle that invalidates it.

The selected current urllib2.urlopen forwards timeout=5.0 to the chosen opener. That excerpt establishes argument forwarding, not an end-to-end deadline for launching, reading, closing, scheduling or handling. This helper measures no total elapsed time and has no retry budget.

The original exception handler records a traceback before forwarding text. The error text has no size cap or redaction, and logging or text-conversion failure has no separate delivery path. BigWorld.callback runs outside that handler and its ID is discarded, so scheduling failures are not caught there. The authored call site does not establish native thread safety or guarantee eventual delivery.

On generation equality, delivery passes the original result/error references to the retained handler. Handler exceptions propagate; there is no retry, serialization or receipt of successful application. A returning worker does not acknowledge current context or handler execution.

6. Context-owned feature controller

This Illustrative controller shows context-key and flag ordering. Its resource hooks are placeholders; it does not implement a complete account or battle lifecycle.

class FeatureController(object):
    def __init__(self):
        self._active = False
        self._context_key = None

    def enter(self, context_key):
        if self._active and self._context_key == context_key:
            return
        self.leave()
        self._active = True
        self._context_key = context_key
        self._install_for_context()

    def leave(self):
        if not self._active:
            return
        self._remove_for_context()
        self._context_key = None
        self._active = False

    def _install_for_context(self):
        pass

    def _remove_for_context(self):
        pass

The hooks are empty: this class installs no subscriptions, callbacks, views or resources. Construction stores only an inactive flag and empty key. enter skips an active equal key without checking readiness or whether prior setup succeeded. Otherwise it leaves the old context, records the new active flag and retains the supplied key before installation; the key is neither copied nor validated against a current client owner.

If installation raises, the new active/key state remains and another equal-key enter returns early. A failure leaving the old context prevents installing the new one. There is no rollback or partial-resource record here.

leave calls removal before clearing the state. A removal failure retains the fields, so a later call can retry removal without proof that repeating it is safe. With the literal pass hooks, normal calls only change flags. Overrides must define owned acquisition, cleanup, idempotence and failure diagnostics.

These flags are not completion receipts. There is no separate in-progress state, guaranteed cleanup after an error or mod-owned exception logger. Set an installed flag after required setup succeeds, or define an explicit partial-setup and recovery policy; see lifecycle ownership.

7. Minimal mod lifecycle

Pseudocode for a single-file mod shell with subscription placeholders and explicit failure limits.

_started = False
_controller = FeatureController()


def init():
    global _started
    if _started:
        return
    _started = True
    install_loader_subscriptions()


def fini():
    global _started
    if not _started:
        return
    remove_loader_subscriptions()
    _controller.leave()
    invalidate_requests()
    invalidate_context()
    _started = False

FeatureController comes from the preceding illustration. The subscription helpers are undefined placeholders; no event wiring, transition handler or current-context seed is provided. The controller is created at import, retained after fini and not entered by init.

init records _started=True before subscription setup. A missing or failing helper leaves it true, so the next call skips installation. This ordering supplies no rollback or proof of complete setup.

fini removes subscriptions, leaves the controller, invalidates requests, invalidates delayed context, then clears the flag. A failure stops subsequent steps and the final reset; a retry can repeat earlier successful operations. There is no per-step catch or finally. Invalidation follows cleanup rather than preceding it, so the shell does not guarantee that stale work was rejected before cleanup began.

Request invalidation only advances its generation; it does not stop workers or acknowledge cancellation. Delayed-context invalidation advances its generation and attempts cancellation of any retained callback ID. The literal request and delayed-action fragments both name the module global _generation: pasting them into one file shares that state, and both successful invalidators advance it twice. Define the intended state ownership when adapting them.

The selected current gui.mods loader calls module hooks through _callModMethod. In that unwrapped built-in function, AttributeError is caught around lookup and execution, so such an error inside a hook can disappear while recipe cleanup remains incomplete. Other exception types are not caught by that boundary. The fragments supply no version attribution or mod-owned exception diagnostics; use traceback-bearing diagnostics for caught unexpected failures when implementing a mod.

This is authored control-flow and selected Python loader inspection. No hook, subscription helper, recipe, cancellation, loader broadcast or game API was executed; it is not runtime evidence of complete setup or cleanup.

Verification checklist

  • Every imported private symbol exists in the target client build.
  • Initialization and teardown can run more than once without duplication or exceptions.
  • No worker thread reads or mutates engine-owned objects.
  • Delayed and asynchronous results carry a context or generation check.
  • The feature works after entering and leaving battle repeatedly.
  • Errors identify the failed subsystem without flooding the log.