Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ryanhcode/sable/llms.txt

Use this file to discover all available pages before exploring further.

Sable fires three events that let you hook into the physics tick lifecycle and level initialization. Each event is a @FunctionalInterface, and all three are registered through SableEventPlatform.INSTANCE — a platform-abstracted singleton that routes to the correct Fabric or NeoForge implementation automatically. Register your listeners during mod initialization before any worlds load.
Multiple physics substeps run per server game tick based on Sable’s configured sub-step count. Both SablePrePhysicsTickEvent and SablePostPhysicsTickEvent fire once per substep, not once per game tick. Any logic that must influence the physics world — such as applying forces — should be placed in these events rather than in a server tick event.

SablePrePhysicsTickEvent

Fired by SubLevelPhysicsSystem immediately before each physics substep is executed. Use this event to apply forces, set velocities, or otherwise prepare state that the physics solver will consume during the upcoming substep. Callback signature:
void prePhysicsTick(SubLevelPhysicsSystem physicsSystem, double timeStep)
ParameterTypeDescription
physicsSystemSubLevelPhysicsSystemThe physics system about to execute a substep
timeStepdoubleDuration of this substep in seconds
Registration:
SableEventPlatform.INSTANCE.onPhysicsTick((physicsSystem, timeStep) -> {
    // Called before every physics substep
    // physicsSystem.getPipeline() gives access to the physics pipeline
});

SablePostPhysicsTickEvent

Fired by SubLevelPhysicsSystem immediately after each physics substep completes. Use this event to read back simulation results — such as updated positions or velocities — or to trigger game logic that depends on the resolved physics state. Callback signature:
void postPhysicsTick(SubLevelPhysicsSystem physicsSystem, double timeStep)
ParameterTypeDescription
physicsSystemSubLevelPhysicsSystemThe physics system that just completed a substep
timeStepdoubleDuration of the completed substep in seconds
Registration:
SableEventPlatform.INSTANCE.onPostPhysicsTick((physicsSystem, timeStep) -> {
    // Called after every physics substep
});

SableSubLevelContainerReadyEvent

Fired once when Sable has finished initializing for a level and the SubLevelContainer for that level is ready to use. This is the appropriate time to look up or register sub-levels that should exist at world load, or to cache a reference to the container for later use. Callback signature:
void onSubLevelContainerReady(Level level, SubLevelContainer container)
ParameterTypeDescription
levelLevelThe level instance whose container is now ready
containerSubLevelContainerThe initialized sub-level container for that level
Registration:
SableEventPlatform.INSTANCE.onSubLevelContainerReady((level, container) -> {
    // Safe to interact with the sub-level container for this level
});

Full registration example

The following example registers all three events in a common mod initializer entry point. The same code works on both Fabric and NeoForge because SableEventPlatform.INSTANCE is resolved at runtime.
public class MyModCommon {

    public static void init() {
        SableEventPlatform.INSTANCE.onSubLevelContainerReady((level, container) -> {
            MyMod.LOGGER.info("Sub-level container ready for level: {}", level.dimension().location());
        });

        SableEventPlatform.INSTANCE.onPhysicsTick((physicsSystem, timeStep) -> {
            // Apply per-substep custom forces here
        });

        SableEventPlatform.INSTANCE.onPostPhysicsTick((physicsSystem, timeStep) -> {
            // Read back physics results here
        });
    }
}

Build docs developers (and LLMs) love