the game loop

hey there! for our first little post here, i wanted to talk about how the idle game loop works and how we’re bridging bevy with tauri to create Mosswood Idle.

why this stack

mostly for speed of development and keeping concerns separate:

if we’re being real, though, it’s also just comfort. i love working in bevy, and while i don’t love react, i’ve used it for a decade so i’m pretty quick with it.

and with that

the world ticks when the client asks it to

the bevy world doesn’t run continuously. instead, every 250ms, the client calls a tauri command that:

  1. advances the simulation by the given delta.

  2. returns a snapshot of the updated world state as JSON.

a simplified example

#[tauri::command]
fn update(delta: f32, state: State<AppState>) -> Snapshot {
    let mut backend = state.backend.lock();
    
    backend.update(Duration::from_millis(delta));
    backend.snapshot()
}

impl Backend {
		fn update(&mut self, delta: Duration) -> {
				self.app.world_mut().resource_mut::<TimeUpdateStrategy>() =
            TimeUpdateStrategy::ManualDuration(delta);

        self.app.update();
		}
}
setInterval(() => {
  invoke("update", { delta: 0.25 }).then((snapshot) => {
    setWorldState(snapshot);
  });
}, 250);

the offline tick

as an idle game, we gotta have offline progression. we do this pretty simply:

about interactivity

it’s all tauri commands!

the client invokes a command → the backend pushes an event into bevy → an action system processes it.

what’s next?

i plan on writing more of these little architecture logs. some ideas i have:

you can follow along with the development of Mosswood Idle by either subscribing here or giving a follow on bluesky.