A delay cap is separate from retry ownership

The client backoff helper selects and jitters a delay, advances its counter, then normalizes the result. With ordered bounds, exponential backoff caps the returned delay while the counter continues advancing. Each nextDelay() consumes policy state; it is not a passive read. The helper supplies neither a scheduler, retry budget nor request deadline, and reset() resets policy state rather than cancelling work.

The Python reconnection owner schedules callbacks after failed/closed notifications and resets its backoff counter on an opened notification. Clearing the owner separately cancels its retained callback and removes listeners. Scheduled retries call the raw client; the wrapper's public open() instead replaces the old reconnection owner and opts in only with reconnect=True. Its default is false.

Counter reset and connection-open notification do not themselves cancel a retained timer. Cleanup uses the retained callback ID; the failure/closed handlers do not cancel an earlier ID when assigning a new one. Native event ordering remains outside these checks. Selected-source controls used synthetic clients, events and a recording scheduler, with no real waits, transport, authentication or native objects. Keep mod-owned attempt limits and lifetime cancellation explicit.

Local HTTP tools

  • Bind to loopback rather than all interfaces.
  • Expose only a small documented method and path set.
  • Cap Content-Length before reading a body and reject malformed JSON explicitly.
  • Serve only known assets or paths resolved under a fixed root.
  • Do not query or mutate engine objects from the request thread.
  • Marshal engine operations through an owner-verified callback bridge. If a background request thread waits, validate a finite timeout and define cancellation and late-result handling separately.

Pseudocode: this helper sketches scheduling and waiting only. The block does not import threading or BigWorld; its caller supplies action. It implements none of the HTTP binding, routing, request-size, JSON, asset-serving or response policies listed above.

import logging

_LOGGER = logging.getLogger(__name__)

def run_on_game_thread(action, timeout=2.0):
    completed = threading.Event()
    outcome = {'value': None, 'error': None}

    def execute():
        try:
            outcome['value'] = action()
        except Exception as exc:
            _LOGGER.exception('Game-thread action failed.')
            outcome['error'] = repr(exc)
        finally:
            completed.set()

    BigWorld.callback(0.0, execute)
    if not completed.wait(timeout):
        raise RuntimeError('Game-thread operation timed out')
    if outcome['error'] is not None:
        raise RuntimeError(outcome['error'])
    return outcome['value']

Each call owns an Event, an outcome dictionary and a closure, but retains no callback ID or cancellation handle. It has no admission/concurrency limit, teardown path, current-context/readiness check or snapshot/serialization boundary. The action and returned value are borrowed object references; returning an engine-owned object would require a separate lifetime and thread-ownership policy.

timeout=2.0 is passed to the wait after scheduling, with no finite, nonnegative or type validation; passing None permits indefinite waiting while the flag is unset. The inspected Python Event and condition implementation returns the Event flag, waits without a timeout for None, and reacquires its condition lock after the wait. That argument is not a whole-request, lock-acquisition or action-execution deadline, and this Python source does not establish native thread safety or callback delivery timing.

If the wait returns false, this helper raises and leaves the scheduled action unchanged. An action dispatched later still runs and updates its retained outcome, without returning a second result to the caller. A timeout does not prove that no side effect occurred; retries need a separate request identity, idempotency and late-result policy. Scheduling failure propagates before the wait. There is no rollback for partial action side effects.

The active action handler logs the original traceback with _LOGGER.exception; the waiting caller receives a new text-only RuntimeError. Error text is neither capped nor redacted. The finally notification is distinct from action success or an HTTP response sent: errors in logging/formatting and exceptions outside the Exception catch have no separate outcome path, and a failed notification is not recovered here.

Keep this waiting shape in an appropriate background request owner. It has no calling-thread check: blocking the game thread needed to dispatch the callback can prevent dispatch during the wait. Define teardown and stale-context rejection before allowing engine work. This is a static contract review; no helper, server, request, thread, wait or native callback was executed, and existing diagnostic controls elsewhere on this page do not test this helper.

Automation and synchronization

  • Use a busy flag so cache updates and callbacks cannot issue duplicate requests.
  • Re-check eligibility immediately before every state-changing request.
  • Distinguish a successful response from confirmed client-cache synchronization.
  • Snapshot the relevant count, item, or state and perform bounded verification checks after success.
  • Do not blindly repeat a successful server-changing request because cache confirmation was delayed.
  • Stop chains when counts reach zero, context changes, the player leaves, or a request fails.

Primary response status can exclude enrichment failures

A client list-response handler follows a successful nonempty primary response with a secondary ratings lookup. A failed secondary response fills default rating values, then invokes the original callback with the original primary response. Its success status therefore does not certify successful enrichment. Empty primary lists skip that lookup.

Keep the primary result, optional-field provenance and enrichment outcome separate when a mod needs those distinctions. Selected-source controls used synthetic responses and manually delivered callbacks to verify fallback and empty-list paths. No transport, server failure, real ratings service or item-wrapper construction was exercised.

Logging

  • Include request type, attempt, account/arena/vehicle context, and outcome.
  • Sample high-frequency diagnostics instead of logging every tick.
  • Redact tokens, private endpoints, message content, and credentials.
  • Remove exploratory logs after the integration is verified.

Background handlers and the game thread

Keep engine queries and mutations on the owning client thread through a bridge verified for the target runtime. Selected public custom-file worker/task/post paths use BigWorld.callback for scheduled delivery; Python caller source does not establish universal native thread safety, a dispatch deadline or delivery after owner teardown. The same helper can invoke a task directly on the submitting caller after a queue-put failure, so execution context must be checked per path.

If a request thread waits for a result, use a finite threading.Event.wait() and inspect its outcome. A wait timeout does not cancel the callback or retract later work. Revalidate context inside the receiving callback and never hold a shared-state lock while waiting.

For a complete loopback server and command-boundary guide, continue with Local Tools & Game-Thread Bridges.