<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Techytechster's Blog]]></title><description><![CDATA[Techytechster's Blog]]></description><link>https://blog.techytechster.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 00:19:06 GMT</lastBuildDate><atom:link href="https://blog.techytechster.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Basic Networked Godot 3D Game in C++ (GDExtension)]]></title><description><![CDATA[Pretext
When writing a game in Godot you have a few options in terms of coding languages.(Very simplified):You can write in GDScript (First class support)You can write in C# (Somewhat first class support)You can write in C++ GDExtension (Essentially ...]]></description><link>https://blog.techytechster.com/a-basic-networked-godot-3d-game-in-c-gdextension</link><guid isPermaLink="true">https://blog.techytechster.com/a-basic-networked-godot-3d-game-in-c-gdextension</guid><category><![CDATA[Godot]]></category><category><![CDATA[C++]]></category><category><![CDATA[gdextension]]></category><dc:creator><![CDATA[Jonathan Wright]]></dc:creator><pubDate>Sat, 03 Jan 2026 11:15:57 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-pretext">Pretext</h2>
<p>When writing a game in Godot you have a few options in terms of coding languages.<br />(Very simplified):<br /><a target="_blank" href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html">You can write in GDScript (First class support)</a><br /><a target="_blank" href="https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/index.html">You can write in C# (Somewhat first class support)</a><br /><a target="_blank" href="https://docs.godotengine.org/en/stable/tutorials/scripting/cpp/index.html">You can write in C++ GDExtension (Essentially a dll you inject into godot to load)</a><br /><a target="_blank" href="https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/index.html">And you can write in third party languages like Rust in GDExtension (Also a dll I assume)</a><br /><a target="_blank" href="https://docs.godotengine.org/en/4.4/contributing/development/core_and_modules/custom_modules_in_cpp.html">You can write in C++ modules (I’m not sure how this works)</a></p>
<p>If you are coming from an Unreal Engine background or even lower level you may be used to C++ and want to stick with C++, unfortunately the documentation for ‘getting started’ in C++ Godot is a bit confusing, this guide is just a writeup of what I did to get a basic ‘starter’ template that is essentially the boilerplate for a Roblox like movement 3D game with networking.</p>
<h2 id="heading-getting-a-development-environment-setup">Getting A Development Environment Setup</h2>
<p>If you’re expecting to be able to setup Rider (or Visual Studio) easily, I found it quite hard, I am sure you can find someone who has written up a third-party approach to getting it working but officially it is not supported.</p>
<p>If you need more help - <a target="_blank" href="https://docs.godotengine.org/en/4.4/tutorials/scripting/gdextension/gdextension_cpp_example.html">GDExtension C++ example — Godot Engine (4.4) documentation in English</a> is fairly good for the basics</p>
<h3 id="heading-end-goal-project-structurehttpsdocsgodotengineorgen44tutorialsscriptinggdextensiongdextensioncppexamplehtml"><a target="_blank" href="https://docs.godotengine.org/en/4.4/tutorials/scripting/gdextension/gdextension_cpp_example.html">End Goal - Project Structure</a></h3>
<p><a target="_blank" href="https://docs.godotengine.org/en/4.4/tutorials/scripting/gdextension/gdextension_cpp_example.html">Project structure wise we are going to</a> end up looking like this.</p>
<pre><code class="lang-plaintext">-demo =&gt; your test godot game to essentially treat as staging grounds
-src =&gt; your cpp source code for the extension
-godot =&gt; the engine for godot (optional but recommended) =&gt; git submodule
-godot-cpp =&gt; the official c++ gdextension binding =&gt; git submodule
-.vscode =&gt; configurations to make vscode build test easier
</code></pre>
<h3 id="heading-step-1-create-and-clone-relevant-folders">Step 1 - Create and clone relevant folders</h3>
<p>Before cloning on anything decide on a godot version, i chose 4.5 so replace &lt;desired_branch_version&gt; with 4.5</p>
<pre><code class="lang-plaintext">mkdir &lt;godot_project_foldername&gt;
cd &lt;godot_project_foldername&gt;
git init
git submodule add -b &lt;desired_branch_version&gt; https://github.com/godotengine/godot-cpp
cd godot-cpp
git submodule update --init
cd ..
git submodule add -b &lt;desired_branch_version&gt; https://github.com/godotengine/godot
cd godot
git submodule update --init
cd ..
mkdir src
</code></pre>
<p>Now you are going to have the godot engine source code as a submodule in godot (you will need this for breakpoints but its optional, feel free to use a release from godot but debugging will be painful), you will have the gd extension bindings in godot-cpp and you have a folder called src we are going to dump our awful C++ code in.</p>
<h3 id="heading-step-2-create-some-initial-files">Step 2 - Create some initial files</h3>
<p>in <code>src</code> we are going to create a few files, you can structure this however you want however i chose to do <code>src/core/…</code> <code>src/resources/…</code> <code>src/state_machines/…</code> and <code>src/register_types.hpp</code>/cpp</p>
<p>For now, I would suggest copypasta the below, you can tweak/delete after you get this garbage running.<br /><strong>arora_player.hpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> once</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/character_body3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/packed_scene.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_player_stats.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_settings.hpp"</span></span>

<span class="hljs-keyword">namespace</span> godot
{
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraPlayer</span> :</span> <span class="hljs-keyword">public</span> CharacterBody3D
    {
        GDCLASS(AroraPlayer, CharacterBody3D)
    <span class="hljs-keyword">private</span>:
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraPlayerMovementStateMachine</span>* <span class="hljs-title">movement_state_machine</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CollisionShape3D</span>* <span class="hljs-title">collider</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node3D</span>* <span class="hljs-title">pivot</span>, *<span class="hljs-title">third_person_body</span>, *<span class="hljs-title">first_person_body</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpringArm3D</span>* <span class="hljs-title">camera_boom</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Camera3D</span>* <span class="hljs-title">camera</span>;</span>

        Ref&lt;AroraPlayerStats&gt; player_stats;
        Ref&lt;AroraSettings&gt; arora_settings;
        Ref&lt;PackedScene&gt; third_person_mesh;
        Ref&lt;PackedScene&gt; first_person_mesh;

        <span class="hljs-keyword">float</span> boom_rot_x = <span class="hljs-number">0.0f</span>, boom_rot_y = <span class="hljs-number">0.0f</span>;

        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Input</span>* <span class="hljs-title">InputSubsystem</span>;</span>
        <span class="hljs-keyword">const</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Engine</span>* <span class="hljs-title">EngineSubsystem</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node3D</span>* _<span class="hljs-title">create_pivot</span>();</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CollisionShape3D</span>* _<span class="hljs-title">create_collider</span>();</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpringArm3D</span>* _<span class="hljs-title">create_spring_arm</span>();</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Camera3D</span>* _<span class="hljs-title">create_camera</span>(<span class="hljs-title">class</span> <span class="hljs-title">Node3D</span>* <span class="hljs-title">parent</span>);</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node3D</span>* _<span class="hljs-title">create_third_person_body</span>(<span class="hljs-title">class</span> <span class="hljs-title">Node3D</span>* <span class="hljs-title">parent</span>);</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node3D</span>* _<span class="hljs-title">create_first_person_body</span>(<span class="hljs-title">class</span> <span class="hljs-title">Node3D</span>* <span class="hljs-title">parent</span>);</span>

        <span class="hljs-keyword">void</span> _swap_bodies_visibility(<span class="hljs-keyword">bool</span> p_is_first_person);
        <span class="hljs-keyword">bool</span> _is_editor() <span class="hljs-keyword">const</span>;
    <span class="hljs-keyword">protected</span>:
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> _bind_methods();
    <span class="hljs-keyword">public</span>:
        AroraPlayer();
        ~AroraPlayer();
        <span class="hljs-function">Ref&lt;AroraPlayerStats&gt; <span class="hljs-title">get_player_stats</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_player_stats</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;AroraPlayerStats&gt; &amp;p_player_stats)</span></span>;
        <span class="hljs-function">Ref&lt;PackedScene&gt; <span class="hljs-title">get_third_person_mesh</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_third_person_mesh</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;PackedScene&gt;&amp; p_third_person_mesh)</span></span>;
        <span class="hljs-function">Ref&lt;PackedScene&gt; <span class="hljs-title">get_first_person_mesh</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_first_person_mesh</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;PackedScene&gt;&amp; p_first_person_mesh)</span></span>;
        <span class="hljs-function">Ref&lt;AroraSettings&gt; <span class="hljs-title">get_arora_settings</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_arora_settings</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;AroraSettings&gt;&amp; p_arora_settings)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> <span class="hljs-title">get_length</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_length</span><span class="hljs-params">(<span class="hljs-keyword">float</span> p_length)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">rotate_boom_xy</span><span class="hljs-params">(<span class="hljs-keyword">float</span> p_x, <span class="hljs-keyword">float</span> p_y)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">const</span> Basis <span class="hljs-title">get_camera_boom_basis</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">face_pivot_to</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Basis&amp; basis)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">is_third_person</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_first_person</span><span class="hljs-params">()</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_third_person</span><span class="hljs-params">()</span></span>;
        <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> _physics_process(<span class="hljs-keyword">double</span> p_delta) <span class="hljs-keyword">override</span>;
        <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> _ready() <span class="hljs-keyword">override</span>;
        <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> _unhandled_input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event) <span class="hljs-keyword">override</span>;
        <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> _input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event) <span class="hljs-keyword">override</span>;
    };
}
</code></pre>
<p><strong>arora_player.cpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/engine.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/node3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/collision_shape3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/capsule_shape3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/scene_tree.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event_mouse_motion.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event_key.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/camera3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/spring_arm3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/animation_tree.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"arora_player.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_player_stats.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"state_machines/arora_player_movement_statemachine.hpp"</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *pivot_name = <span class="hljs-string">"Pivot"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *collider_name = <span class="hljs-string">"Collider"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *camera_name = <span class="hljs-string">"Camera"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *spring_arm_name = <span class="hljs-string">"CameraBoom"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *third_person_mesh_name = <span class="hljs-string">"ThirdPersonBody"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">char</span> *first_person_mesh_name = <span class="hljs-string">"FirstPersonBody"</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">float</span> collider_radius = <span class="hljs-number">2.0f</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">float</span> collider_height = <span class="hljs-number">8.0f</span>;

Node3D *AroraPlayer::_create_pivot()
{
    <span class="hljs-keyword">if</span> (Node *local_pivot = get_node_or_null(pivot_name))
    {
        print_line(<span class="hljs-string">"pivot exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;Node3D&gt;(local_pivot);
    }
    print_line(<span class="hljs-string">"creating a new pivot"</span>);
    Node3D *local_pivot = memnew(Node3D);
    local_pivot-&gt;set_name(pivot_name);
    add_child(local_pivot);
    local_pivot-&gt;set_owner(get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> local_pivot;
}

CollisionShape3D *AroraPlayer::_create_collider()
{
    <span class="hljs-keyword">if</span> (Node *local_collider = get_node_or_null(collider_name))
    {
        print_line(<span class="hljs-string">"collider exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;CollisionShape3D&gt;(local_collider);
    }
    print_line(<span class="hljs-string">"creating a new collider"</span>);
    CapsuleShape3D *collider_shape = memnew(CapsuleShape3D);
    collider_shape-&gt;set_radius(collider_radius);
    collider_shape-&gt;set_height(collider_height);
    CollisionShape3D *local_collider = memnew(CollisionShape3D);
    local_collider-&gt;set_shape(collider_shape);
    local_collider-&gt;set_name(collider_name);
    add_child(local_collider);
    local_collider-&gt;set_owner(get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> local_collider;
}

SpringArm3D *AroraPlayer::_create_spring_arm()
{
    <span class="hljs-keyword">if</span> (Node *local_spring_arm = get_node_or_null(spring_arm_name))
    {
        print_line(<span class="hljs-string">"spring_arm exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;SpringArm3D&gt;(local_spring_arm);
    }
    print_line(<span class="hljs-string">"creating a spring arm"</span>);
    SpringArm3D *local_spring_arm = memnew(SpringArm3D);
    local_spring_arm-&gt;set_name(spring_arm_name);
    add_child(local_spring_arm);
    local_spring_arm-&gt;set_owner(get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> local_spring_arm;
}

Camera3D *AroraPlayer::_create_camera(Node3D *parent)
{
    <span class="hljs-keyword">if</span> (Node *local_camera = parent-&gt;get_node_or_null(camera_name))
    {
        print_line(<span class="hljs-string">"camera exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;Camera3D&gt;(local_camera);
    }
    print_line(<span class="hljs-string">"creating a camera"</span>);
    Camera3D *local_camera = memnew(Camera3D);
    local_camera-&gt;set_name(camera_name);
    parent-&gt;add_child(local_camera);
    local_camera-&gt;set_owner(parent-&gt;get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> local_camera;
}

Node3D *AroraPlayer::_create_third_person_body(Node3D *parent)
{
    <span class="hljs-keyword">if</span> (!third_person_mesh.is_valid())
    {
        print_line(<span class="hljs-string">"warning: third_person_mesh not set, this will crash at runtime"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
    }
    <span class="hljs-keyword">if</span> (Node* local_third_person_body = parent-&gt;get_node_or_null(third_person_mesh_name))
    {
        print_line(<span class="hljs-string">"third_person_mesh exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;Node3D&gt;(local_third_person_body);
    }
    print_line(<span class="hljs-string">"third_person_mesh doesn't exist, creating a new one"</span>);
    Node *instanced_third_person = third_person_mesh-&gt;instantiate();
    instanced_third_person-&gt;set_name(third_person_mesh_name);
    parent-&gt;add_child(instanced_third_person);
    instanced_third_person-&gt;set_owner(parent-&gt;get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> Object::cast_to&lt;Node3D&gt;(instanced_third_person);
}

Node3D *godot::AroraPlayer::_create_first_person_body(Node3D *parent)
{
    <span class="hljs-keyword">if</span> (!first_person_mesh.is_valid())
    {
        print_line(<span class="hljs-string">"warning: first_person_mesh not set, this will crash at runtime"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nullptr</span>;
    }
    <span class="hljs-keyword">if</span> (Node* local_first_person_body = parent-&gt;get_node_or_null(first_person_mesh_name))
    {
        print_line(<span class="hljs-string">"first_person_mesh exists, not creating a new one"</span>);
        <span class="hljs-keyword">return</span> Object::cast_to&lt;Node3D&gt;(local_first_person_body);
    }
    print_line(<span class="hljs-string">"first_person_mesh doesn't exist, creating a new one"</span>);
    Node *instanced_first_person = first_person_mesh-&gt;instantiate();
    instanced_first_person-&gt;set_name(first_person_mesh_name);
    parent-&gt;add_child(instanced_first_person);
    instanced_first_person-&gt;set_owner(parent-&gt;get_tree()-&gt;get_edited_scene_root());
    <span class="hljs-keyword">return</span> Object::cast_to&lt;Node3D&gt;(instanced_first_person);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_first_person</span><span class="hljs-params">()</span>
</span>{
    _swap_bodies_visibility(<span class="hljs-literal">true</span>);
    InputSubsystem-&gt;set_mouse_mode(Input::MouseMode::MOUSE_MODE_CAPTURED);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_third_person</span><span class="hljs-params">()</span>
</span>{
    _swap_bodies_visibility(<span class="hljs-literal">false</span>);
    InputSubsystem-&gt;set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE);
}

<span class="hljs-keyword">void</span> AroraPlayer::_swap_bodies_visibility(<span class="hljs-keyword">bool</span> p_is_first_person)
{
    third_person_body-&gt;set_visible(!p_is_first_person);
    first_person_body-&gt;set_visible(p_is_first_person);
}

<span class="hljs-comment">// in godot version currently used is_editor_hint causes crash sometimes (likely some weird engine race condition)</span>
<span class="hljs-comment">// this is a hack to try and avoid that</span>
<span class="hljs-keyword">bool</span> AroraPlayer::_is_editor() <span class="hljs-keyword">const</span>
{
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-keyword">return</span> !EngineSubsystem || EngineSubsystem-&gt;is_editor_hint();
    }
    <span class="hljs-keyword">catch</span> (...)
    {
        <span class="hljs-comment">// very noisy log...</span>
        <span class="hljs-comment">// print_error("editor threw error trying to use the hint, going to just return true and continue rather then hard crashing");</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}

<span class="hljs-keyword">void</span> AroraPlayer::_bind_methods()
{
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_third_person_mesh"</span>), &amp;AroraPlayer::get_third_person_mesh);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_third_person_mesh"</span>, <span class="hljs-string">"p_third_person_mesh"</span>), &amp;AroraPlayer::set_third_person_mesh);
    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, <span class="hljs-string">"third_person_mesh"</span>, PROPERTY_HINT_RESOURCE_TYPE, <span class="hljs-string">"PackedScene"</span>), <span class="hljs-string">"set_third_person_mesh"</span>, <span class="hljs-string">"get_third_person_mesh"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_player_stats"</span>), &amp;AroraPlayer::get_player_stats);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_player_stats"</span>, <span class="hljs-string">"p_player_stats"</span>), &amp;AroraPlayer::set_player_stats);
    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, <span class="hljs-string">"player_stats"</span>, PROPERTY_HINT_RESOURCE_TYPE, <span class="hljs-string">"AroraPlayerStats"</span>), <span class="hljs-string">"set_player_stats"</span>, <span class="hljs-string">"get_player_stats"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_arora_settings"</span>), &amp;AroraPlayer::get_arora_settings);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_arora_settings"</span>, <span class="hljs-string">"p_arora_settings"</span>), &amp;AroraPlayer::set_arora_settings);
    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, <span class="hljs-string">"arora_settings"</span>, PROPERTY_HINT_RESOURCE_TYPE, <span class="hljs-string">"AroraSettings"</span>), <span class="hljs-string">"set_arora_settings"</span>, <span class="hljs-string">"get_arora_settings"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_first_person_mesh"</span>), &amp;AroraPlayer::get_first_person_mesh);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_first_person_mesh"</span>, <span class="hljs-string">"p_first_person_mesh"</span>), &amp;AroraPlayer::set_first_person_mesh);
    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, <span class="hljs-string">"first_person_mesh"</span>, PROPERTY_HINT_RESOURCE_TYPE, <span class="hljs-string">"PackedScene"</span>), <span class="hljs-string">"set_first_person_mesh"</span>, <span class="hljs-string">"get_first_person_mesh"</span>);
}

AroraPlayer::AroraPlayer()
{
}

AroraPlayer::~AroraPlayer()
{
}

<span class="hljs-function">Ref&lt;AroraPlayerStats&gt; <span class="hljs-title">AroraPlayer::get_player_stats</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> player_stats;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_player_stats</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;AroraPlayerStats&gt;&amp; p_player_stats)</span>
</span>{
    player_stats = p_player_stats;
}

<span class="hljs-function">Ref&lt;PackedScene&gt; <span class="hljs-title">AroraPlayer::get_third_person_mesh</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> third_person_mesh;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_third_person_mesh</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;PackedScene&gt; &amp;p_third_person_mesh)</span>
</span>{
    third_person_mesh = p_third_person_mesh;
}

Ref&lt;PackedScene&gt; godot::AroraPlayer::get_first_person_mesh() <span class="hljs-keyword">const</span>
{
    <span class="hljs-keyword">return</span> first_person_mesh;
}

<span class="hljs-keyword">void</span> godot::AroraPlayer::set_first_person_mesh(<span class="hljs-keyword">const</span> Ref&lt;PackedScene&gt; &amp;p_first_person_mesh)
{
    first_person_mesh = p_first_person_mesh;
}

<span class="hljs-function">Ref&lt;AroraSettings&gt; <span class="hljs-title">AroraPlayer::get_arora_settings</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> arora_settings;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_arora_settings</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Ref&lt;AroraSettings&gt; &amp;p_arora_settings)</span>
</span>{
    arora_settings = p_arora_settings;
}

<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> godot::AroraPlayer::get_length() <span class="hljs-keyword">const</span>
{
    <span class="hljs-keyword">return</span> camera_boom-&gt;get_length();
}

<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">float</span> THIRD_PERSON_MIN_DISTANCE = <span class="hljs-number">0.05f</span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::set_length</span><span class="hljs-params">(<span class="hljs-keyword">float</span> p_length)</span>
</span>{
    <span class="hljs-keyword">if</span> (third_person_body)
    {
        <span class="hljs-keyword">if</span> (third_person_body-&gt;is_visible() &amp;&amp; p_length &lt;= THIRD_PERSON_MIN_DISTANCE)
        {
            set_first_person();
        }
        <span class="hljs-keyword">if</span> (!third_person_body-&gt;is_visible() &amp;&amp; p_length &gt;= THIRD_PERSON_MIN_DISTANCE)
        {
            set_third_person();
        }
    }
    camera_boom-&gt;set_length(p_length);
}

<span class="hljs-keyword">static</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">float</span> MAX_X_ROTATION_DEGREES = <span class="hljs-number">85.0f</span>;
<span class="hljs-keyword">static</span> <span class="hljs-keyword">float</span> MAX_X_ROTATION_RADIANS = Math::deg_to_rad(MAX_X_ROTATION_DEGREES);
<span class="hljs-comment">// https://docs.godotengine.org/en/stable/tutorials/3d/using_transforms.html</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::rotate_boom_xy</span><span class="hljs-params">(<span class="hljs-keyword">float</span> p_x, <span class="hljs-keyword">float</span> p_y)</span>
</span>{
    boom_rot_x = CLAMP(boom_rot_x + p_x, MAX_X_ROTATION_RADIANS * <span class="hljs-number">-1</span>, MAX_X_ROTATION_RADIANS);
    boom_rot_y += p_y;
    <span class="hljs-keyword">if</span> (Math::<span class="hljs-built_in">abs</span>(Math::rad_to_deg(boom_rot_x)) &gt;= <span class="hljs-number">360.0f</span>)
    {
        boom_rot_x = <span class="hljs-number">0</span>;
        print_line(<span class="hljs-string">"reset x rotation to 0"</span>);
    }
    <span class="hljs-keyword">if</span> (Math::<span class="hljs-built_in">abs</span>(Math::rad_to_deg(boom_rot_y)) &gt;= <span class="hljs-number">360.0f</span>)
    {
        boom_rot_y = <span class="hljs-number">0</span>;
        print_line(<span class="hljs-string">"reset y rotation to 0"</span>);
    }
    camera_boom-&gt;set_basis(Basis());
    camera_boom-&gt;rotate_object_local(Vector3(<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>), boom_rot_y);
    camera_boom-&gt;rotate_object_local(Vector3(<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>), boom_rot_x);
}

<span class="hljs-function"><span class="hljs-keyword">const</span> Basis <span class="hljs-title">AroraPlayer::get_camera_boom_basis</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> camera_boom-&gt;get_basis();
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayer::face_pivot_to</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Basis&amp; basis)</span>
</span>{
    pivot-&gt;set_basis(basis);
}

<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">AroraPlayer::is_third_person</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> camera_boom-&gt;get_length() &gt;= THIRD_PERSON_MIN_DISTANCE;
}

<span class="hljs-keyword">void</span> AroraPlayer::_physics_process(<span class="hljs-keyword">double</span> p_delta)
{
    <span class="hljs-keyword">if</span> (!InputSubsystem || !EngineSubsystem)
    {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">if</span> (!_is_editor())
    {
        movement_state_machine-&gt;_physics_process(InputSubsystem, p_delta);
    }
}

<span class="hljs-keyword">void</span> AroraPlayer::_ready()
{
    InputSubsystem = Input::get_singleton();
    EngineSubsystem = Engine::get_singleton();
    pivot = _create_pivot();
    collider = _create_collider();
    camera_boom = _create_spring_arm();
    camera = _create_camera(camera_boom);
    third_person_body = _create_third_person_body(pivot);
    first_person_body = _create_first_person_body(camera);
    <span class="hljs-comment">// AnimationTree* third_person_state_machine = third_person_body-&gt;get_node&lt;AnimationTree&gt;("AnimationTree");</span>
    movement_state_machine = <span class="hljs-keyword">new</span> AroraPlayerMovementStateMachine(<span class="hljs-keyword">this</span>, player_stats, arora_settings);
    <span class="hljs-keyword">if</span> (!_is_editor())
    {
        set_length(<span class="hljs-number">0.0f</span>);
    }
}

<span class="hljs-keyword">void</span> AroraPlayer::_unhandled_input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event)
{
    <span class="hljs-keyword">if</span> (!_is_editor() &amp;&amp; movement_state_machine)
    {
        movement_state_machine-&gt;_unhandled_input(p_event);
    }
}

<span class="hljs-keyword">void</span> AroraPlayer::_input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event)
{
    <span class="hljs-keyword">if</span> (!p_event.is_valid() || !InputSubsystem)
    {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">if</span> (movement_state_machine)
    {
        movement_state_machine-&gt;_input(InputSubsystem, p_event);
    }
}
</code></pre>
<p><strong>arora_player_movement_statemachine.hpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> once</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/ref.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"core/arora_player.hpp"</span></span>

<span class="hljs-keyword">namespace</span> godot
{
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraPlayerMovementStateMachine</span>
    {</span>
    <span class="hljs-keyword">private</span>:
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraPlayer</span>* <span class="hljs-title">arora_player</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Ref</span>&lt;class AroraPlayerStats&gt; <span class="hljs-title">arora_player_stats</span>;</span>
        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Ref</span>&lt;class AroraSettings&gt; <span class="hljs-title">arora_settings</span>;</span>
        Vector3 target_velocity;
        <span class="hljs-keyword">bool</span> is_rotating_in_third_person;
    <span class="hljs-keyword">public</span>:
        AroraPlayerMovementStateMachine(class AroraPlayer* InAroraPlayer, class Ref&lt;class AroraPlayerStats&gt; AroraPlayerStats, class Ref&lt;class AroraSettings&gt; AroraSettings);
        ~AroraPlayerMovementStateMachine();
        <span class="hljs-keyword">void</span> _unhandled_input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event);
        <span class="hljs-keyword">void</span> _physics_process(class Input* InputSubsystem, <span class="hljs-keyword">double</span> p_delta);
        <span class="hljs-keyword">void</span> _input(class Input* InputSubsystem, <span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event);
    };
}
</code></pre>
<p><strong>arora_player_movement_statemachine.cpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input_event_mouse_motion.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/character_body3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/input.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/spring_arm3d.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"arora_player_movement_statemachine.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_player_stats.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_settings.hpp"</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

AroraPlayerMovementStateMachine::AroraPlayerMovementStateMachine(AroraPlayer* InAroraPlayer, Ref&lt;AroraPlayerStats&gt; AroraPlayerStats, Ref&lt;AroraSettings&gt; AroraSettings)
{
    is_rotating_in_third_person = <span class="hljs-literal">false</span>;
    arora_player = InAroraPlayer;
    arora_player_stats = AroraPlayerStats;
    arora_settings = AroraSettings;
}

AroraPlayerMovementStateMachine::~AroraPlayerMovementStateMachine()
{
}

<span class="hljs-keyword">void</span> AroraPlayerMovementStateMachine::_unhandled_input(<span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event)
{
    <span class="hljs-keyword">if</span> (!p_event.is_valid())
    {
        <span class="hljs-keyword">return</span>;
    }
    Ref&lt;InputEventMouseMotion&gt; input_mouse_motion = p_event;
    <span class="hljs-keyword">if</span> (input_mouse_motion.is_valid() &amp;&amp; (is_rotating_in_third_person || !arora_player-&gt;is_third_person()))
    {
        arora_player-&gt;rotate_boom_xy(
            -input_mouse_motion-&gt;get_relative().y * arora_settings-&gt;get_mouse_sensitivity(),
            -input_mouse_motion-&gt;get_relative().x * arora_settings-&gt;get_mouse_sensitivity()
        );
    }
}

<span class="hljs-keyword">void</span> AroraPlayerMovementStateMachine::_physics_process(Input* InputSubsystem, <span class="hljs-keyword">double</span> p_delta)
{
    Vector2 input_direction = InputSubsystem-&gt;get_vector(<span class="hljs-string">"move_left"</span>, <span class="hljs-string">"move_right"</span>, <span class="hljs-string">"move_back"</span>, <span class="hljs-string">"move_forward"</span>);
    Basis boom_basis = arora_player-&gt;get_camera_boom_basis();
    Vector3 movement_direction = boom_basis.xform(Vector3(input_direction.x, <span class="hljs-number">0.0f</span>, input_direction.y));
    <span class="hljs-keyword">if</span> (!movement_direction.is_zero_approx())
    {
        movement_direction.normalize();
    }
    target_velocity.x = movement_direction.x * <span class="hljs-number">-1</span> * arora_player_stats-&gt;get_speed();
    target_velocity.z = movement_direction.z * <span class="hljs-number">-1</span> * arora_player_stats-&gt;get_speed();
    <span class="hljs-keyword">if</span> (!arora_player-&gt;is_on_floor())
    {
        target_velocity.y = target_velocity.y - (arora_player_stats-&gt;get_fall_acceleration() * p_delta);
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (InputSubsystem-&gt;is_action_pressed(<span class="hljs-string">"jump"</span>))
    {
        target_velocity.y = arora_player_stats-&gt;get_jump_speed();
    }
    <span class="hljs-comment">// Can now safely modify the vector3 movement_direction to remove y component?</span>
    <span class="hljs-keyword">if</span> (!movement_direction.is_zero_approx())
    {
        movement_direction.y = <span class="hljs-number">0.0f</span>;
        arora_player-&gt;face_pivot_to(Basis::looking_at(movement_direction));
    }
    arora_player-&gt;set_velocity(target_velocity);
    arora_player-&gt;move_and_slide();
}

<span class="hljs-keyword">void</span> AroraPlayerMovementStateMachine::_input(Input *InputSubsystem, <span class="hljs-keyword">const</span> Ref&lt;InputEvent&gt; &amp;p_event)
{
    <span class="hljs-keyword">if</span> (p_event-&gt;is_action_pressed(<span class="hljs-string">"third_person_rotate"</span>))
    {
        InputSubsystem-&gt;set_mouse_mode(arora_player-&gt;is_third_person() ? Input::MOUSE_MODE_CAPTURED : InputSubsystem-&gt;get_mouse_mode());
        is_rotating_in_third_person = <span class="hljs-literal">true</span>;
    }
    <span class="hljs-keyword">if</span> (p_event-&gt;is_action_released(<span class="hljs-string">"third_person_rotate"</span>))
    {
        InputSubsystem-&gt;set_mouse_mode(arora_player-&gt;is_third_person() ? Input::MOUSE_MODE_VISIBLE : InputSubsystem-&gt;get_mouse_mode());
        is_rotating_in_third_person = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (p_event-&gt;is_action_pressed(<span class="hljs-string">"open_menu"</span>))
    {
        InputSubsystem-&gt;get_mouse_mode() == Input::MouseMode::MOUSE_MODE_CAPTURED ? InputSubsystem-&gt;set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE) : InputSubsystem-&gt;set_mouse_mode(Input::MouseMode::MOUSE_MODE_CAPTURED);
    }
    <span class="hljs-keyword">if</span> (p_event-&gt;is_action_released(<span class="hljs-string">"zoom_out"</span>))
    {
        <span class="hljs-keyword">float</span> newLength = MIN(arora_player-&gt;get_length() + arora_settings-&gt;get_zoom_speed(), arora_settings-&gt;get_max_camera_length());
        arora_player-&gt;set_length(newLength);
    }
    <span class="hljs-keyword">if</span> (p_event-&gt;is_action_released(<span class="hljs-string">"zoom_in"</span>))
    {
        <span class="hljs-keyword">float</span> newLength = MAX(arora_player-&gt;get_length() - arora_settings-&gt;get_zoom_speed(), arora_settings-&gt;get_min_camera_length());
        arora_player-&gt;set_length(newLength);
    }
}
</code></pre>
<p><strong>arora_player_stats.hpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> once</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/resource.hpp&gt;</span></span>
<span class="hljs-keyword">namespace</span> godot
{
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraPlayerStats</span> :</span> <span class="hljs-keyword">public</span> Resource
    {
        GDCLASS(AroraPlayerStats, Resource)
    <span class="hljs-keyword">private</span>:
        <span class="hljs-keyword">float</span> speed, fall_acceleration, jump_speed;
    <span class="hljs-keyword">protected</span>:
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> _bind_methods();
    <span class="hljs-keyword">public</span>:
        AroraPlayerStats();
        ~AroraPlayerStats();

        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_speed)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_jump_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_jump_speed)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_jump_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_fall_acceleration</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_fall_acceleration)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_fall_acceleration</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
    };
}
</code></pre>
<p><strong>arora_player_stats.cpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"arora_player_stats.hpp"</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

<span class="hljs-keyword">void</span> AroraPlayerStats::_bind_methods()
{
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_speed"</span>), &amp;AroraPlayerStats::get_speed);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_speed"</span>, <span class="hljs-string">"p_speed"</span>), &amp;AroraPlayerStats::set_speed);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"speed"</span>), <span class="hljs-string">"set_speed"</span>, <span class="hljs-string">"get_speed"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_jump_speed"</span>), &amp;AroraPlayerStats::get_jump_speed);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_jump_speed"</span>, <span class="hljs-string">"p_jump_speed"</span>), &amp;AroraPlayerStats::set_jump_speed);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"jump_speed"</span>), <span class="hljs-string">"set_jump_speed"</span>, <span class="hljs-string">"get_jump_speed"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_fall_acceleration"</span>), &amp;AroraPlayerStats::get_fall_acceleration);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_fall_acceleration"</span>, <span class="hljs-string">"p_fall_acceleration"</span>), &amp;AroraPlayerStats::set_fall_acceleration);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"fall_acceleration"</span>), <span class="hljs-string">"set_fall_acceleration"</span>, <span class="hljs-string">"get_fall_acceleration"</span>);
}

AroraPlayerStats::AroraPlayerStats()
{
    jump_speed = <span class="hljs-number">8.0f</span>;
    speed = <span class="hljs-number">14</span>;
    fall_acceleration = <span class="hljs-number">75</span>;
}

AroraPlayerStats::~AroraPlayerStats()
{
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayerStats::set_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_speed)</span>
</span>{
    speed = p_speed;
}

<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">AroraPlayerStats::get_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> speed;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayerStats::set_jump_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_jump_speed)</span>
</span>{
    jump_speed = p_jump_speed;
}

<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">AroraPlayerStats::get_jump_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> jump_speed;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraPlayerStats::set_fall_acceleration</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_fall_acceleration)</span>
</span>{
    fall_acceleration = p_fall_acceleration;
}

<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">AroraPlayerStats::get_fall_acceleration</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> fall_acceleration;
}
</code></pre>
<p><strong>arora_settings.hpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">pragma</span> once</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/classes/resource.hpp&gt;</span></span>
<span class="hljs-keyword">namespace</span> godot
{
    <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> swap to configfile? https://docs.godotengine.org/en/stable/classes/class_configfile.html</span>
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AroraSettings</span> :</span> <span class="hljs-keyword">public</span> Resource
    {
        GDCLASS(AroraSettings, Resource)
    <span class="hljs-keyword">private</span>:
        <span class="hljs-keyword">float</span> mouse_sensitivity, max_camera_length, min_camera_length, zoom_speed;
    <span class="hljs-keyword">protected</span>:
        <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> _bind_methods();
    <span class="hljs-keyword">public</span>:
        AroraSettings();
        ~AroraSettings();

        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_mouse_sensitivity</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_mouse_sensitivity)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_mouse_sensitivity</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_max_camera_length</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_max_camera_length)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_max_camera_length</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_min_camera_length</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_min_camera_length)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_min_camera_length</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">set_zoom_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_zoom_speed)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">get_zoom_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span></span>;
    };
}
</code></pre>
<p><strong>arora_settings.cpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"arora_settings.hpp"</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

<span class="hljs-keyword">void</span> AroraSettings::_bind_methods()
{
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_mouse_sensitivity"</span>), &amp;AroraSettings::get_mouse_sensitivity);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_mouse_sensitivity"</span>, <span class="hljs-string">"p_mouse_sensitivity"</span>), &amp;AroraSettings::set_mouse_sensitivity);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"mouse_sensitivity"</span>), <span class="hljs-string">"set_mouse_sensitivity"</span>, <span class="hljs-string">"get_mouse_sensitivity"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_max_camera_length"</span>), &amp;AroraSettings::get_max_camera_length);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_max_camera_length"</span>, <span class="hljs-string">"p_max_camera_length"</span>), &amp;AroraSettings::set_max_camera_length);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"max_camera_length"</span>), <span class="hljs-string">"set_max_camera_length"</span>, <span class="hljs-string">"get_max_camera_length"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_min_camera_length"</span>), &amp;AroraSettings::get_min_camera_length);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_min_camera_length"</span>, <span class="hljs-string">"p_min_camera_length"</span>), &amp;AroraSettings::set_min_camera_length);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"min_camera_length"</span>), <span class="hljs-string">"set_min_camera_length"</span>, <span class="hljs-string">"get_min_camera_length"</span>);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"get_zoom_speed"</span>), &amp;AroraSettings::get_zoom_speed);
    ClassDB::bind_method(D_METHOD(<span class="hljs-string">"set_zoom_speed"</span>, <span class="hljs-string">"p_zoom_speed"</span>), &amp;AroraSettings::set_zoom_speed);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, <span class="hljs-string">"zoom_speed"</span>), <span class="hljs-string">"set_zoom_speed"</span>, <span class="hljs-string">"get_zoom_speed"</span>);
}

AroraSettings::AroraSettings()
{
}

AroraSettings::~AroraSettings()
{
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraSettings::set_mouse_sensitivity</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_mouse_sensitivity)</span>
</span>{
    mouse_sensitivity = p_mouse_sensitivity;
}

<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">AroraSettings::get_mouse_sensitivity</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> mouse_sensitivity;
}

<span class="hljs-keyword">void</span> godot::AroraSettings::set_max_camera_length(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_max_camera_length)
{
    max_camera_length = p_max_camera_length;
}

<span class="hljs-keyword">float</span> godot::AroraSettings::get_max_camera_length() <span class="hljs-keyword">const</span>
{
    <span class="hljs-keyword">return</span> max_camera_length;
}

<span class="hljs-keyword">void</span> godot::AroraSettings::set_min_camera_length(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_min_camera_length)
{
    min_camera_length = p_min_camera_length;
}

<span class="hljs-keyword">float</span> godot::AroraSettings::get_min_camera_length() <span class="hljs-keyword">const</span>
{
    <span class="hljs-keyword">return</span> min_camera_length;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AroraSettings::set_zoom_speed</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> p_zoom_speed)</span>
</span>{
    zoom_speed = p_zoom_speed;
}

<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">AroraSettings::get_zoom_speed</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span>
</span>{
    <span class="hljs-keyword">return</span> zoom_speed;
}
</code></pre>
<h3 id="heading-step-3-register-types">Step 3 - Register Types</h3>
<p>In GD Extension we essentially boil down to a register_types file, this file will handle saying what classes you have created for runtime/internal/etc and just in general what to call as the initializer.<br />In my case I have called my library <code>arora_library</code> but feel free to just replace with whatever name you want like <code>mycoollib_library</code></p>
<p><strong>src/register_types.cpp</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;gdextension_interface.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/core/defs.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/godot.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"register_types.h"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"core/arora_player.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"core/arora_multiplayer.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_player_stats.hpp"</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"resources/arora_settings.hpp"</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">initialize_arora_module</span><span class="hljs-params">(ModuleInitializationLevel p_level)</span> </span>{
    <span class="hljs-keyword">if</span> (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        <span class="hljs-keyword">return</span>;
    }
    GDREGISTER_CLASS(AroraPlayer);
    GDREGISTER_CLASS(AroraPlayerStats);
    GDREGISTER_CLASS(AroraSettings);
    GDREGISTER_RUNTIME_CLASS(AroraMultiplayer);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">uninitialize_arora_module</span><span class="hljs-params">(ModuleInitializationLevel p_level)</span> </span>{
    <span class="hljs-keyword">if</span> (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
        <span class="hljs-keyword">return</span>;
    }
}

<span class="hljs-keyword">extern</span> <span class="hljs-string">"C"</span> {
<span class="hljs-comment">// Initialization.</span>
<span class="hljs-function">GDExtensionBool GDE_EXPORT <span class="hljs-title">arora_library_init</span><span class="hljs-params">(GDExtensionInterfaceGetProcAddress p_get_proc_address, <span class="hljs-keyword">const</span> GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)</span> </span>{
    godot::<span class="hljs-function">GDExtensionBinding::InitObject <span class="hljs-title">init_obj</span><span class="hljs-params">(p_get_proc_address, p_library, r_initialization)</span></span>;

    init_obj.register_initializer(initialize_arora_module);
    init_obj.register_terminator(uninitialize_arora_module);
    init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);

    <span class="hljs-keyword">return</span> init_obj.init();
}
}
</code></pre>
<p><strong>src/register_types.h</strong></p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> GDEXAMPLE_REGISTER_TYPES_H</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> GDEXAMPLE_REGISTER_TYPES_H</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;godot_cpp/core/class_db.hpp&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> godot;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">initialize_arora_module</span><span class="hljs-params">(ModuleInitializationLevel p_level)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">uninitialize_arora_module</span><span class="hljs-params">(ModuleInitializationLevel p_level)</span></span>;

<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span> <span class="hljs-comment">// GDEXAMPLE_REGISTER_TYPES_H</span></span>
</code></pre>
<h3 id="heading-step-4-setting-up-godot">Step 4 - Setting up Godot</h3>
<p>Next build the godot folder so you have a editor to use:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> godot
scons dev_build=<span class="hljs-literal">true</span> target=editor debug_symbols=yes
</code></pre>
<p>Let this run for a bit and finish, if you fail because of any reason, you’re likely missing some dependency, <a target="_blank" href="https://docs.godotengine.org/en/latest/engine_details/development/compiling/">Building from source — Godot Engine (latest) documentation in English</a> has a good explanation of what’s needed.</p>
<p>Now open the built godot <code>.\godot\bin\</code><a target="_blank" href="http://godot.windows.editor.dev"><code>godot.windows.editor.dev</code></a><code>.x86_64.exe</code> was what i need but for you it might be called something else, create a new godot project in the <code>demo</code> folder and save, close godot.</p>
<p>Create a new file in demo/bin called <code>mylibname.gdextension</code> in my case it was <code>libarora.gdextension</code></p>
<pre><code class="lang-bash">[configuration]

entry_symbol = <span class="hljs-string">"arora_library_init"</span>
compatibility_minimum = <span class="hljs-string">"4.1"</span>
reloadable = <span class="hljs-literal">true</span>

[libraries]

macos.debug = <span class="hljs-string">"res://bin/libarora.macos.template_debug.dev.framework"</span>
macos.release = <span class="hljs-string">"res://bin/libarora.macos.template_release.framework"</span>
ios.debug = <span class="hljs-string">"res://bin/libarora.ios.template_debug.dev.xcframework"</span>
ios.release = <span class="hljs-string">"res://bin/libarora.ios.template_release.xcframework"</span>
windows.debug.x86_32 = <span class="hljs-string">"res://bin/libarora.windows.template_debug.dev.x86_32.dll"</span>
windows.release.x86_32 = <span class="hljs-string">"res://bin/libarora.windows.template_release.x86_32.dll"</span>
windows.debug.x86_64 = <span class="hljs-string">"res://bin/libarora.windows.template_debug.dev.x86_64.dll"</span>
windows.release.x86_64 = <span class="hljs-string">"res://bin/libarora.windows.template_release.x86_64.dll"</span>
linux.debug.x86_64 = <span class="hljs-string">"res://bin/libarora.linux.template_debug.dev.x86_64.so"</span>
linux.release.x86_64 = <span class="hljs-string">"res://bin/libarora.linux.template_release.x86_64.so"</span>
linux.debug.arm64 = <span class="hljs-string">"res://bin/libarora.linux.template_debug.dev.arm64.so"</span>
linux.release.arm64 = <span class="hljs-string">"res://bin/libarora.linux.template_release.arm64.so"</span>
linux.debug.rv64 = <span class="hljs-string">"res://bin/libarora.linux.template_debug.dev.rv64.so"</span>
linux.release.rv64 = <span class="hljs-string">"res://bin/libarora.linux.template_release.rv64.so"</span>
android.debug.x86_64 = <span class="hljs-string">"res://bin/libarora.android.template_debug.dev.x86_64.so"</span>
android.release.x86_64 = <span class="hljs-string">"res://bin/libarora.android.template_release.x86_64.so"</span>
android.debug.arm64 = <span class="hljs-string">"res://bin/libarora.android.template_debug.dev.arm64.so"</span>
android.release.arm64 = <span class="hljs-string">"res://bin/libarora.android.template_release.arm64.so"</span>

[dependencies]
ios.debug = {
    <span class="hljs-string">"res://bin/libgodot-cpp.ios.template_debug.dev.xcframework"</span>: <span class="hljs-string">""</span>
}
ios.release = {
    <span class="hljs-string">"res://bin/libgodot-cpp.ios.template_release.xcframework"</span>: <span class="hljs-string">""</span>
}
</code></pre>
<p>You can modify the values once you see the compiled results (they will be dumped in the bin folder, and you can modify to what you need).</p>
<h3 id="heading-step-5-setup-compilation">Step 5 - Setup Compilation</h3>
<p>In .vscode create the following files:</p>
<p><strong>.vscode/launch.json</strong></p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
    <span class="hljs-attr">"configurations"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Launch Godot"</span>,
            <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"cppvsdbg"</span>,
            <span class="hljs-attr">"args"</span>: [
                <span class="hljs-string">"--editor"</span>,
                <span class="hljs-string">"--path"</span>,
                <span class="hljs-string">"demo"</span>
            ],
            <span class="hljs-attr">"cwd"</span>: <span class="hljs-string">"${workspaceFolder}"</span>,
            <span class="hljs-attr">"console"</span>: <span class="hljs-string">"internalConsole"</span>,
            <span class="hljs-attr">"program"</span>: <span class="hljs-string">"${workspaceFolder}/godot/bin/godot.windows.editor.dev.x86_64.exe"</span>,
            <span class="hljs-attr">"visualizerFile"</span>: <span class="hljs-string">"${workspaceFolder}/godot/platform/windows/godot.natvis"</span>
        }
    ]
}
</code></pre>
<p><strong>.vscode/tasks.json</strong></p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2.0.0"</span>,
    <span class="hljs-attr">"tasks"</span>: [
        {
            <span class="hljs-attr">"label"</span>: <span class="hljs-string">"build"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"shell"</span>,
            <span class="hljs-attr">"command"</span>: <span class="hljs-string">"scons"</span>,
            <span class="hljs-attr">"args"</span>: [
                <span class="hljs-comment">// enable for debugging with breakpoints</span>
                <span class="hljs-string">"dev_build=yes"</span>
              ],
            <span class="hljs-attr">"group"</span>: {
                <span class="hljs-attr">"kind"</span>: <span class="hljs-string">"build"</span>,
                <span class="hljs-attr">"isDefault"</span>: <span class="hljs-literal">true</span>
            },
            <span class="hljs-attr">"problemMatcher"</span>: <span class="hljs-string">"$msCompile"</span>
        }
    ]
}
</code></pre>
<p>in the root of the project create a file called <strong>SConstruct</strong><br />This is where the lib output for bin folder is defined (replace libarora with what you want it called)</p>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">import</span> warnings

env = SConscript(<span class="hljs-string">"godot-cpp/SConstruct"</span>)
<span class="hljs-keyword">if</span> <span class="hljs-string">"dev_build"</span> <span class="hljs-keyword">in</span> ARGUMENTS <span class="hljs-keyword">and</span> ARGUMENTS[<span class="hljs-string">"dev_build"</span>] == <span class="hljs-string">"yes"</span> <span class="hljs-keyword">or</span> <span class="hljs-string">"dev_build"</span> <span class="hljs-keyword">in</span> ARGUMENTS <span class="hljs-keyword">and</span> ARGUMENTS[<span class="hljs-string">"dev_build"</span>] == <span class="hljs-literal">True</span> <span class="hljs-keyword">or</span> <span class="hljs-string">"include_server_content"</span> <span class="hljs-keyword">in</span> ARGUMENTS:
    <span class="hljs-comment">#warnings.warn("include_server_content is specified, the output will have server code")</span>
    env.Append(CPPDEFINES=[<span class="hljs-string">"INCLUDE_SERVER_CONTENT"</span>])

<span class="hljs-comment"># For reference:</span>
<span class="hljs-comment"># - CCFLAGS are compilation flags shared between C and C++</span>
<span class="hljs-comment"># - CFLAGS are for C-specific compilation flags</span>
<span class="hljs-comment"># - CXXFLAGS are for C++-specific compilation flags</span>
<span class="hljs-comment"># - CPPFLAGS are for pre-processor flags</span>
<span class="hljs-comment"># - CPPDEFINES are for pre-processor defines</span>
<span class="hljs-comment"># - LINKFLAGS are for linking flags</span>

<span class="hljs-comment"># tweak this if you want to use different folders, or more folders, to store your source code in.</span>
env.Append(CPPPATH=[<span class="hljs-string">"src/"</span>])
sources = Glob(<span class="hljs-string">"src/**/*.cpp"</span>) + Glob(<span class="hljs-string">"src/*.cpp"</span>)

<span class="hljs-keyword">if</span> env[<span class="hljs-string">"platform"</span>] == <span class="hljs-string">"macos"</span>:
    library = env.SharedLibrary(
        <span class="hljs-string">"demo/bin/libarora.{}.{}.framework/libarora.{}.{}"</span>.format(
            env[<span class="hljs-string">"platform"</span>], env[<span class="hljs-string">"target"</span>], env[<span class="hljs-string">"platform"</span>], env[<span class="hljs-string">"target"</span>]
        ),
        source=sources,
    )
<span class="hljs-keyword">elif</span> env[<span class="hljs-string">"platform"</span>] == <span class="hljs-string">"ios"</span>:
    <span class="hljs-keyword">if</span> env[<span class="hljs-string">"ios_simulator"</span>]:
        library = env.StaticLibrary(
            <span class="hljs-string">"demo/bin/libarora.{}.{}.simulator.a"</span>.format(env[<span class="hljs-string">"platform"</span>], env[<span class="hljs-string">"target"</span>]),
            source=sources,
        )
    <span class="hljs-keyword">else</span>:
        library = env.StaticLibrary(
            <span class="hljs-string">"demo/bin/libarora.{}.{}.a"</span>.format(env[<span class="hljs-string">"platform"</span>], env[<span class="hljs-string">"target"</span>]),
            source=sources,
        )
<span class="hljs-keyword">else</span>:
    library = env.SharedLibrary(
        <span class="hljs-string">"demo/bin/libarora{}{}"</span>.format(env[<span class="hljs-string">"suffix"</span>], env[<span class="hljs-string">"SHLIBSUFFIX"</span>]),
        source=sources,
    )

Default(library)
</code></pre>
<p>Now when you hit <code>Control Shift B</code> it should compile and output relevant bin to bin folder<br />And hit <code>Control F5</code> it will start Godot in your project, if you use just <code>F5</code> (the debugger) you can place breakpoints anywhere and it will work engine code or your code, try it out.</p>
<h2 id="heading-follow-up">Follow Up</h2>
<p>If you got to this point and it builds/runs successfully, you can probably do a final read over <a target="_blank" href="https://docs.godotengine.org/en/4.4/tutorials/scripting/gdextension/gdextension_cpp_example.html">GDExtension C++ example — Godot Engine (4.4) documentation in English</a> and understand what each file here is doing.<br />With this setup you can easily debug and add as you go, goodluck.</p>
]]></content:encoded></item><item><title><![CDATA[Metrics And Tracing on AWS Lambda in Golang]]></title><description><![CDATA[Introduction
When writing an application, you want to make sure there are metrics and tracing, both of these are extremely fundamental in any debugging process once it is in production.
AWS Lambda is a great problem space to utilize a language such a...]]></description><link>https://blog.techytechster.com/metrics-and-tracing-on-aws-lambda-in-golang</link><guid isPermaLink="true">https://blog.techytechster.com/metrics-and-tracing-on-aws-lambda-in-golang</guid><category><![CDATA[cloudwatch-emf]]></category><category><![CDATA[golang]]></category><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[#CloudWatch]]></category><category><![CDATA[OpenTelemetry]]></category><category><![CDATA[CDK]]></category><dc:creator><![CDATA[Jonathan Wright]]></dc:creator><pubDate>Fri, 19 Apr 2024 10:58:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713524193305/010ffc2f-2fe2-4a1f-93ad-159830c99612.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>When writing an application, you want to make sure there are metrics and tracing, both of these are extremely fundamental in any debugging process once it is in production.</p>
<p>AWS Lambda is a great problem space to utilize a language such as Golang or Rust as they have small deployment sizes and very low cold starts, however, the tooling to get metrics and tracing from lambda to CloudWatch is confusing to a beginner, this blog post will help you setup tracing and metrics using the most modern and best practice approach which is <a target="_blank" href="https://opentelemetry.io/">Open Telemetry</a> + <a target="_blank" href="https://docs.aws.amazon.com/xray/">CloudWatch X-Ray</a> + <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html">CloudWatch EMF</a>.</p>
<h2 id="heading-infrastructure-cdk-typescript">Infrastructure (CDK - Typescript)</h2>
<p>To connect your lambda to CloudWatch you need to instrument it with a layer and relevant permissions in your infrastructure.</p>
<h3 id="heading-permissions">Permissions</h3>
<p>You need to create a policy for your Lambda that allows your lambda to post metrics via EMF to a CloudWatch log group.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> partition = Stack.of(<span class="hljs-built_in">this</span>).partition;
<span class="hljs-keyword">const</span> region = Stack.of(<span class="hljs-built_in">this</span>).region;
<span class="hljs-keyword">const</span> account = Stack.of(<span class="hljs-built_in">this</span>).account;
<span class="hljs-keyword">const</span> cwPermission = <span class="hljs-keyword">new</span> PolicyStatement({
  effect: Effect.ALLOW,
  actions: [<span class="hljs-string">'logs:PutRetentionPolicy'</span>],
  resources: [
    <span class="hljs-string">`arn:<span class="hljs-subst">${partition}</span>:logs:<span class="hljs-subst">${region}</span>:<span class="hljs-subst">${account}</span>:log-group:&lt;LOG_GROUP_NAME&gt;`</span>,
    <span class="hljs-string">`arn:<span class="hljs-subst">${partition}</span>:logs:<span class="hljs-subst">${region}</span>:<span class="hljs-subst">${account}</span>:log-group:&lt;LOG_GROUP_NAME&gt;:*`</span>
  ]
});
</code></pre>
<h3 id="heading-open-telemetry-layer">Open Telemetry Layer</h3>
<p>AWS Lambda connects to Open Telemetry via a custom layer called the <a target="_blank" href="https://aws-otel.github.io/docs/getting-started/lambda/lambda-go">ADOT Collector Layer (written by AWS)</a>. If you need to support another partition such as China or GovCloud you will have to somehow replicate the layer into the partition.</p>
<p>Note: This layer only works in certain regions, check the ADOT Collector Layer docs for the relevant regions</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> otelLayer = LayerVersion.fromLayerVersionArn(
  <span class="hljs-built_in">this</span>,
  <span class="hljs-string">'LambdaOtelLayer'</span>,
  <span class="hljs-string">`arn:aws:lambda:<span class="hljs-subst">${region}</span>:901920570463:layer:aws-otel-collector-&lt;architecture&gt;-ver-0-90-1:1`</span>
);
</code></pre>
<h3 id="heading-open-telemetry-config-file">Open Telemetry Config File</h3>
<p>You will need to bundle a collector.yaml file into your uploaded lambda zip. The specification for this file is defined by open telemetry docs but below is the example I used for this blog post.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">recievers:</span>
  <span class="hljs-attr">otlp:</span>
    <span class="hljs-attr">protocols:</span>
      <span class="hljs-attr">grpc:</span>
        <span class="hljs-attr">endpoint:</span> <span class="hljs-string">"localhost:4317"</span>
      <span class="hljs-attr">http:</span>
        <span class="hljs-attr">endpoint:</span> <span class="hljs-string">"localhost:4318"</span>
<span class="hljs-attr">exporters:</span>
  <span class="hljs-attr">logging:</span>
  <span class="hljs-attr">awsxray:</span>
  <span class="hljs-attr">awsemf:</span>
    <span class="hljs-attr">log_group_name:</span> <span class="hljs-string">"&lt;DESIRED_LOG_GROUP&gt;"</span>
    <span class="hljs-attr">namespace:</span> <span class="hljs-string">"&lt;DESIRED_NAMESPACE&gt;"</span>
    <span class="hljs-attr">dimension_rollup_option:</span> <span class="hljs-string">"NoDimensionRollup"</span>
    <span class="hljs-attr">log_retention:</span> <span class="hljs-number">60</span>
<span class="hljs-attr">service:</span>
  <span class="hljs-attr">pipelines:</span>
    <span class="hljs-attr">traces:</span>
      <span class="hljs-attr">receivers:</span> [<span class="hljs-string">otlp</span>]
      <span class="hljs-attr">exporters:</span> [<span class="hljs-string">awsxray</span>]
    <span class="hljs-attr">metrics:</span>
      <span class="hljs-attr">receivers:</span> [<span class="hljs-string">otlp</span>]
      <span class="hljs-attr">exporters:</span> [<span class="hljs-string">awsemf</span>]
    <span class="hljs-attr">telemetry:</span>
      <span class="hljs-attr">metrics:</span>
        <span class="hljs-attr">address:</span> <span class="hljs-string">localhost:8888</span>
</code></pre>
<h3 id="heading-lambda">Lambda</h3>
<p>Finally, you need to hook it all together in CDK to a lambda, in this example I will be using a Function construct, but you can use something like the GoFunction construct and it should work the same.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> lambda = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(<span class="hljs-built_in">this</span>, <span class="hljs-string">'otelLambda'</span>, {
   ...lambdaCommonProps, <span class="hljs-comment">// code: ... etc</span>
   layers: [otelLayer],
   functionName: <span class="hljs-string">'OtelLambda'</span>,
   handler: <span class="hljs-string">'bootstrap'</span>,
   environment: {
     OPENTELEMETRY_COLLECTOR_CONFIG_FILE: <span class="hljs-string">'/var/task/collector.yaml'</span>,
     HANDLER_NAME: <span class="hljs-string">'OtelLambda'</span>
   }
});
lambda.addToRolePolicy(cwPermission);
</code></pre>
<h2 id="heading-service-code">Service Code</h2>
<p>In order to finish instrumentation, you now need to write the actual service level code i.e. the Lambda.</p>
<h3 id="heading-dependencies">Dependencies</h3>
<pre><code class="lang-bash">go get go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda
go get go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig
go get go.opentelemetry.io/contrib/propagators/aws
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/oltpmetricgrpc
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/sdk/metric
</code></pre>
<h3 id="heading-tracing-instrumentation-with-cloudwatch-x-ray">Tracing Instrumentation with CloudWatch X-Ray</h3>
<p>To begin with before trying to tackle metrics lets simply setup Open Telemetry with X-Ray as this is a fairly simple process.</p>
<p>First, we create a file <em>/internal/otel/metrics.go</em></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">defaultShutdownXray</span><span class="hljs-params">(ctx context.Context)</span> <span class="hljs-title">error</span></span> {
  <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"unconfigured xray"</span>)
}
<span class="hljs-keyword">var</span> shutdownXray = defaultShutdownXray

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cleanupXray</span><span class="hljs-params">(tp *sdktrace.TracerProvider)</span> <span class="hljs-title">func</span><span class="hljs-params">(ctx context.Context)</span></span> {
  shutdownXray = tp.Shutdown
  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(ctx context.Context)</span></span> {
    err := shutdownXray(ctx)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
      log.Err(err).Msg(<span class="hljs-string">"error shutting down tracer provider"</span>)
    }
  }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">XrayDecorator</span><span class="hljs-params">(lambdaHandler any)</span> <span class="hljs-title">any</span></span> {
  ctx := context.Background()
  tp, err := xrayconfig.NewTracerProvider(ctx)
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Err(err).Msg(<span class="hljs-string">"error creating tracer provider"</span>)
  }
  cleanup := cleanupXray(tp)
  <span class="hljs-keyword">defer</span> cleanup(ctx)
  otel.SetTracerProvider(tp)
  otel.SetTextMapPropagator(xray.Propagator{})
  <span class="hljs-keyword">return</span> otellambda.InstrumentHandler(lambdaHandler)
}
</code></pre>
<p>Finally, we create our basic lambda handler: <em>/cmd/lambda/main.go</em></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">HandleRequest</span><span class="hljs-params">(ctx context.Context, event <span class="hljs-keyword">interface</span>{})</span> <span class="hljs-params">(<span class="hljs-keyword">string</span>, error)</span></span> {
  <span class="hljs-keyword">return</span> <span class="hljs-string">"dummy"</span>, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  lambda.Start(internalOtel.XrayDecorator(HandleRequest))
}
</code></pre>
<p>Now you can go deploy the infrastructure + service code (<em>cdk build &amp;&amp; cdk deploy</em>) and then run the lambda.<br />If you have setup the code correctly you should now see in CloudWatch X-Ray traces flowing through, congratulations you can stop here if you have no need for custom metrics.</p>
<h3 id="heading-metrics-instrumentation-with-cloudwatch-emf">Metrics Instrumentation with CloudWatch EMF</h3>
<p>In this section we will setup a basic decorator that will initialize open telemetry in the lambda and will push a custom metric for latency i.e time taken for your function to run. You can extend this to publish any metric you may want.</p>
<p>First edit the <em>/internal/otel/metrics.go</em> file from before.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> int64ObservableGauge <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>, options ...metric.Int64ObservableGaugeOption)</span> <span class="hljs-params">(metric.Int64ObservableGauge, error)</span></span>

<span class="hljs-keyword">type</span> NewIntMetricParams <span class="hljs-keyword">struct</span> {
  Name <span class="hljs-keyword">string</span>
  Description <span class="hljs-keyword">string</span>
  Unit <span class="hljs-keyword">string</span>
  Value <span class="hljs-keyword">int64</span>
  Attributes []attribute.KeyValue
}

<span class="hljs-comment">// Unit is ucum standard =&gt; https://ucum.org/ucum</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewIntMetric</span><span class="hljs-params">(params NewIntMetricParams)</span> <span class="hljs-title">error</span></span> {
  log.Info().Interface(<span class="hljs-string">"params"</span>, params).Msg(<span class="hljs-string">"New Int Metric To Record"</span>)
  _, err := int64ObservableGauge(
    params.Name,
    metric.WithDescription(params.Description),
    metric.WithUnit(params.Unit),
    metric.WithInt64Callback(intObserver(params.Value, params.Attributes...)),
  )
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> errors.Join(ErrFailedToRecordMetric, err)
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cleanupMetrics</span><span class="hljs-params">(ctx context.Context, flush <span class="hljs-keyword">func</span>(ctx context.Context)</span> <span class="hljs-title">error</span>)</span> {
  log.Info().Msg(<span class="hljs-string">"flushed metrics"</span>)
  err := flush(ctx)
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Err(err).Msg(<span class="hljs-string">"failed to shutdown meter provider"</span>)
  }
  log.Info().Msg(<span class="hljs-string">"flushed metrics"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">MetricsDecorator</span>[<span class="hljs-title">TIn</span> <span class="hljs-title">any</span>, <span class="hljs-title">TOut</span> <span class="hljs-title">any</span>]<span class="hljs-params">(lambdaHandler LambdaHandler[TIn, TOut])</span> <span class="hljs-title">LambdaHandler</span>[<span class="hljs-title">TIn</span>, <span class="hljs-title">TOut</span>]</span> {
  res, err := resource.Merge(resource.Default(), resource.NewWithAttributes(<span class="hljs-string">"https://opentelemetry.io/schemas/1.24.0"</span>, semconv.ServiceName(<span class="hljs-string">"&lt;APPLICATION_NAME&gt;"</span>), semconv.ServiceVersion(<span class="hljs-string">"&lt;APPLICATION_VERSION&gt;"</span>))
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Err(err).Msg(<span class="hljs-string">"failed to initialize metrics"</span>)
  }
  metricExporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithInsecure(), oltpmetricgrpc.WithEndpoint(<span class="hljs-string">"0.0.0.0:4317"</span>), oltpmetricgrpc.WithDialOption(grpc.WithBlock()))
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Err(err).Msg(<span class="hljs-string">"failed to initialize metrics"</span>)
  }
  mp := sdkMetric.NewMeterProvider(
    sdkMetric.WithReader(sdkMetric.NewPeriodicReader(metricExporter)),
    sdkMetric.WithResource(res),
  )
  otel.SetMeterProvider(mp)
  meter = mp.Meter(<span class="hljs-string">"otellambda"</span>)
  int64ObservableGauge = meter.Int64ObservableGauge
  t1 := time.Now()
  resp, err := lambdaHandler(ctx, event)
  t2 := time.Now()
  duration := t2.Sub(t1).Milliseconds()
  NewIntMetric(NewIntMetricParams{
    Name: <span class="hljs-string">"duration"</span>,
    Description: <span class="hljs-string">"duration taken for handler"</span>,
    Unit: <span class="hljs-string">"ms"</span>,
    Value: duration,
    Attributes: []attribute.KeyValue{attribute.String(<span class="hljs-string">"handler"</span>, os.Getenv(<span class="hljs-string">"HANDLER_NAME"</span>))},
  })
  cleanupMetrics(ctx, mp.ForceFlush)
  log.Info().Msg(<span class="hljs-string">"cleaned up returning"</span>)
  <span class="hljs-keyword">return</span> resp, err
}
</code></pre>
<p>Now we update the handler from before <em>/cmd/lambda/main.go</em></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  lambda.Start(internalOtel.XrayDecorator(internalOtel.MetricsDecorator[lambdaRequest, <span class="hljs-keyword">string</span>](HandleRequest)))
}
</code></pre>
<p>Now if you rerun your lambda, you should be able to look in your log group and see an EMF Metric log emitted, and eventually, a metric will appear into CloudWatch.<br />Congratulations, if you reached this point, you now have custom metrics and tracing implemented on AWS Lambda in Golang using Open Telemetry</p>
]]></content:encoded></item><item><title><![CDATA[How To Measure Digital Dexterity - A Starting Point]]></title><description><![CDATA[Introduction
TLDR: https://digitaldexterity.techytechster.com
Digital Dexterity is a buzz word in companies these days, everyone seems to define it differently and no one can objectively say 'it is x y z and you measure it with b'.
Depending on where...]]></description><link>https://blog.techytechster.com/how-to-measure-digital-dexterity-a-starting-point</link><guid isPermaLink="true">https://blog.techytechster.com/how-to-measure-digital-dexterity-a-starting-point</guid><category><![CDATA[PlanetScale]]></category><category><![CDATA[PlanetScaleHackathon]]></category><category><![CDATA[golang]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Jonathan Wright]]></dc:creator><pubDate>Sun, 17 Jul 2022 08:08:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658660961700/heWx-wpsj.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p><em>TLDR: https://digitaldexterity.techytechster.com</em></p>
<p>Digital Dexterity is a buzz word in companies these days, everyone seems to define it differently and no one can objectively say 'it is x y z and you measure it with b'.
Depending on where you read you may come into the general consensus that digital dexterity is a build up of multiple agreed upon skills and/or mindsets rather then just digital literacy. How do you measure this when its hard to objectively measure a mindset, e.g. how do we know that x person is <a target="_blank" href="https://www.emerald.com/insight/content/doi/10.1108/JMD-01-2016-0011/full/html#:~:text=Self%2Defficacy%20and%20self%2Dawareness%20are%20constructs%20that%20define%20one%27s,128">'Aware of their self efficacy'</a>, depending on what you read there are great solutions and there are poor solutions to <a target="_blank" href="https://www.frameworksinstitute.org/wp-content/uploads/2021/04/MindsetShifts-MethodsSupplement.pdf">measuring behaviors and mindsets</a>.
So we know that its hard to define and its also hard to measure, but is there actually value in measuring something like this, well according to Gartner 'High digital dexterity in an organization increases the likelihood of successful digital transformation by 3.3 times', so the answer to this is that it is important.</p>
<h1 id="heading-whats-the-problem">What's The Problem?</h1>
<p>The problem that I have hoped to at least start on solving is the step of 'defining digital dexterity' and 'measuring digital dexterity' within a organization.</p>
<h2 id="heading-what-have-i-built-as-a-starting-point">What Have I Built As A Starting Point</h2>
<p>I have built a <a target="_blank" href="https://github.com/Techypanda/Digital_Dexterity">open source solution</a> that measures a series of behaviors and mindsets such as 'Self Sufficient Learning', <a target="_blank" href="https://digitaldexterity.techytechster.com/explain">you can read them all here</a>. I in no way believe that every company/person is going to agree with the behaviors and mindsets I have listed here, but that's why its open source, an organization could hypothetically take this solution into there environment, modify/add new behaviors and then deploy it to measure what they believe is a digitally dexterous person.</p>
<h2 id="heading-is-it-complete-ie-is-this-a-solved-problem">Is It Complete? (i.e is this a solved problem)</h2>
<p>No, the way I have gone about measuring digital dexterity could be seen as a naiive approach to measuring &amp; defining digital dexterity, however, this gets the conversation started in your organization and hypothetically gives the organization a starting ground to build upon.</p>
<h1 id="heading-what-can-it-do">What Can It Do?</h1>
<p>The tool I have created will allow users to measure themselves and then get others to measure them against a series of behaviors/mindsets, using these evaluations they will then see whether maybe they are overrating themselves in some parts or underrating. Take the two examples below:</p>
<h2 id="heading-example-1-jimmy">Example 1: Jimmy</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658044343832/pozgWuYAW.png" alt="image.png" />
Jimmy is a timid person in LeftBank Co where he believes himself to be a average employee that doesn't really think he excels at anything, we can cross reference this at the graph above and see that he values each of his behaviors quite low, however, when we look at what his employees see about him which is that he is actually quite a digitally dexterous person (and they rate his self-efficacy low which is a good indicator to say he doesn't believe in himself basically).</p>
<h2 id="heading-example-2-jeremy">Example 2: Jeremy</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658044625517/fJKUyfznh.png" alt="image.png" />
Jeremy has a reputation of being a overconfident talker at RightBank Co where he is seen as being able to talk a lot, but when it comes to it he actually isn't that digitally dexterous. He has had managers in the past tell him this but he doesn't believe it and believes the managers to simply be misguided. After taking his mandatory digital dexterity review and looking at the graph, he can see that the majority of people in his company have rated him as quite low in digital dexterity (apart of self-efficacy) and this tells him that maybe they are saying something he needs to look into.</p>
<h1 id="heading-techstack">Techstack</h1>
<p><em>Please note that the solution has been designed with microservices in mind, this means that if a company has a need of using x frontend instead of my web consumer, they can simply point a new frontend at the REST API and it should hypothetically be identical (once they code the new frontend).</em></p>
<h2 id="heading-consumer-service">Consumer Service</h2>
<p>The consumer is a typical web consumer consisting of the following:</p>
<ul>
<li>ReactJS Framework (Typescript)</li>
<li>pnpm to install packages/maintain scripts</li>
<li>Docker to allow running on any system easier</li>
<li><a target="_blank" href="https://digitaldexterity.techytechster.com">Deployed On Github Pages</a></li>
</ul>
<h2 id="heading-backend-service">Backend Service</h2>
<p>The backend is a REST API consisting of the following:</p>
<ul>
<li>Golang<ul>
<li>Echo Framework</li>
<li>GORM (Golang ORM to interact with DB)</li>
</ul>
</li>
<li>Docker to allow running on any system easier</li>
<li>Pulumi to make deploying on kubernetes easy</li>
<li>Planetscale MySQL Database (Works well as I can branch for each branch I create in github)</li>
<li><a target="_blank" href="https://api.digitaldexterity.techytechster.com">Deployed On Kubernetes On OKE (Oracle Managed Kubernetes)</a></li>
</ul>
<h1 id="heading-future-work">Future Work</h1>
<ul>
<li>When deployed in a organization it could be worthwhile to investigate adding bias to peer reviews of your digital dexterity, e.g. a line manager's review is probably more indicative of being accurate then your friend.</li>
</ul>
]]></content:encoded></item></channel></rss>