<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://lachlanbarclay.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://lachlanbarclay.net/" rel="alternate" type="text/html" /><updated>2025-01-21T06:08:20+00:00</updated><id>https://lachlanbarclay.net/feed.xml</id><title type="html">Lachlan (Rocklan) Barclay’s Blog</title><subtitle>Lachlan Barclay (Rocklan&apos;s) Technical Blog</subtitle><author><name>Lachlan Barclay</name></author><entry><title type="html">Splitting up the monolith</title><link href="https://lachlanbarclay.net/2025/01/adding-a-circuit-breaker-to-asp-net-6-application" rel="alternate" type="text/html" title="Splitting up the monolith" /><published>2025-01-21T00:00:00+00:00</published><updated>2025-01-21T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2025/01/Adding-A-Circuit-Breaker-To-Asp-Net-6-Application</id><content type="html" xml:base="https://lachlanbarclay.net/2025/01/adding-a-circuit-breaker-to-asp-net-6-application"><![CDATA[<p>A few years back my team took on the challenge of breaking down the <a href="https://www.rome2rio.com">Rome2Rio</a> Monolith. This was an epic project, and broadly speaking the main goal was to make it easier for separate teams to work on different parts of the codebase. So just today I’ve written up our findings in a three part article and published it here:</p>

<p><a href="https://www.rome2rio.com/blog/2025/01/10/splitting-up-the-monolith/">Splitting Up Rome2rio’s Monolith</a></p>

<p>A few of the things that I’ve covered are:</p>

<ul>
  <li>Our approach to dismantling the code and converting it to .NET Core</li>
  <li>Refactoring techniques that made a difference</li>
  <li>Implications of HTTP and other insights</li>
</ul>

<p>Hopefully this helps out some other people who are embarking on a similar journey. All feedback appreciated!</p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="c#" /><category term="monolith" /><category term="dotnet" /><category term="framework" /><category term="microservices" /><summary type="html"><![CDATA[A few years back, my team took on the challenge of breaking down the Rome2Rio monolith. The goal was to make it easier for separate teams to work on different parts of the codebase. In this three-part series, I've written hopefully useful stuff like detailing our approach to dismantling the code and converting it to .NET Core, refactoring techniques that made a difference and implications of HTTP and other insights.]]></summary></entry><entry><title type="html">Jailbreaking a Japanese wii</title><link href="https://lachlanbarclay.net/2023/10/modding-a-japanese-wii" rel="alternate" type="text/html" title="Jailbreaking a Japanese wii" /><published>2023-10-16T00:00:00+00:00</published><updated>2023-10-16T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2023/10/Modding%20a%20Japanese%20Wii</id><content type="html" xml:base="https://lachlanbarclay.net/2023/10/modding-a-japanese-wii"><![CDATA[<p>So modding this Japanese wii has been an adventure.</p>

<p>Can’t use the normal exploit that I’ve used in the past which is named “LetterBomb”, because you need to know the MAC address of the wii, and I can’t open the settings menu on the wii. Not sure exactly why but I am guessing someone modded it in the past and stuffed it up
Can’t use the “str2hax” exploit because you have to change the DNS server, and I can’t get into settings</p>

<p>I then tried using “BlueBomb”, which exploits a vulnerability in the bluetooth driver, and you can only run it from linux, it would connect via bluetooth but the exploit would hang. No dice.</p>

<p>The only exploit left to run is an “old school” saved game exploit. You load a hacked saved game onto an SD card, copy it to your wii, run the game and load the saved game. They exist for a few games, none of which I own.</p>

<p>One of the games is “Lego Batman”, someone in Bayswater is selling it for $5. I buy it. Take it home, runs on the wii fine. Great. Copy the hacked save came onto an SD card, put it into the wii… and the wii doesn’t recognise my SD card. It doesn’t want to read ANY of my (5) SD cards. I find out it needs to be an original “SD Card”, not a “sdxc” or a “sdhc” card.</p>

<p>Find out a friend has an ancient SD card that should work. Meet up with him, take it home, it doesn’t work.</p>

<p>Find out my friend wiped it on a linux machine and didn’t create a partition on it. Boot linux, re-format SD card, create a partition, I can read it!</p>

<p>Copy hacked saved game onto SD card, put into wii, it appears! Copied save game file onto the wii.</p>

<p>Run Batman Lego. Saved game doesn’t appear.</p>

<p>After MUCH faffing and different attempts, it appears you need to do it in the following specific order:
Play the first stage of lego batman (I’ve done it 3 times now) and compete it until it creates a saved game on the wii
Copy this save game file onto your SD card (this sort of ‘initialises’ the SD card, not sure what it does)</p>

<p>Delete the save game file off the wii</p>

<p>Copy the hacked saved game onto the SD card</p>

<p>Copy the hacked saved game file from the SD card onto the wii</p>

<p>The save game now appears inside Lego Batman!!</p>

<p>Load the save game, do the exploit, and the exploit started up! Hooray! Here’s how it looks:</p>

<div class="embed-container"><iframe src="https://www.youtube.com/embed/2ggyx7_5wPc" frameborder="0" allowfullscreen=""></iframe></div>

<p>But oh no! The Exploit then hung! It turns out it doesn’t work on wii’s that haven’t been upgraded to the latest firmware!</p>

<p>So that’s where I am right now. Apparently I can use a different program to upgrade the firmware to the latest version but there’s a danger in bricking the wii. Oh well, I’m going to try it anyway.</p>

<p>Trying to fix the firmware installed on the wii manually, what could possibly go wrong</p>

<p><img src="/pics/wii/wii-fix-firmware.jpg" alt="Flashing firmware" class="img-responsive" /></p>

<p>Now let’s see if I’ve bricked it:</p>

<p><img src="/pics/wii/wii-maybe-bricked.jpg" alt="Maybe bricked?" class="img-responsive" /></p>

<p><img src="/pics/wii/hackmii.jpg" alt="Maybe bricked?" class="img-responsive" /></p>

<p><img src="/pics/wii/menu-version.jpg" alt="Menu version" class="img-responsive" /></p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="wii" /><category term="modding" /><category term="jailbreaking" /><summary type="html"><![CDATA[So modding this Japanese wii has been an adventure.]]></summary></entry><entry><title type="html">Inside my Yamaha DG-130 and Marshall 1936 cab</title><link href="https://lachlanbarclay.net/2023/03/amp-insides" rel="alternate" type="text/html" title="Inside my Yamaha DG-130 and Marshall 1936 cab" /><published>2023-03-24T00:00:00+00:00</published><updated>2023-03-24T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2023/03/Amp-Insides</id><content type="html" xml:base="https://lachlanbarclay.net/2023/03/amp-insides"><![CDATA[<p><img src="/pics/amp/amp-1.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/amp-2.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/amp-3.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/amp-4.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/cab-1.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/cab-2.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/cab-3.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/cab-4.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>

<p><img src="/pics/amp/cab-5.jpg" alt="Yamaha dg 130" class="img-responsive" /></p>]]></content><author><name>Lachlan Barclay</name></author><category term="music" /><category term="amp" /><category term="mould" /><category term="yamaha" /><category term="marshall" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Adding a circuit breaker to your ASP.NET 6 application</title><link href="https://lachlanbarclay.net/2023/01/adding-a-circuit-breaker-to-asp-net-6-application" rel="alternate" type="text/html" title="Adding a circuit breaker to your ASP.NET 6 application" /><published>2023-01-03T00:00:00+00:00</published><updated>2023-01-03T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2023/01/Adding-A-Circuit-Breaker-To-Asp-Net-6-Application</id><content type="html" xml:base="https://lachlanbarclay.net/2023/01/adding-a-circuit-breaker-to-asp-net-6-application"><![CDATA[<p>If your application is making many calls to an API that periodically fails, it can be handy to set up some kind of “<a href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern" target="_blank">circuit breaker</a>” to stop your code from making unnecessary calls to an API that you know is down.</p>

<p>ASP.NET suggests using a library named <a href="https://www.thepollyproject.org/" target="_blank">Polly</a> for this, and it’s fantastic, but it’s also not exactly clear how to implement it properly… especially when you need <strong>multiple</strong> circuit breakers. So let’s do it.</p>

<h2 id="using-polly-in-your-aspnet-6-application">Using Polly in your ASP.NET 6 application</h2>

<p>In this example we have an application that calls two API’s - one is the <a href="https://developers.google.com/maps/documentation/timezone/overview">google timezone API</a>, which is called by using a standard <code class="language-plaintext highlighter-rouge">HttpClient</code>, and another is Redis, which uses it’s own <a href="https://github.com/StackExchange/StackExchange.Redis/">custom client</a>.</p>

<p>As a first step, create a few policies and add them to a <a href="https://github.com/App-vNext/Polly/wiki/PolicyRegistry" target="_blank">PolicyRegistry</a>. This is done so that each individual class can specify which policy it wishes to use.</p>

<p>In your <code class="language-plaintext highlighter-rouge">Program.cs</code> file:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WebApplicationBuilder</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// I'm using Serilog, if you are using something else, you can add it here</span>
<span class="n">PollyPolicies</span> <span class="n">circuitBreakerManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PollyPolicies</span><span class="p">(</span><span class="n">Serilog</span><span class="p">.</span><span class="n">Log</span><span class="p">.</span><span class="n">Logger</span><span class="p">);</span>

<span class="c1">// The creation of the policies is inside the PollyPolicies class. This is done</span>
<span class="c1">// just so that they are grouped together in one place alongside the logging</span>
<span class="kt">var</span> <span class="n">googleApiCircuitBreakerPolicy</span> <span class="p">=</span> <span class="n">circuitBreakerManager</span><span class="p">.</span><span class="nf">GetGoogleApiCircuitBreakerPolicy</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">redisCircuitBreakerPolicy</span> <span class="p">=</span> <span class="n">circuitBreakerManager</span><span class="p">.</span><span class="nf">GetRedisCircuitBreakerPolicy</span><span class="p">();</span>

<span class="c1">// We add our policies to a Registry which is just a glorified List&lt;&gt;. </span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddPolicyRegistry</span><span class="p">(</span><span class="k">new</span> <span class="nf">PolicyRegistry</span><span class="p">()</span>
<span class="p">{</span>
    <span class="p">{</span> <span class="n">PollyPolicies</span><span class="p">.</span><span class="n">GooglePolicyName</span><span class="p">,</span> <span class="n">googleApiCircuitBreakerPolicy</span> <span class="p">},</span>
    <span class="p">{</span> <span class="n">PollyPolicies</span><span class="p">.</span><span class="n">RedisPolicyName</span><span class="p">,</span> <span class="n">redisCircuitBreakerPolicy</span> <span class="p">}</span>
<span class="p">});</span>

<span class="c1">// When we setup our HttpClient, we need to also add the "addPolicyHandler" call as shown:</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHttpClient</span><span class="p">&lt;</span><span class="n">IGoogleTimezoneService</span><span class="p">,</span> <span class="n">GoogleTimezoneService</span><span class="p">&gt;()</span>
    <span class="p">.</span><span class="nf">AddPolicyHandler</span><span class="p">(</span><span class="n">googleApiCircuitBreakerPolicy</span><span class="p">);</span>

<span class="p">...</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>

<p>Now we need to create our Policies, so create a file named <code class="language-plaintext highlighter-rouge">PollyPolicies.cs</code>:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">PollyPolicies</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">GooglePolicyName</span> <span class="p">=</span> <span class="s">"googleApiCircuitBreaker"</span><span class="p">;</span>
    <span class="k">public</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">RedisPolicyName</span> <span class="p">=</span> <span class="s">"redisCircuitBreaker"</span><span class="p">;</span>

    <span class="k">private</span> <span class="k">readonly</span> <span class="n">Serilog</span><span class="p">.</span><span class="n">ILogger</span> <span class="n">_logger</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">PollyPolicies</span><span class="p">(</span><span class="n">Serilog</span><span class="p">.</span><span class="n">ILogger</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="n">IAsyncPolicy</span><span class="p">&lt;</span><span class="n">HttpResponseMessage</span><span class="p">&gt;</span> <span class="nf">GetGoogleApiCircuitBreakerPolicy</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="c1">// The google API is called using a HttpClient, so we can use the handy</span>
        <span class="c1">// HandleTransientHttpError() call. But this will not catch timeouts!</span>
        <span class="c1">// Make sure to fine-tune these numbers for your specific use-case</span>

        <span class="k">return</span> <span class="n">HttpPolicyExtensions</span>
            <span class="p">.</span><span class="nf">HandleTransientHttpError</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">AdvancedCircuitBreakerAsync</span><span class="p">(</span>
                <span class="n">failureThreshold</span><span class="p">:</span> <span class="m">0.1</span><span class="p">,</span>
                <span class="n">samplingDuration</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">20</span><span class="p">),</span>
                <span class="n">minimumThroughput</span><span class="p">:</span> <span class="m">5</span><span class="p">,</span>
                <span class="n">durationOfBreak</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">60</span><span class="p">),</span>
                <span class="k">this</span><span class="p">.</span><span class="n">OnGoogleBreak</span><span class="p">,</span>
                <span class="k">this</span><span class="p">.</span><span class="n">OnGoogleReset</span><span class="p">,</span>
                <span class="k">this</span><span class="p">.</span><span class="n">OnGoogleHalfOpen</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="n">AsyncCircuitBreakerPolicy</span> <span class="nf">GetRedisCircuitBreakerPolicy</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="c1">// The Redis API throws quite a few different exceptions (about 5) depending</span>
        <span class="c1">// on what the problem is, so we're just going to handle any exception:</span>

        <span class="k">return</span> <span class="n">Policy</span><span class="p">.</span><span class="n">Handle</span><span class="p">&lt;</span><span class="n">Exception</span><span class="p">&gt;().</span><span class="nf">AdvancedCircuitBreakerAsync</span><span class="p">(</span>
            <span class="n">failureThreshold</span><span class="p">:</span> <span class="m">0.01</span><span class="p">,</span>
            <span class="n">samplingDuration</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">5</span><span class="p">),</span>
            <span class="n">minimumThroughput</span><span class="p">:</span> <span class="m">15</span><span class="p">,</span>
            <span class="n">durationOfBreak</span><span class="p">:</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">360</span><span class="p">),</span>
            <span class="k">this</span><span class="p">.</span><span class="n">OnRedisBreak</span><span class="p">,</span>
            <span class="k">this</span><span class="p">.</span><span class="n">OnRedisReset</span><span class="p">,</span>
            <span class="k">this</span><span class="p">.</span><span class="n">OnRedisHalfOpen</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnGoogleReset</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Google API circuit closed, requests flow normally."</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnGoogleHalfOpen</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Google API circuit half open"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnGoogleBreak</span><span class="p">(</span><span class="n">DelegateResult</span><span class="p">&lt;</span><span class="n">HttpResponseMessage</span><span class="p">&gt;</span> <span class="n">result</span><span class="p">,</span> <span class="n">TimeSpan</span> <span class="n">ts</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Google API circuit cut because {ResultStatusCode} "</span> <span class="p">+</span>
            <span class="s">"or {Exception}, so requests will not flow."</span><span class="p">,</span>
            <span class="n">result</span><span class="p">.</span><span class="n">Exception</span><span class="p">,</span> <span class="n">result</span><span class="p">.</span><span class="n">Result</span><span class="p">?.</span><span class="n">StatusCode</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnRedisReset</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Redis circuit closed, requests flow normally."</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnRedisHalfOpen</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Redis circuit half open"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">OnRedisBreak</span><span class="p">(</span><span class="n">Exception</span> <span class="n">result</span><span class="p">,</span> <span class="n">TimeSpan</span> <span class="n">ts</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span><span class="p">?.</span><span class="nf">Warning</span><span class="p">(</span><span class="s">"Redis circuit cut because {Exception}, "</span> <span class="p">+</span> 
            <span class="s">"so requests will not flow."</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We’ve now added two different circuit breaker policies that we can use in our code wherever we want. Let’s go ahead and use this policy in some of our code.</p>

<h2 id="using-the-circuit-breaker">Using the circuit breaker</h2>

<p>Now we need to update our code that calls the google API to use our circuit breaker. The cool part of the previous call to <code class="language-plaintext highlighter-rouge">AddPolicyHandler()</code> that is chained to the <code class="language-plaintext highlighter-rouge">AddHttpClient</code> call means that it will be used automatically! I’m not sure if this is cool or a bit too magical, but that’s what you need to do.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">GoogleTimezoneService</span> <span class="p">:</span> <span class="n">IGoogleTimezoneService</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">HttpClient</span> <span class="n">_httpClient</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">GoogleTimezoneService</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ICircuitBreakerPolicy</span><span class="p">&lt;</span><span class="n">HttpResponseMessage</span><span class="p">&gt;</span> <span class="n">_breaker</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">GoogleTimezoneService</span><span class="p">(</span>
        <span class="n">HttpClient</span> <span class="n">httpClient</span><span class="p">,</span> 
        <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">GoogleTimezoneService</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">,</span> 
        <span class="n">IReadOnlyPolicyRegistry</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">pollyPolicyRegistry</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="n">_httpClient</span> <span class="p">=</span> <span class="n">httpClient</span><span class="p">;</span>
        <span class="n">_breaker</span> <span class="p">=</span> <span class="n">pollyPolicyRegistry</span><span class="p">.</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">ICircuitBreakerPolicy</span><span class="p">&lt;</span><span class="n">HttpResponseMessage</span><span class="p">&gt;&gt;(</span>
            <span class="n">PollyPolicyNames</span><span class="p">.</span><span class="n">GooglePolicyName</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">GetResponseContentAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">endpoint</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="c1">// This is a bit of optimisation. When the circuit breaker is closed, it </span>
            <span class="c1">// will throw a BrokenCircuitException if we try to use it. However if we </span>
            <span class="c1">// know it's closed, there's no point in making the request and throwing</span>
            <span class="c1">// the exception, so let's just return</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">_breaker</span><span class="p">.</span><span class="n">CircuitState</span> <span class="p">==</span> <span class="n">CircuitState</span><span class="p">.</span><span class="n">Open</span> <span class="p">||</span> 
                <span class="n">_breaker</span><span class="p">.</span><span class="n">CircuitState</span> <span class="p">==</span> <span class="n">CircuitState</span><span class="p">.</span><span class="n">Isolated</span><span class="p">)</span>
                <span class="k">return</span> <span class="k">null</span><span class="p">;</span>

            <span class="n">HttpResponseMessage</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_httpClient</span><span class="p">.</span><span class="nf">GetAsync</span><span class="p">(</span><span class="n">endpoint</span><span class="p">);</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStringAsync</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">BrokenCircuitException</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// this can happen when the circuit has been set to half open, and </span>
            <span class="c1">// we are verifying that it's back up, but it's not, so it throws a</span>
            <span class="c1">// BrokenCircuitException, so in that case, we might as well log it </span>
            <span class="c1">// and just return null</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">"Exception {e} when trying to re-open circuit for "</span> <span class="p">+</span> 
                <span class="s">"google timezone api at {Url}"</span><span class="p">,</span> 
                <span class="n">ex</span><span class="p">,</span> <span class="n">_httpClient</span><span class="p">.</span><span class="n">BaseAddress</span> <span class="p">+</span> <span class="n">endpoint</span><span class="p">);</span>
                
            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">"Exception {e} when calling google timezone api at {Url}"</span><span class="p">,</span> 
                <span class="n">ex</span><span class="p">,</span> <span class="n">_httpClient</span><span class="p">.</span><span class="n">BaseAddress</span> <span class="p">+</span> <span class="n">endpoint</span><span class="p">);</span>

            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we need to update our Redis code to use our circuit breaker. In this case we need to retrieve it from the polly registry and then use it. We are going to use the <code class="language-plaintext highlighter-rouge">ExecuteAsync()</code> call, which executes any code you want, and behaves like how we defined it in the policy. In this case  if the code throws any <code class="language-plaintext highlighter-rouge">Exception</code>, it will handle it according to our desired circuit breaker policy logic.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">RedisCache</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">RedisCache</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">AsyncCircuitBreakerPolicy</span> <span class="n">_circuitBreakerPolicy</span><span class="p">;</span>

    <span class="k">public</span> <span class="nf">RedisCache</span><span class="p">(</span><span class="n">ILogger</span><span class="p">&lt;</span><span class="n">RedisCache</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">,</span> 
                      <span class="n">IReadOnlyPolicyRegistry</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">pollyPolicyRegistry</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="n">_circuitBreakerPolicy</span> <span class="p">=</span> <span class="n">pollyPolicyRegistry</span><span class="p">.</span><span class="n">Get</span><span class="p">&lt;</span><span class="n">AsyncCircuitBreakerPolicy</span><span class="p">&gt;(</span>
             <span class="n">PollyPolicyNames</span><span class="p">.</span><span class="n">RedisPolicyName</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="p">...</span>
    
    <span class="c1">// returns a string from redis</span>
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">GetStringAsync</span><span class="p">(</span><span class="kt">string</span> <span class="n">key</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// this stops us from making the call if the circuit is open. If we take</span>
        <span class="c1">// this out, the ExecuteAsync() method below will throw a BrokenCircuitException</span>
        <span class="c1">// on every call, so this is just a performance optimisation:</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">_circuitBreakerPolicy</span><span class="p">.</span><span class="n">CircuitState</span> <span class="p">==</span> <span class="n">CircuitState</span><span class="p">.</span><span class="n">Open</span> <span class="p">||</span> 
            <span class="n">_circuitBreakerPolicy</span><span class="p">.</span><span class="n">CircuitState</span> <span class="p">==</span> <span class="n">CircuitState</span><span class="p">.</span><span class="n">Isolated</span><span class="p">)</span>
            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>

        <span class="c1">// Where the magic happens. ExecuteAsync will handle and then re-raise any </span>
        <span class="c1">// exceptions thrown, so we need to catch them here too.</span>
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="n">_circuitBreakerPolicy</span><span class="p">.</span><span class="nf">ExecuteAsync</span><span class="p">(()</span> <span class="p">=&gt;</span>
                <span class="n">_redisClient</span><span class="p">.</span><span class="nf">GetStringAsync</span><span class="p">(</span><span class="n">key</span><span class="p">));</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span><span class="s">"Error with Redis when fetching {key}: {e}"</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And that’s the bare minimum that you need to be able to enforce a circuit breaker for any code that might throw an exception.</p>

<h2 id="adding-extra-policies">Adding extra policies</h2>
<p>If you would also like to use other policies like automatic retries, you can add them at startup:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">retryPolicy</span> <span class="p">=</span> <span class="n">HttpPolicyExtensions</span>
    <span class="p">.</span><span class="nf">HandleTransientHttpError</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">WaitAndRetryAsync</span><span class="p">(</span><span class="m">3</span><span class="p">,</span> <span class="n">retryNumber</span> <span class="p">=&gt;</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
</code></pre></div></div>

<p>and you can chain policies together:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddHttpClient</span><span class="p">&lt;</span><span class="n">IGoogleTimezoneService</span><span class="p">,</span> <span class="n">GoogleTimezoneService</span><span class="p">&gt;()</span>
    <span class="p">.</span><span class="nf">AddPolicyHandler</span><span class="p">(</span><span class="n">googleApiRetryPolicy</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddPolicyHandler</span><span class="p">(</span><span class="n">googleApiCircuitBreakerPolicy</span><span class="p">);</span>
</code></pre></div></div>

<p>That should be enough to get you started. Now make sure you test your circuit breaker code! It’s easy to get wrong!</p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="c#" /><category term="polly" /><category term="circuit-breaker" /><category term="aspnet" /><summary type="html"><![CDATA[In this example we have an application that calls two API's - one is the google timezone API, which is called by using a standard HttpClient, and another is Redis, which uses it's own custom client. As a first step, we need to create a few policies and add them to a PolicyRegistry.]]></summary></entry><entry><title type="html">Mounting a bike rack to your brick wall</title><link href="https://lachlanbarclay.net/2023/01/mounting-bike-rack-to-brick" rel="alternate" type="text/html" title="Mounting a bike rack to your brick wall" /><published>2023-01-03T00:00:00+00:00</published><updated>2023-01-03T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2023/01/Mounting-Bike-Rack-To-Brick</id><content type="html" xml:base="https://lachlanbarclay.net/2023/01/mounting-bike-rack-to-brick"><![CDATA[<p>Let’s talk about how to mount a bike rack to a brick wall. I’m using a <a href="https://www.bunnings.com.au/handy-storage-wall-mounted-bike-and-helmet-hook_p0074299">Handy Storage Bike Rack</a>:</p>

<p><img src="/pics/bike/bike-rack-1.jpg" alt="Handy Storage Bike Rack" class="img-responsive" /></p>

<p>The first step is to throw out the useless wall plug and screw that it came with:</p>

<p><img src="/pics/bike/bike-rack-2.jpg" alt="Useless wall plugs" class="img-responsive" /></p>

<p>And instead, buy some <a href="https://www.bunnings.com.au/ramset-8-x-40mm-dynabolt-plus-hex-nut-bolt_p2269155">8x40mm dynabolts</a>:</p>

<p><img src="/pics/bike/bike-rack-9.jpg" alt="Dynabolt Plus 8mm by 40mm" class="img-responsive" /></p>

<p>Next step, remove the two allen key bolts, as they get in the way:</p>

<p><img src="/pics/bike/bike-rack-3.jpg" alt="Bike Rack Bracket" class="img-responsive" /></p>

<p>You’ll end up with the bracket:</p>

<p><img src="/pics/bike/bike-rack-4.jpg" alt="Bike Rack Bracket without hooks" class="img-responsive" /></p>

<p>Put it up against the wall and draw a dot inside each of the three holes:</p>

<p><img src="/pics/bike/bike-rack-5.jpg" alt="Bike Rack Bracket against brick wall" class="img-responsive" /></p>

<p>You should end up with something like this:</p>

<p><img src="/pics/bike/bike-rack-7.jpg" alt="Brick wall sharpie marks" class="img-responsive" /></p>

<p>And the next step is to drill out one of those holes. You’ll need a some <a href="https://www.kangotools.com.au/products/details/10-piece-masonry-drill-bit-set-kmdb10s2">masonry drill bits</a>. You’ll also need a drill with a hammer action - I suggest getting a corded drill if you only have a battery operated one, as they have a lot more power.</p>

<p>You’ll also need to work out how far to drill in. The dynabolt needs to go in as far as the bolt on the end:</p>

<p><img src="/pics/bike/bike-rack-10.jpg" alt="Drill bit against dynabolt" class="img-responsive" /></p>

<p>So to make sure you don’t drill in too far, line up the drill bit and the dynabolt, and you can either use a bit of tape around the drill bit to make sure you don’t go in too far, or in my case the drill that I’m using has a depth gauge:</p>

<p><img src="/pics/bike/bike-rack-11.jpg" alt="Drill depth gauge and dynabolt" class="img-responsive" /></p>

<p>Now get yourself some protection. Ears, eyes, mouth and nose:</p>

<p><img src="/pics/bike/bike-rack-8.jpg" alt="Ear, eyes, nose protection" class="img-responsive" /></p>

<p>Start with the smallest drill bit size (5mm) and get drilling. In my case I started with the bottom hole. Hopefully you’ll end up with a hole like this:</p>

<p><img src="/pics/bike/bike-rack-12.jpg" alt="Brick wall small drilled hole" class="img-responsive" /></p>

<p>Now this is the crucial bit! Put the bracket over the hole that you just drilled and make sure things still line up:</p>

<p><img src="/pics/bike/bike-rack-13.jpg" alt="Bracket against brick wall" class="img-responsive" /></p>

<p>If not, get out that sharpie again and create a new mark. Drill the top hole next, and then make sure the bracket holes still line up. In this case you can see that the middle hole was a tiny bit too high:</p>

<p><img src="/pics/bike/bike-rack-14.jpg" alt="Bracket against brick wall holes" class="img-responsive" /></p>

<p>Now that we have three 5mm holes drilled, we need to expand them to fit the dynabolt. In this case, the 8mm dynabolt needs an 8mm drill bit:</p>

<p><img src="/pics/bike/bike-rack-15.jpg" alt="8mm Drill bit and dynabolt" class="img-responsive" /></p>

<p>Let’s line them up to make sure. As you can see here, the bolt sticks out from behind the drill bit, and the drill bit is the same width as the sleeve bolt itself:</p>

<p><img src="/pics/bike/bike-rack-16.jpg" alt="Dynabolt and drill bit aligned" class="img-responsive" /></p>

<p>Now that you’ve drilled the 8mm holes, take the bolts off the dynabolts:</p>

<p><img src="/pics/bike/bike-rack-17.jpg" alt="Detached dynabolts" class="img-responsive" /></p>

<p>And attach them to the bracket. You only want to screw them on a little bit as shown here:</p>

<p><img src="/pics/bike/bike-rack-18.jpg" alt="Dynabolts attached to bike rack bracket" class="img-responsive" /></p>

<p>Now line them up with your holes:</p>

<p><img src="/pics/bike/bike-rack-19.jpg" alt="Bike Rack Bracket ready to attach to wall" class="img-responsive" /></p>

<p>And with a good old fashioned hammer, smack them in. Do each one 1 or 2cm at a time. Make sure to follow through with your hammer strokes! You should end up with the bracket against the bricks. Don’t stress if it’s not flush against the bricks. Now for the fun part. Using a wrench, tighten up those bolts. Go crazy!</p>

<p><img src="/pics/bike/bike-rack-22.jpg" alt="Bike Rack Bracket attached to wall" class="img-responsive" /></p>

<p>Now re-attach the bike rack to the bracket using the allen key bolts, and voila, one bike rack!</p>

<p><img src="/pics/bike/bike-rack-23.jpg" alt="Bike Rack attached to wall with bikes" class="img-responsive" /></p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="mounting" /><category term="bike" /><category term="rack" /><category term="hook" /><category term="wall" /><category term="brick" /><category term="dynabolt" /><summary type="html"><![CDATA[Mounting a bike rack to a brick wall ain't easy. Here's how I did it.]]></summary></entry><entry><title type="html">Programming an Arduino to connect a dance pad via USB for StepMania</title><link href="https://lachlanbarclay.net/2022/11/stepmania-ddr-arduino-dance-pad-controller" rel="alternate" type="text/html" title="Programming an Arduino to connect a dance pad via USB for StepMania" /><published>2022-11-16T00:00:00+00:00</published><updated>2022-11-16T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2022/11/StepMania-DDR-Arduino-Dance-Pad-Controller</id><content type="html" xml:base="https://lachlanbarclay.net/2022/11/stepmania-ddr-arduino-dance-pad-controller"><![CDATA[<p>So for the past few months I’ve been getting into DDR on the Wii. It’s great fun and I’m getting pretty good, and what I really need now is to be able to slow down the game to practice some of the faster, trickier sections. But… the game doesn’t support that.</p>

<p>Fortunately <a href="https://www.stepmania.com/">StepMania</a> does, but it only runs on a PC, so to be able to play it there, I need a dance pad that I can plug in via USB.</p>

<p>Unfortunately in Australia they are crazy expensive. <strong>The</strong> one to get is the <a href="https://www.maty-taneczne.pl/shop/dance-mat-ltek-ex-pro-2/">L-TEK hard pad</a> from Poland. However that will cost me like $550.</p>

<p>Luckily, I found the <a href="https://www.ddrgame.com/dance-dance-revolution-pc-energy-metal-stepmania.html">DDR A Game</a> pad on marketplace:</p>

<p><img src="/images/ddr-pad-4.jpg" alt="Stepmania dance pad" class="img-responsive" /></p>

<p>While it looks cool - these particular pads don’t have the best reviews. Apparently they aren’t made particularly well.. but hey, how hard can it be to improve? I also had no idea if it worked, but really… it’s just a bunch of buttons. So I bought it.</p>

<p>The connector at the top of the pad is a “VGA” port (technically a <a href="https://en.wikipedia.org/wiki/D-subminiature">DE-15 d-sub</a>, and it seems like you can buy different connectors for it so that you can plug your pad into a PC, or a PS-2, or a wii, etc. So yeah, I could just <a href="https://www.ddrgame.com/dance-dance-revolution-control-box-4-in-1-blue.html">buy the right one online</a>, but where’s the fun in that? Maybe I can build one myself using an Arduino and have lots of fun in the process?</p>

<p>After a bit of poking around the VGA connector with a multimeter, I found that the middle pin is the ground, and the pads are just simple switches hooked up to individual pins. So all I needed was to connect it up to an Arduino and make it simulate a keyboard.</p>

<p>Here’s my first attempt, I bought a male VGA plug, soldered up the connectors and plugged them into an Arduino Uno:</p>

<p><img src="/images/ddr-pad-1.jpg" alt="Arduino Uno dance pad controller" class="img-responsive" /></p>

<p>And then I programmed it to light up the onboard LED whenever a button is pressed:</p>

<div class="embed-container"><iframe src="https://www.youtube.com/embed/cRqkQR6YpxA" frameborder="0" allowfullscreen=""></iframe></div>

<p>Woo! All the pads/buttons seem to work ok!</p>

<p>All I need now is to emulate a USB keyboard from the Arduino… but it turns out that an Arduino Uno can’t emulate a USB device, what I needed was an <a href="https://docs.arduino.cc/hardware/leonardo">Arduino Leonardo</a>. So I bought one of them, wired it up:</p>

<p><img src="/images/ddr-leonardo.jpg" alt="Arduino Leonardo dance pad controller" class="img-responsive" /></p>

<p>And wrote some code to emulate keyboard presses:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"Keyboard.h"</span><span class="cp">
</span>
<span class="c1">// the number of the onboard LED pin</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">ledPin</span> <span class="o">=</span> <span class="mi">13</span><span class="p">;</span>    

<span class="c1">// these are the inputs on the arduino that we expect the pads to be wired into</span>
<span class="kt">int</span> <span class="n">buttonPin</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span>        <span class="o">=</span> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">12</span><span class="p">};</span>

<span class="c1">// current button states</span>
<span class="kt">int</span> <span class="n">buttonState</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span>      <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">};</span>

<span class="c1">// previous button states</span>
<span class="kt">int</span> <span class="n">prevButtonState</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">};</span>

<span class="c1">// these are the keys that are simulated by the keypad</span>
<span class="kt">int</span> <span class="n">keyboardButtons</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span>  <span class="o">=</span> <span class="p">{</span><span class="n">KEY_RIGHT_ARROW</span><span class="p">,</span> <span class="n">KEY_DOWN_ARROW</span><span class="p">,</span> <span class="n">KEY_UP_ARROW</span><span class="p">,</span> <span class="n">KEY_PAGE_DOWN</span><span class="p">,</span> <span class="n">KEY_PAGE_UP</span><span class="p">,</span> <span class="n">KEY_LEFT_ARROW</span><span class="p">};</span>

<span class="c1">// flag indicating that at least one button is pressed, if true, we light up the onboard LED</span>
<span class="kt">bool</span> <span class="n">buttonPressed</span><span class="p">;</span>

<span class="kt">void</span> <span class="n">setup</span><span class="p">()</span> <span class="p">{</span>

  <span class="c1">// initialize the onboard LED pin as an output  </span>
  <span class="n">pinMode</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>                  
  
  <span class="c1">// initialize the 6 inputs as pullup inputs</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o">&lt;</span><span class="mi">6</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">pinMode</span><span class="p">(</span><span class="n">buttonPin</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">INPUT_PULLUP</span><span class="p">);</span> 
  <span class="p">}</span>

  <span class="c1">// Initialise keyboard</span>
  <span class="n">Keyboard</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">loop</span><span class="p">()</span> <span class="p">{</span>

  <span class="n">buttonPressed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

  <span class="c1">// read the state of the input buttons</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o">&lt;</span><span class="mi">6</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>

    <span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">digitalRead</span><span class="p">(</span><span class="n">buttonPin</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">LOW</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="n">digitalWrite</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>  <span class="c1">// turn led on</span>
      <span class="n">buttonPressed</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// If a button has changed state and it's pressed</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">prevButtonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">LOW</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Keyboard</span><span class="p">.</span><span class="n">press</span><span class="p">(</span><span class="n">keyboardButtons</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
        <span class="n">prevButtonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="c1">// If a button has changed state and it's released</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">prevButtonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">HIGH</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Keyboard</span><span class="p">.</span><span class="n">release</span><span class="p">(</span><span class="n">keyboardButtons</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
        <span class="n">prevButtonState</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">buttonState</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// If none of the buttons are pressed, turn off the LED</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buttonPressed</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">digitalWrite</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>And it worked!</p>

<div class="embed-container"><iframe src="https://www.youtube.com/embed/IZYFiDaa7q4" frameborder="0" allowfullscreen=""></iframe></div>

<p>Here’s a link to the code in github if anyone needs it: <a href="https://github.com/rocklan/ddr-dance-pad">https://github.com/rocklan/ddr-dance-pad</a>.</p>

<p>So what’s the moral of the story? Building stuff yourself is fun. Don’t be afraid to try!</p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="ddr" /><category term="wii" /><category term="stepmania" /><category term="arduino" /><category term="dance" /><category term="pad" /><category term="mat" /><summary type="html"><![CDATA[Programming an Arduino based USB controller for a DDR/StepMania dance pad. How hard can it be?]]></summary></entry><entry><title type="html">Song ‘You are a star’ chord chart</title><link href="https://lachlanbarclay.net/2022/08/you-are-a-star" rel="alternate" type="text/html" title="Song ‘You are a star’ chord chart" /><published>2022-08-07T00:00:00+00:00</published><updated>2022-08-07T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2022/08/You-Are-A-Star</id><content type="html" xml:base="https://lachlanbarclay.net/2022/08/you-are-a-star"><![CDATA[<p>I’ve started playing “Hottest Dance Party 3” on the wii. It’s a bit of fun. Quite impressed with this track, the chord progressions are great and the instrumentation is very cool.</p>

<div class="embed-container"><iframe src="https://www.youtube.com/embed/yAxrz2ZMkvU" frameborder="0" allowfullscreen=""></iframe></div>

<p>Here’s the chart:</p>

<pre>
<b>Verse 1</b>

Bm
It's 5AM, now it's time to get out of bed

Another day is ready to start again

             G
The shower's running, The coffee is burning now

E                                      A  G  F#7
Gotta get out the door in time for the train


<b>Verse 2</b>

Bm
Cell phone is ringing, the message is coming in

The traffic's jammed,  now let's see who is gonna win. yeah the

G              A                    Bsus4              B7 
game is on, if you are strong and   you wanna play...


<b>Pre-Chorus</b>

                Em                   F#/Bb
No one said the race would be just a piece of cake

F#m                  B7
Work your way to the top! You've got to

Em                       E
Put in the time pay your dues

           A                 F#/Bb 
The corner office is there,  Waiting for you!


<b>Chorus:</b>

Emaj7					Eb7             	
You, yeah, you are a star there's no mistaking you will

Abmsus4               F#m  B7
Make it to the end!        I know you've   

Emaj7                  Eb7
got what it takes to believe in yourself 

Abm                 F#m           B7
You will never give in,    you're a star!
       
Emaj7                              F#
No one else in the world could replace the way you smile

Ebm          F#m      B7         Emaj7
The way you look when you're inspired

                Ebm      Abm
I know you will shine no matter what you will face

'Cause you're are a star!

<b>Outro</b> 

Bm x6  

F#m7 x2

Bm

</pre>]]></content><author><name>Lachlan Barclay</name></author><category term="music" /><category term="ddr" /><category term="wii" /><summary type="html"><![CDATA[Chord chart for "You are a star", which is a song featured in "Hottest Dance Party 3" wii game.]]></summary></entry><entry><title type="html">Updating IIS certificates with powershell</title><link href="https://lachlanbarclay.net/2022/01/updating-iis-certificates-with-powershell" rel="alternate" type="text/html" title="Updating IIS certificates with powershell" /><published>2022-01-10T00:00:00+00:00</published><updated>2022-01-10T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2022/01/Updating-IIS-Certificates-With-Powershell</id><content type="html" xml:base="https://lachlanbarclay.net/2022/01/updating-iis-certificates-with-powershell"><![CDATA[<p>Let’s say you were in a situation where you had to update a certificate that was installed on a lot of servers. Do you want to do it all manually? Heck no. Let’s hack together some powershell to make it work. This code is very loosely based on <a href="https://devblogs.microsoft.com/scripting/weekend-scripter-use-powershell-to-update-ssl-bindings/">this one</a> except the typos have been fixed and there’s no magic IP addresses :)</p>

<h3 id="importing-the-certificate-locally">Importing the certificate locally</h3>

<p>Let’s get it working locally first. Within a powershell window, let’s try a few commands. First off, let’s load in our new certificate that we want to install. Assuming that we have a certificate in <code class="language-plaintext highlighter-rouge">pfx</code> format that is ready to be imported, let’s install it into our certificate store:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$certname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"c:\my-certificate.pfx"</span><span class="w">
</span><span class="nv">$certpwd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"certificate-install-password"</span><span class="w">

</span><span class="nv">$pfxpass</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$certpwd</span><span class="w"> </span><span class="o">|</span><span class="n">ConvertTo-SecureString</span><span class="w"> </span><span class="nt">-AsPlainText</span><span class="w"> </span><span class="nt">-Force</span><span class="w">

</span><span class="nv">$newCert</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Import-PfxCertificate</span><span class="w"> </span><span class="nt">-FilePath</span><span class="w"> </span><span class="nv">$certname</span><span class="w"> </span><span class="se">`
</span><span class="w">    </span><span class="nt">-CertStoreLocation</span><span class="w"> </span><span class="s2">"Cert:\LocalMachine\My"</span><span class="w"> </span><span class="se">`
</span><span class="w">    </span><span class="nt">-password</span><span class="w"> </span><span class="nv">$pfxpass</span><span class="w">
</span></code></pre></div></div>

<p>Your new certificate should be installed and viewiable in IIS by clicking on your computer name (not on one of the sites!) and then choosing “Server Certificates”, and you should see something like this:</p>

<p><img src="/images/cert-in-iis.png" alt="certificate showing in IIS" class="img-responsive" /></p>

<p>Great!</p>

<h3 id="updating-iis-to-use-the-new-certificate">Updating IIS to use the new certificate</h3>

<p>Now how about we update one particular binding to use this new certificate:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># load in the admin scripts for doing stuff with IIS:</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Webadministration</span><span class="w">

</span><span class="c"># fetch the default web site:</span><span class="w">
</span><span class="nv">$site</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"IIS:\Sites"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">where</span><span class="w"> </span><span class="p">{(</span><span class="w"> </span><span class="bp">$_</span><span class="o">.</span><span class="nf">Name</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="s2">"Default Web Site"</span><span class="w"> </span><span class="p">)}</span><span class="w">
</span></code></pre></div></div>

<p>You might want to check that your <code class="language-plaintext highlighter-rouge">$site</code> variable has the right web site before you proceed. Just run <code class="language-plaintext highlighter-rouge">echo $site</code> and it should be pretty obvious.</p>

<p>Now let’s fetch the first SSL binding that is mapped to port 443:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="nv">$binding</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$site</span><span class="o">.</span><span class="nf">Bindings</span><span class="o">.</span><span class="nf">Collection</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="err">`</span><span class="w">
    </span><span class="n">where</span><span class="w"> </span><span class="p">{(</span><span class="w"> </span><span class="bp">$_</span><span class="o">.</span><span class="nf">protocol</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="s1">'https'</span><span class="w"> </span><span class="o">-and</span><span class="w"> </span><span class="bp">$_</span><span class="o">.</span><span class="nf">bindingInformation</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="s1">'*:443:'</span><span class="p">)}</span><span class="w">

</span></code></pre></div></div>

<p>and let’s do the magic:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="nv">$binding</span><span class="o">.</span><span class="nf">AddSslCertificate</span><span class="p">(</span><span class="nv">$newCert</span><span class="o">.</span><span class="nf">Thumbprint</span><span class="p">,</span><span class="w"> </span><span class="s2">"my"</span><span class="p">)</span><span class="w">

</span></code></pre></div></div>

<p>BTW, this <code class="language-plaintext highlighter-rouge">AddSslCertificate</code> command doesn’t seem to be documented anywhere, and in my opinion it’s confusingly named - if there’s already an SSL certificate assigned to the binding then it will <em>update</em> it to use the new one. But hey, it works:</p>

<p><img src="/images/cert-binding-updated.png" alt="certificate updated IIS binding" class="img-responsive" /></p>

<p>Woo!</p>

<p>Now this is all well and good, but how do we do this across multiple servers, remotely?</p>

<h2 id="installing-certs-on-multiple-servers">Installing certs on multiple servers</h2>

<p>Let’s copy the cert to a few servers:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$servers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@(</span><span class="s1">'server1'</span><span class="p">,</span><span class="s1">'server2'</span><span class="p">,</span><span class="s1">'server3'</span><span class="p">)</span><span class="w">
</span><span class="nv">$servers</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">foreach-Object</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">copy-item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$certname</span><span class="w"> </span><span class="nt">-Destination</span><span class="w"> </span><span class="s2">"\\</span><span class="bp">$_</span><span class="s2">\c</span><span class="se">`$</span><span class="s2">"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This will copy the cert into the <code class="language-plaintext highlighter-rouge">c:\</code> drive on the desired servers. Note the extra confusing use of the tilde character which you have to use in powershell to escape a dollar sign in a string!</p>

<p>Now let’s connect to all of these servers and import the certificate:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Establish a connection to all of those servers:</span><span class="w">
</span><span class="nv">$session</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-PsSession</span><span class="w"> </span><span class="nt">-ComputerName</span><span class="w"> </span><span class="nv">$servers</span><span class="w">

</span><span class="nv">$importCertificatesCommand</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w">
    </span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Webadministration</span><span class="w">

    </span><span class="nv">$newCert</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Import-PfxCertificate</span><span class="w"> </span><span class="se">`
</span><span class="w">      </span><span class="nt">-FilePath</span><span class="w"> </span><span class="s2">"c:\</span><span class="si">$(</span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">certname</span><span class="si">)</span><span class="s2">"</span><span class="w">  </span><span class="se">`
</span><span class="w">      </span><span class="nt">-CertStoreLocation</span><span class="w"> </span><span class="s2">"Cert:\LocalMachine\My"</span><span class="w"> </span><span class="se">`
</span><span class="w">      </span><span class="nt">-password</span><span class="w"> </span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">pfxpass</span><span class="w">
</span><span class="p">})</span><span class="w">

</span><span class="n">Invoke-Command</span><span class="w"> </span><span class="nt">-session</span><span class="w"> </span><span class="nv">$session</span><span class="w"> </span><span class="nt">-scriptblock</span><span class="w"> </span><span class="nv">$importCertificatesCommand</span><span class="w">
</span></code></pre></div></div>

<p>There’s a bunch of magic here. <code class="language-plaintext highlighter-rouge">New-PsSession</code> creates a new connection to a remote server, for the supplied list of server names. <code class="language-plaintext highlighter-rouge">Invoke-Command</code> executes the desired command against all of the servers that we’ve opened a connection to, and then we are adding the <code class="language-plaintext highlighter-rouge">$using</code> prefix to any variables that we wish to access outside of this command.</p>

<p>Once that’s done, we can delete the certificate off the servers:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Invoke-command</span><span class="w"> </span><span class="nt">-Session</span><span class="w"> </span><span class="nv">$session</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">remove-item</span><span class="w"> </span><span class="nt">-path</span><span class="w"> </span><span class="s2">"c:\</span><span class="si">$(</span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">certname</span><span class="si">)</span><span class="s2">"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Things are starting to look good! We have the cert imported, shall we update IIS to use this new certificate across all our servers, if they are using an older certificate that we know is about to expire? Let’s update our command:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$importCertificatesCommand</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w">

    </span><span class="nv">$newCert</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Import-PfxCertificate</span><span class="w"> </span><span class="se">`
</span><span class="w">      </span><span class="nt">-FilePath</span><span class="w"> </span><span class="s2">"c:\</span><span class="si">$(</span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">certname</span><span class="si">)</span><span class="s2">"</span><span class="w">  </span><span class="se">`
</span><span class="w">      </span><span class="nt">-CertStoreLocation</span><span class="w"> </span><span class="s2">"Cert:\LocalMachine\My"</span><span class="w"> </span><span class="se">`
</span><span class="w">      </span><span class="nt">-password</span><span class="w"> </span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">pfxpass</span><span class="w">

    </span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Webadministration</span><span class="w">

    </span><span class="nv">$sites</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nx">IIS:\Sites</span><span class="w">

    </span><span class="kr">foreach</span><span class="w"> </span><span class="p">(</span><span class="nv">$site</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">$sites</span><span class="p">)</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="kr">foreach</span><span class="w"> </span><span class="p">(</span><span class="nv">$binding</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">$site</span><span class="o">.</span><span class="nf">Bindings</span><span class="o">.</span><span class="nf">Collection</span><span class="p">)</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="nv">$binding</span><span class="o">.</span><span class="nf">protocol</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="s1">'https'</span><span class="p">)</span><span class="w">
            </span><span class="p">{</span><span class="w">
                </span><span class="nv">$search</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Cert:\LocalMachine\My\</span><span class="si">$(</span><span class="nv">$binding</span><span class="o">.</span><span class="nf">certificateHash</span><span class="si">)</span><span class="s2">"</span><span class="w">
                </span><span class="nv">$certs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-path</span><span class="w"> </span><span class="nv">$search</span><span class="w"> </span><span class="nt">-Recurse</span><span class="w">
                </span><span class="nv">$hostname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hostname</span><span class="w">
                
                </span><span class="kr">if</span><span class="w"> </span><span class="p">((</span><span class="nv">$certs</span><span class="o">.</span><span class="nf">count</span><span class="w"> </span><span class="o">-gt</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="o">-and</span><span class="w"> 
                    </span><span class="p">(</span><span class="nv">$certs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="nf">Subject</span><span class="o">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s2">"CN=MyOldCertificate"</span><span class="p">)))</span><span class="w">
                </span><span class="p">{</span><span class="w">
                    </span><span class="n">echo</span><span class="w"> </span><span class="s2">"Updating </span><span class="nv">$hostname</span><span class="s2">, site: </span><span class="se">`"</span><span class="si">$(</span><span class="nv">$site</span><span class="o">.</span><span class="nf">name</span><span class="si">)</span><span class="se">`"</span><span class="s2">, binding: </span><span class="se">`"</span><span class="si">$(</span><span class="nv">$binding</span><span class="o">.</span><span class="nf">bindingInformation</span><span class="si">)</span><span class="se">`"</span><span class="s2">, current cert: </span><span class="se">`"</span><span class="si">$(</span><span class="nv">$certs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="nf">Subject</span><span class="si">)</span><span class="se">`"</span><span class="s2">, Expiry Date: </span><span class="se">`"</span><span class="si">$(</span><span class="nv">$certs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="nf">NotAfter</span><span class="si">)</span><span class="se">`"</span><span class="s2">"</span><span class="w">

                    </span><span class="nv">$binding</span><span class="o">.</span><span class="nf">AddSslCertificate</span><span class="p">(</span><span class="nv">$newCert</span><span class="o">.</span><span class="nf">Thumbprint</span><span class="p">,</span><span class="w"> </span><span class="s2">"my"</span><span class="p">)</span><span class="w">
                </span><span class="p">}</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">})</span><span class="w">
</span></code></pre></div></div>

<p>With a bit of luck, you should see something like this:</p>

<p><img src="/images/cert-output.png" alt="output from script" class="img-responsive" /></p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="powershell" /><category term="iis" /><category term="certificates" /><summary type="html"><![CDATA[Let's say you needed to update your SSL certificates on IIS, across a whole lot of servers. Do you want to do it all manually? Heck no. Let's hack something together in powershell to make it work. The method binding.AddSslCertificate() is the one you're looking for!]]></summary></entry><entry><title type="html">Talking To Doom In 1993 From Discord In 2021</title><link href="https://lachlanbarclay.net/2021/08/talking-to-doom-in-1993-from-discord-in-2021" rel="alternate" type="text/html" title="Talking To Doom In 1993 From Discord In 2021" /><published>2021-08-17T00:00:00+00:00</published><updated>2021-08-17T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2021/08/Talking-To-Doom-In-1993-From-Discord-In-2021</id><content type="html" xml:base="https://lachlanbarclay.net/2021/08/talking-to-doom-in-1993-from-discord-in-2021"><![CDATA[<p>Did you know that multiplayer doom is still alive and kicking? Nope, not Doom Eternal. Not 2016 Doom.. but 1993 doom! Thanks to the legends who work on <a href="https://zandronum.com">Zandronum</a>, you can play deathmatch or co-operative play over the internet using the original doom engine. Except the engine has been improved for the modern age - 3D acceleration, higher resolutions, transparency, better lighting effects… it’s still the original Doom, just <em>more awesomer</em>:</p>

<p><img src="/images/zandronum-aa.png" alt="image-title-here" class="img-responsive" /></p>

<p>A few friends and I started playing Doom online, and we quickly discovered it’s a lot more fun with real-time voice chat. So we added a Discord Server, and we chat over that while we’re playing. It’s great fun! Except… being a nerd… I want <strong>more</strong>.</p>

<p>The problem that I was having was administering the doom server. Simple things like changing maps, changing the difficulty and seeing which players have died, is difficult. It’s hard for anyone else to do, and it’s hard to see what’s going on. What I really wanted was a way of typing commands into Discord and controlling Zandronum. Plus, it would be really nice if we could receive messages from Zandronum, telling us what’s going on.</p>

<h3 id="writing-the-discord-bot">Writing the Discord Bot</h3>

<p>So to get started, I needed to write a c# discord bot. I found a great library named <a href="https://github.com/discord-net/Discord.Net" target="_blank">discord.net</a> that I could use to connect to a discord server and send a message:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">discordClient</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DiscordSocketClient</span><span class="p">();</span>

<span class="n">discordClient</span><span class="p">.</span><span class="n">MessageReceived</span> <span class="p">+=</span> <span class="p">(</span><span class="n">socket</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">Content</span> <span class="p">==</span> <span class="s">"Hi testbot"</span><span class="p">)</span>
        <span class="n">socket</span><span class="p">.</span><span class="n">Channel</span><span class="p">.</span><span class="nf">SendMessageAsync</span><span class="p">(</span><span class="s">"Hi, "</span> <span class="p">+</span> <span class="n">socket</span><span class="p">.</span><span class="n">Author</span><span class="p">.</span><span class="nf">ToString</span><span class="p">());</span>
    <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">await</span> <span class="n">discordClient</span><span class="p">.</span><span class="nf">LoginAsync</span><span class="p">(</span><span class="n">TokenType</span><span class="p">.</span><span class="n">Bot</span><span class="p">,</span> <span class="s">"MyBotTokenCredentials"</span><span class="p">);</span>
</code></pre></div></div>

<p>This little block of code connects to Discord using the <code class="language-plaintext highlighter-rouge">MyBotTokenCredentials</code>, and then if someone types <code class="language-plaintext highlighter-rouge">Hi testbot</code> into the chat it will reply with <code class="language-plaintext highlighter-rouge">Hi -username-</code>. Pretty cool! Of course the first order of the day was to make it reply with the classic Dad joke:</p>

<p><img src="/images/doombot-dadjoke.png" alt="image-title-here" class="img-responsive" /></p>

<p>On reflection this was as good as it was going to get so I probably should have just stopped here. But anyway, there’s a good guide on <a href="https://docs.stillu.cc/guides/getting_started/first-bot.html">setting up a discord.net bot here</a> that I followed if you want to have a crack at it yourself.</p>

<p>So once this was working I needed it to run in Azure, as part of an always-running asp.net application. So I threw it into a <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services">hosted service</a>, which basically looked like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="n">DiscordSocketClient</span> <span class="n">_discordClient</span><span class="p">;</span>
 
<span class="k">protected</span> <span class="k">override</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ExecuteAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">stoppingToken</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// initialise the _discordClient here, then connect:</span>
    <span class="p">...</span>
    <span class="n">_discordClient</span><span class="p">.</span><span class="n">MessageReceived</span> <span class="p">+=</span> <span class="n">_client_MessageReceived</span><span class="p">;</span>
    <span class="k">await</span> <span class="n">_discordClient</span><span class="p">.</span><span class="nf">StartAsync</span><span class="p">();</span>
<span class="p">}</span>
 
<span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">_client_MessageReceived</span><span class="p">(</span><span class="n">SocketMessage</span> <span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s">"!doombot"</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="p">...</span> <span class="k">do</span> <span class="n">something</span> <span class="n">clever</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Freaking sweet. Oh by the way, one little gotcha is that if you want to send stuff to a channel at bot startup, you have to wait until your bot is online:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">discordClient</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">DiscordSocketClient</span><span class="p">();</span>

<span class="kt">bool</span> <span class="n">isReady</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>

<span class="n">discordClient</span><span class="p">.</span><span class="n">Ready</span> <span class="p">+=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="n">isReady</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">await</span> <span class="n">discordClient</span><span class="p">.</span><span class="nf">LoginAsync</span><span class="p">(</span><span class="n">TokenType</span><span class="p">.</span><span class="n">Bot</span><span class="p">,</span> <span class="s">"BotToken"</span><span class="p">);</span>

<span class="k">while</span> <span class="p">(!</span><span class="n">_isReady</span><span class="p">)</span>
    <span class="n">System</span><span class="p">.</span><span class="n">Threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="m">1000</span><span class="p">);</span>

<span class="kt">var</span> <span class="n">botTestingChannel</span> <span class="p">=</span> <span class="n">_discordClient</span>
    <span class="p">.</span><span class="n">Guilds</span>
    <span class="p">.</span><span class="nf">First</span><span class="p">()</span>
    <span class="p">.</span><span class="n">Channels</span>
    <span class="p">.</span><span class="nf">First</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="s">"bot-testing"</span><span class="p">)</span> <span class="k">as</span> <span class="n">IMessageChannel</span><span class="p">;</span>
   
<span class="k">await</span> <span class="n">botTestingChannel</span><span class="p">.</span><span class="nf">SendMessageAsync</span><span class="p">(</span><span class="s">"Hello world!"</span><span class="p">);&lt;/</span><span class="n">pre</span><span class="p">&gt;</span>
</code></pre></div></div>

<p>That one took me a while to figure out!</p>

<p>So that’s the Discord bot side of things worked out. Now we need to get it to talk to our Doom Server, which is named Zandronum.</p>

<h3 id="talking-to-zandronum-over-udp">Talking to Zandronum over UDP</h3>

<p>Zandronum has two different protocols you can use to talk to it. One is named the <a href="https://wiki.zandronum.com/Launcher_protocol">Launcher Protocol</a> that operates over UDP. It deals with unauthenticated requests, so you can query a public doom server and ask some basic questions like ‘how many players are online’ and ‘what is the current map’. Behind the scenes it’s what tools like <a href="https://doomseeker.drdteam.org/">DoomSeeker</a> use.</p>

<p>I say simple, but of course the devil is in the details. The most complicated part is that every message is compressed using <a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman coding</a>… but for some reason with a hard-coded customised unique tree. There is some example code provided in java and python…. but nothing dotnet!</p>

<p>Fortunately, <a href="https://twitter.com/bananaboysam">Sam Izzo</a> is a games developer and a very smart guy, and he was able to get the basics of the UDP Launcher Protocol working. We could send messages to the server! Here’s the console app dumping out some basic info:</p>

<p><img src="/images/zandronum-query.png" alt="image-title-here" class="img-responsive" /></p>

<p>The next step was to try to make it do something more interesting - we needed to be able to authenticate and send through some commands. For that, we need to use the <a href="https://wiki.zandronum.com/RCon_protocol">RCon (Remote Console) Protocol</a>. The protocol is very similar, but we have to do a handshake first, and then we can start streaming messages from the doom console, and sending it commands. In this convoluted screenshot, you can see the Zandronum server running, and you can see the console application in the background dumping out what’s going on:</p>

<p><img src="/images/zandronum-zanstat.png" alt="image-title-here" class="img-responsive" /></p>

<h3 id="zandronum-dotnet-nuget-package">Zandronum dotnet NuGet Package</h3>

<p>Once all of this code was up and running, I bundled it into a <a href="https://github.com/rocklan/zanstat">nuget package</a> so that nobody else has to go through the same pain again! It seems to be reasonably stable but I’m sure there’s always bugs.</p>

<h2 id="putting-it-all-together">Putting it all together</h2>

<p>Now it’s a matter of gluing the two together. We needed to be able to read and write console data from the zandronum server and stream it back to Discord. So let’s do that:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">zandronum</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Zandronum</span><span class="p">(</span><span class="n">_zandronumServer</span><span class="p">,</span> <span class="n">_zandronumPort</span><span class="p">);</span>
<span class="n">zandronum</span><span class="p">.</span><span class="n">Rcon</span><span class="p">.</span><span class="n">ServerMessage</span> <span class="p">+=</span> <span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">eventArgs</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">ZandronumMessageEventArgs</span> <span class="n">ea</span> <span class="p">=</span> <span class="n">e</span> <span class="k">as</span> <span class="n">ZandronumMessageEventArgs</span><span class="p">;</span>
    <span class="n">botTestingChannel</span><span class="p">.</span><span class="nf">SendMessageAsync</span><span class="p">(</span><span class="n">ea</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
<span class="p">};</span>
<span class="n">zandronum</span><span class="p">.</span><span class="n">Rcon</span><span class="p">.</span><span class="nf">ConnectToRcon</span><span class="p">(</span><span class="n">_zandronumRConPassword</span><span class="p">);</span>
</code></pre></div></div>

<p>Now let’s add some bots to a server (in this case, named Chubbs, Crash and Gamma) and see what happens:</p>

<p><img src="/images/doombot-output.png" alt="output of doombot" /></p>

<p>Cool! Now let’s hook up the bot to be able to pass commands back to the server:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">discordClient</span><span class="p">.</span><span class="n">MessageReceived</span> <span class="p">+=</span> <span class="p">(</span><span class="n">socket</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s">"!doom "</span><span class="p">))</span> <span class="p">{</span>
        <span class="kt">string</span> <span class="n">rconCommand</span> <span class="p">=</span> <span class="n">arg</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">Substring</span><span class="p">(</span><span class="m">6</span><span class="p">,</span> <span class="n">arg</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="n">Length</span> <span class="p">-</span> <span class="m">6</span><span class="p">);</span>
        <span class="n">_zandronum</span><span class="p">.</span><span class="n">Rcon</span><span class="p">.</span><span class="nf">SendCommand</span><span class="p">(</span><span class="n">rconCommand</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And let’s give it a shot:</p>

<p><img src="/images/doombot-commands.png" alt="doombot commands" /></p>

<p>So far it’s been running ok and it’s pretty stable. The discord.net library is very nice in that it handles disconnections and re-connects!</p>

<p>The next step would be to implement <a href="https://blog.discord.com/slash-commands-are-here-8db0a385d9e6">discord’s slash commands</a>… but maybe that’s for version 2! :)</p>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="discord" /><category term="c#" /><category term="dotnet" /><category term="zandronum" /><category term="udp" /><category term="nuget" /><summary type="html"><![CDATA[Did you know that multiplayer doom is still alive and kicking? Nope, not Doom Eternal. Not 2016 Doom.. but 1993 doom! Thanks to the legends who work on Zandronum, you can play deathmatch or co-operative play over the internet using the original doom engine. Except the engine has been improved for the modern age - 3D acceleration, higher resolutions, transparency, better lighting effects… it’s still the original Doom, just more awesomer:]]></summary></entry><entry><title type="html">Recommended Multiplayer Doom WADS</title><link href="https://lachlanbarclay.net/2021/08/recommended-multiplayer-doom-wads-copy" rel="alternate" type="text/html" title="Recommended Multiplayer Doom WADS" /><published>2021-08-17T00:00:00+00:00</published><updated>2021-08-17T00:00:00+00:00</updated><id>https://lachlanbarclay.net/2021/08/Recommended-Multiplayer-Doom-WADS%20copy</id><content type="html" xml:base="https://lachlanbarclay.net/2021/08/recommended-multiplayer-doom-wads-copy"><![CDATA[<p>For a while now a bunch of friends and I have been playing co-operative Doom using custom WADS. These WADS are utterly incredible, vastly improving the original doom with mind-blowing map design, new monsters, new weapons, new sounds &amp; new puzzles. The amount of things that can be done with the Doom engine is amazing, and people are constantly pushing the limits.</p>

<p>So why co-op? We started off playing deathmatch but found that playing co-op was more fun. Working together in Survival Mode to beat a map forces a lot of cooperation and planning, and there’s a lot more at stake when you’re on your last life!</p>

<p>Now finding good co-op maps is difficult. There’s thousands to choose from and you don’t want to waste your time on the boring ones. Some of them aren’t designed for cooperative play. A lot of the time I would go through the <a href="https://www.doomworld.com/cacowards/">Cacowards</a>, pick out a few and try them out, but even then it’s a bit of a guess into what is good. Some of them are just too hard, some are too easy… it’s a difficult balancing act.</p>

<p>The following is a list of WADS that we have played so that we can keep track of what we’ve done, and what we’d like to play next.</p>

<h4 id="awesome-co-op--survival-wads-that-are-strongly-recommended">Awesome co-op / survival wads that are strongly recommended</h4>

<ul>
  <li><a href="https://www.doomworld.com/forum/topic/103425-final-release-eviternity/">Eviternity</a> - massive, incredible Mega WAD. Freaking awesome.</li>
  <li><a href="https://doomwiki.org/wiki/Valiant">Valiant</a>  - Skillsaw is a genius</li>
  <li><a href="https://doomwiki.org/wiki/Sunlust">Sunlust</a> - Fun, amazing, very ‘dark and moody’ though.</li>
  <li><a href="http://www.doom2.net/toke/">Toke’s Coop Build</a> - Old school WAD with some great ideas. Super creative.</li>
  <li><a href="https://www.doomworld.com/forum/topic/105215-lost-civilization-22-map-fun-and-nonlinear-megawad-now-with-download-link/">Lost Civilisation</a> - Utterly fantastic WAD</li>
  <li><a href="https://doomwiki.org/wiki/Ancient_Aliens">Ancient Aliens</a> - Amazing WAD. Awesome new monsters, storyline and quirky. So much fun.</li>
  <li>Resurge</li>
</ul>

<h4 id="other-wads-that-were-good-fun">Other WADS that were good fun</h4>

<ul>
  <li><a href="https://www.doomworld.com/idgames/deathmatch/skulltag/du_se">Destination Unknown</a></li>
  <li><a href="https://zandronum.com/forum/viewtopic.php?t=7919">DoomWare</a> - This is one CRAZY WAD. Hilarious, but gets boring pretty quickly!</li>
  <li><a href="https://doomwiki.org/wiki/Antaresian_Reliquary">Antaresian Reliquary</a> - Really good, if a bit unbalanced… MAP04 will absolutely kick your butt. We spent 3 weekends on it!!</li>
</ul>]]></content><author><name>Lachlan Barclay</name></author><category term="technical" /><category term="doom" /><category term="multiplayer" /><category term="zandronum" /><summary type="html"><![CDATA[For a while now a bunch of friends and I have been playing co-operative Doom using custom WADS. These WADS are utterly incredible, vastly improving the original doom with mind-blowing map design, new monsters, new weapons, new sounds &amp; new puzzles. The amount of things that can be done with the Doom engine is amazing, and people are constantly pushing the limits.]]></summary></entry></feed>