<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dani Bengl</title>
    <description>The latest articles on DEV Community by Dani Bengl (@cuddlybunion341).</description>
    <link>https://dev.to/cuddlybunion341</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1339339%2F5347ffce-9320-479a-9fad-1775941fb413.png</url>
      <title>DEV Community: Dani Bengl</title>
      <link>https://dev.to/cuddlybunion341</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cuddlybunion341"/>
    <language>en</language>
    <item>
      <title>Exploring Rust: A Rubyist's Perspective</title>
      <dc:creator>Dani Bengl</dc:creator>
      <pubDate>Fri, 28 Feb 2025 21:24:18 +0000</pubDate>
      <link>https://dev.to/renuo/exploring-rust-a-rubyists-perspective-45f0</link>
      <guid>https://dev.to/renuo/exploring-rust-a-rubyists-perspective-45f0</guid>
      <description>&lt;p&gt;At Renuo, we love Ruby. It's simple, elegant, and powerful. But let's be honest, Ruby isn't the fastest language out there.&lt;/p&gt;

&lt;p&gt;Over the last couple of months, I've been exploring low-level programming, hoping to bridge the gap between the high-level world of Ruby and the lower-level world of systems programming. To do this, I started working on my first Rust project: a blazingly fast voxel "game" called &lt;a href="https://github.com/CuddlyBunion341/rsmc" rel="noopener noreferrer"&gt;rsmc&lt;/a&gt;. It features a terrain generator, meshing, a scalable client-server architecture, and custom serialized messages for high-speed communication. This project has been my playground for learning Rust, and in this post, I'll share some of the lessons I've learned along the way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx2cywgbq0i2jatmqt6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx2cywgbq0i2jatmqt6o.png" alt="Early stage of development in RSMC. Renet visualiser for simultanous client/server connections." width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust?
&lt;/h2&gt;

&lt;p&gt;Rust is one of the most appreciated programming languages, as highlighted in the &lt;a href="https://octoverse.github.com/" rel="noopener noreferrer"&gt;GitHub Octoverse Survey&lt;/a&gt;. It offers memory safety, high performance, and strong tooling, making it a solid choice for both small utilities and large-scale applications. Many of the tools I use daily, like &lt;a href="https://github.com/alacritty/alacritty" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt; and &lt;a href="https://1password.com/" rel="noopener noreferrer"&gt;1Password&lt;/a&gt;, benefit from Rust's speed and reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Performance:&lt;/strong&gt; Comparable to C and C++, but with safety mechanisms that prevent common errors.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Memory safety:&lt;/strong&gt; Eliminates null pointer dereferences, segmentation faults, and data races.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Modern syntax:&lt;/strong&gt; Readable and expressive, making it accessible despite its low-level capabilities.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Powerful tooling:&lt;/strong&gt; &lt;a href="https://doc.rust-lang.org/cargo/" rel="noopener noreferrer"&gt;Cargo&lt;/a&gt; simplifies dependency management, builds, and testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my voxel game, Rust's speed and safety make it an excellent choice for terrain generation, networking, and real-time interactions. Unlike dynamically typed languages such as Ruby, Rust catches entire categories of bugs at compile time, improving maintainability.&lt;/p&gt;

&lt;p&gt;Beyond CLI tools, Rust powers game engines, operating systems, simulations, and even web browsers, proving its adaptability across different domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bevy: Game Development Made Fun
&lt;/h2&gt;

&lt;p&gt;Since my project is built with &lt;a href="https://bevyengine.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt;, understanding its core concepts was very important. Bevy's entity-component-system (ECS) architecture makes game development modular and efficient, allowing for highly decoupled systems.&lt;/p&gt;

&lt;p&gt;Some of my favorite takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Systems:&lt;/strong&gt; Keep them small and focused on one task. This way they are easier to test and extend.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Plugins:&lt;/strong&gt; Encapsulate resources, systems, and components into distinguishable modules.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Events:&lt;/strong&gt; Use events to the fullest extent to decouple systems and keep code modular.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;States:&lt;/strong&gt; Run systems only when they are relevant (e.g., Menu, Playing). This helps with UI and logic separation. In particular this PR: &lt;a href="https://github.com/CuddlyBunion341/rsmc/pull/32" rel="noopener noreferrer"&gt;#32&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bevy makes structuring a game engine intuitive, and its Rust-first approach ensures safety and performance while keeping things flexible. If you're interested in learning more about the ECS approach to game development, I wrote a blog article about planning an ECS: &lt;a href="https://dev.to/renuo/multiplayer-in-rust-using-renet-and-bevy-17p6"&gt;Multiplayer in Rust Using Renet and Bevy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Flags: Shipping Less in Production
&lt;/h2&gt;

&lt;p&gt;Feature flags allow enabling or disabling specific functionality at compile time, making it easy to toggle features based on configuration.&lt;/p&gt;

&lt;p&gt;In my voxel game, feature flags help manage debugging tools like wireframe rendering and debug UI. What's neat about these feature flags is that debug code doesn't get shipped in production, reducing binary size and keeping the release build clean.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;Cargo.toml&lt;/code&gt;, you can define feature flags like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[features]&lt;/span&gt; 
&lt;span class="py"&gt;egui_layer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="py"&gt;terrain_visualizer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"egui_layer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;renet_visualizer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"egui_layer"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then use them in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(feature&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"egui_layer"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;bevy_inspector_egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;bevy_egui&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EguiPlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.add_plugins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DefaultPlugins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.add_plugins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EguiPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downside is that testing all possible feature combinations is a challenge. With just five features, you already have 32 different configurations to check. But that's the price of flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cargo Watch: Automating Workflows
&lt;/h2&gt;

&lt;p&gt;During the development of rsmc, I often needed to recompile code, and manually restarting my binary after every change quickly became tedious. I really wish I had discovered &lt;a href="https://docs.rs/crate/cargo-watch/latest" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-watch&lt;/code&gt;&lt;/a&gt; earlier.&lt;/p&gt;

&lt;p&gt;Just install it and let the watcher do its thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo watch &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s1"&gt;'run --bin client'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Composition Over Inheritance
&lt;/h2&gt;

&lt;p&gt;Coming from Ruby, where inheritance is common, Rust's approach felt different. Rust doesn't have classes. It uses structs and traits. This forced me to use composition over inheritance and think differently about code structure.&lt;/p&gt;

&lt;p&gt;Here's an example from my terrain generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;NoiseFunctionParams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;octaves&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HeightParams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;     
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;noise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NoiseFunctionParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Composition!&lt;/span&gt;
  &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;splines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vec2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of inheriting from a base class, &lt;code&gt;HeightParams&lt;/code&gt; contains a &lt;code&gt;NoiseFunctionParams&lt;/code&gt; struct. This keeps the code flexible and avoids deep inheritance hierarchies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Macros: Code That Writes Code
&lt;/h2&gt;

&lt;p&gt;Rust's macros are like Ruby's metaprogramming but more structured and powerful. They help eliminate boilerplate while maintaining type safety.&lt;/p&gt;

&lt;p&gt;Here's a macro I used to &lt;a href="https://github.com/CuddlyBunion341/rsmc/blob/main/src/client/terrain/util/blocks.rs" rel="noopener noreferrer"&gt;define blocks&lt;/a&gt; in my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;macro_rules!&lt;/span&gt; &lt;span class="n"&gt;add_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$block_id:expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$is_solid:expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$block_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;is_solid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$is_solid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;BLOCKS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nd"&gt;add_block!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;BlockId&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Air&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;add_block!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;BlockId&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Grass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Macros don't affect runtime performance. They are a zero-cost abstraction, a great feature of Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust's Learning Curve: Worth the Effort?
&lt;/h2&gt;

&lt;p&gt;Rust isn't the easiest language to pick up. The borrow checker takes time to understand, and coming from Ruby, its verbosity stands out. Ruby achieves more with fewer symbols. While Rust's explicitness helps with maintainability and reducing hidden behaviors, it also means writing more boilerplate.&lt;/p&gt;

&lt;p&gt;One of the biggest disadvantages to me is compile times. They can be frustrating since Rust enforces strict checks, but this reduces runtime errors. There's even an &lt;a href="https://xkcd.com/303/" rel="noopener noreferrer"&gt;XKCD comic&lt;/a&gt; about it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjbrwv9is0lo4a9cmmw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjbrwv9is0lo4a9cmmw6.png" alt="XKCD 303 - The #1 programmer excuse for legitimately slacking off: " width="413" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From my experience, Rust has its trade-offs. Catching many errors at compile time reduces debugging effort, but the strict rules and verbosity make writing new code slower compared to Ruby. That said, Rust's language servers provide excellent refactoring support, which makes working with larger projects easier.&lt;/p&gt;

&lt;p&gt;For quick prototyping and iteration, scripting languages such as Ruby are still the better choice. However, when stability, performance, and long-term maintainability matters, Rust seems to be the better pick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Rust isn't just another language. It changes how you think about programming. It makes you more aware of memory, safety, and performance. The learning curve is steep, but if you stick with it, the rewards are worth it.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this journey into Rust! If you're a Ruby developer who has also tried Rust, what challenges have you faced? I'd love to hear your thoughts!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Multiplayer in Rust using Renet and Bevy</title>
      <dc:creator>Dani Bengl</dc:creator>
      <pubDate>Wed, 03 Jul 2024 15:24:33 +0000</pubDate>
      <link>https://dev.to/renuo/multiplayer-in-rust-using-renet-and-bevy-17p6</link>
      <guid>https://dev.to/renuo/multiplayer-in-rust-using-renet-and-bevy-17p6</guid>
      <description>&lt;p&gt;Here at Renuo, we specialize in web technologies such as Ruby on Rails, React, Angular, and Spring. One of our core company values is continuous learning: we love exploring new technologies even beyond our usual scope of expertise.&lt;/p&gt;

&lt;p&gt;Inspired by Michael’s Unity Powerday, I decided to delve into how multiplayer games operate. As a team, we held a competition to implement FPS (First-Person Shooter) games using C# boilerplate. Initially, the sheer amount of boilerplate required felt overwhelming. Beyond that, I wanted to understand client/server data synchronization at a lower level of abstraction.&lt;/p&gt;

&lt;p&gt;My recent experiences with &lt;a href="https://rustlang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and &lt;a href="https://bevyengine.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt; convinced me to write this blog article to share my newfound learnings of game development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose Rust
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;p&gt;Rust is a statically typed, memory-safe, multi-paradigm programming language that matches the performance of C. Due to its safety, concurrency features, and modern syntax, it has gained popularity among developers in recent years.&lt;/p&gt;

&lt;p&gt;Some notable software written in Rust includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rapier3d&lt;/strong&gt;: A performant physics engine often used with ThreeJS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ripgrep&lt;/strong&gt;: A performant command line search tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alacritty&lt;/strong&gt;: A performant, minimalistic cross-platform terminal emulator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warp&lt;/strong&gt;: A performant, modern terminal IDE.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tauri&lt;/strong&gt;: A performant and lightweight alternative to ElectronJS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amethyst&lt;/strong&gt;: A performant tiling window manager for MacOS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condorium Blockchain&lt;/strong&gt;: A performant and secure blockchain technology.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I mean, there is no such thing as a perfect programming language Rust is merely a statically type low-level multi-paradigm perfect programming language&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=TGfQu0bQTKc&amp;amp;t=95s" rel="noopener noreferrer"&gt;YouTube interview&lt;/a&gt; by &lt;a href="https://www.youtube.com/@programmersarealsohuman5909" rel="noopener noreferrer"&gt;Programmers Are Also Human&lt;/a&gt;, Rust is a perfect programming language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6owazv7mdfhl28879ywg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6owazv7mdfhl28879ywg.png" alt="Ferris the Rustacean" width="749" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking a game engine
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;There are currently 5 games written in Rust.  And 50 game engines.&lt;br&gt;
Interview with a Senior Rust Developer - &lt;a href="https://www.youtube.com/watch?v=TGfQu0bQTKc&amp;amp;t=168s" rel="noopener noreferrer"&gt;2:52&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are too many game engines available for Rust. An excellent resource is &lt;a href="https://arewegameyet.rs/ecosystem/engines/" rel="noopener noreferrer"&gt;Are We Game Yet&lt;/a&gt;. I also recommend &lt;a href="https://www.geeksforgeeks.org/rust-game-engines/" rel="noopener noreferrer"&gt;this article by GeeksforGeeks&lt;/a&gt;, which makes picking the optimal engine easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Difference Between Bevy and Other Engines
&lt;/h3&gt;

&lt;p&gt;While big game engines like Godot, Unity, and Unreal Engine come with graphical editors, Bevy focuses on providing a simple yet powerful, multithreaded system to manage game state with minimal code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding ECS
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Entity_component_system" rel="noopener noreferrer"&gt;ECS&lt;/a&gt; (Entity Component System) is a software pattern that emphasizes a modular design. It is commonly utilized in game and game engine development. This approach separates the data and behaviour of game entities into components, making it easier to manage and organize complex systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components of ECS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Entities:&lt;/strong&gt; Unique identifiers of a group of components (A u32 wrapper in bevy).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Components:&lt;/strong&gt; Modular data pieces that represent specific Entity attributes. (A struct that derives the Component macro in bevy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System:&lt;/strong&gt; Logic that operates on entities and their components. (A struct that derives the Resource macro in bevy)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Systems in Bevy
&lt;/h3&gt;

&lt;p&gt;Systems in Bevy are functions that take various parameters such as queries, EventReaders, assets, and resources and apply logic to them.&lt;/p&gt;

&lt;p&gt;One powerful feature of Bevy systems is the Query interface. It allows you to fetch specific data for entities in your project. For instance, if no entity is found, the &lt;code&gt;single_mut()&lt;/code&gt; function will raise an error. Multiple queries are possible as long as entities do not overlap.&lt;/p&gt;

&lt;p&gt;Below is an example where the &lt;code&gt;MyPlayer&lt;/code&gt; component doesn't contain any data but is used to denote that the entity belongs to the client player.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;update_player_movement_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;keyboard_events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventReader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyboardInput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MyPlayer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.single_mut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keyboard_events&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="py"&gt;.key_code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KeyW&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt;&lt;span class="py"&gt;.z&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KeyS&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt;&lt;span class="py"&gt;.z&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KeyA&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KeyD&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="py"&gt;.translation&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="py"&gt;.translation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above has a flaw: the player position update has a fixed step. Instead of using a fixed-step update, consider using the time passed since the last step. This will ensure a consistent movement speed regardless of the frame rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking Networking Libraries
&lt;/h2&gt;

&lt;p&gt;We need to decide on networking libraries after choosing Bevy as our game engine. Here are a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Matchbox&lt;/li&gt;
&lt;li&gt;Naia&lt;/li&gt;
&lt;li&gt;Renet&lt;/li&gt;
&lt;li&gt;Bootleg_networking&lt;/li&gt;
&lt;li&gt;Spicy_networking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose &lt;strong&gt;Renet&lt;/strong&gt; because of its popularity and my good experiences with its boilerplate. Additionally, I included &lt;strong&gt;Serde&lt;/strong&gt; for efficient binary message encoding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sketching the scene
&lt;/h2&gt;

&lt;p&gt;Before coding, let's sketch a simple scene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Camera:&lt;/strong&gt; Renders the scene.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plane:&lt;/strong&gt; Represents the floor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Green Cube:&lt;/strong&gt; Represents the player.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red Cubes:&lt;/strong&gt; Represent other players.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attributes to synchronize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Position:&lt;/strong&gt; &lt;code&gt;Vec3&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Input method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard (WASD):&lt;/strong&gt; Used to translate the player.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Handling Player inputs
&lt;/h3&gt;

&lt;p&gt;There are three main ways to handle player inputs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client-side:&lt;/strong&gt; The client handles inputs, moves the player, and sends the position to the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side:&lt;/strong&gt; The client sends input data to the server, and the server responds with the position.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid:&lt;/strong&gt; The client handles inputs and shares them with the server, which then responds with position synchronization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The client-side approach can reduce latency, but is less secure. The server-side approach is more secure but adds server load. The hybrid approach offers a balance, but is more complex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Client
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;PlayerEntity(ClientId)&lt;/td&gt;
&lt;td&gt;Represents an enemy player entity.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;MyPlayer&lt;/td&gt;
&lt;td&gt;Marks the current player entity.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event&lt;/td&gt;
&lt;td&gt;PlayerSpawnEvent(ClientId)&lt;/td&gt;
&lt;td&gt;Emitted when a player joins. Adds a player object to the scene.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event&lt;/td&gt;
&lt;td&gt;PlayerDespawnEvent(ClientId)&lt;/td&gt;
&lt;td&gt;Emitted when a player leaves. Removes a player object from the scene.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event&lt;/td&gt;
&lt;td&gt;PlayerMoveEvent(ClientId, Vec3)&lt;/td&gt;
&lt;td&gt;Emitted by the player controller when a player moves.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event&lt;/td&gt;
&lt;td&gt;LobbySyncEvent(HashMap)&lt;/td&gt;
&lt;td&gt;Emitted when the client receives sync messages from the server. Updates other player positions using their ID and position.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;send_message_system&lt;/td&gt;
&lt;td&gt;Shares MyPlayer position data with the server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;receive_message_system&lt;/td&gt;
&lt;td&gt;Processes messages received from the server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;update_player_movement_system&lt;/td&gt;
&lt;td&gt;Updates player position from keyboard input.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;setup_system&lt;/td&gt;
&lt;td&gt;Sets up the scene with a camera, a ground plane, and a mesh for the current player.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;handle_player_spawn_event_system&lt;/td&gt;
&lt;td&gt;Adds enemy players to the scene once they join in.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;handle_lobby_sync_event_system&lt;/td&gt;
&lt;td&gt;Updates enemy player positions and potentially spawns missed players into the scene.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Server
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Resource&lt;/td&gt;
&lt;td&gt;PlayerLobby(HashMap)&lt;/td&gt;
&lt;td&gt;Holds attributes of all players currently in the game. Used to synchronize these attributes with the clients.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;send_message_system&lt;/td&gt;
&lt;td&gt;Broadcasts player positions to keep enemy player positions in clients up-to-date.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;receive_message_system&lt;/td&gt;
&lt;td&gt;Updates player lobby position based on messages received from the RenetClient.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;handle_events_system&lt;/td&gt;
&lt;td&gt;Handles events such as ClientConnected and ClientDisconnected from the Bevy Renet plugin.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Deciding on a project structure
&lt;/h2&gt;

&lt;p&gt;I separated the ECS components into specific modules to structure the Bevy project and used two entry points: one for the client and one for the server. Shared code, such as structures for Client-Server communication, can be placed in a global &lt;code&gt;lib&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
├── client
│   ├── components.rs
│   ├── events.rs
│   ├── main.rs
│   ├── resources.rs
│   └── systems.rs
├── lib.rs
└── server
    ├── main.rs
    ├── resources.rs
    └── systems.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defining various entry points is as simple as adding this to the &lt;code&gt;Cargo.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[bin]]
name = "server"
path = "src/server/main.rs"

[[bin]]
name = "client"
path = "src/client/main.rs"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, the binaries can be run with the &lt;code&gt;--bin&lt;/code&gt; argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo run --bin server
cargo run --bin client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up Boilerplate
&lt;/h2&gt;

&lt;p&gt;To integrate &lt;code&gt;bevy_renet&lt;/code&gt; into the bevy project, I followed the &lt;a href="https://github.com/lucaspoffo/renet/blob/master/bevy_renet/README.md" rel="noopener noreferrer"&gt;Bevy Renet documentation&lt;/a&gt;. In my setup, I used these two default channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unreliable:&lt;/strong&gt; Used for sending and receiving messages for player attribute synchronization. (We don't care about every state change, we can pick the last one)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ReliableOrdered:&lt;/strong&gt; Used for sending and receiving messages for player actions such as joining and leaving.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Synchronising player positions
&lt;/h2&gt;

&lt;p&gt;Here's an example of sending player attributes from the client to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;send_message_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RenetClient&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MyPlayer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;player_sync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PlayerAttributes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="py"&gt;.translation&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;bincode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;player_sync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;DefaultChannel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Unreliable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handling messages from the client on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;receive_message_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RenetServer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;player_lobby&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerLobby&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="nf"&gt;.clients_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="nf"&gt;.receive_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;DefaultChannel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Unreliable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PlayerAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;bincode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;player_lobby&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sending attributes of all players back to the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;send_message_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RenetServer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_lobby&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerLobby&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chanel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DefaultChannel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Unreliable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lobby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player_lobby&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;multiplayer_demo&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;LobbySync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lobby&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;bincode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;print_lobby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;player_lobby&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="nf"&gt;.broadcast_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chanel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Synchronizing the client scene with the player attributes from the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_lobby_sync_event_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;spawn_events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventWriter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerSpawnEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;sync_events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventReader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LobbySyncEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PlayerEntity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;my_clinet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyClientId&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event_option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sync_events&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.last&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event_option&lt;/span&gt;&lt;span class="nf"&gt;.is_none&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_option&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_sync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;my_clinet_id&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player_entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.iter_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;player_entity&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player_sync&lt;/span&gt;&lt;span class="py"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="py"&gt;.translation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_position&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Spawning player {}: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player_sync&lt;/span&gt;&lt;span class="py"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;spawn_events&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;PlayerSpawnEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The multiplayer demo project demonstrates the intricate planning and attention to detail needed to synchronize player attributes between the client and server. This showcases the complexity of creating a seamless multiplayer experience at a lower level.&lt;/p&gt;

&lt;p&gt;For more detailed code, visit the MIT-Licensed &lt;a href="https://github.com/CuddlyBunion341/bevy-multiplayer" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>2 Simple Configurations to Supercharge your TMUX Setup!</title>
      <dc:creator>Dani Bengl</dc:creator>
      <pubDate>Sun, 14 Apr 2024 11:22:48 +0000</pubDate>
      <link>https://dev.to/renuo/2-simple-configurations-to-supercharge-your-tmux-setup-5572</link>
      <guid>https://dev.to/renuo/2-simple-configurations-to-supercharge-your-tmux-setup-5572</guid>
      <description>&lt;p&gt;Tmux is a terminal multiplexer. It is a tool for managing multiple shells inside a single terminal window. It is integrated into most modern terminal emulators like Iterm2 or Warp.  In this blog article, I will show you two simple configurations that drastically improved my tmux experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autoresizing panes
&lt;/h2&gt;

&lt;p&gt;The main problem I had with tmux when starting out was that panes were not automatically resized as I was used from Warp:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjyfdlwmxlsb9bkqhxfv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjyfdlwmxlsb9bkqhxfv.gif" alt="Managing window panes in tmux with auto-resizing not configured." width="760" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't get confused by the remapped tmux prefix. The main thing to notice here is that I am using &lt;code&gt;&amp;lt;PREFIX&amp;gt; + E&lt;/code&gt; to spread the panes out evenly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we now need is:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The command to resize the windows&lt;/li&gt;
&lt;li&gt;The hook(s) for executing the command&lt;/li&gt;
&lt;li&gt;Update the tmux configuration with the hook and the command.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Part1: Finding the command
&lt;/h3&gt;

&lt;p&gt;According to the manual entry for tmux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select-layout [-Enop] [-t target-pane] [layout-name]
                             (alias: selectl)
                 Choose a specific layout for a window.  If layout-name is not given, the last preset layout used (if any) is
                 reapplied.  -n and -p are equivalent to the next-layout and previous-layout commands.  -o applies the last
                 set layout if possible (undoes the most recent layout change).  -E spreads the current pane and any panes
                 next to it out evenly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the &lt;code&gt;select-layout -E&lt;/code&gt; command to spread the panes evenly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part2: Finding the hooks
&lt;/h3&gt;

&lt;p&gt;The first part is done. Now we need the hooks.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, this can also be found in the manual entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%window-pane-changed window-id pane-id
     The active pane in the window with ID window-id changed to the pane with ID pane-id.

...

window-resized          Run when a window is resized.  This may be after the client-resized hook is run.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Part3: Updating the configuration file
&lt;/h3&gt;

&lt;p&gt;If you &lt;a href="https://unix.stackexchange.com/questions/644819/is-it-possible-to-move-tmux-conf-to-config-folder" rel="noopener noreferrer"&gt;haven't moved&lt;/a&gt; the &lt;code&gt;.tmux.conf&lt;/code&gt; to &lt;code&gt;.config&lt;/code&gt;, you can find the default configuration file for MacOS at &lt;code&gt;~/.tmux.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To execute the command on the resize and pane changed hooks, we can add these lines to the tmux configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .tmux.conf&lt;/span&gt;
...
set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; window-pane-changed &lt;span class="s1"&gt;'select-layout -E'&lt;/span&gt;
set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; client-resized &lt;span class="s1"&gt;'select-layout -E'&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how the panes behave after adjusting the configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4pg3e849imrdnyf78q7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4pg3e849imrdnyf78q7.gif" alt="Creating window panes in tmux with auto-resizing configured." width="760" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the manual entry, we can also shorten the command to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; window-pane-changed &lt;span class="s1"&gt;'selectl -E'&lt;/span&gt;
set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; client-resized &lt;span class="s1"&gt;'selectl -E'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dimming Inactive Panes
&lt;/h2&gt;

&lt;p&gt;Next to automatically resizing windows, we can also automatically dim panes that aren't active by using the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; pane-focus-out &lt;span class="s1"&gt;'select-pane -P bg=colour233,fg=colour10'&lt;/span&gt;
set-hook &lt;span class="nt"&gt;-g&lt;/span&gt; pane-focus-in &lt;span class="s1"&gt;'select-pane -P bg=default,fg=default'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kgr00agqsw70mb5qrau.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kgr00agqsw70mb5qrau.gif" alt="Switching panes in tmux with dimming enabled." width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, this is a much nicer alternative than the active border color. If you are wondering how I got rid of it, I used this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pane-active-border-style &lt;span class="s2"&gt;"bg=#000000,fg=white"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pane-border-style &lt;span class="s2"&gt;"bg=#000000,fg=white"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that &lt;code&gt;black&lt;/code&gt; and &lt;code&gt;#000000&lt;/code&gt; are not the same thing when configuring your terminal, as the color names are defined by your color scheme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While tmux isn't as usable/powerful out of the box as the integrated tmux in Iterm or Warp, with some simple configuration, it can be significantly improved. The manual page for tmux is the go to source for looking up commands and events and will save a lot of time googling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools and Sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screen recorder: &lt;a href="https://cleanshot.com/" rel="noopener noreferrer"&gt;CleanShotX&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Keystroke recorder: &lt;a href="https://github.com/keycastr/keycastr" rel="noopener noreferrer"&gt;KeyCastr&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Terminal Emulator: &lt;a href="https://github.com/alacritty/alacritty" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Other configuration: &lt;a href="https://github.com/CuddlyBunion341/dotfiles" rel="noopener noreferrer"&gt;My Dotfiles&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cli</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Extending Hotwire beyond Ruby on Rails</title>
      <dc:creator>Dani Bengl</dc:creator>
      <pubDate>Fri, 08 Mar 2024 19:30:00 +0000</pubDate>
      <link>https://dev.to/renuo/extending-hotwire-beyond-ruby-on-rails-k7c</link>
      <guid>https://dev.to/renuo/extending-hotwire-beyond-ruby-on-rails-k7c</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07t16uvg252gn2vhn6ox.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07t16uvg252gn2vhn6ox.gif" alt=" " width="600" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This blog article is inspired by a &lt;a href="https://www.writesoftwarewell.com/understanding-hotwire-turbo-streams/" rel="noopener noreferrer"&gt;Hotwire Turbo streams tutorial for Sinatra&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you search for Hotwire tutorials on Google, you’ll find that most of the results are related to Ruby on Rails. Even the Hotwire guides predominantly use Ruby on Rails as an example for implementing Turbo Streams.&lt;/p&gt;

&lt;p&gt;This does not really come as a surprise, as the framework has been created by the same company behind the Ruby on Rails framework.&lt;/p&gt;

&lt;p&gt;However, it’s important to recognize that Hotwire is not exclusively a Rails framework. In this blog article, I aim to convince you that Hotwire can be used beyond the Rails context, especially Turbo, its core feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;In this blog article, I will explain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up a BunJS Application
&lt;/li&gt;
&lt;li&gt;Implementing client and server-side Web Sockets
&lt;/li&gt;
&lt;li&gt;Using turbo streams to update the UI
&lt;/li&gt;
&lt;li&gt;Creating a stimulus controller attached to the DOM&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Set up a BunJS Application
&lt;/h2&gt;

&lt;p&gt;Initially, I considered using Java Spring for this blog, but I encountered challenges with Web Sockets. Instead, I opted for a much simpler TypeScript application.&lt;/p&gt;

&lt;p&gt;Let’s start by initializing a new BunJS application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;bunjs-turbo-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;bunjs-turbo-demo
bun init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun run index.ts
Hello via Bun!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Implement a Simple Web Socket / HTTP Server with BunJS
&lt;/h2&gt;

&lt;p&gt;Web Sockets serve as the backbone for real-time communication in our project. In this step, we’ll create a basic Web socket HTTP server using Bun. Web Sockets are essential for enabling bidirectional communication between clients (such as web browsers) and the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Multiple Publisher - Multiple Subscriber Pattern
&lt;/h3&gt;

&lt;p&gt;Our approach involves implementing the &lt;strong&gt;multiple publisher - multiple subscriber pattern&lt;/strong&gt;. Here’s how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client Interaction&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clients (users' web browsers) initiate actions, such as sending chat messages or requesting updates.
&lt;/li&gt;
&lt;li&gt;These interactions trigger Web Socket connections to the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server Processing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server receives messages from multiple clients.
&lt;/li&gt;
&lt;li&gt;It processes these messages and prepares appropriate responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Broadcasting Updates&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a client sends a message (e.g., a new chat message), the server broadcasts it to all connected clients.
&lt;/li&gt;
&lt;li&gt;This ensures that everyone receives the latest updates in real time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Seamless Communication&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web Sockets allow seamless, low-latency communication.
&lt;/li&gt;
&lt;li&gt;Clients can instantly receive updates without the need for manual page refreshes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a simple sequence diagram showing the core idea of this project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrxh7bkqbr2r0h1tyr0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrxh7bkqbr2r0h1tyr0i.png" alt="Sequence diagram of the client-server interactions" width="614" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement the server
&lt;/h3&gt;

&lt;p&gt;For brevity, I’ve omitted the code for view helpers such as &lt;code&gt;layoutHTML&lt;/code&gt; and &lt;code&gt;chatRoomHTML&lt;/code&gt;. These helpers handle rendering HTML components and chat room layouts. While important, their details won’t significantly impact the core concepts discussed in this blog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chatroom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Bun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;layoutHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Chatroom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;chatRoomHTML&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Couldn't upgrade to a WebSocket connection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;404!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Websocket opened&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;messageHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Someone joined the chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Websocket received: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;messageHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Anonymous: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Websocket closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publishText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;messageHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Someone left the chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;publishToSelf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Implement the client
&lt;/h4&gt;

&lt;p&gt;The application is incomplete without the client, which connects to the Web Socket server. Following the Web Socket server connection documentation, connecting to the backend is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws://localhost:8080/subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chat-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chatFeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chat-feed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;chatFeed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to add the route for the JavaScript file to the backend and include it inside your view using a module script tag. Also, make sure to implement the backend response.&lt;/p&gt;

&lt;h5&gt;
  
  
  The problem
&lt;/h5&gt;

&lt;p&gt;As you can see here, every change in UI needs to be manually implemented. At the moment, we are listening to a single event and updating one single element. When the application grows in complexity, the JavaScript code grows, too. What if I told you that we can "almost" completely eliminate the client code by introducing Turbo streams?&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Implement Turbo streams
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://turbo.hotwired.dev/" rel="noopener noreferrer"&gt;Turbo&lt;/a&gt; is a vital part of the Hotwire Framework. It enables you to dramatically reduce the amount of custom JavaScript you need to write.&lt;/p&gt;

&lt;p&gt;The most relevant feature for this application are the turbo streams. They enable us to deliver page changes in the form of HTML over the wire.&lt;/p&gt;

&lt;p&gt;Importing Turbo is as simple as including this snippet of code inside our layout file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;hotwiredTurbo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.3/+esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to use Web Sockets for Turbo streams in the frontend, we can use the following snippet from the &lt;a href="https://turbo.hotwired.dev/handbook/streams" rel="noopener noreferrer"&gt;stream documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;turbo-stream-source&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"ws://localhost:8080/subscribe"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to update the UI when a message is sent, we broadcast the following HTML through Web Sockets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;turbo-stream&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"append"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"chat-feed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"notice"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           Anonymous: Hello World!
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/turbo-stream&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method of HTML updates stands out for its transparency and simplicity. Firstly, we select an element with the &lt;code&gt;#chat-feed&lt;/code&gt; selector and then &lt;code&gt;append&lt;/code&gt; to it the contents of the broadcasted template. In this case, a paragraph containing the user message. This also eliminates &lt;em&gt;almost&lt;/em&gt; all the client-side JavaScript needed for page update.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Implement Form Controller
&lt;/h4&gt;

&lt;p&gt;Before introducing turbo, we added a simple event listener to reset the Form after the data has been sent to the server. We now need to bring the functionality back, but without reusing the old code. We could use a turbo-stream to reset the form or even a turbo-frame, but rather than using that, I decided to use another library of the Hotwire framework, namely Stimulus:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stimulus is a JavaScript framework with modest ambitions. It doesn’t seek to take over your entire front-end—in fact, it’s not concerned with rendering HTML at all. Instead, it’s designed to augment your HTML with just enough behavior to make it shine. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://stimulus.hotwired.dev/" rel="noopener noreferrer"&gt;https://stimulus.hotwired.dev/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a simple code snippet for the Form Stimulus controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net/npm/stimulus@3.2.2/+esm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FormController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the form HTML looks like, with data attributes used to attach the controller to the DOM and hook the events up to the corresponding controller methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chat-form"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/submit"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;data-controller=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt; &lt;span class="na"&gt;data-action=&lt;/span&gt;&lt;span class="s"&gt;"turbo:submit-end-&amp;gt;form#clear"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"message-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Message:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="na"&gt;data-form-target=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"clientId"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"${clientId}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The event that works best for form submission in that case is &lt;a href="https://turbo.hotwired.dev/reference/events#turbo%3Asubmit-end" rel="noopener noreferrer"&gt;turbo:submit-end&lt;/a&gt;. Following the documentation of &lt;a href="https://stimulus.hotwired.dev/reference/actions#descriptors" rel="noopener noreferrer"&gt;Stimulus descriptors,&lt;/a&gt; we can call the &lt;code&gt;#clear()&lt;/code&gt; method after the form submission event. We are not using the &lt;code&gt;submit&lt;/code&gt; event because this would clear the form prematurely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hotwire is a JavaScript framework that helps us make applications more interactive while keeping the JavaScript code to a minimum. While the framework has been created by the authors of Ruby on Rails, the framework itself is backend agnostic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turbo streams enable us to update client user interfaces asynchronously without the need for any (in some cases, just very little) frontend code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stimulus enables us to add simple JavaScript behavior to our HTML with the use of Stimulus Controllers and data-attributes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where can I find the source?
&lt;/h4&gt;

&lt;p&gt;You can find the complete chatting application with additional features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client identification via Query Parameters&lt;/li&gt;
&lt;li&gt;Random username generation&lt;/li&gt;
&lt;li&gt;Real-time user list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The GitHub Repository for this project can be found here: &lt;a href="https://github.com/CuddlyBunion341/bunjs-turbo-demo" rel="noopener noreferrer"&gt;https://github.com/CuddlyBunion341/bunjs-turbo-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
      <category>bunjs</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
