<?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: Anton Dolganin</title>
    <description>The latest articles on DEV Community by Anton Dolganin (@antonds).</description>
    <link>https://dev.to/antonds</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%2F3239903%2F685d8291-a6b2-45fa-8b3b-64f99c6e3219.jpg</url>
      <title>DEV Community: Anton Dolganin</title>
      <link>https://dev.to/antonds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antonds"/>
    <language>en</language>
    <item>
      <title>Rust: Transparent Wrappers with Deref Coercion</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Thu, 08 Jan 2026 16:41:00 +0000</pubDate>
      <link>https://dev.to/antonds/rust-transparent-wrappers-with-deref-coercion-4bgi</link>
      <guid>https://dev.to/antonds/rust-transparent-wrappers-with-deref-coercion-4bgi</guid>
      <description>&lt;p&gt;The &lt;strong&gt;Newtype&lt;/strong&gt; pattern (&lt;code&gt;struct Username(String)&lt;/code&gt;) is great for type safety, but it often feels clunky because you lose direct access to the inner type's methods. Implementing the &lt;code&gt;Deref&lt;/code&gt; trait solves this, making your wrapper behave like the data it holds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Use It&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Cost Abstraction:&lt;/strong&gt; Strict type differentiation (e.g., &lt;code&gt;Username&lt;/code&gt; vs. &lt;code&gt;Password&lt;/code&gt;) without the boilerplate of proxying every method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Ergonomics:&lt;/strong&gt; Seamlessly use methods like &lt;code&gt;.len()&lt;/code&gt;, &lt;code&gt;.is_empty()&lt;/code&gt;, or &lt;code&gt;.contains()&lt;/code&gt; directly on your custom struct.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&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;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Deref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Deref&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The type we want to "mimic"&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;deref&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;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Target&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;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="c1"&gt;// Return a reference to the inner value&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;&lt;strong&gt;In Action&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks to Deref Coercion, the compiler automatically triggers &lt;code&gt;.deref()&lt;/code&gt; when it encounters a type mismatch that can be resolved by a reference.&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;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Anton"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Direct access to &amp;amp;str methods&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;        
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; 

&lt;span class="c1"&gt;// 2. Passing to functions expecting &amp;amp;str&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, {s}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;greet&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;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Coercion kicks in here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Intent Matters:&lt;/strong&gt; Only use this when the wrapper is conceptually a “smart pointer” or a transparent extension. If the inner type’s methods could break your struct’s internal logic, stick to manual method delegation.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>deref</category>
      <category>newtype</category>
    </item>
    <item>
      <title>Rust CI: Security, Dependency Policy, Coverage Gate, and Fast Builds</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Sun, 23 Nov 2025 16:52:38 +0000</pubDate>
      <link>https://dev.to/antonds/rust-ci-security-dependency-policy-coverage-gate-and-fast-builds-5f5b</link>
      <guid>https://dev.to/antonds/rust-ci-security-dependency-policy-coverage-gate-and-fast-builds-5f5b</guid>
      <description>&lt;p&gt;A typical GitHub Actions workflow for Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install cargo-audit, cargo-deny, tarpaulin, chef&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo install cargo-audit cargo-deny cargo-tarpaulin cargo-chef&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security check&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo audit&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dependency policy check&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo deny check&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test coverage gate&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo tarpaulin --fail-under &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build using cargo chef&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;cargo chef prepare --recipe-path recipe.json&lt;/span&gt;
      &lt;span class="s"&gt;cargo chef cook --recipe-path recipe.json&lt;/span&gt;
      &lt;span class="s"&gt;cargo build --release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;performs security validation,&lt;/li&gt;
&lt;li&gt;enforces dependency and license policies,&lt;/li&gt;
&lt;li&gt;ensures test coverage quality,&lt;/li&gt;
&lt;li&gt;and builds your Rust project quickly using dependency caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What each step does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security check (cargo-audit)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cargo audit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scans Cargo.lock for vulnerable, deprecated, or compromised dependencies using the RustSec advisory database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency policy check (cargo-deny)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cargo deny check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validates your dependency graph: licenses, banned crates, duplicates, and other policy rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test coverage gate (cargo-tarpaulin)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cargo tarpaulin --fail-under &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measures test coverage and fails the CI pipeline if coverage is below 80%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fast build using cargo-chef&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="s"&gt;cargo chef prepare --recipe-path recipe.json&lt;/span&gt;
&lt;span class="s"&gt;cargo chef cook --recipe-path recipe.json&lt;/span&gt;
&lt;span class="s"&gt;cargo build --release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;prepare: generates a dependency recipe.&lt;/li&gt;
&lt;li&gt;cook: builds and caches dependencies separately.&lt;/li&gt;
&lt;li&gt;cargo build --release: final build using a warmed dependency cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;via &lt;a href="https://www.youtube.com/@letsgetrusty" rel="noopener noreferrer"&gt;@Let's Get Rusty&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>devops</category>
      <category>githubactions</category>
      <category>security</category>
    </item>
    <item>
      <title>Makefiles — add a make help command</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Wed, 19 Nov 2025 17:48:57 +0000</pubDate>
      <link>https://dev.to/antonds/makefiles-add-a-make-help-command-1o7l</link>
      <guid>https://dev.to/antonds/makefiles-add-a-make-help-command-1o7l</guid>
      <description>&lt;p&gt;If you’re using Makefiles — add a make help command.&lt;br&gt;
It auto-parses comments and shows all tasks instantly.&lt;/p&gt;

&lt;p&gt;Simple hack, huge productivity boost.&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;help&lt;/span&gt;:
    @awk &lt;span class="s1"&gt;'BEGIN {FS=":"} \
    /^#/ {comment=substr($$0,3)} \
    /^[a-zA-Z0-9_-]+:/ {printf "\033[36m%-20s\033[0m %s\n", $$1, comment}'&lt;/span&gt; Makefile
&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%2Fow4jw0iiqbhfemjsmnl0.jpg" 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%2Fow4jw0iiqbhfemjsmnl0.jpg" alt=" " width="800" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>makefile</category>
      <category>devtips</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Scraping and Summarizing LinkedIn Jobs with a Chrome Extension + ChatGPT</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Tue, 09 Sep 2025 13:06:40 +0000</pubDate>
      <link>https://dev.to/antonds/scraping-and-summarizing-linkedin-jobs-with-a-chrome-extension-chatgpt-33m0</link>
      <guid>https://dev.to/antonds/scraping-and-summarizing-linkedin-jobs-with-a-chrome-extension-chatgpt-33m0</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%2F2xoh6x5eytw5o5995wxo.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%2F2xoh6x5eytw5o5995wxo.png" alt=" " width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intro&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scrolling through endless job descriptions on LinkedIn can be overwhelming.&lt;/p&gt;

&lt;p&gt;I wanted a faster way to get the essence of a role — so I built a &lt;a href="https://github.com/anton-ds/linkedin-scraper-ext" rel="noopener noreferrer"&gt;small Chrome extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This extension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrapes job descriptions from LinkedIn (directly from the job page)&lt;/li&gt;
&lt;li&gt;Cleans up the text (removes tags, weird spacing, extra line breaks)&lt;/li&gt;
&lt;li&gt;Sends it to ChatGPT with a custom prompt&lt;/li&gt;
&lt;li&gt;Displays a structured summary right inside the popup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All processing happens locally in the browser, and ChatGPT is called directly via API. No backend, no middlemen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the extension locally (Load unpacked in chrome://extensions)&lt;/li&gt;
&lt;li&gt;Open any LinkedIn job post&lt;/li&gt;
&lt;li&gt;Click the extension icon → job description appears in a popup&lt;/li&gt;
&lt;li&gt;Hit Send to ChatGPT → receive a clean, structured summary (role, requirements, stack, seniority)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why Build It?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I often check LinkedIn job posts but most descriptions are long walls of text.&lt;/p&gt;

&lt;p&gt;This extension reduces noise and lets me quickly see if a role is relevant.&lt;/p&gt;

&lt;p&gt;It’s also a fun example of how browser scripting + OpenAI API can be combined for productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clone repo and load unpacked in Chrome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/anton-ds/linkedin-scraper-ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add your OpenAI API key and prompt in the Options page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrap-up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re curious, the code is open-source:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/anton-ds/linkedin-scraper-ext" rel="noopener noreferrer"&gt;https://github.com/anton-ds/linkedin-scraper-ext&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback, ideas, or PRs!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>String + Length: A Zero-Overhead Trick in C</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Mon, 01 Sep 2025 13:39:46 +0000</pubDate>
      <link>https://dev.to/antonds/string-length-a-zero-overhead-trick-in-c-j50</link>
      <guid>https://dev.to/antonds/string-length-a-zero-overhead-trick-in-c-j50</guid>
      <description>&lt;p&gt;&lt;strong&gt;📌 Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;C&lt;/code&gt; you often need to keep a string together with its length. Calling &lt;code&gt;strlen()&lt;/code&gt; every time means extra memory scans. We want it compact and efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A tiny trick with typedef + macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#define SV(s) { (s), sizeof(s) - 1 }
&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Messages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sv&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rep_prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Messages&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;SV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;SV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;SV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server reply: "&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;After preprocessing it becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Messages&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt; &lt;span class="o"&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="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;        &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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="s"&gt;"Server reply: "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server reply: "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;— string and its length, no redundant strlen().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_prefix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OK&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;Handy in network protocols, logging, binary formats — string + size always at hand, zero overhead.&lt;/p&gt;

</description>
      <category>c</category>
    </item>
    <item>
      <title>The “best stack” is a myth</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Fri, 11 Jul 2025 21:00:21 +0000</pubDate>
      <link>https://dev.to/antonds/the-best-stack-is-a-myth-192p</link>
      <guid>https://dev.to/antonds/the-best-stack-is-a-myth-192p</guid>
      <description>&lt;p&gt;There’s no universal tech stack that fits every project.&lt;br&gt;
When choosing a stack, I always focus on 3 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The task — what really needs to be done&lt;/li&gt;
&lt;li&gt;The team — who will maintain it tomorrow&lt;/li&gt;
&lt;li&gt;The context — budget, deadlines, infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes it’s Java + Kafka + Kubernetes.&lt;br&gt;
Sometimes it’s just PHP + MySQL on a basic VPS.&lt;/p&gt;

&lt;p&gt;Both can be right, if they solve the problem.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>Working on combat logic that’s completely independent of the game code</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Fri, 27 Jun 2025 22:30:47 +0000</pubDate>
      <link>https://dev.to/antonds/working-on-combat-logic-thats-completely-independent-of-the-game-code-51i5</link>
      <guid>https://dev.to/antonds/working-on-combat-logic-thats-completely-independent-of-the-game-code-51i5</guid>
      <description>&lt;p&gt;The idea is to let a designer (aka a human) create combat mechanics through an editor, which are then delivered to the game as JSON. It’s exciting to apply old knowledge to new areas like this.&lt;/p&gt;

&lt;p&gt;P.S. Spent a good while wrestling with the grid system. Eventually tracked it down to an issue with child components in the engine.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/VaF7i41duaY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>unrealengine</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>PostgreSQL: Don't Rush to Index BOOLEAN Columns</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Mon, 23 Jun 2025 22:03:28 +0000</pubDate>
      <link>https://dev.to/antonds/postgresql-dont-rush-to-index-boolean-columns-4565</link>
      <guid>https://dev.to/antonds/postgresql-dont-rush-to-index-boolean-columns-4565</guid>
      <description>&lt;p&gt;It might feel natural to index is_active — but in PostgreSQL, it often does more harm than good.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BOOLEAN has only three values: true, false, NULL.&lt;/p&gt;

&lt;p&gt;If values are evenly distributed (e.g. 50/50), the B-tree index is inefficient. The planner will usually pick a sequential scan instead of using the index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index helps when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One value is rare (e.g. 1% are false)&lt;/li&gt;
&lt;li&gt;Queries target only rare values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Better option: use a partial index&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_inactive&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&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="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;is_active&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>postgres</category>
    </item>
    <item>
      <title>Rust for Kids</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Wed, 11 Jun 2025 22:04:41 +0000</pubDate>
      <link>https://dev.to/antonds/rust-for-kids-m0n</link>
      <guid>https://dev.to/antonds/rust-for-kids-m0n</guid>
      <description>&lt;p&gt;This article doesn’t aim to teach you Rust or unveil some hidden feature. It’s more like an experiment — a way to show a kid what Rust looks like on the inside (Rust, not the kid), rather than how to write idiomatic Rust code.&lt;/p&gt;

&lt;p&gt;In other words, devs are used to judging a language by how well it fits into their mental model of “how code should be written.” And when it doesn’t? That’s when the frustration kicks in.&lt;/p&gt;

&lt;p&gt;Here, I tried flipping the perspective — think “picture book”: how the language lives and breathes, not how we’ve learned to structure it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Disclaimer: This article isn’t about Copy types like &lt;code&gt;i32&lt;/code&gt;, &lt;code&gt;f64&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, or &lt;code&gt;&amp;amp;T.&lt;/code&gt; We're diving into move types — like String — where values are actually moved around.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alright, let’s roll.&lt;/p&gt;

&lt;p&gt;We’ll start with a core idea in Rust: a value (a memory cell) can only have one owner at a time.&lt;br&gt;
Let’s introduce a bit of “storybook” notation:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;let&lt;/code&gt; = “let it be”&lt;br&gt;
&lt;code&gt;=&lt;/code&gt; = “takes ownership”&lt;br&gt;
&lt;code&gt;mut&lt;/code&gt; = “money”, yep, just a money ;)&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;let&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s let &lt;code&gt;a&lt;/code&gt; own a House:&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;let&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"House"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s let &lt;code&gt;b&lt;/code&gt; own that same House. According to the rules, the previous owner has to move out — no exceptions:&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;let&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point:&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;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom — error.&lt;/p&gt;

&lt;p&gt;Why? Because &lt;code&gt;a&lt;/code&gt; no longer has the right to do anything with the value (its former House). Ownership was transferred, and Rust enforces that hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mut&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But wait — there’s more. Just owning a House doesn’t mean you can start remodeling it. Think of it like renting someone else's place — ownership without modification rights.&lt;/p&gt;

&lt;p&gt;So this will trigger an error:&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="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" with Pool"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 🔴 error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to make changes, you have to buy the House with some extra money &lt;code&gt;mut&lt;/code&gt;.&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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" with pool"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// House with pool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;c&lt;/code&gt; has the keys and the renovation permit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;amp;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can share access to the House — say, by letting a buddy crash for a while. You hand them a copy of the keys using &lt;code&gt;&amp;amp;&lt;/code&gt;:&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;let&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there’s a catch. According to the social contract, if guests are inside, no renovations allowed. The following code will throw an error:&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;let&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" with sandbox"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// can't renovate while guests are over&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// guest is here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to wait until the guests leave:&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;let&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// guest leaves&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" with sandbox"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still confused? There’s a full “guest etiquette” section at the end of the article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;amp; mut&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let’s say your buddy wants to add a hot tub. Since that’s a modification to the House, you ask him to chip in — and in return, you give him the real keys with change privileges: &lt;code&gt;&amp;amp;mut&lt;/code&gt;.&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;let&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&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;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" and with hot tub"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just remember: same rule applies — if someone else is holding the keys, you can’t do any remodeling. In fact, while your buddy is chilling in the hot tub, you can’t even enter the house:&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;let&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&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;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" and with hot tub"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// buddy’s in the hot tub &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to wait for them to finish:&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;let&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&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;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" and with hot tub"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// buddy’s done&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only applies when you gave out “paid” access (&lt;code&gt;&amp;amp;mut&lt;/code&gt;) — not when you just let someone couch-surf with &lt;code&gt;&amp;amp;.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mut &amp;amp;mut&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we rent out the House again — to another buddy, this time with full mod rights. But this buddy also has their own extra money — &lt;code&gt;mut&lt;/code&gt;.&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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&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;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" and with garden"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things are going well, and we decide to buy a second place: Apartment.&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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;c2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apartment"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our buddy’s not slacking either — “Let me try out the Apartment too,” he says, waving his &lt;code&gt;mut&lt;/code&gt; wallet. So he moves out of the House, into the Apartment, and starts upgrading that too:&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="n"&gt;f&lt;/span&gt; &lt;span class="o"&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;c2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" with elevator"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same core idea: ownership with editing rights = &lt;code&gt;&amp;amp;mut&lt;/code&gt;. And declaring &lt;code&gt;let mut f&lt;/code&gt; means this buddy isn’t tied to just one location — he can move between places and make changes. Because money mut, yep&lt;/p&gt;

&lt;p&gt;After he’s gone, we end up with both properties modified:&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;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c2: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//c: House with pool and with sandbox and with hot tub and with garden&lt;/span&gt;
&lt;span class="c1"&gt;//c2: Apartment with elevator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: When I say “the buddy left,” I mean it literally — he’s gone, out of scope. Rust tracks that. If a variable is no longer used later in the code, Rust considers it dead — like you called &lt;code&gt;delete&lt;/code&gt; on it.&lt;/p&gt;

&lt;p&gt;That’s why things can seem weird: while the guest is “around,” you’re blocked from doing stuff, but it’s not always clear they’ve “left.” Rust figures it out by analyzing whether the variable is used again — if not, it’s safe to proceed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With a few rare exceptions, the same rules apply to functions too. I won’t overload this post with function examples — maybe next time :)&lt;/p&gt;

</description>
      <category>rust</category>
      <category>ownership</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Logic Separation — One Thread per Role</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Sun, 08 Jun 2025 17:22:14 +0000</pubDate>
      <link>https://dev.to/antonds/logic-separation-one-thread-per-role-4l3</link>
      <guid>https://dev.to/antonds/logic-separation-one-thread-per-role-4l3</guid>
      <description>&lt;p&gt;You write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt; &lt;span class="nf"&gt;network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serve_clients&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt; &lt;span class="nf"&gt;game&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;game_loop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt; &lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flush_logs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, everything still runs in sequence (depending on CPU), but the logic is cleanly separated across threads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s way simpler than stuffing everything into one giant loop with a million &lt;code&gt;ifs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Even on a single core — this model is easier to read, test, and extend.&lt;/li&gt;
&lt;li&gt;And once you upgrade to 4+ cores — performance improves instantly, without changing the architecture.&lt;/li&gt;
&lt;li&gt;Bonus: each thread handles its own domain. Easier to reason about, debug, and scale independently&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cpp</category>
      <category>programming</category>
      <category>architecture</category>
      <category>networking</category>
    </item>
    <item>
      <title>Cloud build of Unreal Engine 5.5 server for €0.5</title>
      <dc:creator>Anton Dolganin</dc:creator>
      <pubDate>Mon, 02 Jun 2025 18:52:39 +0000</pubDate>
      <link>https://dev.to/antonds/cloud-build-of-unreal-engine-55-server-for-eu05-13gg</link>
      <guid>https://dev.to/antonds/cloud-build-of-unreal-engine-55-server-for-eu05-13gg</guid>
      <description>&lt;p&gt;Let’s start with the problem. Although Unreal Engine is cross-platform, it makes logical sense to host the server on Linux. Of course, you could host it on a Mac, but it’s hardly justifiable. So, a reasonable question arises — how do you build the server for Linux?&lt;/p&gt;

&lt;p&gt;Next is the problem of building the server for Linux while working on another OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On Linux, there are no issues if you have the resources and space (at least 200 GB of storage, preferably with 8 cores and 32 GB of memory).&lt;/li&gt;
&lt;li&gt;On Windows, it involves a lot of workarounds, but the problem can be the same as in point 1.&lt;/li&gt;
&lt;li&gt;On Mac – this is my case – no matter how hard I tried, I couldn’t get it to work, and each such experiment would overheat the Mac, disrupting my other tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution? I decided to try building it in the cloud. Ideally, I wanted it to be so cheap that I could do it over and over again. &lt;/p&gt;

&lt;p&gt;AWS does offer something similar, but only through a request (not to be confused with GameLift), and it would probably cost a fortune. I found a solution for just &lt;strong&gt;50 cents&lt;/strong&gt; 😉&lt;/p&gt;

&lt;p&gt;And about the time. In general, building such a project is a slow process on any hardware. But just a heads-up — on the chosen configuration below, it took me &lt;strong&gt;5-6 hours&lt;/strong&gt; from logging into the server to running the built server.&lt;/p&gt;

&lt;p&gt;Below is a dry todo list for copy-pasting, with some comments. But trust me, I learned my lessons the hard way while working through the drafts.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Server selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was choosing between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS&lt;/strong&gt;, but for a suitable machine, I’d be paying around the same 50-70 cents per hour, if not more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean&lt;/strong&gt;, about twice as cheap as AWS, but with very inconvenient scaling — either not enough resources or space. Or I’d have to purchase extra volumes, which isn’t cost-effective.&lt;/li&gt;
&lt;li&gt;Unexpectedly, my choice fell on &lt;strong&gt;Hetzner Cloud&lt;/strong&gt;. Specifically, their cloud infrastructure, where you can also run a server by the hour. And it only costs €0.093. Isn’t that awesome?!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, it’s not worth being stingy and it’s best to go for this one right away:&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%2Ffu2x4icfduuqumdedo9i.webp" 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%2Ffu2x4icfduuqumdedo9i.webp" alt="Cloud Servers" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because you’ll need about 200 GB of storage, and it runs much faster on 8 cores. Yes, I did experiment with the previous option as well.&lt;/p&gt;

&lt;p&gt;The OS is &lt;code&gt;Ubuntu&lt;/code&gt;. And it has to be version &lt;code&gt;22.04&lt;/code&gt;, as it’s the officially supported version by &lt;a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/hardware-and-software-specifications-for-unreal-engine" rel="noopener noreferrer"&gt;Epic&lt;/a&gt;. Without reading the requirements, I installed the latest version right away, and the build failed at the final stages — it was frustrating (&lt;code&gt;ispc.generated&lt;/code&gt; files didn’t build).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation and building the engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You need to create a user in the &lt;code&gt;sudo&lt;/code&gt; group, since building as root is not allowed. Since the server is purely for a single task for a few hours, I didn’t bother with security or permissions systems — I just created a user named &lt;code&gt;builder&lt;/code&gt;.&lt;br&gt;
And of course, you should also update the packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
adduser builder
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;builder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating the user, it will ask for a password — you’ll need it later.&lt;/p&gt;

&lt;p&gt;Next, simply switch to the created user and from there, you can clone the engine repository of the required version (as a reminder, this article is about version &lt;code&gt;5.5&lt;/code&gt;). To access and clone the UE repository, don’t forget that your GitHub account needs to be linked to your EpicGames account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;su - builder
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; 5.5 https://github.com/EpicGames/UnrealEngine.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above operation will take about half an hour. Next, go to the engine directory, run the setup and generation steps, and also remove two plugins that the server doesn’t need — otherwise, they’ll just get in the way.&lt;br&gt;
At the setup stage, you’ll be prompted to enter the password for the &lt;code&gt;builder&lt;/code&gt; user.&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;cd &lt;/span&gt;UnrealEngine/
./Setup.sh
./GenerateProjectFiles.sh
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./Engine/Plugins/Fab
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./Engine/Plugins/Bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ve reached the most critical stage — the build (approx. 3 hours). To be safe, we’ll run everything inside &lt;code&gt;screen&lt;/code&gt;, so we can easily reconnect later if we get disconnected (&lt;code&gt;screen -r&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;screen &lt;span class="nt"&gt;-S&lt;/span&gt; unreal_build
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Building the game server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alright, we’ve finished building the engine, now let’s move on. A quick reminder — we’re currently in the engine directory, so let’s move up a level and clone our own project.&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;cd&lt;/span&gt; ../
git clone https://github.com/anton-ds/DedicateTest.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After cloning, switch to the &lt;strong&gt;engine directory&lt;/strong&gt; and run the server build command. This will take a &lt;strong&gt;couple more hours&lt;/strong&gt;, but future rebuilds of the server will definitely be faster.&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;cd &lt;/span&gt;UnrealEngine/
./Engine/Build/BatchFiles/RunUAT.sh BuildCookRun &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/builder/DedicateTest/DedicateTest.uproject"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-noP4&lt;/span&gt; &lt;span class="nt"&gt;-platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Linux &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-build&lt;/span&gt; &lt;span class="nt"&gt;-cook&lt;/span&gt; &lt;span class="nt"&gt;-stage&lt;/span&gt; &lt;span class="nt"&gt;-pak&lt;/span&gt; &lt;span class="nt"&gt;-archive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-archivedirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/builder/DedicateTest/BuildOutput"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A reminder: &lt;code&gt;builder&lt;/code&gt; is our user under which we’re building, and it’s also the name of the directory where we cloned the project (in my case, it’s &lt;code&gt;DedicateTest&lt;/code&gt;). The final result will appear in the &lt;code&gt;BuildOutput&lt;/code&gt; subdirectory of our project.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Running the server and connecting the client&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I deliberately did not mention how to properly develop multiplayer games in Unreal Engine in this article (since, to be honest, it’s mostly ready out of the box), because the title suggests you are reading this for a specific purpose — building the server — and you already know how everything else works.&lt;/p&gt;

&lt;p&gt;It’s worth noting here that the cloud server has completed its task and can be deleted. You simply copy the &lt;code&gt;BuildOutput/LinuxServer&lt;/code&gt; directory to your production server and launch it there. However, I recommend checking the following steps once on the current setup.&lt;/p&gt;

&lt;p&gt;On the server, switch to the directory containing our server build and launch the server (first, make sure to open the port the server will listen on — in my case, it’s &lt;code&gt;7777&lt;/code&gt;). Keep in mind, we are still working under the user &lt;code&gt;builder&lt;/code&gt;, and we’ll continue as this user. The project code is — &lt;code&gt;DedicateTest&lt;/code&gt;.&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;sudo &lt;/span&gt;ufw allow 7777/udp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw reload
&lt;span class="nb"&gt;cd&lt;/span&gt; ./LinuxServer
./DedicateTestServer.sh ThirdPersonMap &lt;span class="nt"&gt;-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7777
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, note that in the server launch parameters I specify the name of the map the server will run. In my case, it’s more convenient this way.&lt;/p&gt;

&lt;p&gt;Alright, let’s assume your client is built on the same version — launch the game. Perhaps you already have the logic for connecting to the server implemented, but if not, you can open the in-game console (&lt;code&gt;~&lt;/code&gt;) and enter a command with the server IP and port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open &amp;lt;ip&amp;gt;:7777
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the server logs, you should see a successful connection from your client’s IP. Once you connect with another client, you’ll be able to interact between clients through the server.&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%2Fdmpjq5gtptbrsfy2oqe8.webp" 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%2Fdmpjq5gtptbrsfy2oqe8.webp" alt="Success Connection" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there’s a catch...&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There’s a logical requirement — both the server and the client must be built from the same code version. However, Unreal Engine takes this even further — for example, a build from source and a build from the binary version (the one you run from the Launcher) are actually different builds. (And sometimes this version mismatch doesn’t even immediately throw an error)&lt;/p&gt;

&lt;p&gt;The simplest and most reliable solution is to build the client from source as well, and this is even recommended for multiplayer games. But we assumed that, for now, we either can’t or don’t want to do this.&lt;/p&gt;

&lt;p&gt;So, the compatibility error manifests as follows — when the client connects, the server throws an error in the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;LogHandshake: CheckVersion: Incompatible version. bValidHandshakeVersion: 1, bValidNetVersion: 0, GHandshakeEnforceNetworkCLVersion: 0, RemoteMinVersion: 3, RemoteCurVersion: 4, MinSupportedHandshakeVersion: 3, CurrentHandshakeVersion: 4, RemoteNetworkVersion: 2530911350, LocalNetworkVersion: 4263286266, RemoteNetworkFeatures: GenericReplication, LocalNetworkFeatures: GenericReplication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CheckVersion: Incompatible version ... RemoteNetworkVersion: &lt;code&gt;2530911350&lt;/code&gt;, LocalNetworkVersion: &lt;code&gt;4263286266&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a quick fix to check if your client works with the server (important! This fix is NOT for production!), you can do the following. Or, you can write your own version checking logic.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Source/DedicateTest/DedicateTest.cpp&lt;/code&gt; file (the filename may vary depending on your project’s name), add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Copyright Epic Games, Inc. All Rights Reserved.&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"DedicateTest.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"Modules/ModuleManager.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FDedicateTestModule&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;FDefaultGameModuleImpl&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;StartupModule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FNetworkVersion&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IsNetworkCompatibleOverride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindStatic&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;FDedicateTestModule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IsNetworkCompatible&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;FNetworkVersion&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetLocalNetworkVersionOverride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindStatic&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;FDedicateTestModule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetLocalNetworkVersion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShutdownModule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FNetworkVersion&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IsNetworkCompatibleOverride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unbind&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;FNetworkVersion&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetLocalNetworkVersionOverride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unbind&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNetworkCompatible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="n"&gt;LocalNetworkVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="n"&gt;RemoteNetworkVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// here you can add your own logic for checking network compatibility&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt; &lt;span class="nf"&gt;GetLocalNetworkVersion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// must be equal to the server version&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;4263286266&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="n"&gt;IMPLEMENT_PRIMARY_GAME_MODULE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;FDedicateTestModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DedicateTest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"DedicateTest"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That number &lt;code&gt;4263286266&lt;/code&gt; is the network version number from the server logs. Rebuild the client build (not the server), and run it. Everything should work.&lt;/p&gt;

&lt;p&gt;That’s it — don’t forget to delete the build server. 😉&lt;/p&gt;

</description>
      <category>unrealengine</category>
      <category>gamedev</category>
      <category>multiplayer</category>
    </item>
  </channel>
</rss>
