Reference: Physics Contract
Last verified: 2026-09-02 against engine v1.0.232.
engine v1.0.232Gessa physics is an integration contract over Rapier 3D for the rigid-body world plus a shared deterministic character controller for player movement. This page is the contract surface: what the physics components accept, what the runtime enforces, and which named tests prove each guarantee. It is a thin pointer over the generated component reference; the exact field shapes are generated facts and live there, not here. For the model behind the contract (the timestep derivation, the mass model, the transaction and epoch, and the controller equations), read Physics Simulation.
Canonical source
The physics component fields are a Tier-2 generated artifact. Read them directly; do not hand-edit them.
docs/spec/generated/component-types.md- the generated inventory and per-component field contracts.
The Tier-1 source of truth is code: COMPONENT_TYPE_DEFINITIONS and ComponentSchema in packages/ecs/src/index.ts, the runtime physics modules under server/src/modules/runtime/physics/, and the shared controller under packages/runtime-shared/src/. The Rapier version is pinned as @dimforge/rapier3d-compat in server/package.json.
Body types
A body's type is the bodyType field of componentRigidBodyComponent RigidBodyComponent, which also carries mass, linearDamping, angularDamping, gravityScale, lockTranslations, lockRotations, and ccdEnabled. The runtime lowers each type to a Rapier descriptor in componentSync.ts:
staticbecomes a fixed descriptor.kinematicbecomes a position-based kinematic descriptor driven bysetNextKinematicTranslation; Rapier derives its velocity from the per-tick delta.dynamicbecomes a dynamic descriptor.
Continuous collision detection defaults on for dynamic bodies and is opt-in for kinematic and static, resolved by ccdEnabledForBody; an authored ccdEnabled: false is honored. Changing bodyType re-fingerprints the body (bodyFingerprint), so the sync recreates it with the new authority, which is how a grab hands a body between dynamic and kinematic while it is held.
Colliders and contact defaults
Collider geometry is the shape field of componentColliderComponent ColliderComponent, one of box, sphere, disc, capsule, or mesh (a string form or an object form), with size, collision (solid, trigger, or none), layers, friction, and restitution. A string disc lowers to a flat Rapier cylinder. Contact material defaults come from the schema: friction defaults to 0.5 and restitution to 0, both clamped to [0, 1] by ColliderComponentSchema. No friction or restitution combine rule is configured, so Rapier's own default combine rule applies (the default value is a property of the pinned Rapier build, not this contract).
Mass is scalar: mass is explicit (default 1), angular inertia is isotropic and equal to the mass scalar via angularInertiaForMass, and collider density is forced to 0 by setDensity(0), so geometry never contributes mass.
Aggregate colliders for an componentInstanceSetComponent InstanceSetComponent lower through instanceSetCollider.ts to a single static Rapier collider: a grid becomes voxels, a free set becomes a trimesh.
Force commands and preflight
Runtime forces are the per-tick pending queue of componentForceComponent ForceComponent. Its authored entry schema, ForceEntrySchema, defines force, impulse, torque, torqueImpulse, applyAtPoint, teleport, and reset. Persistent user forces are cleared each tick before this tick's commands are applied, so a one-shot force does not integrate forever.
The whole command batch is preflighted for count and magnitude before any native mutation, and is rejected atomically on violation (physicsCommandMagnitudeViolation plus the batch count check in componentSync.ts): nothing partial is applied. The magnitude ceilings are safety bounds in RUNTIME_PHYSICS_POLICY: maxForceMagnitude 1e6, maxImpulseMagnitude 1e5, maxTorqueMagnitude 1e6, and maxLaunchVelocityMagnitude 1e3 m/s. The per-room budgets maxRigidBodies, maxColliders, and maxActiveForces are 1000 each.
A launch command (a velocity set) is not an authored ForceEntrySchema field: it is injected by runtime command translation (physicsPendingEntryFromBodyCommand in runtimeSystems.ts). For a character that carries the movement velocity lane (componentVelocityComponent VelocityComponent), a launch is redirected to a VelocityComponent patch (preflighted against maxLaunchVelocityMagnitude, emitting a RuntimeCharacterLaunched effect); a body without that lane falls through to the ForceComponent queue, where componentSync.ts applies it as a Rapier setLinvel.
Collision layers
Interaction layers are the layers field of ColliderComponent. The runtime caps interaction layers at MAX_RAPIER_INTERACTION_LAYERS (16) in layers.ts; layers beyond the cap are sliced off the Rapier interaction groups and a physics.collision_layer_budget_exceeded correction is pushed from componentSync.ts.
Queries
Physics queries are raycast, shapecast, and overlap, exposed to scripts through the runtime physics query host and resolved in queries.ts and rapierQueries.ts. Two limits are part of the contract: shapecast and overlap shapes are limited to box, sphere, and capsule (mesh query shapes are unsupported), and historical (lag-compensated) rewind is supported only for raycast; historical shapecast and overlap are not offered.
Joints
Joints are componentJointComponent JointComponent, lowered to Rapier impulse joints in componentSync.ts. The supported types are fixed, revolute, prismatic, spherical, spring, and rope. Motors and limits are honored only on revolute and prismatic joints and fail closed elsewhere; a generic six-degree joint is deferred.
Character controller surface
Player movement is the KCC. Its authored inputs are componentCharacterMovementComponent CharacterMovementComponent (maxSpeed, gravity, jumpSpeed, and the enableKcc governance flag) and the live velocity on VelocityComponent. deriveMovementParams (movementParams.ts) turns those components into controller parameters over the named defaults in RUNTIME_KCC_DEFAULTS, and both the server tick and the client predictor derive from the same components with the same function.
Two governance facts are part of the contract:
- The KCC steps only a character whose
CharacterMovementComponent.enableKccistrue; the schema default isfalse, resolved by the sharedownsKccGovernancepredicate (kccGovernance.ts). For which character motor the runtime calls on each path, see the traced conclusion in Physics Simulation. deriveMovementParamsreadsgravityas a non-negative field defaulting to 0, so an unauthored character does not fall under the KCC (vertical motion is opt-in).
The static-collision snapshot the KCC sweeps is derived by deriveCollisionSnapshot and is bounded at RUNTIME_COLLISION_SNAPSHOT_MAX_TRIANGLES (50000) triangles. It ignores collider rotation for box solids only (a world axis-aligned box cannot represent an oriented box); mesh-collider triangles carry the full authored rotation. It excludes movers (a VelocityComponent, a CharacterMovementComponent, or a dynamic RigidBodyComponent), so a kinematic or rigidbody-less solid is swept as static world geometry and character-versus-character stays server-authoritative.
Proof surfaces
Each guarantee is bound to a named conformance test or check:
- Golden corpus (
check:physics-golden-corpus, in the root check chain): per-tick observable output stays byte-identical across refactors at 6-decimal canon precision. - Native isolation (
physicsKernelArchitecture.test.ts): every native Rapier dependency stays inside the adapter family, with exactly oneworld.step()owner. - Tick transaction (
physicsTransaction.test.ts): a prepared step does not advance the coordinate, commit is monotonic, abort cold-rebuilds, and a config change is an observable epoch restart. - Character parity (
runtimeNetcodeKccParity.test.ts): server authority and client predictor produce bit-identical{position, velocity, onFloor}over randomized tapes. - Body-type authority (
physicsBodyTypeAuthority.test.ts): flippingbodyTypechanges the fingerprint and recreates the body with the new authority. - Governance (
kccGovernance.test.ts): KCC governance is owned only by the explicitenableKccflag. - Backend registry (
physicsBackendRegistry.test.ts): the registry fails closed on an uninstalled backend and validates adapter output at the boundary. - Command atomicity (
physicsFoundationFuzz.test.ts,physicsFoundationDefects.test.ts): an over-budget or over-magnitude command batch applies nothing.
Limits
- No first-party integrator or solver; the engine drives Rapier
world.step()(rapierAdapter.ts). - Angular inertia is isotropic and equal to the mass scalar, not shape-derived (
angularInertiaForMass). - Mesh colliders require a static body; on a dynamic body they emit
physics.mesh_collider_requires_static_bodyand are skipped. - Interaction layers are capped at
MAX_RAPIER_INTERACTION_LAYERS; overflow is dropped with a correction. shapecastandoverlapshapes are limited to box, sphere, and capsule; historical rewind israycast-only.- The backend selection object accepts only
backendId; per-room tuning (gravity, solver and friction iterations, substep bound, CCD substeps) is the runtime-budget profile, hashed intophysicsEpochKey. - Motors and limits apply only to revolute and prismatic joints; a generic joint is deferred.
- No friction or restitution combine rule is configured; Rapier's default applies.
- Sleep state is kept internal and never patched onto the authored component.
Related
- Physics Simulation - the model, the equations, and the traced two-motor conclusion.
- ECS Components - how to read the generated component catalog this page points into.
- Runtime Playability - the runtime modules and readiness registries.