The clock

There is one clock: NativeCore.nowNs(). It is a monotonic count of nanoseconds from std::time::Instant inside activerse_core. The frame loop, the input ring, and the body step all use that value. GLFW is the window. It is not the timer.

One frame

GameLoop does this, in order, on the main thread:

  1. Poll GLFW. Mouse and key callbacks stamp samples with nowNs() and push them into the Rust ring.
  2. Read nowNs() once. That reading is frameNs.
  3. Elapsed time for this frame is frameNs - previous, capped at 0.25 s.
  4. Apply look in Java from the summed mouse delta. Look is still once per drawn frame.
  5. Call world.prepareSimulation(). If the world publishes a character, the Rust step turns on.
  6. integrateTo(frameNs) runs every 1 ms step up to that same timestamp.
  7. world.pullSimulation() copies the body back when a step actually ran.
  8. Gameplay ticks drain the same elapsed time at 1 / fps. Each tick calls worldAct().
  9. Stream meshes, draw, swap buffers. Vsync still decides when the picture is shown.

The first integrateTo only anchors the clock and returns 0. A gap longer than 250 ms is clamped. A stall does not replay a quarter-second of old input as if it were live.

Two rates

RateWhat runsClock
1 msPlayer body: wish velocity, gravity, jump, voxel collisionframeNs
fps in toml (default 60)Actor.act, weapons, menus, savesthe same elapsed
Display refreshThe picturevsync, not a second timer

A key edge 3 ms into a 16 ms frame moves the body on that 1 ms step. The gun and the pixels still update on the gameplay tick and the swap. Record experiment times with NativeCore.nowNs(). Do not call glfwGetTime or System.nanoTime for anything that must line up with input or the body.

Publishing a character

The default prepareSimulation does nothing, so the Rust step stays off and FirstPersonController.update or ThirdPersonController.update still performs one Java step of the dt you pass. Sandbox and OptiShoot publish only while play is active: not during loading, not while a menu is open, and not while Sandbox chat or the inventory is open.

@Override
public void prepareSimulation() {
    if (!playing()) {
        return;
    }
    player.publishToSim(getInput(), getCamera());
}

@Override
public void pullSimulation() {
    if (!NativeCore.characterSteppedThisFrame() || player == null) {
        return;
    }
    player.pullFromSim(getCamera());
}

publishToSim copies settings, bindings, yaw, and the body into Rust, then enables the sim. characterSteppedThisFrame() stays true for that frame even when less than 1 ms elapsed and zero steps ran, so update does not also step the body. The loop sets the sim disabled at the start of every frame. Forgetting to publish freezes the character. Publishing during a menu lets them walk through the UI.

What is not on this clock yet