<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>karan.codes</title>
<link>https://karan.codes/</link>
<atom:link href="https://karan.codes/index.xml" rel="self" type="application/rss+xml"/>
<description></description>
<generator>quarto-1.9.36</generator>
<lastBuildDate>Fri, 07 Nov 2025 13:00:00 GMT</lastBuildDate>
<item>
  <title>Analysing Midairs netcode &amp; lag compensation</title>
  <link>https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/</link>
  <description><![CDATA[ 





<section id="authors-note" class="level1" data-number="1">
<h1 data-number="1"><span class="header-section-number">1</span> Author’s note</h1>
<p>I have no experience (personal or professional) in game development or with Unreal Engine 4. Therefore, some interpretations presented in this article may not be fully accurate due to the complexity of Unreal Engine. This project was undertaken purely for educational purposes, focusing on reverse engineering, mathematical analysis, and software analysis. All findings are based on independent analysis and publicly available information.</p>
</section>
<section id="glossary" class="level1" data-number="2">
<h1 data-number="2"><span class="header-section-number">2</span> Glossary</h1>
<p><strong>DLL</strong> : Dynamic-link library, a file that contains code and data which programs can load and use at runtime</p>
<p><strong>Ghidra</strong> : A free, open-source software reverse engineering framework used to analyse and decompile binaries</p>
<p><strong>Ping</strong> : The round-trip time (RTT) for data between a client and the server, measured in seconds</p>
<p><strong>UE4</strong> : Unreal Engine 4, a game engine developed by Epic Games</p>
<p><strong>UT4</strong> : Unreal Tournament 4, a game built on UE4 and developed by Epic Games</p>
<section id="table-of-variables" class="level2" data-number="2.1">
<h2 data-number="2.1" class="anchored" data-anchor-id="table-of-variables"><span class="header-section-number">2.1</span> Table of variables</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th>Variable name</th>
<th>Description</th>
<th>Equivalence</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BTR%7D"></td>
<td>Server tick rate (Hz)</td>
<td></td>
<td><img src="https://latex.codecogs.com/png.latex?30"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?%7B%5CDelta%20T%7D_s"></td>
<td>Server tick period (s)</td>
<td>(<img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B%7B%5Ctext%7BTR%7D%7D%7D">)</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B30%7D%20%5Capprox%200.033"></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BFPS%7D"></td>
<td>Client tick rate (Hz)</td>
<td></td>
<td><img src="https://latex.codecogs.com/png.latex?240"></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?%7B%5CDelta%20T%7D_c"></td>
<td>Client tick period (s)</td>
<td>(<img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B%5Ctext%7BFPS%7D%7D">)</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B240%7D%20%5Capprox%200.00417"></td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPing%7D"></td>
<td>Ping</td>
<td>RTT (s)</td>
<td></td>
</tr>
<tr class="even">
<td><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BOWD%7D"></td>
<td>One Way Delay (s)</td>
<td>(<img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B%5Ctext%7BPing%7D%7D%7B2%7D">)</td>
<td></td>
</tr>
</tbody>
</table>
</section>
</section>
<section id="prerequisite-knowledge" class="level1" data-number="3">
<h1 data-number="3"><span class="header-section-number">3</span> Prerequisite knowledge</h1>
<p>This article assumes the reader</p>
<ul>
<li>has high school level mathematical proficiency</li>
<li>is familiar with basic networking concepts</li>
</ul>
<p>Derivations have been provided where possible.</p>
<p>Readers unfamiliar with the fundamentals of concepts discussed are strongly encouraged to consult additional sources to gain a full understanding.</p>
</section>
<section id="introduction" class="level1" data-number="4">
<h1 data-number="4"><span class="header-section-number">4</span> Introduction</h1>
<p>Midair was a community and externally funded multiplayer first-person shooter released on Steam in May 2018, developed by Archetype Studios. It aimed to be a spiritual successor to the Tribes franchise, belonging to a subgenre colloquially referred to as FPS-Z (first-person shooters with a significant vertical axis). The defining mechanic is momentum-based skiing, where players exploit terrain slopes to reach extreme speeds, combined with a jetpack for vertical movement and projectile-based weapons that reward leading fast-moving targets. The project spanned between 2014 and 2019, funded initially via a Kickstarter campaign before ultimately shutting due to being a buggy, incomplete, unbalanced mess that could no longer be maintained due to fledgling resources.</p>
<p>Central to Midairs identity, and the subject of this article, were claims made by the development team and the broader community regarding the quality of the game’s netcode and lag compensation. These claims were numerous, persistent, and emphatic - spanning years of forum posts, Discord messages, Reddit comments, and YouTube videos.</p>
<p>This article examines those claims through two complementary approaches: a mathematical analysis of the lag compensation mechanism, and a code analysis via reverse engineering of the publicly available Midair client. The findings are unambiguous. Midairs netcode is stock Unreal Engine 4 with no meaningful modifications. The lag compensation is a direct copy of Epic Games’ Unreal Tournament 4, reduces mathematically to a single naive kinematic offset, and is covered under a license that explicitly prohibits its use in other projects. The claims made about these systems are not merely overstated - they are false.</p>
</section>
<section id="sec-background" class="level1" data-number="5">
<h1 data-number="5"><span class="header-section-number">5</span> Background and claims</h1>
<p>The implementation of Midairs lag compensation and custom tailored netcode is credited to <a href="https://www.reddit.com/user/Mabeline/">Bazika “Mabel (Mabeline)” Huffman-Kinyalolo</a>.</p>
<p>Quoting directly from Midairs <a href="https://www.kickstarter.com/projects/archetypestudios/midair">Kickstarter</a>:</p>
<blockquote class="blockquote">
<p><strong>Custom Tailored Networking</strong></p>
<p>Modern First-Person Shooters require highly customized netcode and lag compensation to ensure that players around the world can enjoy games without worrying about the effects of packet loss, latency, and server location. We have spent and will continue to spend a great amount of effort to specially design our netcode to achieve this end, enabling all players with under 150ms to have the same, high-quality experience. During pre-alpha testing we have brought in players from a variety of FPS-Z titles across both North America and Europe to ensure we provide a seamless cross-continent experience that trumps all others.</p>
</blockquote>
<p>There are dozens upon dozens (totaling a number probably somewhere in the hundreds) of posts/comments across various forums (e.g.&nbsp;Discord, Legions: Overdrive forums, Reddit, YouTube, etc) since Midairs inception till now which make claim to the superior performance and implementation of Midairs netcode and lag compensation.</p>
<p>Unfortunately two of the largest resources, the official Midair forums (playmidair.com), and official Midair discord, have both been essentially “nuked”.</p>
<section id="midair-today" class="level2" data-number="5.1">
<h2 data-number="5.1" class="anchored" data-anchor-id="midair-today"><span class="header-section-number">5.1</span> Midair today</h2>
<p>In early 2020 a new independent studio called Vector Z Studios began development for Midair: Community Edition, which was eventually renamed to Midair 2. As of the writing of this article, Midair 2 remains in development.</p>
<p>Also, as of the writing of this article, analysis performed via reverse engineering confirms that Midair 2 uses the same netcode and lag compensation as Midair.</p>
</section>
<section id="claims-quotes" class="level2" data-number="5.2">
<h2 data-number="5.2" class="anchored" data-anchor-id="claims-quotes"><span class="header-section-number">5.2</span> Claims (quotes)</h2>
<p>The provided quotes do not serve any real purpose other than entertainment (or cringe fuel) due to the fact they are grossly overstated, and raise serious questions about the authors credibility. The remarks suggest either a misunderstanding of the facts or a deliberate distortion (i.e.&nbsp;shilling).</p>
<section id="generic-claims" class="level3" data-number="5.2.1">
<h3 data-number="5.2.1" class="anchored" data-anchor-id="generic-claims"><span class="header-section-number">5.2.1</span> Generic claims</h3>
<p>A <em>very small</em> collection of relevant claims (quotes) I found via googling certain keywords related to Midair are provided below.</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-1-contents" aria-controls="callout-1" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Read at your own risk - excessive larping ahead
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-1" class="callout-1-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Netcode is really, really good and is getting even better as time passes. We have Euros playing on NY servers and not being disadvantaged.</p>
</blockquote>
<p>- <a href="https://forums.legionsoverdrive.com/threads/new-game-midair.5238/post-110159"><strong>Darklord</strong></a> (Ex-Midair 2 developer) on April 27, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Midair is coming along quite nicely. The testers have regular pickup games and even in its pre-alpha state it plays very well; things like the netcode have been written from the ground up already to support advanced lag compensation.</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Games/comments/4i1a7h/comment/d2u56ub/"><strong>PROJTHEBENIGNANT</strong></a> on May 5, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>What you’re getting is essentially an updated t1/t2c but with amazing net code/lag comp, matchmaking to find balanced, fun games and the possibility of an alive player-base which is something you won’t be getting with the older tribes games.</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Tribes/comments/4i15dq/comment/d2u4l86/"><strong>WorkingAsIntended</strong></a> on May 5, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>…and we (finally!) have some exciting netcode improvements like lag compensation. All of these are pretty huge for the game experience. Lag compensation alone opens up a huge amount of options. Users across the whole of the US can play on the same server with basically no issues</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Games/comments/4iecnd/comment/d2xo9rd/"><strong>Mabeline</strong></a> (Midair developer) on May 8, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>T:A is very ping dependant while Midair will lag compensate everything so pings below 150 will feel like 0 ping.</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Tribes/comments/4l6fj4/comment/d3ln4b5/"><strong>Shreq</strong></a> (Ex-Midair 2 developer) on May 27, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Yeah big time. The best in the business in fact, you won’t find better lag comp in the market.</p>
</blockquote>
<p>- <a href="https://steamcommunity.com/app/439370/discussions/0/1473095331496124404/#c1473095331497561536"><strong>Wildefyr</strong></a> on August 26, 2017</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>One of the founding members of the original dev team, Mabel, wrote a bunch of netcode. My understanding is the lag compensation is a lot better than default ue4</p>
</blockquote>
<p>- <a href="https://discord.com/channels/681439528292057097/681439528292057177/740934330796212243"><strong>ehme</strong></a> (Midair 2 developer) on August 7, 2020</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Midair 2 netcode is really good. Projectiles are the hardest to get right and it nails it on all fronts. Mabel was originally in charge of it and did an amazing job.</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Tribes/comments/18kqkr5/comment/kduli36/"><strong>Cykon</strong></a> on December 12, 2023</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Nearly perfect netcode has been achieved before in Midair. You can be very generous with it in Tribes games according to Mabel.</p>
</blockquote>
<p>- <a href="https://discord.com/channels/1169300582222286918/1183811326628339855/1183860419211841587"><strong>afireinasa</strong></a> on December 18, 2023</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Yeah its bad luck because in previous patches the netcode was top notch even at 150+ ping</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Tribes/comments/1audo0l/comment/kr44tbm/"><strong>Aesdotjs</strong></a> (Midair 2 developer) on February 19, 2024</p>
</blockquote>
</div>
</div>
</div>
</section>
<section id="archetype-studios" class="level3" data-number="5.2.2">
<h3 data-number="5.2.2" class="anchored" data-anchor-id="archetype-studios"><span class="header-section-number">5.2.2</span> Archetype Studios</h3>
<p>A collection of relevant claims (quotes) I found via searching transcripts of YouTube videos related to interviewing Archetype Studios or videos posted by the official Archetype Studios channel itself are provided below.</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-2-contents" aria-controls="callout-2" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Read at your own risk - excessive larping ahead
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-2" class="callout-2-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>So I know the way that the netcode works is has been heavily modified by Mabel in particular. There’s been a lot of effort spent on getting this right… and some of the benefits we see from that are some nice smooth gameplay especially with inconsistent ping and latency and that sort of thing. But yeah, so it was like one of the other benefits is actually cross region play…</p>
</blockquote>
<p>- <a href="https://youtu.be/jR_0L4F94CI?t=4707"><strong>BugsPray</strong></a> (Midair developer, CEO of Archetype Studios) on March 20, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>As far as consistency goes, the guys going through and making the game right now they put a lot of work into the netcode… and the netcode in this game is pretty remarkable, especially for an FPS-Z game.</p>
</blockquote>
<p>- <a href="https://youtu.be/4vvFmfy_fMI?t=163"><strong>GuitarGuy</strong></a> on May 8, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>Yeah the other thing thats contributing to that is Mabel is something of a savant when it comes to netcode… the netcode we have in Midair is second to none.</p>
</blockquote>
<p>- <a href="https://youtu.be/4vvFmfy_fMI?t=8152">Unknown</a> on May 8, 2016</p>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>We’ve actually done something really cool with Midair in particular with match making, so we’ve taken Unreals netcode and stripped out a bunch and done a bunch of our own work to it and one of the neat impacts of that is that we are going to have cross region play for matchmaking. Which is super neat because it means that, and we actually do that now, I in Seattle play all the time with people in Europe in a server in New York.</p>
</blockquote>
<p>- <a href="https://youtu.be/R6EtcIRfoMQ?t=1340"><strong>BugsPray</strong></a> (Midair developer, CEO of Archetype Studios) on August 29, 2017</p>
</blockquote>
</div>
</div>
</div>
</section>
</section>
</section>
<section id="research-questions" class="level1" data-number="6">
<h1 data-number="6"><span class="header-section-number">6</span> Research questions</h1>
<p>This article specifically seeks to answer the following questions:</p>
<ol type="1">
<li><strong>What is Midairs lag compensation doing mathematically?</strong> Is it reducible to a known formula, and if so, how well does it actually perform?</li>
<li><strong>Was Midairs netcode custom built?</strong> Or is it stock Unreal Engine 4 with superficial (or no) modifications?</li>
<li><strong>Was Midairs lag compensation custom built?</strong> Or was it sourced from an existing codebase?</li>
<li><strong>Are the claims made about Midairs netcode and lag compensation accurate?</strong> Or are they overstated, misleading, or outright false?</li>
</ol>
</section>
<section id="tldr" class="level1" data-number="7">
<h1 data-number="7"><span class="header-section-number">7</span> TL;DR</h1>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-3-contents" aria-controls="callout-3" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Let me spoil it for you
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-3" class="callout-3-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<ul>
<li>Midair uses <strong>stock standard UE4 netcode</strong> (replication, UNet*)</li>
<li>Midairs lag compensation uses <strong>basic kinematics</strong></li>
<li>Midairs lag compensation is <strong>copied from Epic Games UT4</strong></li>
<li>Epic Games UT4 repository license clearly says it’s code <strong>may not be used in another project</strong></li>
<li>Midair heavily relies on client-side simulation to <strong>fake reduction of latency</strong> (e.g.&nbsp;fake projectiles)</li>
<li>Any perceived benefits of Midairs lag compensation system is <strong>largely placebo</strong></li>
<li>Mabel, BugsPray, the Midair development team and notable members of the community made a <strong>conscious effort to mislead</strong> those who would not investigate any further</li>
</ul>
</div>
</div>
</div>
</section>
<section id="theory" class="level1" data-number="8">
<h1 data-number="8"><span class="header-section-number">8</span> Theory</h1>
<section id="sec-netcode-lagcomp" class="level2" data-number="8.1">
<h2 data-number="8.1" class="anchored" data-anchor-id="sec-netcode-lagcomp"><span class="header-section-number">8.1</span> Defining netcode and lag compensation</h2>
<p>Netcode is defined as the part of a game’s programming that manages communication between the client (player) and the server.</p>
<p>Lag compensation is defined as the techniques used to reduce the perceived effect of network latency (i.e.&nbsp;ping).</p>
<p>In the gaming community, these terms are often confused, however they represent two very different concepts and should <strong>NOT</strong> be used interchangeably.</p>
</section>
<section id="predicting-the-location-of-moving-objects" class="level2" data-number="8.2">
<h2 data-number="8.2" class="anchored" data-anchor-id="predicting-the-location-of-moving-objects"><span class="header-section-number">8.2</span> Predicting the location of moving objects</h2>
<section id="continuous-time-domain" class="level3" data-number="8.2.1">
<h3 data-number="8.2.1" class="anchored" data-anchor-id="continuous-time-domain"><span class="header-section-number">8.2.1</span> Continuous time domain</h3>
<p>As formulated in the kinematic equations, the position of a moving object under ideal conditions can be expressed as <img src="https://latex.codecogs.com/png.latex?%0As%5Cleft(t%5Cright)%20=%20s_%7B0%7D%20+%20v_%7B0%7Dt%20+%20%5Cdfrac%7B1%7D%7B2%7Da%7Bt%7D%5E2%0A"> where <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0At%20&amp;:%20%5Ctext%7Btime%7D%20%5C%5C%0As_%7B0%7D%20&amp;:%20%5Ctext%7Binitial%20position%7D%20%5C%5C%0Av_%7B0%7D%20&amp;%20:%20%5Ctext%7Binitial%20velocity%7D%20%5C%5C%0Aa%20&amp;%20:%20%5Ctext%7Bacceleration%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center collapsed" data-bs-toggle="collapse" data-bs-target=".callout-4-contents" aria-controls="callout-4" aria-expanded="false" aria-label="Toggle callout">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Derivation of the kinematic equation for position at a given time
</div>
<div class="callout-btn-toggle d-inline-block border-0 py-1 ps-1 pe-0 float-end"><i class="callout-toggle"></i></div>
</div>
<div id="callout-4" class="callout-4-contents callout-collapse collapse">
<div class="callout-body-container callout-body">
<p>Define acceleration as a constant.</p>
<p>Let <img src="https://latex.codecogs.com/png.latex?t%20%3E%20t_0%20%5Cgeq%200."></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bequation%7D%0Aa%20=%20%5Cdfrac%7Bdv%7D%7Bdt%7D%20=%20%5Cdfrac%7Bd%5E2s%7D%7Bdt%5E2%7D%0A%5Cend%7Bequation%7D%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5Cdfrac%7Bdv%7D%7Bdt%7D%20&amp;=%20a%20%5C%5C%0A%5Cint_%7Bt_0%7D%5E%7Bt%7D%7Bdv%7D%20&amp;=%20%5Cint_%7Bt_0%7D%5E%7Bt%7D%7Ba%20%5Ccdot%20dt%7D%20%5C%5C%0A%5Cleft%5Bv%5Cright%5D_%7Bt_0%7D%5E%7Bt%7D%20&amp;=%20a%5Cleft%5Bt%5Cright%5D_%7Bt_0%7D%5E%7Bt%7D%20%5C%5C%0Av%5Cleft(t%5Cright)%20-%20v%5Cleft(t_0%5Cright)%20&amp;=%20a%20%5Ccdot%20%5Cleft(t%20-%20t_0%5Cright)%20%5C%5C%0Av%5Cleft(t%5Cright)%20&amp;=%20v%5Cleft(t_0%5Cright)%20+%20a%20%5Ccdot%20%5Cleft(t%20-%20t_0%5Cright)%20%5C%5C%0A&amp;=%20%20v%5Cleft(t_0%5Cright)%20+%20at%20-%20at_0%0A%5Cend%7Bsplit%7D%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5Cdfrac%7Bds%7D%7Bdt%7D%20&amp;=%20v%20%5C%5C%0A%5Cint_%7Bt_0%7D%5E%7Bt%7D%7Bds%7D%20&amp;=%20%5Cint_%7Bt_0%7D%5E%7Bt%7D%7B%5Cleft(v%5Cleft(t_0%5Cright)%20+%20at%20-%20at_0%5Cright)%20%5Ccdot%20dt%7D%20%5C%5C%0A&amp;=%20%5Cint_%7Bt_0%7D%5E%7Bt%7D%7Bv%5Cleft(t_0%5Cright)%20%5Ccdot%20dt%20+%20at%20%5Ccdot%20dt%20-%20at_0%20%5Ccdot%20dt%7D%20%5C%5C%0A%5Cleft%5Bs%5Cright%5D_%7Bt_0%7D%5E%7Bt%7D%20&amp;=%20%5Cleft%5Bv%5Cleft(t_0%5Cright)%20%5Ccdot%20t%20+%20%5Cdfrac%7B1%7D%7B2%7Dat%5E2%20-%20at_0%20%5Ccdot%20t%5Cright%5D_%7Bt_0%7D%5E%7Bt%7D%20%5C%5C%0As%5Cleft(t%5Cright)%20-%20s%5Cleft(t_0%5Cright)%20&amp;=%20%20%5Cleft(v%5Cleft(t_0%5Cright)%20%5Ccdot%20t%20+%20%5Cdfrac%7B1%7D%7B2%7Dat%5E2%20-%20at_0%20%5Ccdot%20t%5Cright)%20-%20%20%5Cleft(v%5Cleft(t_0%5Cright)%20%5Ccdot%20t_0%20+%20%5Cdfrac%7B1%7D%7B2%7Da%7Bt_0%7D%5E2%20-%20at_0%20%5Ccdot%20t_0%5Cright)%5C%5C%0As%5Cleft(t%5Cright)%20&amp;=%20s%5Cleft(t_0%5Cright)%20+%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20t%20+%20%5Cdfrac%7B1%7D%7B2%7Dat%5E2%20-%20at_0%20%5Ccdot%20t%20-%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20t_0%20-%20%5Cdfrac%7B1%7D%7B2%7Da%7Bt_0%7D%5E2%20+%20a%7Bt_0%7D%5E2%20%5C%5C%0As%5Cleft(t%5Cright)%20&amp;=%20s%5Cleft(t_0%5Cright)%20+%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20t%20-%20%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20t_0%20+%20%5Cdfrac%7B1%7D%7B2%7Dat%5E2%20-%20at_0%20%5Ccdot%20t%20+%20%5Cdfrac%7B1%7D%7B2%7Da%7Bt_0%7D%5E2%20%5C%5C%0A&amp;=%20s%5Cleft(t_0%5Cright)%20+%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20%5Cleft(%20t%20-%20t_0%20%5Cright)%20+%20%5Cdfrac%7B1%7D%7B2%7Da%5Cleft(t%5E2%20-%202%20t_0%20%5Ccdot%20t%20+%20%7Bt_0%7D%5E2%5Cright)%20%5C%5C%0A&amp;=%20s%5Cleft(t_0%5Cright)%20+%20v%5Cleft(t_0%5Cright)%20%5Ccdot%20%5Cleft(%20t%20-%20t_0%20%5Cright)%20+%20%5Cdfrac%7B1%7D%7B2%7Da%5Cleft(t%20-%20%7Bt_0%7D%5Cright)%5E2%0A%5Cend%7Bsplit%7D%0A"></p>
<p>For convenience, we set <img src="https://latex.codecogs.com/png.latex?t_0%20=%200">.</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5Ctherefore%20s%5Cleft(t%5Cright)%20&amp;=%20s%5Cleft(0%5Cright)%20+%20v%5Cleft(0%5Cright)%20%5Ccdot%20%5Cleft(%20t%20-%200%20%5Cright)%20+%20%5Cdfrac%7B1%7D%7B2%7Da%5Cleft(t%20-%200%5Cright)%5E2%20%20%5C%5C%0A&amp;=%20s%5Cleft(0%5Cright)%20+%20v%5Cleft(0%5Cright)%20%5Ccdot%20%5Cleft(t%5Cright)%20+%20%5Cdfrac%7B1%7D%7B2%7Da%5Cleft(t%5Cright)%5E2%20%5C%5C%0A&amp;=%20s_0%20+%20v_0t%20+%20%5Cdfrac%7B1%7D%7B2%7Dat%5E2%0A%5Cend%7Bsplit%7D%0A"></p>
</div>
</div>
</div>
</section>
<section id="working-in-the-discretised-time-domain" class="level3" data-number="8.2.2">
<h3 data-number="8.2.2" class="anchored" data-anchor-id="working-in-the-discretised-time-domain"><span class="header-section-number">8.2.2</span> Working in the discretised time domain</h3>
<p>Physics calculations in video games are evaluated in discrete time, not continuous time as above.</p>
<p>When working with discrete time steps the continuous differential kinematic equation is instead represented by its discrete time analogue - a difference equation, as follows <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bn+1%7D%20&amp;=%20s_%7Bn%7D%20+%20v_%7Bn%7D%5CDelta%20t_n%20+%20%5Cdfrac%7B1%7D%7B2%7Da_%7Bn%7D%5Cleft(%5CDelta%20t_n%5Cright)%5E2%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5CDelta%20t_n%20&amp;:%20%5Ctext%7Btime%20step%7D%20%5C%5C%0As_%7Bn+1%7D%20&amp;:%20%5Ctext%7Bfinal%20position%20at%20step%20n+1%7D%20%5C%5C%0As_%7Bn%7D%20&amp;:%20%5Ctext%7Binitial%20position%20at%20step%20n%7D%20%5C%5C%0Av_%7Bn%7D%20&amp;%20:%20%5Ctext%7Binitial%20velocity%20at%20step%20n%7D%20%5C%5C%0Aa_%7Bn%7D%20&amp;%20:%20%5Ctext%7Bacceleration%20at%20step%20n%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Player movement in UE4 is server authoritative which means the server is the one and only source of truth. During each tick the server will factor all input forces to eventually calculate a players final location and final velocity for said tick. This information is then communicated to clients, which then further simulates the remote players movement until a subsequent update is received from the server.</p>
<p>Therefore, on the client, the difference kinematic equation is simplified to <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7Bn+1%7D%7D%20&amp;=%20s_%7Bc_%7Bn%7D%7D%20+%20v_%7Bc_%7Bn%7D%7D%20%5CDelta%20T_c%20+%20%7B%5Cepsilon%7D_n%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%7B%5Cepsilon%7D_n%20&amp;:%20%5Ctext%7Berror%20(due%20to%20smoothing,%20corrections,%20etc)%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>given</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%7D%20%5C%5C%0Av_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20velocity%20received%20from%20server%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>constrained by <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0An%20%5Ccdot%20%5CDelta%20T_c%20%3C%20%5CDelta%20T_s%0A%5Cend%7Bsplit%7D%0A"></p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This constraint conveys that the equation is only valid for a specific number of steps - until the client receives a new update from the server.</p>
</div>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>This equation performs simple extrapolation of a players position between receiving updates from the server. The rate at which these updates occur is simply equal to <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BTR%7D">. At a given step the position on the client will delayed by approximately <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BOWD%7D"> with respect to the actual position on the server for the same step.</p>
<p>Whatever process the server/client actually uses to calculate/predict a players location in UE4 is practically irrelevant. The aforementioned equations simply help to provide a simplistic high level model of what is happening under the hood.</p>
</div>
</div>
</section>
<section id="error-between-the-client-and-server" class="level3" data-number="8.2.3">
<h3 data-number="8.2.3" class="anchored" data-anchor-id="error-between-the-client-and-server"><span class="header-section-number">8.2.3</span> Error between the client and server</h3>
<p>The error between the client predicted location and actual server position is affected by both ping and the server tick rate.</p>
<p>This equation can be loosely modelled as</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5CDelta%20%5Ctext%7BError%7D_%7Bn%7D%20&amp;%5Capproxeq%20%7Bs%7D_%7B%7Bs%7D_%7Bn%7D%7D%20-%20%7Bs%7D_%7B%7Bc%7D_%7Bn%7D%7D%20%5C%5C%0A&amp;=%20%5Cunderbrace%7B%5Cleft(%20%7Bs%7D_%7B%7Bs%7D_%7B0%7D%7D%20+%20%5Csum_%7Bi%20=%201%7D%5E%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7B%5CDelta%20s_%7B%7Bs_i%7D%7D%7D%20%5Cright)%7D_%7B%7Bs%7D_%7B%7Bs%7D_%7Bn%7D%7D%7D%20-%20%5Cunderbrace%7B%5Cleft(%20%20%7Bs%7D_%7B%7Bs%7D_%7B0%7D%7D%20+%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%7B%5CDelta%20s_%7B%7Bc_i%7D%7D%7D%20%5Cright)%7D_%7B%7Bs%7D_%7B%7Bc%7D_%7Bn%7D%7D%7D%20%5C%5C%0A&amp;=%20%5Csum_%7Bi%20=%201%7D%5E%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7B%5CDelta%20s_%7B%7Bs_i%7D%7D%7D%20-%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%7B%5CDelta%20s_%7B%7Bc_i%7D%7D%7D%20%20%5C%5C%0A&amp;=%20%5Cunderbrace%7B%5Csum_%7Bi%20=%201%7D%5E%7BF%7D%7B%5CDelta%20s_%7B%7Bs_i%7D%7D%7D%7D_%7BS%7D%20-%20%20%5Cunderbrace%7B%5Csum_%7Bi=1%7D%5E%7Bn%7D%7B%5CDelta%20s_%7B%7Bc_i%7D%7D%7D%7D_%7BC_n%7D%20%5C%5C%0A&amp;=%20S%20-%20C_n%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0AF%20&amp;:%20%5Ctext%7Bnumber%20of%20server%20ticks%20elapsed%20since%20latest%20server%20update%20received%20by%20client%7D%20%5C%5C%0An%20&amp;:%20%5Ctext%7Bindex%20of%20client%20tick%20since%20latest%20server%20update%20received%20by%20client%7D%20%5C%5C%0As_%7B%7Bs%7D_0%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%20by%20client%7D%20%5C%5C%0Av_%7B%7Bc%7D_0%7D%20&amp;:%20%5Ctext%7Binitial%20velocity%20received%20from%20server%20by%20client%7D%20%5C%5C%0A%5CDelta%20s_%7B%7Bs_i%7D%7D%20&amp;%20:%20%5Ctext%7Bchange%20in%20server%20position%20during%20server%20tick%20$i$%7D%20%5C%5C%0A%5CDelta%20s_%7B%7Bc_i%7D%7D%20&amp;%20:%20%5Ctext%7Bchange%20in%20client%20position%20during%20client%20tick%20$i$%7D%20%5C%5C%0A%5C%5C%0AS%20&amp;:%20%5Ctext%7Btotal%20change%20in%20server%20position%20since%20latest%20server%20update%20received%20by%20client%7D%20%5C%5C%0AC_n%20&amp;:%20%5Ctext%7Btotal%20change%20in%20client%20position%20since%20latest%20server%20update%20received%20by%20client%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>constrained by</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0AN%20%5Ccdot%5CDelta%20T_c%20&amp;%20%3C%20%5CDelta%20T_s%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Using the client as the frame of reference, at <img src="https://latex.codecogs.com/png.latex?n%20=%200"> (the step the client receives an update from the server), the error is reduced to</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5CDelta%20%5Ctext%7BError%7D_%7B0%7D%20&amp;%5Capproxeq%20%20S%20-%20C_0%20%5C%5C%0A&amp;=%20S%20%5C%5C%0A&amp;=%20%5Csum_%7Bi%20=%201%7D%5E%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7B%5CDelta%20s_%7B%7Bs_i%7D%7D%7D%20%5C%5C%0A&amp;%5Capproxeq%20%5CDelta%20%7B%7Bs_1%7D%7D%20+%20%5CDelta%20%7B%7Bs_2%7D%7D%20+%20%5Ccdots%20+%20%5CDelta%20%7B%7Bs_%7B%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7D%7D%7D%20%5C%5C%0A&amp;%5Capproxeq%20v_1%5CDelta%20T_s%20+%20v_2%5CDelta%20T_s%20+%20%5Ccdots%20+%20v_%7B%7B%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7D%7D%5CDelta%20T_s%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>For the given step the client only has the information for <img src="https://latex.codecogs.com/png.latex?v_0"> and <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPing%7D">. Thus the only simple and realistic way to approximate this error in terms of what the client knows is</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5CDelta%20%5Ctext%7BError%7D_%7B0%7D%20&amp;%5Capproxeq%20v_1%5CDelta%20T_s%20+%20v_2%5CDelta%20T_s%20+%20%5Ccdots%20+%20v_%7B%7B%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D%7D%7D%5CDelta%20T_s%20%5C%5C%0A&amp;%5Capproxeq%20v_0%20%5Ccdot%20%5Ctext%7BOWD%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>If a client has a RTT ping of <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPing%7D"> with respect to the server, then said clients view of the world will be <img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B%5Ctext%7BPing%7D%7D%7B2%7D"> (i.e.&nbsp;<img src="https://latex.codecogs.com/png.latex?%5Ctext%7BOWD%7D">) seconds in the past.</p>
</div>
</div>
</section>
<section id="sec-min_of_error" class="level3" data-number="8.2.4">
<h3 data-number="8.2.4" class="anchored" data-anchor-id="sec-min_of_error"><span class="header-section-number">8.2.4</span> Minimisation of error (compensating for lag)</h3>
<p>We have formulated an expression for the approximate error using only information accessible to the client at the step at which it receives an update from the server. Now we can attempt to formulate an updated difference equation which minimises said error.</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%5CDelta%20%5Ctext%7BError%7D_%7B0%7D%20&amp;%5Capproxeq%20%20S%20-%20C_0%20%5C%5C%0A&amp;%5Capproxeq%20v_0%20%5Ccdot%20%5Ctext%7BOWD%7D%20%5C%5C%0A%5C%5C%0A%5CDelta%20%5Ctext%7BError%7D_%7B0%7D%20-%20%20v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D%20&amp;%5Capprox%20S%20-%20C_0%20-%20v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D%20%5C%5C%0A&amp;%5Capprox%200%5C%5C%0A%5C%5C%0A%5Ctherefore%20S%20-%20%5Cleft(C_0%20+%20v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D%20%5Cright)%20&amp;=%20S%20-%20%5Chat%7BC_0%7D%20%5C%5C%0A&amp;%5Capprox%200%5C%5C%0A&amp;=%20%5Chat%7B%5CDelta%20%5Ctext%7BError%7D_0%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Thus to attempt in minimising the error we can shift the players location by <img src="https://latex.codecogs.com/png.latex?v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D"> at the specified step.</p>
<p>The difference equation for the client can now be updated to <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7Bn+1%7D%7D%20&amp;=%20s_%7Bc_%7Bn%7D%7D%20+%20v_%7Bc_%7Bn%7D%7D%20%5CDelta%20T_c%20+%20%7B%5Cepsilon%7D_n%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%7B%5Cepsilon%7D_n%20&amp;:%20%5Ctext%7Berror%20(due%20to%20smoothing,%20corrections,%20etc)%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>given</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%7D%20+%20%20v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D%5C%5C%0Av_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20velocity%20received%20from%20server%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>constrained by <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0An%20%5Ccdot%20%5CDelta%20T_c%20%3C%20%5CDelta%20T_s%0A%5Cend%7Bsplit%7D%0A"></p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is the exact same equation as the original simply with a change in initial value for <img src="https://latex.codecogs.com/png.latex?s_%7Bc_%7B0%7D%7D">.</p>
</div>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>The thought process behind this form of lag compensation can be described as: “I know my client is <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BOWD%7D"> seconds in the past compared to server. To combat this, the client shall simply guess where other players will be <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BOWD%7D"> seconds in the future”.</p>
<p>It is extremely simple, naive and generally ineffective (as discussed further below).</p>
</div>
</div>
</section>
<section id="the-effects-of-ping-on-error" class="level3" data-number="8.2.5">
<h3 data-number="8.2.5" class="anchored" data-anchor-id="the-effects-of-ping-on-error"><span class="header-section-number">8.2.5</span> The effects of ping on error</h3>
<p>Clearly as a players ping increases, so does the total error between the client predicted location and the actual server position.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>The error is not linearly proportional to the ping.</p>
<p>As acceleration may be chaotic the resulting error may also be inherently chaotic. This will result in the client side error approximation to be inaccurate to an unacceptable degree.</p>
</div>
</div>
<p>This form of prediction is only useful (i.e.&nbsp;accurate to an acceptable degree) within a certain threshold of ping. This threshold is governed mainly by how fast players move and how often they receive changing movement inputs.</p>
<p>Therefore, as the pace of a video game increases, the effective threshold decreases.</p>
</section>
<section id="the-effects-of-server-tick-rate-on-error" class="level3" data-number="8.2.6">
<h3 data-number="8.2.6" class="anchored" data-anchor-id="the-effects-of-server-tick-rate-on-error"><span class="header-section-number">8.2.6</span> The effects of server tick rate on error</h3>
<p>Generally the effects of server tick rate on the total error are negligible. Increasing the server tick rate does not reduce errors in prediction due to ping, but can reduce error during position simulations on the client.</p>
</section>
<section id="when-does-error-minimisation-prediction-work" class="level3" data-number="8.2.7">
<h3 data-number="8.2.7" class="anchored" data-anchor-id="when-does-error-minimisation-prediction-work"><span class="header-section-number">8.2.7</span> When does error minimisation prediction work</h3>
<p>Obviously the error minimisation prediction derived above works to an acceptable degree (in regards to accuracy) if a player maintains constant velocity (i.e.&nbsp;zero acceleration) for <img src="https://latex.codecogs.com/png.latex?%7B%5Clfloor%20%5Ctext%7BOWD%7D%20%5Ccdot%20%5Cdfrac%7B1%7D%7B%5CDelta%20T_s%7D%20%5Crfloor%7D"> server ticks since the last server update on the client.</p>
<p>This can be further explained using plots - where the location is mapped to an arbitrary sinusoidal function (which has the properties of having both non-constant velocity and acceleration).</p>
<p>The plot below shows a players position both server side and client side (for varying amounts of RTT ping) when no lag compensation is applied.</p>
<div id="fig-position-no-lag" class="quarto-float quarto-figure quarto-figure-center anchored" width="100%">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-position-no-lag-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/png/3.png" id="fig-position-no-lag" class="img-fluid figure-img" style="width:100.0%">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig quarto-uncaptioned" id="fig-position-no-lag-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1
</figcaption>
</figure>
</div>
<p>As the ping increases, so too does the lag between the server side (real) player position and client side (local) player position.</p>
<p>Next, we apply the error minimisation prediction as our form of lag compensation.</p>
<div id="fig-position-with-lag" class="quarto-float quarto-figure quarto-figure-center anchored" width="100%">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-position-with-lag-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/png/4.png" id="fig-position-with-lag" class="img-fluid figure-img" style="width:100.0%">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig quarto-uncaptioned" id="fig-position-with-lag-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2
</figcaption>
</figure>
</div>
<p>The client side player position (predicted) is nowhere near accurate to the server side (real) player location when the acceleration is non-zero. Conversely, we can clearly see that the client side player position (predicted) is relatively accurate to the server side (real) player location when the acceleration is near zero.</p>
</section>
</section>
</section>
<section id="preparations-for-analysing-midairs-lag-compensation" class="level1" data-number="9">
<h1 data-number="9"><span class="header-section-number">9</span> Preparations for analysing Midairs lag compensation</h1>
<section id="server-hosting" class="level2" data-number="9.1">
<h2 data-number="9.1" class="anchored" data-anchor-id="server-hosting"><span class="header-section-number">9.1</span> Server hosting</h2>
<p>Any analysis performed must require an isolated Midair server for our client to connect to. As Midair never shipped with any server hosting tools, this functionality must be developed independently through reverse engineering and/or any other techniques.</p>
<p>This process is outside the scope of this investigation and will be omitted.</p>
</section>
<section id="server-side-player-position-recording-and-playback" class="level2" data-number="9.2">
<h2 data-number="9.2" class="anchored" data-anchor-id="server-side-player-position-recording-and-playback"><span class="header-section-number">9.2</span> Server side player position recording and playback</h2>
<p>To gather consistent and accurate data a server side mod must be developed to simulate player movement. Essentially, the player will move in a preset path so we can conduct our analysis accordingly.</p>
<p>This process is outside the scope of this investigation and will be omitted.</p>
</section>
<section id="identifying-the-existing-mechanisms-for-lag-compensation-in-the-midair-client" class="level2" data-number="9.3">
<h2 data-number="9.3" class="anchored" data-anchor-id="identifying-the-existing-mechanisms-for-lag-compensation-in-the-midair-client"><span class="header-section-number">9.3</span> Identifying the existing mechanisms for lag compensation in the Midair client</h2>
<p>The Midair client uses a console command to set the degree of lag compensation to perform. This console command is called <code>Predict</code>. It’s usage is documented as follows: <code>Predict &lt;value&gt;</code> (i.e.&nbsp;<code>Predict 160</code>).</p>
<p>Once player position playback is running server side, the <code>Predict</code> value can be varied to see the effects of lag compensated player position on the client.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>Predict</code> console command is <strong>NOT</strong> a part of UE4. This is a custom console command that was introduced by the Midair development team.</p>
<p>It’s logic and implementation is actually plagiarised from UT4. More on this will be covered later below.</p>
</div>
</div>
</section>
</section>
<section id="mathematical-analysis-of-midairs-lag-compensation" class="level1" data-number="10">
<h1 data-number="10"><span class="header-section-number">10</span> Mathematical analysis of Midairs lag compensation</h1>
<section id="replaying-a-set-path-with-varying-predict-values" class="level2" data-number="10.1">
<h2 data-number="10.1" class="anchored" data-anchor-id="replaying-a-set-path-with-varying-predict-values"><span class="header-section-number">10.1</span> Replaying a set path with varying Predict values</h2>
<p>Choosing an arbitrary axis (X in this case) and replaying a set path for varying <code>Predict</code> values produces the results as shown below. As the predict value increases we see the error decrease. A key thing to note however is that in this image the position appears to be almost linear, which is why this prediction <em>appears</em> to be working well.</p>
<div id="fig-predict-varying" class="quarto-float quarto-figure quarto-figure-center anchored" width="100%">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-predict-varying-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/png/0.png" id="fig-predict-varying" class="img-fluid figure-img" style="width:100.0%">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig quarto-uncaptioned" id="fig-predict-varying-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3
</figcaption>
</figure>
</div>
</section>
<section id="setting-up-for-a-linear-regression" class="level2" data-number="10.2">
<h2 data-number="10.2" class="anchored" data-anchor-id="setting-up-for-a-linear-regression"><span class="header-section-number">10.2</span> Setting up for a linear regression</h2>
<p>Assume that the predicted position can be expressed as a linear function of the initial position and initial velocity multiplied by a scalar related to <code>Predict</code> (<img src="https://latex.codecogs.com/png.latex?K">).</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0AL_%7Bx_%7Bt_%7Bp%7D%7D%7D%20&amp;=%20L_%7Bx_%7Bt_%7Bc%7D%7D%7D%20+%20K%20%5Ccdot%20V_%7Bx_%7Bt_%7Bc%7D%7D%7D%20%5Ccdot%20%5CDelta%20t%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0AL_%7Bx_%7Bt_%7Bp%7D%7D%7D%20&amp;:%20%5Ctext%7Bpredicted%20position%20at%20t%7D%20%5C%5C%0AL_%7Bx_%7Bt_%7Bc%7D%7D%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%20by%20client%7D%20%5C%5C%0AV_%7Bx_%7Bt_%7Bc%7D%7D%7D%20&amp;:%20%5Ctext%7Binitial%20velocity%20received%20from%20server%20by%20client%7D%20%5C%5C%0AK%20&amp;:%20%5Ctext%7BPredict%20value%20multiplied%20by%20a%20scalar%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Rearrange the equation as follows</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0AL_%7Bx_%7Bt_%7Bp%7D%7D%7D%20&amp;=%20L_%7Bx_%7Bt_%7Bc%7D%7D%7D%20+%20K%20%5Ccdot%20V_%7Bx_%7Bt_%7Bc%7D%7D%7D%20%5Ccdot%20%5CDelta%20t%20%5C%5C%0A%5Cdfrac%7B%5Cleft(L_%7Bx_%7Bt_%7Bp%7D%7D%7D%20-%20L_%7Bx_%7Bt_%7Bc%7D%7D%7D%5Cright)%7D%7B%5CDelta%20t%7D%20&amp;=%20K%20%5Ccdot%20V_%7Bx_%7Bt_%7Bc%7D%7D%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Let <img src="https://latex.codecogs.com/png.latex?%5CDelta%20t%20=%20%5Cdfrac%7B1%7D%7B1000%7D%20=%200.001"> so our velocity scalar (<img src="https://latex.codecogs.com/png.latex?K">) will be computed in milliseconds.</p>
<p>A plot of this equation is provided below.</p>
<div id="fig-velocity-scalar" class="quarto-float quarto-figure quarto-figure-center anchored" width="100%">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-velocity-scalar-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/png/1.png" id="fig-velocity-scalar" class="img-fluid figure-img" style="width:100.0%">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig quarto-uncaptioned" id="fig-velocity-scalar-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4
</figcaption>
</figure>
</div>
</section>
<section id="performing-a-linear-regression" class="level2" data-number="10.3">
<h2 data-number="10.3" class="anchored" data-anchor-id="performing-a-linear-regression"><span class="header-section-number">10.3</span> Performing a linear regression</h2>
<p>Simply apply the OLS (Ordinary Least Squares) method for linear regression.</p>
<div id="fig-regression" class="quarto-float quarto-figure quarto-figure-center anchored" width="100%">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-regression-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/png/2.png" id="fig-regression" class="img-fluid figure-img" style="width:100.0%">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig quarto-uncaptioned" id="fig-regression-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;5
</figcaption>
</figure>
</div>
<p>Representing the results above in tabular form we can see that <img src="https://latex.codecogs.com/png.latex?K%20%5Capprox%20%5Cdfrac%7B%5Ctext%7BPredict%7D%7D%7B2%7D">.</p>
<div id="tbl-regression-results" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl quarto-uncaptioned" id="tbl-regression-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Table&nbsp;1
</figcaption>
<div aria-describedby="tbl-regression-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Predict value</th>
<th>Regression slope (<img src="https://latex.codecogs.com/png.latex?K">)</th>
<th>Regression slope <img src="https://latex.codecogs.com/png.latex?%5Cdiv"> Predict value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>40</td>
<td>19.95</td>
<td>0.50</td>
</tr>
<tr class="even">
<td>80</td>
<td>40.65</td>
<td>0.51</td>
</tr>
<tr class="odd">
<td>120</td>
<td>64.66</td>
<td>0.54</td>
</tr>
<tr class="even">
<td>160</td>
<td>79.09</td>
<td>0.49</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>The difference equation for the client can now be expressed as <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7Bn+1%7D%7D%20&amp;=%20s_%7Bc_%7Bn%7D%7D%20+%20v_%7Bc_%7Bn%7D%7D%20%5CDelta%20T_c%20+%20%7B%5Cepsilon%7D_n%0A%5Cend%7Bsplit%7D%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0A%7B%5Cepsilon%7D_n%20&amp;:%20%5Ctext%7Berror%20(due%20to%20smoothing,%20corrections,%20etc)%7D%0A%5Cend%7Bsplit%7D%0A"></p>
<p>given</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%7D%20+%20v_0%5Cleft(%5Cdfrac%7B1%7D%7B2%7D%5Ccdot%5Cdfrac%7B%5Ctext%7BPredict%7D%7D%7B1000%7D%5Cright)%5C%5C%0Av_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20velocity%20received%20from%20server%7D%20%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<p>constrained by <img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0An%20%5Ccdot%20%5CDelta%20T_c%20%3C%20%5CDelta%20T_s%0A%5Cend%7Bsplit%7D%0A"></p>
<p>Now, for the case where <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPing%7D%20%5Ccdot%201000%20%5Cleq%20%5Ctext%7BPredict%7D">,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Bsplit%7D%0As_%7Bc_%7B0%7D%7D%20&amp;:%20%5Ctext%7Binitial%20position%20received%20from%20server%7D%20+%20v_0%20%5Ccdot%20%20%5Ctext%7BOWD%7D%5C%5C%0A%5Cend%7Bsplit%7D%0A"></p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is the exact same equation as what was derived in the final part of Section&nbsp;8.2.4!</p>
<p>Thus conclusively proving that Midairs lag compensation is simply using the kinematic equations.</p>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>Predict</code> command sets the prediction limit in milliseconds.</p>
</div>
</div>
</section>
</section>
<section id="code-analysis-of-midairs-lag-compensation-and-netcode" class="level1" data-number="11">
<h1 data-number="11"><span class="header-section-number">11</span> Code analysis of Midairs lag compensation and netcode</h1>
<p>I won’t go too much into this because if you don’t understand code then this won’t make much sense to you… and if you do understand code then this is pretty simple and straight forward.</p>
<p>The folks at Vector Z Studios were kind enough to ship the earliest release of Midair: Community Edition with a PDB (Program Database) file.</p>
<p>I’ll use the PDB in combination with the Midair: Community Edition client in Ghidra to find certain functionality related to lag compensation and netcode. Only functions will be listed, as providing class definitions and their comparisons will be too time consuming.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>I’ve linked relevant sections of code from the official UT4 source code that are associated with the code identified in Midair.</p>
</div>
</div>
<section id="analysing-lag-compensation" class="level2" data-number="11.1">
<h2 data-number="11.1" class="anchored" data-anchor-id="analysing-lag-compensation"><span class="header-section-number">11.1</span> Analysing lag compensation</h2>
<section id="getpredictiontime" class="level3" data-number="11.1.1">
<h3 data-number="11.1.1" class="anchored" data-anchor-id="getpredictiontime"><span class="header-section-number">11.1.1</span> GetPredictionTime</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb1-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> __thiscall AMAPlayerController<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>GetPredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>AMAPlayerController <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-2"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb1-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> Ping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-4">  ENetMode EVar2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> PredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-6">  </span>
<span id="cb1-7">  EVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> AActor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>InternalGetNetMode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">((</span>AActor <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*)</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb1-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">((</span>EVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> NM_Standalone<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>_padding_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb1-9">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb1-10">    Ping <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>SmoothedRTT<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-11">    PredictionTime <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">((</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> Ping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>PredictionTime <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MaxPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.001</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Ping <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> PredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb1-13">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb1-14">      PredictionTime <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Ping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-15">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb1-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> PredictionTime <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-17">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb1-18">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-19"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>Returns <img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B2%7D%5Ctext%7BPing%7D"> if <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPing%7D%20%3C%20%5Cdfrac%7B%5Ctext%7BPredict%7D%7D%7B1000%7D"> else <img src="https://latex.codecogs.com/png.latex?%5Cdfrac%7B1%7D%7B2%7D%20%5Cdfrac%7B%5Ctext%7BPredict%7D%7D%7B1000%7D">. <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BPredict%7D"> in this case refers to the <code>MaxPredictionPing</code> variable inside the <code>AMAPlayerController</code> class.</p>
<p>The UT4 equivalent function is <a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTPlayerController.cpp#L281">float AUTPlayerController::GetPredictionTime</a>.</p>
<p>The logic is <em>practically</em> identical.</p>
<p><strong>Note that the function and variable names have been kept identical.</strong></p>
</section>
<section id="servernegotiatepredictionping_implementation" class="level3" data-number="11.1.2">
<h3 data-number="11.1.2" class="anchored" data-anchor-id="servernegotiatepredictionping_implementation"><span class="header-section-number">11.1.2</span> ServerNegotiatePredictionPing_Implementation</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb2-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">void</span> __thiscall AMAPlayerController<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>ServerNegotiatePredictionPing_Implementation <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>AMAPlayerController <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> NewPredictionPing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-2"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> MaxPredictionPing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-4">  </span>
<span id="cb2-5">  pUVar1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> UMAGameEngine<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>GetPrivateStaticClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">();</span></span>
<span id="cb2-6">  pUVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pUVar1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>ClassDefaultObject<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>pUVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> NULL<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-8">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-9">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(**(</span>code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**)(</span>pUVar1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>_padding_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x358</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))(</span>pUVar1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb2-10">    pUVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pUVar1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>ClassDefaultObject<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-11">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb2-12">  MaxPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-13">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">((</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> NewPredictionPing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>MaxPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*(</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*)(</span>pUVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x5b</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> NewPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*(</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*)(</span>pUVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x5b</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)))</span></span>
<span id="cb2-14">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-15">    MaxPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> NewPredictionPing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-16">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb2-17">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MaxPredictionPing <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> MaxPredictionPing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-18">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-19"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>The implementation of the <code>Predict</code> console command.</p>
<p>The UT4 equivalent function is <a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTPlayerController.cpp#L264">void AUTPlayerController::ServerNegotiatePredictionPing_Implementation</a>.</p>
<p>The logic is <em>practically</em> identical.</p>
<p><strong>Note that the function and variable names have been kept identical.</strong></p>
</section>
<section id="maupdatesimulatedposition" class="level3" data-number="11.1.3">
<h3 data-number="11.1.3" class="anchored" data-anchor-id="maupdatesimulatedposition"><span class="header-section-number">11.1.3</span> MAUpdateSimulatedPosition</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb3-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">void</span> __thiscall AMACharacter<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>MAUpdateSimulatedPosition<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>AMACharacter <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span>FVector <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>param_1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span>FRotator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>param_2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-2"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">float</span> PredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb3-4">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb3-5"></span>
<span id="cb3-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MACharacterMovement <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> NULL<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-7">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-8">      pbVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>byte <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*)((</span>longlong<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)&amp;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MACharacterMovement<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>_padding_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb3-9">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>pbVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>pbVar2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|</span> <span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x20</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb3-10">      PredictionTime <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> UMAGameplayStatics<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>GetCatchUpTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">((</span>AActor <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*)</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Same as calling GetPredictionTime</span></span>
<span id="cb3-11">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> PredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-12">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-13">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(**(</span>code <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**)(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MACharacterMovement<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>_padding_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x910</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Same as calling UMACharacterMovement::SimulateMovement</span></span>
<span id="cb3-14">                  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">this</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span>MACharacterMovement<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span>PredictionTime<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb3-15">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-16">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-17">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb3-18"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>Performs the prediction of player position.</p>
<p>The UT4 equivalent function is <a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTCharacter.cpp#L6135">void AUTCharacter::UTUpdateSimulatedPosition</a>.</p>
<p>The logic is <em>practically</em> identical.</p>
<p><strong>Note that the function and variable names are almost identical. The prefix ‘UT’ has been replaced by ‘MA’.</strong></p>
</section>
<section id="other-functions" class="level3" data-number="11.1.4">
<h3 data-number="11.1.4" class="anchored" data-anchor-id="other-functions"><span class="header-section-number">11.1.4</span> Other functions</h3>
<p>Listing all the functions used for lag compensation, their code, what they do, and finding their equivalents from UT4 would be overly exhausting.</p>
<p>Instead I’ll provide a table of function names from Midair and their equivalent function from UT4.</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>Midair function</th>
<th>UT4 function</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>AMAProjectile::BeginFakeProjectileSync</td>
<td><a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTProjectile.cpp#L372">AUTProjectile::BeginFakeProjectileSynch</a></td>
</tr>
<tr class="even">
<td>AMAProjectile::InitFakeProjectile</td>
<td><a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTProjectile.cpp#L774">AUTProjectile::InitFakeProjectile</a></td>
</tr>
<tr class="odd">
<td>AMAProjectile::CatchUp</td>
<td><a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTProjectile.cpp#L364">AUTProjectile::CatchUpTick</a></td>
</tr>
<tr class="even">
<td>AMAPlayerController::Predict</td>
<td><a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTPlayerController.cpp#L274">AUTPlayerController::Predict</a></td>
</tr>
<tr class="odd">
<td>AMAPlayerController::ServerNegotiatePredictionPing_Validate</td>
<td><a href="https://github.com/karan283861/unrealTournament/blob/cac883d9143e72d21c046bfa4a0915b3ad533dad/UnrealTournament/Source/UnrealTournament/Private/UTPlayerController.cpp#L269">AUTPlayerController::ServerNegotiatePredictionPing_Validate</a></td>
</tr>
</tbody>
</table>
<p>The list goes on and on. The logic used in the Midair functions and their equivalent UT4 function is <em>practically</em> identical.</p>
<p><strong>Note that the function names are almost identical.</strong></p>
</section>
<section id="findings" class="level3" data-number="11.1.5">
<h3 data-number="11.1.5" class="anchored" data-anchor-id="findings"><span class="header-section-number">11.1.5</span> Findings</h3>
<p>Midairs lag compensation system is clearly copied from UT4.</p>
<p>In fact, Mabel loosely admitted to it in a reddit post as quoted below.</p>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>For other players, we basically decide to ‘split the difference’ to hide latency. You predict other players ahead by about 1/2 your RTT, and when the server sees you fire things, it basically ‘fast forwards’ your projectiles from their predicted point in time (1/2 RTT ago, basically). This is done to mitigate the effect of player movement misprediction errors, hiding some of the latency on the receiving end of projectiles.</p>
</blockquote>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>…</p>
</blockquote>
</blockquote>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>This is essentially the same model that Reflex and UT4 use. We’re able to use prediction because we don’t have very precise hitboxes, we basically just have big capsules jumping around and you really don’t need pixel-perfect accuracy to hit individual hitboxes. If we did, we’d have to use a model more like Overwatch as we simply couldn’t deal with misprediction errors that bad. If you’ve ever played OW with &gt;200 ping (which is around where it starts doing prediction) you can see how fast the hitreg starts failing when the game starts using prediction.</p>
</blockquote>
<p>- <a href="https://www.reddit.com/r/Games/comments/4iecnd/comment/d2y7ti6/"><strong>Mabeline</strong></a> (Midair developer) on May 9, 2016</p>
</blockquote>
<p>The UT4 repository <a href="https://github.com/Rukgul/unrealTournament51/blob/clean-master/LICENSE.pdf">license</a> is quite clear about the usage of its assets from the repository.</p>
</section>
</section>
<section id="analysing-netcode" class="level2" data-number="11.2">
<h2 data-number="11.2" class="anchored" data-anchor-id="analysing-netcode"><span class="header-section-number">11.2</span> Analysing netcode</h2>
<p>I won’t waste my time going into a deep explanation of my findings regarding Midairs “custom tailored” netcode. Midair uses stock UE4 netcode. If there were any modifications made to the netcode, their impact is practically negligible. In fact, contextually, Midair did not require any modifications to the netcode in the first place to perform at an acceptable benchmark.</p>
<p>A quote from an ex-Midair 2 developer confirms this.</p>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>netcode is mostly just stock ue netcode iirc</p>
</blockquote>
<p>- <a href="https://discord.com/channels/1118630610961960970/1241076254057304145/1401068949256278077"><strong>Implosions</strong></a> (Ex-Midair 2 developer) on August 2, 2025</p>
</blockquote>
<p>All videos I could find involving Archetype Studios developers which mentioned the application of their custom netcode actually referred to their lag compensation system. As mentioned in Section&nbsp;8.1, these two terms are <strong>NOT</strong> interchangeable.</p>
</section>
</section>
<section id="how-does-midairs-lag-compensation-work" class="level1" data-number="12">
<h1 data-number="12"><span class="header-section-number">12</span> How does Midairs lag compensation work?</h1>
<section id="prediction" class="level2" data-number="12.1">
<h2 data-number="12.1" class="anchored" data-anchor-id="prediction"><span class="header-section-number">12.1</span> Prediction</h2>
<p>Already covered fairly extensively above.</p>
</section>
<section id="fake-projectiles" class="level2" data-number="12.2">
<h2 data-number="12.2" class="anchored" data-anchor-id="fake-projectiles"><span class="header-section-number">12.2</span> Fake projectiles</h2>
<p>Fake projectiles (also called client-side predicted projectiles) are visual-only representations of projectiles spawned immediately on the shooter’s client when they fire, before the server confirms the shot.</p>
<p>The server runs its own authoritative simulation of the projectile separately. The client’s projectile is “fake” because it doesn’t determine hit results - it just exists to mask latency.</p>
<section id="how-it-works" class="level3" data-number="12.2.1">
<h3 data-number="12.2.1" class="anchored" data-anchor-id="how-it-works"><span class="header-section-number">12.2.1</span> How it works</h3>
<ol type="1">
<li>Player fires and the client instantly spawns a fake projectile moving towards the target</li>
<li>The input is sent to the server, which spawns the real projectile and simulates it authoritatively</li>
<li>When the server result arrives, the fake projectile is either destroyed quietly or reconciled with the real one (the latter in the case of Midair)</li>
</ol>
</section>
</section>
<section id="how-good-is-midairs-lag-compensation" class="level2" data-number="12.3">
<h2 data-number="12.3" class="anchored" data-anchor-id="how-good-is-midairs-lag-compensation"><span class="header-section-number">12.3</span> How good is Midairs lag compensation?</h2>
<p>At low pings (30ms or under), it’s fine. As the ping increases, the game state on the client becomes more and more inaccurate. The use of fake projectiles mask the perceived latency resulting in a placebo effect regarding the efficacy of the lag compensation.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>It’s a simple trade off - a responsive game client side in return for an inaccurate game state.</p>
<p><strong>The game lies to you about its real states to hide latency and feel responsive. As ping increases, the game remains responsive, however, the lie becomes bigger.</strong> Sometimes, the lie just happens to be accurate, and you manage to successfully land shots. However, majority of the time, even if you aim perfectly for your client game state, your shot will not land.</p>
</div>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>So, does the lag compensation make 150ms of ping feel like zero? Does it provide a seamless cross-continent experience that trumps all others?
</div>
</div>
<div class="callout-body-container callout-body">
<p>No.</p>
<p>It would probably pass for UT4, but, Midair isn’t UT4.</p>
<p>In fact, there’s a few glaring issues with this lag compensation implementation, essentially leaving it incomplete. One major issue being the amount of leading required remains a function of ping, which defeats the purpose of lag compensation in the first place.</p>
</div>
</div>
</section>
<section id="how-does-it-compare-to-tribes-titles" class="level2" data-number="12.4">
<h2 data-number="12.4" class="anchored" data-anchor-id="how-does-it-compare-to-tribes-titles"><span class="header-section-number">12.4</span> How does it compare to Tribes titles</h2>
<p>It’s basically the same functionality as PFT (Predict Forward Time) from Starsiege: Tribes and Interpolate from Tribes 2, just a smoother implementation (the player models do not teleport/warp as much during prediction thanks to what’s happening under the hood in UE4). This smoother implementation comes at the obvious cost of updating large positional changes slowly, which results in (unnoticeable) desync.</p>
<p>Tribes: Ascend has no form of lag compensation.</p>
<p>Tribes 3: Rivals uses a similar but more complete implementation that provides better overall results with the occasional non-reg at higher pings (again, due to unnoticeable desync).</p>
</section>
</section>
<section id="what-alternatives-are-there" class="level1" data-number="13">
<h1 data-number="13"><span class="header-section-number">13</span> What alternatives are there?</h1>
<section id="server-side-rewind" class="level2" data-number="13.1">
<h2 data-number="13.1" class="anchored" data-anchor-id="server-side-rewind"><span class="header-section-number">13.1</span> Server-side rewind</h2>
<p>Server-side rewind (also called lag compensation via history buffers) is the industry standard approach. Rather than guessing where a player will be in the future on the client, the server maintains a rolling history of every player’s position over recent time. When a client fires a shot and the server receives that input, the server rewinds world state back to the moment the client fired (accounting for ping), evaluates the hit against that historical snapshot, and then restores the present state.</p>
<section id="why-it-is-strictly-better" class="level3" data-number="13.1.1">
<h3 data-number="13.1.1" class="anchored" data-anchor-id="why-it-is-strictly-better"><span class="header-section-number">13.1.1</span> Why it is strictly better</h3>
<ul>
<li><strong>Accuracy is not a function of acceleration</strong>: because the server replays recorded positions rather than extrapolating from velocity, the result is accurate regardless of whether the target was accelerating, decelerating, or changing direction at the time of the shot.</li>
<li><strong>The shooter hits what they aim at</strong>: from the shooter’s perspective, if they aim correctly at the target visible on their screen at the moment of firing, the shot registers. This is not guaranteed under Midairs model.</li>
<li><strong>The trade-off is shifted, not eliminated:</strong> the receiver of a shot may feel they were hit “around a corner” - a well-known side effect. However, this is a far more acceptable and consistent artefact than shots simply failing to register.</li>
</ul>
</section>
<section id="implementing-server-side-rewind-in-midair" class="level3" data-number="13.1.2">
<h3 data-number="13.1.2" class="anchored" data-anchor-id="implementing-server-side-rewind-in-midair"><span class="header-section-number">13.1.2</span> Implementing server-side rewind in Midair</h3>
<p>An example of a server-side rewind implementation in Midair is provided below.</p>
<p>This process is outside the scope of this investigation and will be omitted.</p>
<div class="quarto-video ratio ratio-16x9"><iframe data-external="1" src="https://www.youtube.com/embed/KqzfK8Jg51s" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>
</section>
</section>
</section>
<section id="conclusion" class="level1" data-number="14">
<h1 data-number="14"><span class="header-section-number">14</span> Conclusion</h1>
<p>The findings of this investigation are unambiguous.</p>
<p>Mabel, who was directly and publicly credited as the architect of these systems, repeatedly and demonstrably misrepresented the nature of the work. The broader community of developers, ex-developers, and prominent players who amplified these claims - many of whom are quoted in this article - either lacked the technical understanding to scrutinise them or chose not to. Either way, the result was that a paying community was misled for years about the quality of a product they funded.</p>
<p>The technical reality is straightforward: Midair shipped a game with stock UE4 netcode and plagiarised, rudimentary lag compensation, wrapped in marketing language designed to sound impressive to an audience unlikely to investigate further. The claims were not just overstated - they were false.</p>
<section id="how-did-this-happen" class="level2" data-number="14.1">
<h2 data-number="14.1" class="anchored" data-anchor-id="how-did-this-happen"><span class="header-section-number">14.1</span> How did this happen?</h2>
<p>The straightforward answer is cronyism - and it is almost inevitable in communities built around dead or dying franchises.</p>
<p>In a healthy consumer relationship, there is distance between the developer and the audience. Critics can be critical. Players can be skeptical. A bad product receives honest feedback. In a community-developed successor project, that distance does not exist. Criticising the game means criticising your friends in a community you depend on for social belonging. Skepticism about the netcode means implicitly questioning Mabel, who everyone knows personally and who is generally liked and hailed as a “genius”. The social cost of honest scrutiny is high, and the reward is low - so almost nobody does it.</p>
<p>This dynamic allowed a simple pattern to persist for years: one person made a technically impressive-sounding claim, people who trusted that person repeated it, and the repetition created the appearance of consensus.</p>
<p>In Midair 2, of which the development includes several people who were part of or adjacent to the original Archetype Studios circle, repeated these same claims years after the original game had shut down . This is not coincidental. The same cronyist dynamic that shielded the original claims from scrutiny carried them forward intact into the successor project, where they continue to circulate today.</p>
<p>It is worth being clear: this is not a phenomenon unique to Midair. It is the default mode of operation for community-developed successors to niche dead franchises. The Tribes community is simply a particularly well-documented example of it.</p>
</section>
</section>
<section id="response" class="level1" data-number="15">
<h1 data-number="15"><span class="header-section-number">15</span> Response</h1>
<section id="midair-community" class="level2" data-number="15.1">
<h2 data-number="15.1" class="anchored" data-anchor-id="midair-community"><span class="header-section-number">15.1</span> Midair community</h2>
<p>Unsurprisingly, the response from the Midair community has been overwhelmingly negative towards the findings this article. I won’t bother to include quotes of the numerous cringe inducing responses because the authors are clearly deranged.</p>
<p>It seems Mabel slander will not be tolerated… despite the fact he deliberately misled a community that trusted him. Or, perhaps they just can’t handle the fact the game they cherish so much is a dead, technically mediocre product built on borrowed code and propped up by years of unchallenged myth.</p>
</section>
<section id="vector-z-studios" class="level2" data-number="15.2">
<h2 data-number="15.2" class="anchored" data-anchor-id="vector-z-studios"><span class="header-section-number">15.2</span> Vector Z Studios</h2>
<ol type="1">
<li>Midair was <a href="https://store.steampowered.com/news/app/439370/view/628943104979764954">retired</a> from the steam store weeks after the posting of this article</li>
<li>The development team has begun <a href="https://discord.com/channels/681439528292057097/681443115756027944/1456936953546276894">implementing their own attempt at server-side rewind solution</a> for Midair 2</li>
</ol>
</section>
</section>
<section id="other-findings" class="level1" data-number="16">
<h1 data-number="16"><span class="header-section-number">16</span> Other findings</h1>
<section id="failure-to-deliver-kickstarter-goals" class="level2" data-number="16.1">
<h2 data-number="16.1" class="anchored" data-anchor-id="failure-to-deliver-kickstarter-goals"><span class="header-section-number">16.1</span> Failure to deliver Kickstarter goals</h2>
<p>As mentioned in Section&nbsp;5, Midair was initially funded using Kickstarter, which listed “Custom Tailored Networking” as one it’s features.</p>
<p>Now that the game has released and been abandoned, let’s take a look at the list of features the Kickstarter promised, and which (if any) features were delivered.</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>Feature</th>
<th>Delivered</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Free to Play</td>
<td>⚠️</td>
<td><em>Technically</em> it was free to play. However the game required <a href="https://steamcommunity.com/id/Ryotaiku/recommended/439370/">either paying or excessive grinding to be playable</a>, resulting in early and almost instantaneous death on public release</td>
</tr>
<tr class="even">
<td>Maps &amp; Map Making</td>
<td>❌</td>
<td></td>
</tr>
<tr class="odd">
<td>Stats &amp; Rankings</td>
<td>❌</td>
<td></td>
</tr>
<tr class="even">
<td>Achievements</td>
<td>⚠️</td>
<td>No idea if this was actually implemented</td>
</tr>
<tr class="odd">
<td>Game Replays</td>
<td>❌</td>
<td></td>
</tr>
<tr class="even">
<td>Custom Tailored Networking</td>
<td>❌</td>
<td>As discussed by this article, this is provably false</td>
</tr>
</tbody>
</table>
<p>Archetype Studios raised $128,416 via Kickstarter and didn’t really seem to deliver on anything they said they would.</p>
</section>
<section id="over-500000-of-combined-sales-and-funding" class="level2" data-number="16.2">
<h2 data-number="16.2" class="anchored" data-anchor-id="over-500000-of-combined-sales-and-funding"><span class="header-section-number">16.2</span> Over $500,000 of combined sales and funding</h2>
<p>A quote from the <a href="https://www.mobygames.com/person/435908/allen-kuceba/">Co-Founder/CFO/COO of Archetype Studios</a> LinkedIn page is presented below.</p>
<blockquote class="blockquote">
<blockquote class="blockquote">
<p>// Co-founded an independent game studio of 23 volunteers/paid employees; primary role in finance and business operations, including Marketing, Legal, and HR. Combined sales and funding over $500,000.</p>
</blockquote>
<p><a href="https://www.linkedin.com/in/allenkuceba/"><strong>Allen Kuceba</strong></a> (Co-Founder/CFO/COO of Archetype Studios)</p>
</blockquote>
<p>An internal source to Archetype Studios has alluded to management closing the Kickstarter early once they had raised a large enough sum of funding <strong>and</strong> obtained external funding. This raises serious questions about the integrity of the campaign considering they could barely accomplish a single Kickstarter goal let alone even ship a functional product.</p>


</section>
</section>

 ]]></description>
  <category>reverse engineering</category>
  <category>math</category>
  <category>c++</category>
  <guid>https://karan.codes/posts/analysing-midair-netcode-lag-compenstion/</guid>
  <pubDate>Fri, 07 Nov 2025 13:00:00 GMT</pubDate>
</item>
</channel>
</rss>
