Local web tools and browser hand-offs can improve development and accessibility, but they create a trust boundary outside the normal game UI. Keep exposed routes small, validate every request, and marshal engine work back to the game thread.

Loopback HTTP

Bind locally, whitelist routes, cap bodies, and return explicit errors.

Resource safety

Validate paths, cap payloads, and avoid exposing filesystem internals.

Ownership and lifecycle

A local tool should belong to one explicit runtime context, such as one PlayerAvatar and one spaceID.

  • Start it only after the required space, view, or account owner is ready.
  • Capture a context token containing the owner and generation.
  • On leave, stop accepting requests, close the server, join its thread with a bound, destroy local entities or views, and clear state.
  • Install lifecycle hooks idempotently and restore only wrappers still owned by the mod.
  • Clean partial startup failures just as thoroughly as normal shutdown.

Loopback HTTP boundary

Bind to 127.0.0.1 or localhost, not every interface. Cross-origin access may be intentional for a local editor, but it makes a narrow route and method allowlist even more important.

  • Serve only the embedded UI and known read-only resources.
  • Define a small set of GET and POST operations.
  • Cap Content-Length before reading the body.
  • Distinguish malformed JSON from a valid empty object.
  • Validate IDs, descriptor formats, numeric ranges, enum values, and state transitions.
  • Return explicit 4xx/5xx codes instead of silently returning an empty response.
  • Suppress routine server logs or route them through a low-volume named logger.

Local browser hand-off

A loopback tool may expose a local browser interface for debugging or asset selection, but launching that interface should be explicit and constrained. Treat the browser as a separate local client, not as trusted game code.

  • Bind only to loopback addresses unless there is a deliberate external-tool requirement.
  • Use narrow routes instead of a generic RPC endpoint.
  • Validate every path or identifier that crosses from the browser into the game process.
  • Marshal game mutations back onto the client thread.
  • Disable the route or close the local server when the owning feature shuts down.

Crossing to the game thread

BaseHTTPServer handlers and worker threads run outside the game thread. They must not create, mutate, query, or destroy BigWorld entities directly.

import logging
import BigWorld
import threading

_LOGGER = logging.getLogger(__name__)


def run_on_game_thread(operation, timeout=2.0):
    done = threading.Event()
    result = {'value': None, 'error': None}

    def invoke():
        try:
            if not _context_is_current():
                raise RuntimeError('runtime context changed')
            result['value'] = operation()
        except Exception as error:
            _LOGGER.exception('Game-thread operation failed.')
            result['error'] = error
        finally:
            done.set()

    BigWorld.callback(0.0, invoke)
    if not done.wait(timeout):
        raise RuntimeError('game thread operation timed out')
    if result['error'] is not None:
        raise result['error']
    return result['value']

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.

Revalidate the manager, space, entity, and logical object ID inside the callback. The player may leave the arena while the HTTP thread is waiting.

A lock may protect the shared dictionary, but do not hold that lock while waiting for the game thread. That creates a deadlock if the callback needs the same state.

Keep local and engine identifiers separate

A local editor placement ID is not a BigWorld entity ID. Resolve it through the manager and keep the mapping private.

  • Use local IDs in the browser protocol.
  • Store engine entity references and IDs behind the game-thread owner.
  • Reject operations for placements from an old space generation.
  • Represent UI rotation and engine radians separately when their units differ.
  • Destroy every spawned local entity on all exit paths.

Resource and data safety

Resolve game assets from known resource locations. Do not let a URL path select an arbitrary file from the operating system.

  • Whitelist minimap, icon, or model resource families.
  • Bound list and response sizes.
  • Validate compact descriptors before constructing typed objects.
  • Validate the current entity property schema against the active definition files.
  • Bound appearance-readiness retries and stop them when the owner changes.

Multi-step state changes

A controlled sequence such as unready → select → verify → ready should re-resolve the authoritative object before every step.

  1. Build candidates from the current inventory and exclude hidden, event-only, broken, in-battle, expired-rental, or otherwise unavailable vehicles.
  2. Keep inventory ID, vehicle type descriptor, and display/internal name separate.
  3. Sort deterministically so next/previous behavior is stable.
  4. After selection, wait boundedly for the current-vehicle service to update.
  5. Verify the selected inventory ID before issuing the next action.
  6. Stop when context changes or the retry budget is exhausted.

Local simulated entities

Development tools may create client-side simulated scenery in the active space. Make the boundary explicit: these are local visual entities, not server-owned vehicles or persistent map content.

  • Align requested positions to terrain through a verified collision query.
  • Use the current entity definition to build public and simulation data.
  • Handle delayed appearance creation with a bounded context-aware retry.
  • Drive alive/destroyed/broken presentation through the correct local appearance controllers.
  • Never represent local scenery as an authoritative battle participant.

Local simulated entity tools

Editor-style local tools that spawn simulated scenery must cross to the game thread before touching engine state, align placement to terrain, and treat appearance readiness as delayed. Destroy every spawned entity on teardown, partial startup failure, room exit, and account transition.

Failure modes

Symptom Likely cause Fix
HTTP request hangs forever The request thread waits without a timeout or the callback cannot acquire a held lock. Use bounded waits and release shared locks before crossing threads.
Entity appears in the next battle The old manager or delayed callback survived space teardown. Token every operation by owner and space generation; cancel and destroy on leave.
Request affects the wrong room The prebattle entity was cached across callbacks. Re-resolve and validate room ownership before each mutation.
Local editor is reachable from the network The server bound to all interfaces. Bind explicitly to loopback and log the chosen address.
Malformed JSON acts like an empty request Decode failure was normalized to {}. Return a client error and do not schedule game work.

Verification checklist

  • Start, partial startup failure, normal stop, forced disconnect, and client shutdown.
  • Request during arena leave and after a new space is created.
  • Oversized body, invalid JSON, unknown route, invalid enum, stale ID, and unsupported action.
  • Game-thread timeout and callback exception.
  • Duplicate request, reused invitation/message identity, and identity mismatch.
  • Every local entity destroyed after teardown.