Skip to content

Movement

goshi-hider edited this page Jul 24, 2026 · 1 revision

Movement

Package: dev.goshi.omnimixin.api.event.movement

Hooks around how entities move: ground speed, jumping, sprinting, flight speed, and being pushed by other entities. Everything here runs on the logical server (or singleplayer integrated server) — movement is server-authoritative in vanilla, so that's where these fire.

PlaySpeed.MODIFY_GROUND

Fires whenever an entity's base ground movement speed is calculated. Carries a [SpeedContext](https://claude.ai/chat/a3c01413-be46-46f0-a149-92ab00193506#speedcontext) with the entity, the speed vanilla was about to use, and a MutableFloat to change it.

PlaySpeed.MODIFY_GROUND.register(ctx -> {
    if (ctx.entity().hasStatusEffect(StatusEffects.SLOWNESS)) {
        return; // let vanilla's own slowness handling apply on top
    }
    if (isStandingOnMyCustomIceBlock(ctx.entity())) {
        ctx.modifier().multiply(1.5f);
    }
});

SpeedContext(LivingEntity entity, float currentBase, MutableFloat modifier)

PlayJump

Three phases around a jump — PRE_JUMP, MODIFY_IMPULSE, POST_JUMP — all sharing the same [JumpContext](https://claude.ai/chat/a3c01413-be46-46f0-a149-92ab00193506#jumpcontext).

// Give shift-clicking (sneaking) entities a lower jump.
PlayJump.MODIFY_IMPULSE.register(ctx -> {
    if (ctx.entity().isSneaking()) {
        ctx.verticalImpulse().multiply(0.6f);
    }
});

// Block jumping entirely for a rooted/stunned status effect.
PlayJump.PRE_JUMP.register(ctx -> {
    if (ctx.entity().hasStatusEffect(MyEffects.ROOTED)) {
        ctx.cancel().cancel();
    }
});

JumpContext(LivingEntity entity, Cancellable cancel, MutableFloat verticalImpulse)

PRE_JUMP fires before the jump happens at all — cancel here to stop it outright. MODIFY_IMPULSE fires as the actual vertical velocity is being computed — this is where you scale jump height. POST_JUMP fires after, for reacting to a jump that already happened (playing a custom sound, starting a cooldown, etc.) — cancelling at this point has no effect, since the jump is already done.

PlaySprint

Two events, not phases of the same action — CAN_SPRINT gates whether an entity is allowed to start sprinting, and ON_TOGGLE tells you whenever sprinting turns on or off.

// Prevent sprinting while encumbered by a heavy backpack item.
PlaySprint.CAN_SPRINT.register(ctx -> !isCarryingHeavyLoad(ctx.entity()));

// Play a custom sound whenever any entity starts or stops sprinting.
PlaySprint.ON_TOGGLE.register((ctx, sprinting) -> {
    if (sprinting) {
        playWhooshSound(ctx.entity());
    }
});

CAN_SPRINT's listener returns a boolean — return false to veto. If any registered listener returns false, sprinting is blocked and no further CAN_SPRINT listeners even run.

SprintContext(LivingEntity entity)

PlayFly.MODIFY_FLIGHT_SPEED

Adjusts a player's creative/spectator flight speed.

PlayFly.MODIFY_FLIGHT_SPEED.register(ctx -> {
    if (hasSpeedBoots(ctx.abilities())) {
        ctx.modifier().add(0.02f);
    }
});

FlySpeedContext(PlayerAbilities abilities, float baseFlySpeed, MutableFloat modifier)

Note: only flight speed is covered right now. Toggling flight on/off and elytra-allowed checks are not — see Known Limitations for why.

PlayCollision.ENTITY_PUSH

Fires whenever one entity pushes another apart (the constant gentle nudging that keeps mobs from overlapping).

// My custom "heavy" entities can't be pushed around by regular mobs.
PlayCollision.ENTITY_PUSH.register(ctx -> {
    if (isHeavyEntity(ctx.entity()) && !isHeavyEntity(ctx.other())) {
        ctx.cancel().cancel();
    }
});

PushContext(Entity entity, Entity other, MutableFloat forceMultiplier, Cancellable cancel)

entity is the one being pushed; other is whatever is pushing it. Cancelling stops this specific push interaction between this specific pair for this tick — it doesn't make entity generally unpushable.

Note: only entity-vs-entity pushing is covered. Block collision math and step-height are not — see Known Limitations.

Clone this wiki locally