simvx.core.physics.backends

Precedence (most specific wins)

  1. An explicit PhysicsRoot(backend=...) override on the node.

  2. The project / App-level physics_backend setting (GeneralConfig, .simvx/config.json).

  3. An auto-discovered installed native backend package.

  4. The pure-Python :class:~simvx.core.physics.builtin.BuiltinPhysics default.

Only the resolution lives here; the consumers (PhysicsRoot and the tree’s default world) call :func:resolve_world_factory / :func:resolve_world_factory_2d to get a ready-to-call :data:~simvx.core.physics.root.WorldFactory for the chosen backend. This keeps backend choice out of the resolution walk in root.py (which only finds which world a node belongs to, never which kind).

Auto-discovery (precedence 3)

A native backend self-registers on import, mirroring the engine’s miniaudio “installed -> used” model. The probe (:func:_try_import_native_backends) unions two routes: (a) the in-core pymunk (Chipmunk2D) 2D module, imported directly, and (b) any out-of-tree package advertising a simvx.physics.backends entry point (e.g. simvx-physics-jolt), discovered via :mod:importlib.metadata and register()-ed. Either route calls :func:register_backend to add itself to

data:

_REGISTRY; once registered a backend is both auto-discoverable (3) and addressable by name (2 / 1). When no native backend is installed the registry holds only "builtin" and every level collapses to Builtin.

Auto-discovery (3) never wins over an explicit Builtin choice: an explicitly requested "builtin" resolves at 1 / 2 before 3 is consulted, so an installed native backend is opt-in for the default, not a silent replacement.

A backend further controls whether auto-discovery may pick it at all via

attr:

BackendEntry.auto_default. pymunk follows the “installed -> used” model (auto_default=True) and so wins auto-discovery for 2D; a gameplay-affecting 3D solver like Jolt registers auto_default=False: it stays name-addressable (1 / 2) but installing it does not change the default 3D backend.

Auto-discovery is PER DIMENSION

The registry is one flat namespace but a backend may implement only one half of it, so :func:_auto_discovered_native takes the dimension it is choosing for and never offers a backend that cannot serve it. Without that, a machine with pymunk installed picks "pymunk" for a 3D world, finds no 3D factory, and falls back to Builtin while warning about a choice nothing asked for; and a 3D-only backend that registered second could never win 3D at all, because the one flat answer is registration order and pymunk is imported first.

Backend selection: the single place a physics backend is chosen.

Module Contents

Classes

BackendEntry

A registered physics backend: its name + 3D / 2D world factories.

Functions

register_backend

Register a physics backend so it is name-addressable and auto-discoverable.

resolve_backend_name

Resolve the backend NAME by precedence: explicit > setting > auto > builtin.

resolve_world_factory

Return the 3D :data:WorldFactory for the resolved backend.

resolve_world_factory_2d

Return the 2D :data:WorldFactory2D for the resolved backend.

Data

API

simvx.core.physics.backends.log

‘getLogger(…)’

simvx.core.physics.backends.BUILTIN

‘builtin’

simvx.core.physics.backends.AUTO

‘auto’

exception simvx.core.physics.backends.PhysicsBackendUnavailable[source]

Bases: RuntimeError

A backend was asked for by name and cannot serve the scene.

Raised rather than degraded. A backend named in PhysicsRoot(backend=...) or in a project’s physics_backend is a statement about how the game is meant to simulate, and quietly running a different solver changes gameplay while looking like it worked. Ask for :data:AUTO to get the best available one instead, which is also the default.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class __cause__
class __context__
__delattr__()
__dir__()
__eq__()
__format__()
__ge__()
__getattribute__()
__getstate__()
__gt__()
__hash__()
__le__()
__lt__()
__ne__()
__new__()
__reduce__()
__reduce_ex__()
__repr__()
__setattr__()
__setstate__()
__sizeof__()
__str__()
__subclasshook__()
class __suppress_context__
class __traceback__
add_note()
class args
with_traceback()
class simvx.core.physics.backends.BackendEntry[source]

A registered physics backend: its name + 3D / 2D world factories.

A native backend registers one of these via :func:register_backend. A backend may support only one dimension; the missing factory is None. Under auto that dimension falls through to Builtin, so a 3D-only native backend never breaks a 2D scene. A backend NAMED for a dimension it cannot serve raises :class:PhysicsBackendUnavailable instead: a name is a request, and auto is how a mixed project asks for the best per dimension.

Attributes: name: The token used in physics_backend config / PhysicsRoot(backend=). world_factory: Builds a 3D world for a given gravity, or None. world_factory_2d: Builds a 2D world for a given gravity, or None. A missing factory falls through to Builtin under auto and raises when the backend was named. native: True for an installed native backend (auto-discoverable), False for the always-present Builtin (never auto-selected over a native one; it is the final fallback). auto_default: When True (the miniaudio “installed -> used” model, used by pymunk), the backend may win auto-discovery and become the default for every dimension it serves. When False the backend is still name-addressable (via a node override or the project setting) but never auto-selected: installing it does NOT change the default backend. A gameplay-affecting 3D solver swap (Jolt) sets this False so it is strictly explicit opt-in.

name: str

None

world_factory: simvx.core.physics.root.WorldFactory | None

None

world_factory_2d: simvx.core.physics.root.WorldFactory2D | None

None

native: bool

True

auto_default: bool

True

simvx.core.physics.backends.register_backend(entry: simvx.core.physics.backends.BackendEntry) → None[source]

Register a physics backend so it is name-addressable and auto-discoverable.

The single plug point for a future native backend (Jolt, pymunk): the native package calls this on import to add itself, mirroring miniaudio’s “installed -> used” model. Re-registering the same name replaces the entry (idempotent for re-import). "builtin" is reserved as the fallback name.

Args: entry: The :class:BackendEntry to register.

Raises: ValueError: If entry.name is "builtin" (reserved) or empty.

simvx.core.physics.backends.resolve_backend_name(explicit: str | None, setting: str | None, *, dimension: int | None = None) → str[source]

Resolve the backend NAME by precedence: explicit > setting > auto > builtin.

Args: explicit: A PhysicsRoot(backend=...) override, or None if unset. setting: The project/App physics_backend config value, or None / empty if unset. dimension: 2 or 3 to restrict auto-discovery to backends that serve that dimension, or None for the dimension-agnostic answer. It affects level 3 only: an explicit name or project setting is honoured whatever it can serve, so a backend named for a dimension it does not implement still reaches the warned fall-through in :func:resolve_world_factory.

Returns: The resolved backend name (a key of :data:_REGISTRY).

Raises: PhysicsBackendUnavailable: when explicit or setting names a backend that is not installed. A named backend is a statement about how the game simulates, so it fails rather than degrading; :data:AUTO is how a caller asks for the best available instead.

simvx.core.physics.backends.resolve_world_factory(explicit: str | None = None) → simvx.core.physics.root.WorldFactory[source]

Return the 3D :data:WorldFactory for the resolved backend.

Applies the full precedence (explicit > project setting > auto > Builtin) and returns the chosen backend’s 3D factory. If it has none (a 2D-only native backend), auto falls back to Builtin’s 3D factory and a named backend raises :class:PhysicsBackendUnavailable.

Args: explicit: A PhysicsRoot(backend=...) override, or None.

Returns: A callable (gravity: Vec3) -> PhysicsWorld.

simvx.core.physics.backends.resolve_world_factory_2d(explicit: str | None = None) → simvx.core.physics.root.WorldFactory2D[source]

Return the 2D :data:WorldFactory2D for the resolved backend.

2D sibling of :func:resolve_world_factory. A backend with no 2D factory (a 3D-only native backend) falls back to Builtin’s under auto and raises

Class:

PhysicsBackendUnavailable when named.

Args: explicit: A PhysicsRoot2D(backend=...) override, or None.

Returns: A callable (gravity: Vec2) -> Physics2DWorld.

simvx.core.physics.backends.__all__

[‘AUTO’, ‘PhysicsBackendUnavailable’, ‘BUILTIN’, ‘BackendEntry’, ‘register_backend’, ‘resolve_backen…