<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Vermeil Soft Tech Blog</title>
    <subtitle>Company blog about Tech, Software Engineering, Game Design, and more ...</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.vermeilsoft.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.vermeilsoft.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-09-28T00:00:00+00:00</updated>
    <id>https://blog.vermeilsoft.com/atom.xml</id>
    <entry xml:lang="en">
        <title>The Game Engine that would not have been made without Rust</title>
        <published>2025-09-28T00:00:00+00:00</published>
        <updated>2025-09-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            Andres Franco
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.vermeilsoft.com/2025-09-rust-game-engine/"/>
        <id>https://blog.vermeilsoft.com/2025-09-rust-game-engine/</id>
        
        <content type="html" xml:base="https://blog.vermeilsoft.com/2025-09-rust-game-engine/">&lt;p&gt;In 2018, a man had a dream: make a multiplayer game with an emphasis on cooperation. Just fresh out
of engineering school, he decided to create his company and get started with the project alone.&lt;&#x2F;p&gt;
&lt;p&gt;A few years down the line, enough experience had been gathered to write about it in a formal blog post, and now here
we are.&lt;&#x2F;p&gt;
&lt;p&gt;Let me tell you about the creation of a game engine from scratch, and how it might have never been born without
Rust.&lt;&#x2F;p&gt;
&lt;p&gt;You can take this article just as a raw experience dump, but also as an answer to the eternal question &quot;&lt;em&gt;Why didn&#x27;t you
make it in Unity &#x2F; Unreal &#x2F; Godot &#x2F; Something else &#x2F;... ?&lt;&#x2F;em&gt;&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I want to make it extremely clear that this is &lt;strong&gt;not&lt;&#x2F;strong&gt; as criticism of other Game Engines. I&#x27;m not saying &quot;mine is
better and yours is bad&quot;, it&#x27;s more along the lines of &quot;The existing ones didn&#x27;t really fit my needs so I went my own way&quot;.
Different people, different times will probably lead to different outcomes, and that&#x27;s perfectly fine.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;first-design-considerations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#first-design-considerations&quot; aria-label=&quot;Anchor link for: first-design-considerations&quot;&gt;First Design Considerations&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;It&#x27;s well known that you can&#x27;t just scrap multiplayer on an existing game without some major refactoring
or without major drawbacks, of if you&#x27;re unlucky, both. The famous game &quot;Don&#x27;t Starve Together&quot; is not just an
extension of &quot;Don&#x27;t Starve&quot; exactly because of this reason: the only way they could add Multiplayer was to re-do
everything with multiplayer in mind. Because of that, I needed to consider what kind
of network syncing strategy I would use for my game engine.&lt;&#x2F;p&gt;
&lt;p&gt;There are a lot of techniques available to synchronise multiplayer states, and none of them are perfect.
Some are even hybrids between multiple ideas. A lot of articles exist online to describe all of them, like
&lt;a href=&quot;https:&#x2F;&#x2F;mas-bandwidth.com&#x2F;choosing-the-right-network-model-for-your-multiplayer-game&#x2F;&quot;&gt;this thorough article from Glenn Fiedler&lt;&#x2F;a&gt;,
but a dozen of others exist that are also extremely good.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a side note, if you are interested in making your own, &lt;a href=&quot;https:&#x2F;&#x2F;gafferongames.com&#x2F;&quot;&gt;Gaffer on Games&lt;&#x2F;a&gt; is definitely
a website to bookmark. Sure, most of it you could figure out or adapt yourself, but why reinvent the wheel when
other people have done it for you?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I knew I wanted reactive gameplay with 2 players or more, like an action game or a platformer,
and not be limited to turn-based games.&lt;&#x2F;p&gt;
&lt;p&gt;I also knew I wanted highly interactive gameplay, so distributed authority or parallel simulations were out.
How do you imagine you sync player state if a mage sends a fireball at the same time as the enemy stuns everyone?&lt;&#x2F;p&gt;
&lt;p&gt;Then the final nail in the coffin was the requirement to be playable over long distances. It&#x27;s hard to find games to play in
common when you&#x27;re not living in the same continent, often because games require low ping between players to be playable.
COVID especially showed us that there was a high demand, remember when &lt;a href=&quot;https:&#x2F;&#x2F;steamcommunity.com&#x2F;games&#x2F;892970&#x2F;announcements&#x2F;detail&#x2F;3059603085894895512&quot;&gt;Valheim had 1M sales in one week&lt;&#x2F;a&gt;
in the middle of the pandemic?&lt;&#x2F;p&gt;
&lt;p&gt;So in summary:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2+ players&lt;&#x2F;li&gt;
&lt;li&gt;No to little input delay&lt;&#x2F;li&gt;
&lt;li&gt;Deep Interactivity between players&lt;&#x2F;li&gt;
&lt;li&gt;Playable with stable but high latency&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You probably guessed it, but I had crossed off everything but &lt;strong&gt;rollback-based multiplayer&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am not going to explain how rollback works, there is already a ton of content available on the matter, from this
&lt;a href=&quot;https:&#x2F;&#x2F;drive.google.com&#x2F;file&#x2F;d&#x2F;1cV0fY8e_SC1hIFF5E1rT8XRVRzPjU8W9&#x2F;view&quot;&gt;Article from the GGPO Team&lt;&#x2F;a&gt;, to this excellent
&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=0NLe4IpdS1w&quot;&gt;high level overview by Core-A Gaming on youtube&lt;&#x2F;a&gt;. A good chunk of
this article is going to assume you know what it is, you&#x27;ve been warned.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I should mention that rollback is often used in the context of fighting games in Peer 2 Peer,
but this time it&#x27;s a Server-authoritative base with clients using rollback on their own.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;why-rust&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-rust&quot; aria-label=&quot;Anchor link for: why-rust&quot;&gt;Why Rust&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Then came in the stack to use. At the time GGPO (the de-facto reference when talking about rollback netcode) was
not Open-Source yet, plus it was only for Peer 2 Peer games. There was no game engine that had some kind of rollback
implementation either out of the box or open-source, so I knew I had to build my own.&lt;&#x2F;p&gt;
&lt;p&gt;I looked a bit a C++ and C#, to see if some kind of helpful library existed, but found nothing that
really fit my needs. Since by that point I had been doing Rust personally for a few years, I decided I would start
my own, in Rust. By far one of the best decisions I&#x27;ve made.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-ingredients-of-a-rollback-based-multiplayer-engine&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-ingredients-of-a-rollback-based-multiplayer-engine&quot; aria-label=&quot;Anchor link for: the-ingredients-of-a-rollback-based-multiplayer-engine&quot;&gt;The Ingredients of a Rollback-based Multiplayer Engine&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;A lot of things are necessary to make a Rollback-based Multiplayer Engine, and a few of them would have been a lot
more difficult if not used in Rust. Strap in, because there&#x27;s a lot of them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;determinism&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#determinism&quot; aria-label=&quot;Anchor link for: determinism&quot;&gt;Determinism&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A core part of what makes rollback possible is cross-platform determinism. Not only because players need to see the
exact same thing on their screens, but also because it&#x27;s going to be possible for one client to replay
the same inputs on the same frames multiple times in a row because of rollback.&lt;&#x2F;p&gt;
&lt;p&gt;There were a few important considerations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;numbers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#numbers&quot; aria-label=&quot;Anchor link for: numbers&quot;&gt;Numbers&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is a highly debated topic and thankfully &lt;a href=&quot;https:&#x2F;&#x2F;gafferongames.com&#x2F;post&#x2F;floating_point_determinism&#x2F;&quot;&gt;Gaffer on Games has an excellent breakdown of the problem.&lt;&#x2F;a&gt;
In summary, floating point operations are almost always deterministic, but not always. Some claim you can achieve this with a few flags,
some others say it&#x27;s impossible if the architecture is not the same, etc.&lt;&#x2F;p&gt;
&lt;p&gt;To clarify, some have managed to make deterministic floating point based engines, &lt;a href=&quot;https:&#x2F;&#x2F;rapier.rs&#x2F;&quot;&gt;rapier is a good example&lt;&#x2F;a&gt;.
However, unlike what is being said in some developer comments from the above article, they don&#x27;t just enable a few flags,
they &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dimforge&#x2F;rapier&#x2F;blob&#x2F;3bac79ecacdeaa18de19127b7a6c82cbfab29d14&#x2F;examples3d&#x2F;trimesh3.rs#L27-L30&quot;&gt;use non-std functions in their examples&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you go up the dependency tree, you&#x27;ll notice it comes down to &lt;code&gt;libm&lt;&#x2F;code&gt;&#x27;s functions like &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libm&#x2F;latest&#x2F;libm&#x2F;fn.sin.html&quot;&gt;sin&lt;&#x2F;a&gt;,
of which the implementations are only &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libm&#x2F;latest&#x2F;src&#x2F;libm&#x2F;math&#x2F;sin.rs.html#48-82&quot;&gt;made of basic operations&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Basically it&#x27;s a mess, and while I don&#x27;t doubt there is probably a way of making this work, I chose not to.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;fixed&quot;&gt;Rust has an excellent fixed-point arithmetic crate&lt;&#x2F;a&gt;, and I preferred using this over
risking having problems with floating points in the future. Is this less efficient? Probably, although I haven&#x27;t checked.&lt;&#x2F;p&gt;
&lt;p&gt;Did I need to make a few lookup tables for &lt;code&gt;cos&lt;&#x2F;code&gt;, &lt;code&gt;acos&lt;&#x2F;code&gt; functions? Yes.&lt;&#x2F;p&gt;
&lt;p&gt;Are the computations not accurate? Also yes.&lt;&#x2F;p&gt;
&lt;p&gt;But it gave me several advantages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;NaN&lt;&#x2F;code&gt;, &lt;code&gt;Infinity&lt;&#x2F;code&gt; or silent failures like that. If I divide by zero a fixed point, I get an instant panic and that&#x27;s
exactly what I want. Likewise if I overflow, underflow or anything else.&lt;&#x2F;li&gt;
&lt;li&gt;Fixed point have total order (&lt;code&gt;Ord&lt;&#x2F;code&gt;) and not only partial order. &lt;code&gt;std::cmp::{max, min}&lt;&#x2F;code&gt; are usable, and I can sort
any &lt;code&gt;Vec&amp;lt;FixedPoint&amp;gt;&lt;&#x2F;code&gt; using &lt;code&gt;std&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Peace of mind in the future. No one is going to forget to use &lt;code&gt;libm::sin&lt;&#x2F;code&gt; and
accidentaly use &lt;code&gt;f32.cos()&lt;&#x2F;code&gt; somewhere and break the determinism once every 3 moons on an obscure CPU.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And the drawbacks of not being perfectly accurate? It&#x27;s a game, if a projectile is moving at &lt;code&gt;5.56950&lt;&#x2F;code&gt; instead of
&lt;code&gt;5.56945&lt;&#x2F;code&gt;, realistically no one will care as long as everyone sees the same result on their screens.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a sidenote, I wish fixed point was more available not only in Rust in the std, but also in other languages in general.
They always are a 3rd tier citizen when they shouldn&#x27;t be: they have their uses just as much as floating points.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;random&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#random&quot; aria-label=&quot;Anchor link for: random&quot;&gt;Random&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is one of the points where Rust shines. In most languages, the standard library&#x27;s random is OS-based and definitely
not deterministic.&lt;&#x2F;p&gt;
&lt;p&gt;If you wanted to find another random generator, most likely you would struggle to find
a good implementation that fits your needs of determinism. In Rust, &lt;code&gt;rand_pcg&lt;&#x2F;code&gt;
is perfect: it uses the same API as &lt;code&gt;ThreadRng&lt;&#x2F;code&gt; or &lt;code&gt;OsRng&lt;&#x2F;code&gt;, does not use floating point, is just 2 &lt;code&gt;u128&lt;&#x2F;code&gt; words
(so cloneable easily and guaranteed deterministic cross-platform), and is available just with one line in &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;One of the most important aspects of game simulation, and the problem was solved with just one line.
That&#x27;s nothing but a win in my book.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parallel-processing-or-the-lack-of&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#parallel-processing-or-the-lack-of&quot; aria-label=&quot;Anchor link for: parallel-processing-or-the-lack-of&quot;&gt;Parallel Processing (or the lack of)&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Unfortunately, within the simulation itself, I had to give up on threading. Some ECS libraries have this complex
system of tree execution dependency where it would figure out that if in system 1 you need component A, and system 2
in component B, you could run both in parallel.
As a solo developer on a small project, that kind of hassle wasn&#x27;t worth the risk,
so I designed a much simpler Entity-Component library.&lt;&#x2F;p&gt;
&lt;p&gt;It looks like this roughly:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(entity_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; entity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gravity) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;entities&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;iter_single&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Gravity&amp;gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;gravity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;enabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; collision_body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; entity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;CollisionBody&amp;gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; stepping_on &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; collision_body
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;| c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;stepping_on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;is_some&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;unwrap_or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F; is this entity stepping on something, apply the gravity
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;stepping_on {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; tmp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; entity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        tmp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;-=&lt;&#x2F;span&gt;&lt;span&gt; gravity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;fallaccel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;steps&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;playtime_step &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;F_UPS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        tmp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;y &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;cmp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;max(tmp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;gravity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;max_fallspeed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        entity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(tmp)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Random access of entities from an &lt;code&gt;EntityId&lt;&#x2F;code&gt; is possible, and random access of any component from an &lt;code&gt;Entity&lt;&#x2F;code&gt;
is possible. The trade-off is that all components need to have interior mutability, but since nothing is parallel,
we can wrap most of them in &lt;code&gt;Cell&lt;&#x2F;code&gt; and &lt;code&gt;RefCell&lt;&#x2F;code&gt;. At this point, some (if not most) will think of me as a filthy,
unperformant swine, but I can assure you it&#x27;s not that bad. Even after writing dozens of thousands of LoC with that
architecture, having one &lt;code&gt;get&lt;&#x2F;code&gt; and &lt;code&gt;set&lt;&#x2F;code&gt; every once in a while is better than not having random access of component
from anywhere in your code.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Back when I started the state of ECS libraries was very poor. Nowadays, it&#x27;s much better, and yet still nothing
to fit my needs although &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;hecs&quot;&gt;hecs&lt;&#x2F;a&gt; comes pretty close.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;More details on this in another article, another day.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;network-messaging&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#network-messaging&quot; aria-label=&quot;Anchor link for: network-messaging&quot;&gt;Network Messaging&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This covers the category of &quot;how the hell do I send data over the internet?&quot;.
It&#x27;s not really a breakthrough for anyone who has delved into it, but I thought I would include it just in case.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reliable-ordered-udp&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reliable-ordered-udp&quot; aria-label=&quot;Anchor link for: reliable-ordered-udp&quot;&gt;Reliable Ordered UDP&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Again, I can do nothing but recommend
&lt;a href=&quot;https:&#x2F;&#x2F;gafferongames.com&#x2F;post&#x2F;reliable_ordered_messages&#x2F;&quot;&gt;this excellent article about reliable ordered messaging&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The base logic is simple, instead of a continuous stream like TCP, we&#x27;ll base it around messages, with a beginning
and an end. Because of MTU of UDP being around 1500, if you want to send messages longer than 1500 bytes (which
is going to be often), you need some kind of system to fragment your message around 1500-bytes chunks, and tell
the remote how to reorder them.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s define a &lt;code&gt;Fragment&lt;&#x2F;code&gt; struct.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;Fragment&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;AsRef&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;]&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;sequence_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;fragment_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;fragment_total&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;fragment_meta&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; FragmentMeta,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; T
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The idea is simple: &lt;code&gt;sequence_id&lt;&#x2F;code&gt; a message, &lt;code&gt;fragment_id&lt;&#x2F;code&gt; represents which part of the message we are sending.&lt;&#x2F;p&gt;
&lt;p&gt;No matter the order in which we receive the fragment, we store them until the fragments for a given &lt;code&gt;sequence_id&lt;&#x2F;code&gt;
equals &lt;code&gt;fragment_total&lt;&#x2F;code&gt;. Then we re-assemble the &lt;code&gt;data&lt;&#x2F;code&gt; from each fragment to get the original message.&lt;&#x2F;p&gt;
&lt;p&gt;Note that &lt;code&gt;fragment_total&lt;&#x2F;code&gt; is &lt;code&gt;u8&lt;&#x2F;code&gt;, which means that a message can at most do ~380kB. More than that and we will
need some kind of super fragmenting or streaming. Or you could just go back to TCP for those messages specifically.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t recommend setting &lt;code&gt;fragment_id&lt;&#x2F;code&gt; and &lt;code&gt;fragment_total&lt;&#x2F;code&gt; to u16. While the maximum would then be in the hundreds
of MB, realistically sending thousands of UDP messages at once would probably cause some kind of congestion
somewhere, even in on today&#x27;s internet.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;FragmentMeta &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; FragmentKind,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F; other meta here
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span&gt;(u16)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; FragmentKind {
&lt;&#x2F;span&gt;&lt;span&gt;    Forgettable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;FragmentMeta&lt;&#x2F;code&gt; would be a simple struct with &quot;meta&quot; info, the most obvious would be the kind of packet it is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Forgettable&lt;&#x2F;code&gt; means that if we don&#x27;t receive every fragment because of packet loss, just drop the message and forget it.
&lt;code&gt;Key&lt;&#x2F;code&gt; means that we have to send &lt;code&gt;Ack&lt;&#x2F;code&gt; messages to ensure the full message arrives at some point.&lt;&#x2F;p&gt;
&lt;p&gt;But a fragment is not the only type of message we can send over UDP, so we&#x27;ll need message types:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;Packet&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;P: AsRef&amp;lt;[u8]&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    Fragment(Fragment&amp;lt;P&amp;gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Ack(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Syn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    SynAck&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Heartbeat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    End&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Ack&lt;&#x2F;code&gt; would be to say &quot;Yes, I have received the data of &lt;code&gt;(sequence_id, fragment_id)&lt;&#x2F;code&gt;, you don&#x27;t have to send it in
the future&quot;. &lt;code&gt;Syn&lt;&#x2F;code&gt; and &lt;code&gt;SynAck&lt;&#x2F;code&gt; are akin to TCP to initiate and confirm connection.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Heartbeat&lt;&#x2F;code&gt; is a necessary evil in UDP, because if you send no messages in a certain amount of time,
some routers will assume the connection is over and block incoming messages. A simple message every few seconds
ensures that the connection stays alive.&lt;&#x2F;p&gt;
&lt;p&gt;Each packet would then be encoded, and for instance a &lt;code&gt;Fragment&lt;&#x2F;code&gt; would look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#0f1419;color:#bfbab0;&quot;&gt;&lt;code&gt;&lt;span&gt; 0                   1                   2                   3
&lt;&#x2F;span&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;span&gt;|              CRC32            |          Message Type         |
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;span&gt;|          Sequence ID          | Frag ID |FragTot|  Frag Meta  |
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;span&gt;:                                                               :
&lt;&#x2F;span&gt;&lt;span&gt;:                            Payload                            :
&lt;&#x2F;span&gt;&lt;span&gt;:                                                               :
&lt;&#x2F;span&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A CRC32 check of the bytes 4 to the end is included, and in Rust there are a load of crates that allow you to do just
that. Don&#x27;t forget to check it when receiving it!&lt;&#x2F;p&gt;
&lt;p&gt;This is of course a very high level overview, and the reality is a bit more complex than that, but anyone with a
little bit of experience can build a library like that in less than a few days, Rust is that easy to work with.&lt;&#x2F;p&gt;
&lt;p&gt;One detail I&#x27;m leaving here because I don&#x27;t know where else to put it:
On Windows, UDP sockets have an annoying behavior where they can return &lt;code&gt;ConnectionReset&lt;&#x2F;code&gt;,
which never happens on other platforms.&lt;&#x2F;p&gt;
&lt;p&gt;This is because of Windows&#x27; Virtual UDP circuit, which you can disable to make it more consistent with other platforms.
I&#x27;m putting the code here because I don&#x27;t remember seeing it anywhere else online, and since I always dev on Linux
the only time I came to notice it was when it was in the hands of testers. That was a fun debug session.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;net&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;UdpSocket&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;windows&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Win32&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Networking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;WinSock&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{WSAIoctl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;SOCKET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;SIO_UDP_CONNRESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; WSAGetLastError}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;windows&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Win32&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;Foundation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;BOOL&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; Disables the Virtual UDP Circuit on Windows
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; Returns a `WSA_ERROR` if it failed.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;disable_virtual_udp_circuit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;udp_socket&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;UdpSocket) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; socket &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; udp_socket&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;as_raw_socket&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; bytes_returned &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;BOOL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;from(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;unsafe &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; r &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; WSAIoctl(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;SOCKET&lt;&#x2F;span&gt;&lt;span&gt;(socket &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;SIO_UDP_CONNRESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;*const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;_ as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;*const &lt;&#x2F;span&gt;&lt;span&gt;core&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;ffi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;c_void&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;mem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;size_of_val(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;enable) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; bytes_returned&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;None
&lt;&#x2F;span&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; r &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;== -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; WSAGetLastError()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(e&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(())
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;the-godsent-serde-and-bincode&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-godsent-serde-and-bincode&quot; aria-label=&quot;Anchor link for: the-godsent-serde-and-bincode&quot;&gt;The Godsent: serde and bincode&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So now we can roughly have an API that accept bytes, transfer them over the network and get those exact same bytes in
return in another computer. What can we do with that?&lt;&#x2F;p&gt;
&lt;p&gt;It will seem obvious to anyone already using Rust, but for those who don&#x27;t, the combination of &lt;code&gt;serde&lt;&#x2F;code&gt; and &lt;code&gt;bincode&lt;&#x2F;code&gt;
is terrific.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s define two structs:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Serialize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;ClientMessage &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    InGameMessage(ClientInGameMessage)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Quit { err&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Handshake { name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Serialize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Deserialize)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;ServerMessage &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    InGameMessage(ServerInGameMessage)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Quit { reason&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    HandshakeResponse(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(), HandshakeError&amp;gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Serializing and Deserializing becomes trivial:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;bincode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;encode_to_vec(
&lt;&#x2F;span&gt;&lt;span&gt;    input_client_message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    bincode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;standard()
&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F; assume result is passed over the network...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(decoded_client_message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;(ClientMessage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;bincode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;decode_from_slice(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    bincode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;standard()
&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This may not seem like much, but something you would either write manually or spend days
figuring out the exact templates for in C++ takes only a few minutes to code in Rust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rollback-logic&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rollback-logic&quot; aria-label=&quot;Anchor link for: rollback-logic&quot;&gt;Rollback Logic&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s explain what the rollback logic looks like in Rust:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;Game &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; world displayed on screen
&lt;&#x2F;span&gt;&lt;span&gt;    played_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; World,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; the last valid world where we should rollback to if
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; the inputs we predicted are not equal to the one we
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; received from the server
&lt;&#x2F;span&gt;&lt;span&gt;    last_valid_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; World
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#59c2ff;&quot;&gt;World &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    tick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;u32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F; rest of the data here, not important in our case ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s imagine 2 &lt;code&gt;World&lt;&#x2F;code&gt;, one is the played world being on screen, and the other being the last valid world available
we know is true. Let&#x27;s assign them two colors: red and blue. This will improve clarity later, but note that
&lt;strong&gt;blue is not always the played world&lt;&#x2F;strong&gt; and &lt;strong&gt;red is not always the last valid world&lt;&#x2F;strong&gt;. You&#x27;ll see why shortly.&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.vermeilsoft.com&amp;#x2F;processed_images&amp;#x2F;2025-09-rollback1.cdd23012b209dc7d.jpg&quot; &#x2F;&gt;
&lt;p&gt;Now, consider the scenario: we have received from the server the inputs from 31 to 33, while we are playing tick 34.
We have to begin the rollback process:&lt;&#x2F;p&gt;
&lt;p&gt;First, swap Played World and Last Valid World, so that our &quot;Played&quot; is back in time:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;std&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;mem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;swap(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;last_valid_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played_world)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.vermeilsoft.com&amp;#x2F;processed_images&amp;#x2F;2025-09-rollback2.1b75b320ff6665e6.jpg&quot; &#x2F;&gt;
&lt;p&gt;Then, use the true inputs we just received from the server:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; advance the Played state with true inputs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;true_advance&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;true_inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[WorldInput]) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; true_input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; true_inputs {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;true_input)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.vermeilsoft.com&amp;#x2F;processed_images&amp;#x2F;2025-09-rollback3.091a4d3c98c3bbbf.jpg&quot; &#x2F;&gt;
&lt;p&gt;Once we don&#x27;t have any more true inputs, dump the Played world into Last Valid, with a &lt;code&gt;clone_from&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;last_valid_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;clone_from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played_world)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.vermeilsoft.com&amp;#x2F;processed_images&amp;#x2F;2025-09-rollback4.040c500175591db3.jpg&quot; &#x2F;&gt;
&lt;p&gt;Lastly, restore the played position to where we where when we started the rollback process:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#0f1419;color:#bfbab0;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;&#x2F;&#x2F;&#x2F; advance the Played state with predicted inputs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb454;&quot;&gt;advance_to_played&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;played_tick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; WorldTick, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29718;&quot;&gt;predicted_inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;HashMap&amp;lt;WorldTick, WorldInput&amp;gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tick &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; played_tick {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff7733;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; predicted_input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; predicted_inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tick)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bfbab0cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#39bae6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;played_world&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f07178;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;predicted_input)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.vermeilsoft.com&amp;#x2F;processed_images&amp;#x2F;2025-09-rollback5.096a360242e044cc.jpg&quot; &#x2F;&gt;
&lt;p&gt;If you compare to the first diagram, you&#x27;ll notice that it&#x27;s roughly the same state, except blue and red have
swapped variables. With this rollback method, every time a rollback occurs, those two variables are swapped in memory.
Doing this logic of swapping instead of a basic &lt;code&gt;clone&lt;&#x2F;code&gt; from last valid to played, then &lt;code&gt;clone&lt;&#x2F;code&gt; from played to the new
updated last valid, lowers the amount of cloning necessary from twice to only once a frame, which can be a game-changer
on very populated worlds.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;performance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance&quot; aria-label=&quot;Anchor link for: performance&quot;&gt;Performance&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Because of the diagram above, the &lt;code&gt;update&lt;&#x2F;code&gt; function of the world will not only happen once every frame, but up to
&lt;code&gt;max_rollback&lt;&#x2F;code&gt; times + 1 every frame. Because of this, &lt;code&gt;update&lt;&#x2F;code&gt; time has to be as low as possible.&lt;&#x2F;p&gt;
&lt;p&gt;Other rollback engines mostly don&#x27;t have a lot to work with. Since most of them are Peer 2 Peer fighting games,
2 characters with a few projectiles is not very expensive to compute, even on slow engines.&lt;&#x2F;p&gt;
&lt;p&gt;But our case is different, if we have more than 2 players, with complex interactions on the world, the &lt;code&gt;update&lt;&#x2F;code&gt;
might not be as free.
The answer here is pretty simple: Rust is fast to begin with. Nothing specific to do to optimize performance,
performance was there to begin with.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cloning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#cloning&quot; aria-label=&quot;Anchor link for: cloning&quot;&gt;Cloning&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As per the diagram above, you might have noticed that we use &lt;code&gt;clone_from&lt;&#x2F;code&gt; to deeply clone a struct.&lt;&#x2F;p&gt;
&lt;p&gt;Rust is not the only one being able to deeply clone a struct like that, but it&#x27;s definitely the easiest.&lt;&#x2F;p&gt;
&lt;p&gt;In C++ you have &lt;a href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;language&#x2F;as_operator.html&quot;&gt;copy assignment&lt;&#x2F;a&gt; but it&#x27;s not
guaranteed to be implemented for all classes or structs you use, especially external ones, and there is no automatic
generation of code. You&#x27;ll have to manually overload this operator for &lt;em&gt;every&lt;&#x2F;em&gt; single one struct in your code.
But still, it&#x27;s possible.&lt;&#x2F;p&gt;
&lt;p&gt;In Rust we have &lt;code&gt;#[derive(Clone)]&lt;&#x2F;code&gt;, but it does not derive &lt;code&gt;clone_from&lt;&#x2F;code&gt;. That&#x27;s mostly fine because the compiler
optimises a lot under the hood, &lt;em&gt;except&lt;&#x2F;em&gt; when the struct has some kind of allocation used.
&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;derivative&quot;&gt;derivative&lt;&#x2F;a&gt; allows you to auto write the &lt;code&gt;clone_from&lt;&#x2F;code&gt; methods as well as the
&lt;code&gt;clone&lt;&#x2F;code&gt; methods, use it for all your structs that have any kind of allocation in their child members. For simple structs
that only contain numbers, booleans, etc, keeping &lt;code&gt;Clone&lt;&#x2F;code&gt; without implementing &lt;code&gt;clone_from&lt;&#x2F;code&gt; is perfectly fine.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s a small difference in code, but something this small can have quite an impact.
Implementing &lt;code&gt;clone_from&lt;&#x2F;code&gt; on &lt;code&gt;World&lt;&#x2F;code&gt; and most of its children resulted in a clone time reduction of roughly 30% and 40%,
depending on how populated the world was.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Individually, these points might not seem like much, but together they form a compelling argument:
a solo dev like myself, with limited time, would probably not have been able to build such an engine so quickly
if it weren&#x27;t for Rust.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;visual-comparison&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#visual-comparison&quot; aria-label=&quot;Anchor link for: visual-comparison&quot;&gt;Visual Comparison&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;d like to show what this huge wall of text results in, and especially how they handle ping.&lt;&#x2F;p&gt;
&lt;p&gt;All of these comparisons have simulated ping with &lt;code&gt;netns&lt;&#x2F;code&gt;, with heavy packet reordering (~20%)
and a 0.1% packet loss.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;20ms-rtt&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#20ms-rtt&quot; aria-label=&quot;Anchor link for: 20ms-rtt&quot;&gt;20ms RTT&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At 20ms rollback is barely noticeable, only 1~2 frame of rollback is required if you have one frame of input delay.&lt;&#x2F;p&gt;
&lt;video autoplay muted loop&gt;
  &lt;source src=&quot;&amp;#x2F;rollback_20ms.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
  Your browser does not support video.
&lt;&#x2F;video&gt;&lt;h2 id=&quot;70ms-rtt&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#70ms-rtt&quot; aria-label=&quot;Anchor link for: 70ms-rtt&quot;&gt;70ms RTT&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At 70ms, you can definitely start to notice some rollback, which is about ~3 full frames, plus a little
bit of input delay. That being said, while noticeable, nobody has complained about lagging at this range of ping.&lt;&#x2F;p&gt;
&lt;video autoplay muted loop&gt;
  &lt;source src=&quot;&amp;#x2F;rollback_70ms.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
  Your browser does not support video.
&lt;&#x2F;video&gt;&lt;h2 id=&quot;140ms-rtt&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#140ms-rtt&quot; aria-label=&quot;Anchor link for: 140ms-rtt&quot;&gt;140ms RTT&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At 140ms, you can definitely see heavy rollback. This time it&#x27;s about 6~7 frames of rollback, and it&#x27;s probably
close to the limit before your players start noticing and maybe complaining about the delay, but it&#x27;s still playable.&lt;&#x2F;p&gt;
&lt;p&gt;Not a bad result given that ~150ms is about the RTT between the West Coast of North America, and the west coast of
Europe.
A real-time multiplayer game being playable at that distance is still a small win.&lt;&#x2F;p&gt;
&lt;video autoplay muted loop&gt;
  &lt;source src=&quot;&amp;#x2F;rollback_140ms.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
  Your browser does not support video.
&lt;&#x2F;video&gt;&lt;h1 id=&quot;the-result&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-result&quot; aria-label=&quot;Anchor link for: the-result&quot;&gt;The result&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The project that started it all in 2018 is on the back burner but still planned to be released. A new smaller one has
been created this year, with exactly this engine and this same multiplayer logic, and you can check it out when it
releases later in 2025.&lt;&#x2F;p&gt;
&lt;iframe
  frameborder=&quot;0&quot; 
  style=&quot;width: 100%; height: 220px;&quot;
  src=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;widget&#x2F;3809740&#x2F;?utm_source=webblog&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Until then, there are probably a few more articles that are going to be released here. I hope you enjoyed the read now,
and I hope you&#x27;ll enjoy the read then.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
