<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Moss</title>
    <link>https://log.moss.games/</link>
    <description>A small studio growing quiet worlds.</description>
    <pubDate>Thu, 14 May 2026 06:24:23 +0000</pubDate>
    <item>
      <title>the game loop</title>
      <link>https://log.moss.games/the-game-loop?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[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.&#xA;&#xA;why this stack&#xA;&#xA;mostly for speed of development and keeping concerns separate:&#xA;&#xA;bevy gives me a clean ECS world for a fully headless simulation.&#xA;tauri makes it easy to ship cross-platform desktop/mobile apps.&#xA;react is quick for building menu-driven UIs.&#xA;&#xA;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.&#xA;&#xA;and with that…&#xA;&#xA;the world ticks when the client asks it to&#xA;&#xA;the bevy world doesn’t run continuously. instead, every 250ms, the client calls a tauri command that:&#xA;&#xA;advances the simulation by the given delta.&#xA;&#xA;returns a snapshot of the updated world state as JSON.&#xA;&#xA;a simplified example&#xA;&#xA;[tauri::command]&#xA;fn update(delta: f32, state: StateAppState) -  Snapshot {&#xA;    let mut backend = state.backend.lock();&#xA;    &#xA;    backend.update(Duration::frommillis(delta));&#xA;    backend.snapshot()&#xA;}&#xA;&#xA;impl Backend {&#xA;&#x9;&#x9;fn update(&amp;mut self, delta: Duration) -  {&#xA;&#x9;&#x9;&#x9;&#x9;self.app.worldmut().resource_mut::TimeUpdateStrategy() =&#xA;            TimeUpdateStrategy::ManualDuration(delta);&#xA;&#xA;        self.app.update();&#xA;&#x9;&#x9;}&#xA;}&#xA;&#xA;setInterval(() =  {&#xA;  invoke(&#34;update&#34;, { delta: 0.25 }).then((snapshot) =  {&#xA;    setWorldState(snapshot);&#xA;  });&#xA;}, 250);&#xA;&#xA;the offline tick&#xA;&#xA;as an idle game, we gotta have offline progression. we do this pretty simply:&#xA;&#xA;on startup, we calculate how long the game has been closed.&#xA;we then run the bevy world with the total delta in 250ms chunks.&#xA;every chunk runs the simulation in full.&#xA;a summary is returned of all progress made during catch-up.&#xA;&#xA;about interactivity&#xA;&#xA;it’s all tauri commands!&#xA;&#xA;the client invokes a command → the backend pushes an event into bevy → an action system processes it.&#xA;&#xA;what’s next?&#xA;&#xA;i plan on writing more of these little architecture logs. some ideas i have:&#xA;&#xA;how villagers + tasks work in ECS.&#xA;the data-driven content system.&#xA;how save versions and migrations work.&#xA;&#xA;you can follow along with the development of Mosswood Idle by either subscribing here or giving a follow on bluesky.]]&gt;</description>
      <content:encoded><![CDATA[<p>hey there! for our first little post here, i wanted to talk about how the idle game loop works and how we’re bridging <a href="https://bevy.org/">bevy</a> with <a href="https://v2.tauri.app/">tauri</a> to create <strong>Mosswood Idle</strong>.</p>

<h3 id="why-this-stack" id="why-this-stack">why this stack</h3>

<p>mostly for speed of development and keeping concerns separate:</p>
<ul><li><strong>bevy</strong> gives me a clean ECS world for a fully headless simulation.</li>
<li><strong>tauri</strong> makes it easy to ship cross-platform desktop/mobile apps.</li>
<li><strong>react</strong> is quick for building menu-driven UIs.</li></ul>

<p>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.</p>

<p><em>and with that</em>…</p>

<h3 id="the-world-ticks-when-the-client-asks-it-to" id="the-world-ticks-when-the-client-asks-it-to">the world ticks when the client asks it to</h3>

<p>the bevy world doesn’t run continuously. instead, every 250ms, the client calls a tauri command that:</p>
<ol><li><p><strong>advances the simulation</strong> by the given <code>delta</code>.</p></li>

<li><p><strong>returns a snapshot</strong> of the updated world state as JSON.</p></li></ol>

<p><img src="https://i.snap.as/DULTnLbD.png" alt=""/></p>

<h3 id="a-simplified-example" id="a-simplified-example">a simplified example</h3>

<pre><code class="language-rust">#[tauri::command]
fn update(delta: f32, state: State&lt;AppState&gt;) -&gt; Snapshot {
    let mut backend = state.backend.lock();
    
    backend.update(Duration::from_millis(delta));
    backend.snapshot()
}

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

        self.app.update();
		}
}
</code></pre>

<pre><code class="language-rust">setInterval(() =&gt; {
  invoke(&#34;update&#34;, { delta: 0.25 }).then((snapshot) =&gt; {
    setWorldState(snapshot);
  });
}, 250);
</code></pre>

<h3 id="the-offline-tick" id="the-offline-tick">the offline tick</h3>

<p>as an idle game, we gotta have offline progression. we do this pretty simply:</p>
<ul><li>on startup, we calculate <strong>how long the game has been closed</strong>.</li>
<li>we then run the bevy world with the total delta in 250ms chunks.</li>
<li>every chunk runs the simulation in full.</li>
<li>a summary is returned of all progress made during catch-up.</li></ul>

<h3 id="about-interactivity" id="about-interactivity">about interactivity</h3>

<p>it’s all tauri commands!</p>

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

<h3 id="what-s-next" id="what-s-next">what’s next?</h3>

<p>i plan on writing more of these little architecture logs. some ideas i have:</p>
<ul><li>how villagers + tasks work in ECS.</li>
<li>the data-driven content system.</li>
<li>how save versions and migrations work.</li></ul>

<p>you can follow along with the development of <strong>Mosswood Idle</strong> by either subscribing here or giving a follow on <a href="https://bsky.app/profile/moss.games">bluesky</a>.</p>
]]></content:encoded>
      <guid>https://log.moss.games/the-game-loop</guid>
      <pubDate>Wed, 17 Dec 2025 16:07:18 +0000</pubDate>
    </item>
  </channel>
</rss>