Joe ClayJoe Clay's personal website, blog and portfolio. Features articles/tutorials about programming and music. Zola2023-08-10T00:00:00+00:00https://www.seventeencups.net/atom.xmlCustom Piano Roll Labels in Ableton Live2023-08-10T00:00:00+00:002023-08-10T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/custom-piano-roll-labels/<p>When you use a Drum Rack in Ableton Live, the piano roll changes from showing notes to showing drum pads instead:</p>
<p><img src="https://www.seventeencups.net/posts/custom-piano-roll-labels/drum-rack-piano-roll.png" alt="A comparison of the Ableton piano roll with and without a Drum Rack" /></p>
<p>This feature is great - <em>if</em> you're able to use a Drum Rack! But if you're using a VST like Kontakt for your drums, you're stuck with the basic piano roll.</p>
<p>...or so I thought!</p>
<p>As it turns out, there's a way to use Ableton's MIDI Effect Racks to make VST drum libraries give you the same functionality:</p>
<ol>
<li>Drag a MIDI Effect Rack onto your channel.</li>
<li>Create a named chain for each drum sound.</li>
<li>In the Key Zone Editor, map each chain to the note that triggers it.</li>
<li>Right click on one of the chains, and choose 'Show Names in MIDI Editor'.</li>
<li>Save the rack as a preset (or wrap the rack and the VST up in an Instrument Rack, and save that).</li>
</ol>
<p><img src="https://www.seventeencups.net/posts/custom-piano-roll-labels/finished-rack.png" alt="An example MIDI Effect Rack" /></p>
<p>Now if you go to the piano roll, you'll have the same nice UI as you'd get with a Drum Rack!</p>
<p><img src="https://www.seventeencups.net/posts/custom-piano-roll-labels/vst-piano-roll.png" alt="The piano roll with the customized labels" /></p>
<p>As someone with a terrible memory for MIDI mappings, this has been an absolute lifesaver for me, and I hope it's useful for other people too!</p>
Three Years of Tetra2022-01-10T00:00:00+00:002022-01-10T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/three-years-of-tetra/<p>Happy new year!</p>
<p>It's been just over three years since I released the first version of <a href="https://github.com/17cupsofcoffee/tetra">Tetra</a>, my Rust 2D game framework. I also <a href="https://twitter.com/17cupsofcoffee/status/1479601522661109764">recently announced on Twitter</a> that the next version, 0.7, is probably going to be the last one that I work on for the foreseeable future.</p>
<p>With both of these things in mind, I thought it'd be good to take a look back at what went well with Tetra, what didn't go so well, and what I'm planning to do next.</p>
<h2 id="the-good">The good</h2>
<p>Let's start with the good stuff - and there's a lot!</p>
<h3 id="people-actually-used-tetra">People actually used Tetra?!</h3>
<p>This was by far the most surprising (and the most rewarding) part of the whole project. I fully expected absolutely no-one to pay attention to Tetra, but within a few months of the first release, I started seeing people actually make stuff with it!</p>
<p>Not only was this extremely exciting, it was massively educational - this was the first time any project of mine had actually been used by anyone, and working through their questions, bugs and feature requests forced me to learn a lot, both about graphics programming and about managing an open source project.</p>
<p>I have to give special mention to <a href="https://puppetmaster.itch.io/">puppetmaster</a> here - he was the earliest adopter of Tetra, using it for loads of really cool game jam projects (I spent hours playing <a href="https://puppetmaster.itch.io/shoot-out-your-life">Shoot Out Your Life</a>!) and providing a ton of incredibly useful feedback. I don't think I would have gotten nearly as far with it as I did without his support, and I appreciate it a lot!</p>
<h3 id="the-rust-game-dev-community-is-great">The Rust game dev community is great</h3>
<p>The Rust game development community has consistently been one of the most friendly and inclusive online spaces I've been involved in, and this especially goes for the <a href="https://discord.gg/yNtPTb2">Game Development in Rust</a> Discord server. I cannot recommend it enough to anyone who's interested in making games in Rust - there's a genuine sense of community and camaraderie there despite it having over a thousand members, and no matter how simple or complex the question, someone always comes back with a really well-thought out answer.</p>
<p><em>just don't get them started on color spaces again, please, i beg you</em></p>
<p>That friendly atmosphere ripples through the projects that use Rust, as well - over the years I've been working on Tetra, I genuinely can't remember a bad interaction I've had with a user or contributor. That's not something to be taken for granted, given how toxic/entitled the atmosphere around open source can sometimes be.</p>
<h3 id="the-documentation-is-pretty-good">The documentation is pretty good</h3>
<p>If you'll excuse me tooting my own horn just a little bit... I'm very proud of the effort I put in to documenting Tetra, and I think it's one of the most beginner-friendly engines in the ecosystem!</p>
<p>I think the key factor in this is that I actually really enjoy writing documentation, and it was a bit eye-opening to talk to other devs in the community and realize that isn't the case for a lot of (or even most?) people. It's very easy to chalk bad documentation up to laziness, but especially in open-source projects where people are just contributing their spare time, I don't think it's reasonable or realistic to expect every single developer to absolutely love writing. Instead, I think projects need to do a better job of emphasising the importance of non-code contributions, and seeking out people who actually have an interest in that kind of work.</p>
<p>That's easier said than done though - one of my many new years resolutions is to raise PRs when I run into bad docs instead of griping on Twitter :p</p>
<h2 id="the-bad">The bad</h2>
<p>So, given how positive I've just said working on Tetra was, why do I now want to stop? I think there's a few reasons; two technical and one more personal.</p>
<h3 id="the-design-is-flawed">The design is flawed</h3>
<p>Tetra was my first serious Rust project, and my first <em>ever</em> graphics programming project. In fact, I'm pretty sure the folder was still called <code>opengl_tutorial</code> when I published the first commit...</p>
<p>While I think I've done okay considering, there's a lot of design decisions I made when I was less experienced that are now coming back to bite me:</p>
<ul>
<li>There's too much global state in the renderer, and it makes it really hard to write composable code on top of it. For example, <a href="https://github.com/tesselode/egui-tetra"><code>egui-tetra</code></a> leaves your blend mode and scissor rectangle in a completely different state after calling into it, and there's no way for the developer of that library to do anything about it.</li>
<li>The abstractions for text rendering, animation and screen scaling are really limited and hard to extend without making changes to the engine's internals.</li>
<li>The game loop is designed in a way that makes zero sense on the web, as error reporting is done synchronously through function returns.</li>
<li>There's a lot of places where there has to be a seperate internal and external API for the same thing due to borrow checking issues with <code>Context</code>, and it feels gross.</li>
<li>The list goes on...</li>
</ul>
<p>I have ideas about how I'd fix these issues, but to do so, I'd need to basically rewrite half of the framework, and the thought of that just fills me with dread. Do I really have the energy to spend another <em>X</em> years on this, just to get back to feature-parity with where I am now? And would the end result even be recognizable as Tetra? A clean break feels like the least stressful option to me.</p>
<h3 id="general-purpose-game-engines-are-hard-to-extend">General-purpose game engines are hard to extend</h3>
<p>Towards the end of last year, I ran into an issue while working on a game prototype. Tetra's TTF renderer generated non-premultiplied textures, while my project drew everything with premultiplied alpha, so all my text was being drawn as white squares. I was able to work around this by changing the blend mode midway through drawing, but that felt hacky - surely I could fix things in Tetra itself?</p>
<p>If I were working on a private engine, I could just tweak the texture generation code, and the job would be done. But because Tetra is public and other people are relying on it, I had to come up with a whole new API for switching between the old behaviour and the new behaviour, I had to think about consistency with the other font formats, I had to think about whether I should change the defaults, and so on and so forth. What should have been a one line change turned into multiple days of bikeshedding, with my actual use case falling by the wayside.</p>
<p>This was one example of something that happened <em>constantly</em> while I was working on Tetra. Seemingly innoccuous features would explode in complexity due to the fact I was trying to cater to every possible use case while maintaining backward compatibility, and it just left me feeling exhausted.</p>
<p>I'll admit that some of this was self-inflicted - I have a bit of a perfectionist streak, often to my own detriment - but I also think this is just something that comes with the territory when you try to make an engine that's all things to all people. You end up making compromises everywhere, and the end result is much less coherent than if you'd just tried to do one thing well.</p>
<h3 id="engine-development-is-procrastination">Engine development is procrastination</h3>
<p>Here's the <em>big oof</em> for me, looking back on the last three years: my original goal when I started working on Tetra was to make tools that would make me more productive as a game developer, and if we're measuring by that metric, it's been an abject failure. I've released a grand total of <a href="https://17cupsofcoffee.itch.io/lonely-star">one game</a> since I started working on this project, and that was nearly two years ago now. So what went wrong?</p>
<p>I think it's this: writing engine code was a really good way to trick myself into thinking I was being productive without actually making any games. <em>Sure, I've not touched my RPG project in months, but look, canvases support multisampling now,</em> I'd tell myself, completely ignoring the fact that I exclusively make 2D pixel art games that are never going to use it. This was exacerbated by the fact that Tetra was being used by other people - someone would come to me with an interesting feature request and I'd drop everything to implement it.</p>
<p>I think this is a really easy trap for would-be game developers to fall into, especially if you've got the kind of brain that easily gets sidetracked by fun technical puzzles. I know it's a played out saying at this point, but I can't stress it enough - if you want to write games, then <a href="https://geometrian.com/programming/tutorials/write-games-not-engines/">write games, not engines</a>. Your code will be simpler, and you'll have a lot more to show for the time you put in.</p>
<h2 id="what-s-next-for-tetra">What's next for Tetra?</h2>
<p>I definitely plan to keep passively maintaining it, for the short term at least - people have built really cool stuff on top of Tetra, and I would feel awful if I just pulled the rug out from underneath them.</p>
<p>Beyond that, I'm not entirely sure! I don't know how I feel about the idea of handing over Tetra to a completely new maintainer; partly because I'd rather people rally around one of the other engines like GGEZ or Macroquad, and partly because I feel quite protective of the work I've done and don't want to see it go off in the wrong direction. I'm going to give this some thought over the next few months - please let me know if you have any strong opinions!</p>
<h2 id="what-s-next-for-me">What's next for me?</h2>
<p>I'm hoping that 2022 will be the year I actually make games, rather than spending all my time tinkering! I plan on participating in more game jams, and I want to see if I can turn my RPG prototype into something more substantial.</p>
<p>I don't plan on using Tetra for these. Over the Christmas break I tried using SDL2 and OpenGL (via <a href="https://github.com/grovesNL/glow"><code>glow</code></a>) directly, and I'm really enjoying that approach so far! The API I've built on top of them looks fairly Tetra-ish, but I'm very deliberately only building what I need, as I need it, to keep myself focused. Maybe one day I will release some of that as open-source, but if so, it'll probably be in the form of smaller libraries rather than a full engine.</p>
<p>I'm also very eagerly awaiting the public beta of the <a href="https://luxeengine.com/">Luxe</a> game engine, which is supposedly going to be pretty easy to bind to with Rust.</p>
<p>Outside of coding, I also plan on doing a lot more music in 2022. I'm really happy with how <a href="https://www.youtube.com/playlist?list=PLbi0QO1oQ88s-ek9uLbrMyabGB60mM8Z7">my series of game music covers</a> is turning out, and I really want to throw myself into that again this year - if only to justify the obscene amount I spent on VST plugins on Black Friday...</p>
<p>Thanks for all the support!</p>
Why Did Rust Pick the 'If Let' Syntax?2021-01-12T00:00:00+00:002021-01-12T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/why-if-let/<p>One of my favourite features of Rust is that it has excellent support for pattern matching. This is usually done through <code>match</code> expressions, but there is also a shorter syntax that can be used if you only need a single branch, which is called <code>if let</code>.</p>
<p>If you're unfamiliar, a <code>match</code> expression looks like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span style="color:#fac863;">Some</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">);
</span><span>
</span><span style="color:#c594c5;">match</span><span> x </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#fac863;">Some</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) => { </span><span style="color:#5f6364;">// This matches if `x` is `Some`.
</span><span> println!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">",</span><span> y</span><span style="color:#5fb3b3;">); </span><span style="color:#5f6364;">// This prints '123'.
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#5fb3b3;">_ => {} </span><span style="color:#5f6364;">// This matches all other values.
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>And the equivalent <code>if let</code> expression looks like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span style="color:#fac863;">Some</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">);
</span><span>
</span><span style="color:#c594c5;">if let </span><span style="color:#fac863;">Some</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) =</span><span> x </span><span style="color:#5fb3b3;">{ </span><span style="color:#5f6364;">// This matches if `x` is `Some`.
</span><span> println!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">",</span><span> y</span><span style="color:#5fb3b3;">); </span><span style="color:#5f6364;">// This prints '123'.
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>This shortcut syntax, useful as it is, can be quite confusing to new Rust developers. I commonly see people asking things like:</p>
<ul>
<li>Why is that <code>if</code> statement 'backwards'?</li>
<li>Why is there a <code>let</code> in that conditional?</li>
<li>Why do I have to use <code>=</code> instead of <code>==</code>?</li>
</ul>
<p>I had similar questions when I started learning Rust, and to answer them, we need to take a step back and look at the plain old <code>let</code> syntax. It's relevant, I promise!</p>
<p>In most cases, you see <code>let</code> being used to declare a simple named variable, like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">;
</span></code></pre>
<p>What might not be apparent at first glance is that the left side of a <code>let</code> isn't <em>just</em> a name - it's a pattern, the same as what you'd use in the arm of a <code>match</code> expression!</p>
<p>This means that we can actually do pattern matching when declaring variables, like so:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">enum </span><span>Example </span><span style="color:#5fb3b3;">{
</span><span> Data</span><span style="color:#5fb3b3;">(</span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">),
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">); </span><span style="color:#5f6364;">// Wrap the data.
</span><span style="color:#c594c5;">let </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) =</span><span> x</span><span style="color:#5fb3b3;">; </span><span style="color:#5f6364;">// Unwrap ('destructure') the data via a pattern.
</span><span>
</span><span>println!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">",</span><span> y</span><span style="color:#5fb3b3;">); </span><span style="color:#5f6364;">// This prints '123'.
</span></code></pre>
<p>There is, however, one important restriction here compared to when you're using <code>match</code> - in a <code>let</code> statement, you can only use patterns that are 'irrefutable'. In simpler terms, this means that you can only use a pattern which will match for every possible value of the type that you're matching against.</p>
<p>We can see what happens when this <em>isn't</em> the case by adding an extra variant to our <code>Example</code> enum:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">enum </span><span>Example </span><span style="color:#5fb3b3;">{
</span><span> Data</span><span style="color:#5fb3b3;">(</span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">),
</span><span> Oops</span><span style="color:#5fb3b3;">,
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">);
</span><span style="color:#c594c5;">let </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) =</span><span> x</span><span style="color:#5fb3b3;">;
</span><span>
</span><span>println!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">",</span><span> y</span><span style="color:#5fb3b3;">);
</span></code></pre>
<p>This gives us an error which looks like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span>error</span><span style="color:#5fb3b3;">[</span><span>E0005</span><span style="color:#5fb3b3;">]:</span><span> refutable pattern </span><span style="color:#5fb3b3;">in</span><span> local binding</span><span style="color:#5fb3b3;">:</span><span> `Oops` not covered
</span><span> </span><span style="color:#5fb3b3;">--></span><span> src</span><span style="color:#5fb3b3;">/</span><span>main</span><span style="color:#5fb3b3;">.</span><span>rs</span><span style="color:#5fb3b3;">:</span><span style="color:#f99157;">8</span><span style="color:#5fb3b3;">:</span><span style="color:#f99157;">9
</span><span> </span><span style="color:#5fb3b3;">|
</span><span style="color:#f99157;">2 </span><span style="color:#5fb3b3;">| / </span><span style="color:#c594c5;">enum </span><span>Example </span><span style="color:#5fb3b3;">{
</span><span style="color:#f99157;">3 </span><span style="color:#5fb3b3;">| |</span><span> Data</span><span style="color:#5fb3b3;">(</span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">),
</span><span style="color:#f99157;">4 </span><span style="color:#5fb3b3;">| |</span><span> Oops</span><span style="color:#5fb3b3;">,
</span><span> </span><span style="color:#5fb3b3;">| | ----</span><span> not covered
</span><span style="color:#f99157;">5 </span><span style="color:#5fb3b3;">| | }
</span><span> </span><span style="color:#5fb3b3;">| |</span><span>_____</span><span style="color:#5fb3b3;">-</span><span> `Example` defined here
</span><span style="color:#5fb3b3;">...
</span><span style="color:#f99157;">8 </span><span style="color:#5fb3b3;">| </span><span style="color:#c594c5;">let </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) =</span><span> x</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">|</span><span> ^^^^^^^^^^^^^^^^ pattern `Oops` not covered
</span><span> </span><span style="color:#5fb3b3;">|
</span><span> </span><span style="color:#5fb3b3;">=</span><span> note</span><span style="color:#5fb3b3;">:</span><span> `</span><span style="color:#c594c5;">let</span><span>` bindings require an </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">irrefutable pattern</span><span style="color:#5fb3b3;">",</span><span> like a `</span><span style="color:#c594c5;">struct</span><span>` or an `</span><span style="color:#c594c5;">enum</span><span>` with only one variant
</span><span> </span><span style="color:#5fb3b3;">=</span><span> note</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">for</span><span> more information</span><span style="color:#5fb3b3;">,</span><span> visit https</span><span style="color:#5fb3b3;">:</span><span style="color:#5f6364;">//doc.rust-lang.org/book/ch18-02-refutability.html
</span></code></pre>
<p>So if we can't use <code>let</code> for this pattern, what can we use? This is where we circle back to <code>if let</code>!</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">enum </span><span>Example </span><span style="color:#5fb3b3;">{
</span><span> Data</span><span style="color:#5fb3b3;">(</span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">),
</span><span> Oops</span><span style="color:#5fb3b3;">,
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">123</span><span style="color:#5fb3b3;">);
</span><span style="color:#c594c5;">if let </span><span>Example</span><span style="color:#5fb3b3;">::</span><span>Data</span><span style="color:#5fb3b3;">(</span><span>y</span><span style="color:#5fb3b3;">) =</span><span> x </span><span style="color:#5fb3b3;">{
</span><span> println!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">",</span><span> y</span><span style="color:#5fb3b3;">);
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>Notice that this example is almost identical to the previous one that didn't compile - the only changes are the extra <code>if</code>, and the extra block to mark which code should only be run if the pattern matched.</p>
<p>This is the reason that <code>if let</code> looks the way it does - it's an extension of the existing <code>let</code> syntax (which only supports irrefutable patterns) to support all kinds of patterns!</p>
<p>Once I started thinking about it this way, the syntax started to feel a lot more natural, and I hope this post helps make the relationship between <code>let</code> and <code>if let</code> click for other people too.</p>
Simple HTTPS Domain Redirects with Netlify2020-06-19T00:00:00+00:002020-06-19T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/simple-https-domain-redirects-with-netlify/<p>This week, a website that I help maintain ran into some issues with their domain names. I stumbled upon a quick (and free!) solution which I thought was worth sharing, in case it helps anyone else out of a jam.</p>
<p><strong>TL;DR:</strong> You can use a Netlify <a href="https://docs.netlify.com/routing/redirects"><code>_redirects</code></a> file to redirect both HTTP and HTTPS traffic from one domain to another, without paying for server hosting or an SSL certificate.</p>
<h2 id="the-problem">The Problem</h2>
<p><a href="https://arewegameyet.rs/">Are We Game Yet?</a> is a website which lists resources for game developers in the Rust community. It's hosted on Github Pages, and historically it's been available via <code>https://arewegameyet.com</code>. We also had simple HTTP redirects set up on <code>http://arewegameyet.rs</code> and <code>http://arewegameyet.org</code>. </p>
<p>Since the <code>.rs</code> domain makes it a bit clearer that the website is Rust-related, we decided that it'd be good to make this the new primary URL. But that led us to a bit of a conundrum: pretty much all of the existing links to the site, including the ones on Google, point at <code>https://arewegameyet.com</code>.</p>
<p>GitHub Pages doesn't support multiple domains, and our domain providers only support simple HTTP redirects unless we pay through the nose for a VPS and/or a SSL certificate. So for a while, it looked like we wouldn't be able fix these URLs without moving our hosting or increasing our expenditure, neither of which would be ideal.</p>
<h2 id="the-solution">The Solution</h2>
<p><a href="https://www.netlify.com/">Netlify</a> is a web hosting service that's targeted at static sites. They have an extremely generous free tier, which I'm currently using to host both this blog and <a href="https://tetra.seventeencups.net/">Tetra's documentation</a>, and I'll basically tell anyone who'll listen about how they're the best thing since sliced bread.</p>
<p>One of the really cool features of Netlify is that they allow you to specify <a href="https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file">redirect rules</a> as part of your static deployment. You provide a file called <code>_redirects</code>, and their servers will handle mapping the URLs. It's similar to an Apache <code>.htaccess</code> file, but without having to actually manage the server itself.</p>
<p>I've seen a lot of cool tricks done with <code>_redirects</code> files before; for example, <a href="https://kentcdodds.com/">Kent C. Dodds</a> has used them to create his own <a href="https://github.com/kentcdodds/netlify-shortener">URL shortener</a>. So I was curious whether they could be applied to this problem - and as it turned out, thanks to <a href="https://docs.netlify.com/routing/redirects/redirect-options/#splats">splat redirects</a>, they can!</p>
<p>First, we created a dummy <code>index.html</code> file, as Netlify didn't seem to want to deploy a site without one present. Then, alongside it, we created a <code>_redirects</code> file with only two lines:</p>
<pre style="background-color:#2b2c2f;color:#cccece;"><code><span>/* https://arewegameyet.rs/:splat 301!
</span><span>/ https://arewegameyet.rs 301!
</span></code></pre>
<p>The <code>!</code> at the end is important, as it prevents files in the deployment from <a href="https://docs.netlify.com/routing/redirects/rewrites-proxies/#shadowing">shadowing</a> the redirects.</p>
<p>We deployed these two files to Netlify, added our <code>.com</code> and <code>.org</code> domain names to the project, and activated HTTPS via Netlify's <a href="https://letsencrypt.org/">Let's Encrypt</a> integration.</p>
<p>And that's it! Now both domains redirect all of their traffic on both HTTP and HTTPS to the corresponding page on <code>https://arewegameyet.rs</code>, and we didn't have to pay a penny.</p>
<p>Hopefully this is useful to other people who find themselves in a similar situation!</p>
Introducing Tetra2018-12-02T00:00:00+00:002018-12-02T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/introducing-tetra/<p>I'm extremely excited to announce the release of my first public Rust crate! <a href="https://github.com/17cupsofcoffee/tetra">Tetra</a> is a 2D game framework, primarily inspired by the likes of XNA, MonoGame and Raylib.</p>
<p>It's got a lot in common with some of the other Rust game engines (<a href="https://github.com/ggez/ggez">GGEZ</a> especially was a big inspiration with regards to how to structure the API), but some of the nice features are:</p>
<ul>
<li>Draw call batching by default</li>
<li>Deterministic game loop by default (à la <a href="https://gafferongames.com/post/fix_your_timestep/">Fix Your Timestep</a>)</li>
<li>Simplified input handling (an imperative layer over the top of SDL's events)</li>
<li>Pixel-perfect screen scaling (for those chunky retro pixels)</li>
</ul>
<p>They say a code sample is worth a thousand words (or something like that...), so here's one of the examples from the docs:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">extern crate</span><span> tetra</span><span style="color:#5fb3b3;">;
</span><span>
</span><span style="color:#c594c5;">use </span><span>tetra</span><span style="color:#5fb3b3;">::</span><span>glm</span><span style="color:#5fb3b3;">::</span><span>Vec2</span><span style="color:#5fb3b3;">;
</span><span style="color:#c594c5;">use </span><span>tetra</span><span style="color:#5fb3b3;">::</span><span>graphics</span><span style="color:#5fb3b3;">::{</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">,</span><span> Color</span><span style="color:#5fb3b3;">,</span><span> DrawParams</span><span style="color:#5fb3b3;">,</span><span> Texture</span><span style="color:#5fb3b3;">};
</span><span style="color:#c594c5;">use </span><span>tetra</span><span style="color:#5fb3b3;">::</span><span>input</span><span style="color:#5fb3b3;">::{</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">,</span><span> Key</span><span style="color:#5fb3b3;">};
</span><span style="color:#c594c5;">use </span><span>tetra</span><span style="color:#5fb3b3;">::{</span><span>Context</span><span style="color:#5fb3b3;">,</span><span> ContextBuilder</span><span style="color:#5fb3b3;">,</span><span> State</span><span style="color:#5fb3b3;">};
</span><span>
</span><span style="color:#c594c5;">struct </span><span>GameState </span><span style="color:#5fb3b3;">{
</span><span> texture</span><span style="color:#5fb3b3;">:</span><span> Texture,
</span><span> position</span><span style="color:#5fb3b3;">:</span><span> Vec2,
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">impl </span><span>State </span><span style="color:#c594c5;">for </span><span>GameState </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">update</span><span style="color:#5fb3b3;">(&</span><span style="color:#c594c5;">mut </span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">ctx</span><span style="color:#5fb3b3;">: &</span><span style="color:#c594c5;">mut</span><span> Context</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#c594c5;">if </span><span>input</span><span style="color:#5fb3b3;">::</span><span>is_key_down</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, </span><span>Key</span><span style="color:#5fb3b3;">::</span><span>A</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>position</span><span style="color:#5fb3b3;">.</span><span>x </span><span style="color:#5fb3b3;">-= </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#c594c5;">if </span><span>input</span><span style="color:#5fb3b3;">::</span><span>is_key_down</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, </span><span>Key</span><span style="color:#5fb3b3;">::</span><span>D</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>position</span><span style="color:#5fb3b3;">.</span><span>x </span><span style="color:#5fb3b3;">+= </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#c594c5;">if </span><span>input</span><span style="color:#5fb3b3;">::</span><span>is_key_down</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, </span><span>Key</span><span style="color:#5fb3b3;">::</span><span>W</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>position</span><span style="color:#5fb3b3;">.</span><span>y </span><span style="color:#5fb3b3;">-= </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#c594c5;">if </span><span>input</span><span style="color:#5fb3b3;">::</span><span>is_key_down</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, </span><span>Key</span><span style="color:#5fb3b3;">::</span><span>S</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>position</span><span style="color:#5fb3b3;">.</span><span>y </span><span style="color:#5fb3b3;">+= </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">draw</span><span style="color:#5fb3b3;">(&</span><span style="color:#c594c5;">mut </span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">ctx</span><span style="color:#5fb3b3;">: &</span><span style="color:#c594c5;">mut</span><span> Context, </span><span style="color:#f99157;">_dt</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">f64</span><span style="color:#5fb3b3;">) {
</span><span> graphics</span><span style="color:#5fb3b3;">::</span><span>clear</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, </span><span>Color</span><span style="color:#5fb3b3;">::</span><span>rgb</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">0.769</span><span style="color:#5fb3b3;">, </span><span style="color:#f99157;">0.812</span><span style="color:#5fb3b3;">, </span><span style="color:#f99157;">0.631</span><span style="color:#5fb3b3;">));
</span><span>
</span><span> graphics</span><span style="color:#5fb3b3;">::</span><span>draw</span><span style="color:#5fb3b3;">(
</span><span> ctx</span><span style="color:#5fb3b3;">,
</span><span> </span><span style="color:#5fb3b3;">&</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>texture</span><span style="color:#5fb3b3;">,
</span><span> DrawParams</span><span style="color:#5fb3b3;">::</span><span>new</span><span style="color:#5fb3b3;">()
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">position</span><span style="color:#5fb3b3;">(</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>position</span><span style="color:#5fb3b3;">)
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">origin</span><span style="color:#5fb3b3;">(</span><span>Vec2</span><span style="color:#5fb3b3;">::</span><span>new</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">8.0</span><span style="color:#5fb3b3;">, </span><span style="color:#f99157;">8.0</span><span style="color:#5fb3b3;">)),
</span><span> </span><span style="color:#5fb3b3;">);
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">main</span><span style="color:#5fb3b3;">() -> </span><span>tetra</span><span style="color:#5fb3b3;">::</span><span>Result </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">let</span><span> ctx </span><span style="color:#5fb3b3;">= &</span><span style="color:#c594c5;">mut </span><span>ContextBuilder</span><span style="color:#5fb3b3;">::</span><span>new</span><span style="color:#5fb3b3;">()
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">title</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">Keyboard Input</span><span style="color:#5fb3b3;">")
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">size</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">160</span><span style="color:#5fb3b3;">, </span><span style="color:#f99157;">144</span><span style="color:#5fb3b3;">)
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">scale</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">4</span><span style="color:#5fb3b3;">)
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">resizable</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">true</span><span style="color:#5fb3b3;">)
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">quit_on_escape</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">true</span><span style="color:#5fb3b3;">)
</span><span> </span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">build</span><span style="color:#5fb3b3;">()?;
</span><span>
</span><span> </span><span style="color:#c594c5;">let</span><span> state </span><span style="color:#5fb3b3;">= &</span><span style="color:#c594c5;">mut</span><span> GameState </span><span style="color:#5fb3b3;">{
</span><span> texture</span><span style="color:#5fb3b3;">: </span><span>Texture</span><span style="color:#5fb3b3;">::</span><span>new</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">, "</span><span style="color:#99c794;">./examples/resources/player.png</span><span style="color:#5fb3b3;">")?,
</span><span> position</span><span style="color:#5fb3b3;">: </span><span>Vec2</span><span style="color:#5fb3b3;">::</span><span>new</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">160.0 </span><span style="color:#5fb3b3;">/ </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">, </span><span style="color:#f99157;">144.0 </span><span style="color:#5fb3b3;">/ </span><span style="color:#f99157;">2.0</span><span style="color:#5fb3b3;">),
</span><span> </span><span style="color:#5fb3b3;">};
</span><span>
</span><span> tetra</span><span style="color:#5fb3b3;">::</span><span>run</span><span style="color:#5fb3b3;">(</span><span>ctx</span><span style="color:#5fb3b3;">,</span><span> state</span><span style="color:#5fb3b3;">)
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>The API is likely to evolve a lot over the first few versions as I actually start using the framework for my own projects, but hopefully that's enough to give you a gist of whether or not you'll like Tetra. If you're interested, please give it a try, and let me know what you think on <a href="https://twitter.com/17cupsofcoffee">Twitter</a> or <a href="https://bit.ly/rust-community">Discord</a>.</p>
How Lua Avoids Semicolons2018-04-03T00:00:00+00:002018-04-03T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/how-lua-avoids-semicolons/<p>My current pet project outside of work is developing a little programming language called <a href="https://github.com/17cupsofcoffee/ein">Ein</a>. I decided fairly early on in development that I didn't want Ein to have semicolons, so I've spent a fair chunk of the past week investigating how other languages make this work.</p>
<p>Lua's solution to this problem is (in my opinion) fairly nifty, so I thought I'd write about it on the off-chance that someone else will find it as interesting as I do 😄</p>
<h2 id="the-problem">The Problem</h2>
<p>First, some background - why is getting rid of semicolons tricky? Can't we just remove them from our language's grammar and be done with it?</p>
<p>The answer to this question can be summed up in one snippet of pseudo-code:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c594c5;">let</span><span> x </span><span style="color:#5fb3b3;">= </span><span style="color:#f99157;">1 </span><span style="color:#5f6364;">// Does the statement end here?
</span><span style="color:#5fb3b3;">- </span><span style="color:#f99157;">1 </span><span style="color:#5f6364;">// Or does it end here?
</span></code></pre>
<p>How does our language's parser decide whether this should be one statement (<code>let x = 1 - 1</code>) or two (<code>let x = 1</code> followed by <code>-1</code>)? In the parser's eyes, they're both perfectly valid!</p>
<h2 id="the-potential-solutions">The (Potential) Solutions</h2>
<p>There's several ways that languages try to get around this problem. Some make the whitespace in their language significant, like Python. Others, like Go, try to insert the semicolons for you behind the scenes based on <a href="https://golang.org/ref/spec#Semicolons">a set of rules</a>.</p>
<p>Personally though, I'm not a fan of those solutions. Making whitespace have meaning rubs me the wrong way for reasons I don't quite understand, and automatic semicolon insertion feels like placing too much trust in the compiler to 'guess' where I meant for the statements to end.</p>
<h2 id="how-lua-does-it">How Lua Does It</h2>
<p>Lua's syntax is unambigous, even if you leave out all the semicolons and nice formatting, and the main way it achieves this is by dropping a feature a lot of us take for granted - <em>expressions-as-statements</em>.</p>
<p>In most languages, it's perfectly valid to use an expression (a piece of code that can be evaluated to get a value, like adding two numbers together) in the same place that you would use a statement (a piece of code run for its side effects, like a variable declaration).</p>
<p>Lua takes a much more hardline stance on this - programs are a list of statements, some statements may contain expressions (like the condition of an <code>if</code>), but expressions are <em>not</em> allowed to be used as statements.</p>
<p>Let's go back to our original example, translated to Lua:</p>
<pre data-lang="lua" style="background-color:#2b2c2f;color:#cccece;" class="language-lua "><code class="language-lua" data-lang="lua"><span style="color:#c594c5;">local </span><span>x </span><span style="color:#5fb3b3;">= </span><span style="color:#f99157;">1 </span><span style="color:#5f6364;">-- Does the statement end here?
</span><span style="color:#5fb3b3;">- </span><span style="color:#f99157;">1 </span><span style="color:#5f6364;">-- Or does it end here?
</span></code></pre>
<p>In Lua, a variable declaration is a statement, but <code>-1</code> is an expression - therefore, the only valid way of interpreting this code is <code>local x = 1 - 1</code>!</p>
<h2 id="what-s-the-catch">What's The Catch?</h2>
<p>Ah yes, there's always a catch, and in this case it's a fairly obvious one: what if I <em>want</em> to run an expression for its side effects?</p>
<p>For example, a lot of the time you'll want to use the return value of a function, but sometimes you'll just want to run it. Lua caters for this scenario by making an exception to the rule, allowing function calls to be used both in statement and expression position.</p>
<p>This is one of the only places that Lua bends the rules, however. In some languages, you can use the short circuting behavior of the logical AND/OR operators as short and sweet control flow statements:</p>
<pre data-lang="js" style="background-color:#2b2c2f;color:#cccece;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#5f6364;">// JavaScript
</span><span>isActive </span><span style="color:#5fb3b3;">&& </span><span style="color:#6699cc;">doSomething</span><span>()</span><span style="color:#5fb3b3;">;
</span></code></pre>
<p>The equivalent in Lua isn't valid unless you <a href="http://lua-users.org/wiki/ExpressionsAsStatements">assign the result to a temporary variable:</a></p>
<pre data-lang="lua" style="background-color:#2b2c2f;color:#cccece;" class="language-lua "><code class="language-lua" data-lang="lua"><span style="color:#c594c5;">local </span><span>_ </span><span style="color:#5fb3b3;">= </span><span>isActive </span><span style="color:#5fb3b3;">and </span><span style="color:#6699cc;">doSomething</span><span style="color:#5fb3b3;">()
</span><span style="color:#5f6364;">-- _ has no special meaning - just a common Lua
</span><span style="color:#5f6364;">-- naming convention for throwing away variables!
</span></code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Thank you for reading! I hope I didn't bore you to death rambling on about semicolons for $x words! ❤️</p>
<p>If you're interested in this kind of thing, I'd recommend taking a look at the <a href="http://www.lua.org/manual/5.3/manual.html#9">full grammar for Lua</a> from its reference manual - it's really short and really clean, and there's a lot of really interesting design decisions in there which I'm interested in digging deeper into.</p>
Writing an Audio Plugin in Rust2017-03-25T00:00:00+00:002017-03-25T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/writing-an-audio-plugin-in-rust/<p>Along the long and slightly convoluted path I took to finding my current career as a software developer, I ended up spending a year at college studying music. It didn't really amount to much in the way of gainful employment, but <a href="https://soundcloud.com/17cupsofcoffee">it's still something I'm really passionate about</a>, and I'd really like to find more ways of combining it with my love of programming.</p>
<p>So when I stumbled across the <a href="https://github.com/overdrivenpotato/rust-vst2/"><code>vst2</code> Rust crate</a> (created by the delightfully named 'overdrivenpotato'), I couldn't resist taking some time out of my Saturday to check it out.</p>
<p><img src="https://www.seventeencups.net/posts/writing-an-audio-plugin-in-rust/nerd_sniping.png" alt="There's always a relevant XKCD." /></p>
<p>I'm no audio programming wizard, so I'm going to start really simple by throwing together a bare-bones digital distortion effect, based on the algorithm from <a href="http://www.martin-finke.de/blog/articles/audio-plugins-005-digital-distortion/">this tutorial by Martin Finke</a>. You can find the final source code on GitHub <a href="https://github.com/17cupsofcoffee/digidist">here</a>.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you'll need:</p>
<ul>
<li>The latest stable version of <a href="https://www.rust-lang.org/en-US/">Rust</a> - 1.16.0, at the time of writing.</li>
<li>A VST host to test the plugin with - I'm using Ableton Live 9, but if you don't own a DAW you can download the evaluation version of <a href="http://www.reaper.fm/">Reaper</a> for free.</li>
</ul>
<p>It's worth noting that I'm running the 64-bit version of Ableton on Windows - if you're using a 32-bit plugin host, you'll need to compile your plugin with the 32-bit Rust toolchain. If you're on Mac, you can use <a href="https://github.com/overdrivenpotato/rust-vst2/blob/master/osx_vst_bundler.sh">zywicki's handy shell script</a> to package the library in a format that will work for you.</p>
<h2 id="the-bare-minimum">The Bare Minimum</h2>
<p>Let's start as simple as possible, by getting a completely empty VST plugin loading into our host. All it'll do is pass through audio unaltered - not very exciting, but it'll prove our code works.</p>
<p>First, create a new Rust library using Cargo:</p>
<pre data-lang="bash" style="background-color:#2b2c2f;color:#cccece;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#6699cc;">cargo new digidist
</span><span style="color:#6699cc;">cd digidist
</span></code></pre>
<p>In our <code>cargo.toml</code>, let's add a dependency on <code>vst2</code>. While we're at it, we'll also set the type of our crate to <code>dylib</code>, so that the compiler will output a dynamic library.</p>
<pre data-lang="toml" style="background-color:#2b2c2f;color:#cccece;" class="language-toml "><code class="language-toml" data-lang="toml"><span style="color:#5fb3b3;">[package]
</span><span style="color:#eb606b;">name </span><span style="color:#5fb3b3;">= "</span><span style="color:#99c794;">digidist</span><span style="color:#5fb3b3;">"
</span><span style="color:#eb606b;">version </span><span style="color:#5fb3b3;">= "</span><span style="color:#99c794;">0.1.0</span><span style="color:#5fb3b3;">"
</span><span style="color:#eb606b;">authors </span><span style="color:#5fb3b3;">= ["</span><span style="color:#99c794;">Joe Clay <27cupsofcoffee@gmail.com></span><span style="color:#5fb3b3;">"]
</span><span>
</span><span style="color:#5fb3b3;">[dependencies]
</span><span style="color:#eb606b;">vst2 </span><span style="color:#5fb3b3;">= "</span><span style="color:#99c794;">0.0.1</span><span style="color:#5fb3b3;">"
</span><span>
</span><span style="color:#5fb3b3;">[lib]
</span><span style="color:#eb606b;">crate-type </span><span style="color:#5fb3b3;">= ["</span><span style="color:#99c794;">dylib</span><span style="color:#5fb3b3;">"]
</span></code></pre>
<p>Now we can actually start laying the groundwork for our plugin. In order for the <code>vst2</code> crate to work its magic, we need to do three things:</p>
<ul>
<li>Create a struct that implements <code>Default</code>, to represent our plugin.</li>
<li>Implement the <a href="http://overdrivenpotato.github.io/rust-vst2/vst2/plugin/trait.Plugin.html"><code>Plugin</code></a> trait on our struct. This trait features many methods (some of which we'll go over later), but all of them except <a href="http://overdrivenpotato.github.io/rust-vst2/vst2/plugin/trait.Plugin.html#tymethod.get_info"><code>get_info</code></a> provide default implementations. </li>
<li>Call the <a href="http://overdrivenpotato.github.io/rust-vst2/src/vst2/src/lib.rs.html#144-165"><code>plugin_main!</code></a> macro with our struct - this generates the code that allows a host to hook into our library, as per the VST specification. </li>
</ul>
<p>With all that in place, our code should look something like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#5f6364;">// lib.rs
</span><span>
</span><span style="color:#5fb3b3;">#[</span><span>macro_use</span><span style="color:#5fb3b3;">] </span><span style="color:#c594c5;">extern crate</span><span> vst2</span><span style="color:#5fb3b3;">;
</span><span>
</span><span style="color:#c594c5;">use </span><span>vst2</span><span style="color:#5fb3b3;">::</span><span>buffer</span><span style="color:#5fb3b3;">::</span><span>AudioBuffer</span><span style="color:#5fb3b3;">;
</span><span style="color:#c594c5;">use </span><span>vst2</span><span style="color:#5fb3b3;">::</span><span>plugin</span><span style="color:#5fb3b3;">::{</span><span>Plugin</span><span style="color:#5fb3b3;">,</span><span> Info</span><span style="color:#5fb3b3;">};
</span><span>
</span><span style="color:#5fb3b3;">#[</span><span>derive</span><span style="color:#5fb3b3;">(</span><span>Default</span><span style="color:#5fb3b3;">)]
</span><span style="color:#c594c5;">struct </span><span>DigiDist</span><span style="color:#5fb3b3;">;
</span><span>
</span><span style="color:#c594c5;">impl </span><span>Plugin </span><span style="color:#c594c5;">for </span><span>DigiDist </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_info</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span style="color:#5fb3b3;">) -></span><span> Info </span><span style="color:#5fb3b3;">{
</span><span> Info </span><span style="color:#5fb3b3;">{
</span><span> name</span><span style="color:#5fb3b3;">: "</span><span style="color:#99c794;">DigiDist</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> vendor</span><span style="color:#5fb3b3;">: "</span><span style="color:#99c794;">17cupsofcoffee</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> unique_id</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">25032017</span><span style="color:#5fb3b3;">,
</span><span>
</span><span> </span><span style="color:#5f6364;">// fill in the rest with the default values
</span><span> </span><span style="color:#5fb3b3;">..</span><span>Info</span><span style="color:#5fb3b3;">::</span><span>default</span><span style="color:#5fb3b3;">()
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span>plugin_main!</span><span style="color:#5fb3b3;">(</span><span>DigiDist</span><span style="color:#5fb3b3;">);
</span></code></pre>
<p>Believe it or not, that's all that's required to create our bare minimum VST! Run <code>cargo build</code>, and then copy <code>target/debug/digidist.dll</code> into your host's plugins folder. All being well, you should be able to load it onto a track:</p>
<p><img src="https://www.seventeencups.net/posts/writing-an-audio-plugin-in-rust/ui1.png" alt="Our empty plugin running in Ableton" /></p>
<h2 id="audio-mangling">Audio Mangling</h2>
<div class="post-body__note">
<p class="post-body__note__text">
Before we start writing our actual audio processing code, a quick warning - make sure that you turn down your volume, and place a limiter on your host's audio output when developing VSTs! Your ears will thank you the first time you run into a bug.
</p>
</div>
<p>Our digital distortion plugin is going to have:</p>
<ul>
<li>One parameter - the loudness threshold at which to begin clipping the audio signal</li>
<li>Two inputs (left and right)</li>
<li>Two outputs (left and right)</li>
</ul>
<p>We can add this information to our plugin with very little effort:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#5f6364;">// lib.rs
</span><span>
</span><span style="color:#c594c5;">impl </span><span>Plugin </span><span style="color:#c594c5;">for </span><span>DigiDist </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_info</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span style="color:#5fb3b3;">) -></span><span> Info </span><span style="color:#5fb3b3;">{
</span><span> Info </span><span style="color:#5fb3b3;">{
</span><span> name</span><span style="color:#5fb3b3;">: "</span><span style="color:#99c794;">DigiDist</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> vendor</span><span style="color:#5fb3b3;">: "</span><span style="color:#99c794;">17cupsofcoffee</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> unique_id</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">25032017</span><span style="color:#5fb3b3;">,
</span><span>
</span><span> inputs</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">2</span><span style="color:#5fb3b3;">,
</span><span> outputs</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">2</span><span style="color:#5fb3b3;">,
</span><span> parameters</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">1</span><span style="color:#5fb3b3;">,
</span><span>
</span><span> </span><span style="color:#5fb3b3;">..</span><span>Info</span><span style="color:#5fb3b3;">::</span><span>default</span><span style="color:#5fb3b3;">()
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>We're going to have to store that parameter somewhere, so let's add a field to our struct. While we're at it, we'll also manually implement <code>Default</code>, so that our threshold slider will start at the maximum value (i.e. no distortion) rather than at zero.</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#5f6364;">// lib.rs
</span><span>
</span><span style="color:#c594c5;">struct </span><span>DigiDist </span><span style="color:#5fb3b3;">{
</span><span> threshold</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">f32
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">impl </span><span>Default </span><span style="color:#c594c5;">for </span><span>DigiDist </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">default</span><span style="color:#5fb3b3;">() -></span><span> DigiDist </span><span style="color:#5fb3b3;">{
</span><span> DigiDist </span><span style="color:#5fb3b3;">{
</span><span> threshold</span><span style="color:#5fb3b3;">: </span><span style="color:#f99157;">1.0
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>In order to link this field up to our VST's interface in the host application, we need to implement five methods from the <code>Plugin</code> trait, which all take an integer index:</p>
<ul>
<li><code>get_parameter</code> - gets the parameter's actual value, from 0.0 to 1.0</li>
<li><code>set_parameter</code> - sets the parameter's actual value, from 0.0 to 1.0</li>
<li><code>get_parameter_name</code> - gets the text that should be displayed as the parameter's name in the host application</li>
<li><code>get_parameter_text</code> - gets the text that should be displayed as the parameter's value in the host application</li>
<li><code>get_parameter_label</code> - gets the label that should be displayed next to the value (e.g. dB, %, On/Off)</li>
</ul>
<p>For our plugin, the implementations will look like this:</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#5f6364;">// lib.rs, inside 'impl Plugin for DigiDist'
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_parameter</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">index</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">) -> </span><span style="color:#c594c5;">f32 </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">match</span><span> index </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#f99157;">0 </span><span style="color:#5fb3b3;">=> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold</span><span style="color:#5fb3b3;">,
</span><span> </span><span style="color:#5fb3b3;">_ => </span><span style="color:#f99157;">0.0</span><span style="color:#5fb3b3;">,
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">set_parameter</span><span style="color:#5fb3b3;">(&</span><span style="color:#c594c5;">mut </span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">index</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">i32</span><span>, </span><span style="color:#f99157;">value</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">f32</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#c594c5;">match</span><span> index </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#5f6364;">// We don't want to divide by zero, so we'll clamp the value
</span><span> </span><span style="color:#f99157;">0 </span><span style="color:#5fb3b3;">=> </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold </span><span style="color:#5fb3b3;">=</span><span> value</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">max</span><span style="color:#5fb3b3;">(</span><span style="color:#f99157;">0.01</span><span style="color:#5fb3b3;">),
</span><span> </span><span style="color:#5fb3b3;">_ => (),
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_parameter_name</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">index</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">) -></span><span> String </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">match</span><span> index </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#f99157;">0 </span><span style="color:#5fb3b3;">=> "</span><span style="color:#99c794;">Threshold</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> </span><span style="color:#5fb3b3;">_ => "".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_parameter_text</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">index</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">) -></span><span> String </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">match</span><span> index </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#5f6364;">// Convert to a percentage
</span><span> </span><span style="color:#f99157;">0 </span><span style="color:#5fb3b3;">=> </span><span>format!</span><span style="color:#5fb3b3;">("</span><span style="color:#99c794;">{}</span><span style="color:#5fb3b3;">", </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold </span><span style="color:#5fb3b3;">* </span><span style="color:#f99157;">100.0</span><span style="color:#5fb3b3;">),
</span><span> </span><span style="color:#5fb3b3;">_ => "".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">get_parameter_label</span><span style="color:#5fb3b3;">(&</span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">index</span><span style="color:#5fb3b3;">: </span><span style="color:#c594c5;">i32</span><span style="color:#5fb3b3;">) -></span><span> String </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#c594c5;">match</span><span> index </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#f99157;">0 </span><span style="color:#5fb3b3;">=> "</span><span style="color:#99c794;">%</span><span style="color:#5fb3b3;">".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> </span><span style="color:#5fb3b3;">_ => "".</span><span style="color:#6699cc;">to_string</span><span style="color:#5fb3b3;">(),
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>Now for the exciting part - let's mangle some audio! The algorithm for this distortion effect is going to be very, very simple:</p>
<pre style="background-color:#2b2c2f;color:#cccece;"><code><span>// pseudo-code
</span><span>
</span><span>if input >= 0
</span><span> output = min(input, threshold) / threshold
</span><span>else
</span><span> output = max(input, -threshold) / threshold
</span></code></pre>
<p>Thanks to the nicely designed buffer API provided by the vst2 crate, our actual processing code looks pretty much the same!</p>
<pre data-lang="rust" style="background-color:#2b2c2f;color:#cccece;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#5f6364;">// lib.rs, inside 'impl Plugin for DigiDist'
</span><span>
</span><span style="color:#c594c5;">fn </span><span style="color:#6699cc;">process</span><span style="color:#5fb3b3;">(&</span><span style="color:#c594c5;">mut </span><span style="color:#f99157;">self</span><span>, </span><span style="color:#f99157;">buffer</span><span style="color:#5fb3b3;">: </span><span>AudioBuffer</span><span style="color:#5fb3b3;"><</span><span style="color:#c594c5;">f32</span><span style="color:#5fb3b3;">>) {
</span><span> </span><span style="color:#5f6364;">// Split out the input and output buffers into two vectors
</span><span> </span><span style="color:#c594c5;">let </span><span style="color:#5fb3b3;">(</span><span>inputs</span><span style="color:#5fb3b3;">,</span><span> outputs</span><span style="color:#5fb3b3;">) =</span><span> buffer</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">split</span><span style="color:#5fb3b3;">();
</span><span>
</span><span> </span><span style="color:#5f6364;">// For each buffer, transform the samples
</span><span> </span><span style="color:#c594c5;">for </span><span style="color:#5fb3b3;">(</span><span>input_buffer</span><span style="color:#5fb3b3;">,</span><span> output_buffer</span><span style="color:#5fb3b3;">) in</span><span> inputs</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">iter</span><span style="color:#5fb3b3;">().</span><span style="color:#6699cc;">zip</span><span style="color:#5fb3b3;">(</span><span>outputs</span><span style="color:#5fb3b3;">) {
</span><span> </span><span style="color:#c594c5;">for </span><span style="color:#5fb3b3;">(</span><span>input_sample</span><span style="color:#5fb3b3;">,</span><span> output_sample</span><span style="color:#5fb3b3;">) in</span><span> input_buffer</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">iter</span><span style="color:#5fb3b3;">().</span><span style="color:#6699cc;">zip</span><span style="color:#5fb3b3;">(</span><span>output_buffer</span><span style="color:#5fb3b3;">) {
</span><span>
</span><span> </span><span style="color:#c594c5;">if </span><span style="color:#5fb3b3;">*</span><span>input_sample </span><span style="color:#5fb3b3;">>= </span><span style="color:#f99157;">0.0 </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#5fb3b3;">*</span><span>output_sample </span><span style="color:#5fb3b3;">=</span><span> input_sample</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">min</span><span style="color:#5fb3b3;">(</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold</span><span style="color:#5fb3b3;">) / </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#c594c5;">else </span><span style="color:#5fb3b3;">{
</span><span> </span><span style="color:#5fb3b3;">*</span><span>output_sample </span><span style="color:#5fb3b3;">=</span><span> input_sample</span><span style="color:#5fb3b3;">.</span><span style="color:#6699cc;">max</span><span style="color:#5fb3b3;">(-</span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold</span><span style="color:#5fb3b3;">) / </span><span style="color:#ec5f67;">self</span><span style="color:#5fb3b3;">.</span><span>threshold</span><span style="color:#5fb3b3;">;
</span><span> </span><span style="color:#5fb3b3;">}
</span><span>
</span><span> </span><span style="color:#5fb3b3;">}
</span><span> </span><span style="color:#5fb3b3;">}
</span><span style="color:#5fb3b3;">}
</span></code></pre>
<p>That's it, we're done! Compile your code, install the DLL into your host, and let's see how it looks and sounds. Turn down your volume - Soundcloud's embeds are always way too loud!</p>
<p><img src="https://www.seventeencups.net/posts/writing-an-audio-plugin-in-rust/ui2.png" alt="Look, parameters!" /></p>
<p><iframe title="Example Audio" width="100%" height="450" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/314423898%3Fsecret_token%3Ds-C1L7o&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe></p>
<p>Hm. I'm not exactly Aphex Twin, am I? But it's a start!</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Hopefully you found this quick tour of the <code>vst2</code> crate as fun as I did - I'm floored by how quickly I was able to go from "hey, this sounds like it'd be a cool idea" to having a working plugin!</p>
<p>Honestly, that's my experience of Rust in a nutshell. This language makes so many of the things that used to only be the realm of experienced C++ programmers accessible to the rest of us - I find that really exciting!</p>
Building a MUD with F# and Akka.NET - Part One2017-03-12T00:00:00+00:002017-03-12T00:00:00+00:00Unknownhttps://www.seventeencups.net/posts/building-a-mud-with-f-sharp-and-akka-net-part-one/<p>I feel like no matter how many languages I try, <a href="https://www.youtube.com/watch?v=UPw-3e_pzqU">I always keep getting drawn back to F#</a>. It's got just about everything I love about functional languages like Elm, Haskell and OCaml, while still giving me access to the mountain of great open source libraries that are available in the .NET ecosystem.</p>
<p>This week, I've been taking a look at <a href="http://getakka.net/">Akka.NET</a>, an unofficial (but extremely polished) port of the popular Java/Scala actor framework. It's primarily designed for C#, but they provide an add-on package that exposes a really nice, idiomatic F# API. </p>
<p>As a little exercise for myself (and so my new blog wouldn't be quite so empty!), I thought it'd be fun to try to write a <a href="https://en.wikipedia.org/wiki/MUD">MUD</a> using this API. Given that there's quite a lot of <a href="http://www.kotancode.com/2012/01/29/building-a-mud-in-scala-revisited/">prior art</a> for this, it shouldn't be too hard to get started with, but time will tell if this is a little over-ambitious for a beginner project.</p>
<p>If you're following along at home, make sure you have the <a href="https://www.nuget.org/packages/Akka">Akka</a> and <a href="https://www.nuget.org/packages/Akka.FSharp">Akka.FSharp</a> packages installed into your project using Nuget or Paket. I'll be <a href="https://github.com/17cupsofcoffee/AkkaMUD">committing to GitHub</a> as I go.</p>
<h2 id="the-basics">The Basics</h2>
<p><small><a href="https://github.com/17cupsofcoffee/AkkaMUD/blob/part1a-the-basics/AkkaMUD/Program.fs"><i class="fa fa-github" aria-hidden="true"></i> View the code for this section</a></small></p>
<p>To begin with, let's take a quick look at how to use the Akka.NET F# API by creating a simple actor - all it'll do is say hello or goodbye when it receives a message. If you're already familiar with how to do this, feel free to <a href="https://www.seventeencups.net/posts/building-a-mud-with-f-sharp-and-akka-net-part-one/#network-i-o">skip ahead to the next section</a>!</p>
<p>First, we need to create a 'system' - I'm not going to go into too much detail on the actual concepts behind the Akka actor model here (<a href="http://getakka.net/docs/">the docs</a> explain it much better than I probably would), but at the most simple level, the system is the structure which co-ordinates all of your actors, managing the thread pool that allows them to do their work.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>system </span><span style="color:#c594c5;">=</span><span> System.create </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">system</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;">(</span><span>Configuration.defaultConfig</span><span style="color:#c594c5;">())
</span></code></pre>
<p>Next, we'll define the type of message that our actor will respond to. F#'s discriminated unions are perfect for this:</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">type </span><span>GreeterMsg </span><span style="color:#c594c5;">=
</span><span> </span><span style="color:#c594c5;">|</span><span> Hello </span><span style="color:#c594c5;">of </span><span>string
</span><span> </span><span style="color:#c594c5;">|</span><span> Goodbye </span><span style="color:#c594c5;">of </span><span>string
</span></code></pre>
<p>Now here's the fun part - let's define our actor, and spawn it into our system. If you're coming from the C# or Java versions of Akka, you may be used to defining actors as classes - it's certainly possible to do things that way in F# too, but we can be much more functional:</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>greeter </span><span style="color:#c594c5;">=</span><span> spawn system </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">greeter</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;"><| fun</span><span style="color:#f99157;"> mailbox </span><span style="color:#c594c5;">->
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">|</span><span> Hello name </span><span style="color:#c594c5;">-></span><span> printf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Hello, </span><span style="color:#c594c5;">%s</span><span style="color:#99c794;">!</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> name
</span><span> </span><span style="color:#c594c5;">|</span><span> Goodbye name </span><span style="color:#c594c5;">-></span><span> printf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Goodbye, </span><span style="color:#c594c5;">%s</span><span style="color:#99c794;">!</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> name
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">}
</span><span> loop</span><span style="color:#f99157;">()
</span></code></pre>
<p>Most of this code will seem familiar if you've written much F# - we define an anonymous function that takes in the actor's mailbox and uses tail recursion to loop, then pass it into the spawning function to create a new instance of the actor. But wait - if this just loops forever, how does it not hang the program?</p>
<p>This is where the <a href="http://getakka.net/docs/FSharp%20API#creating-actors-with-actor-computation-expression">actor computation expression</a> comes in - <code>loop</code> may look like a normal synchronous function, but the <code>actor</code> wrapper converts it into a <a href="https://en.wikipedia.org/wiki/Continuation">continuation-based</a> form that can be suspended when no messages need to be processed. This is similar in nature to the async expressions in the F# standard library, or async/await in C#.</p>
<p>Let's test this out! To send a message to a actor in Akka.NET, you use the <code>.Tell</code> method - however, as this is such a common operation, the F# API provides the handy <code><!</code> operator, which does the same thing.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span>greeter </span><span style="color:#c594c5;"><!</span><span> Hello </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Joe</span><span style="color:#5fb3b3;">" </span><span style="color:#5f6364;">// or greeter.Tell(Hello "Joe")
</span><span>greeter </span><span style="color:#c594c5;"><!</span><span> Goodbye </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Joe</span><span style="color:#5fb3b3;">" </span><span style="color:#5f6364;">// or greeter.Tell(Goodbye "Joe")
</span><span>
</span><span>System.Console.ReadLine</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">|></span><span> ignore
</span></code></pre>
<p>Note that we have to call a blocking function like <code>ReadLine</code> at the end of our program - otherwise it will terminate before our actor gets a chance to respond! There's almost certainly more elegant ways of stopping this from happening, but this is the simplest for now.</p>
<p>All being well, you should get output along the lines of this:</p>
<pre data-lang="bash" style="background-color:#2b2c2f;color:#cccece;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#6699cc;">Hello, Joe!
</span><span style="color:#6699cc;">Goodbye, Joe!
</span></code></pre>
<h2 id="network-i-o">Network I/O</h2>
<p>Okay, that's pretty cool, but it's not exactly a MUD, is it? Let's make a start on hooking our actor up to the network. Akka provides an I/O API out of the box; to hook into it, we'll need to define two new actors.</p>
<h3 id="the-server">The Server</h3>
<p><small><a href="https://github.com/17cupsofcoffee/AkkaMUD/blob/part1b-the-server/AkkaMUD/Program.fs"><i class="fa fa-github" aria-hidden="true"></i> View the code for this section</a></small></p>
<p>Let's define another simple actor below our greeter. This time, however, we'll add some code to be run when it gets initialised:</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>listener </span><span style="color:#c594c5;">=</span><span> spawn system </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">listener</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;"><| fun</span><span style="color:#f99157;"> mailbox </span><span style="color:#c594c5;">->
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> mailbox.Context.System.Tcp</span><span style="color:#f99157;">() </span><span style="color:#c594c5;"><!</span><span> Tcp.Bind</span><span style="color:#c594c5;">(</span><span>mailbox.Self</span><span style="color:#c594c5;">,</span><span> IPEndPoint</span><span style="color:#c594c5;">(</span><span>IPAddress.Any</span><span style="color:#c594c5;">, </span><span style="color:#f99157;">9090</span><span style="color:#c594c5;">))
</span><span> loop</span><span style="color:#f99157;">()
</span></code></pre>
<p>We send a message to the built-in TCP manager, passing it a reference to our actor and a port to bind to. When it's done, it'll reply with a message of its own - so let's get our actor to handle it.</p>
<p>Unfortunately, this is where Akka.NET's C# roots start to show a little bit - rather than using a single discriminated union type for its messages, the TCP manager has a separate class for each. We can work around this without making our code too ugly by using the <a href="https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/pattern-matching#type-test-pattern">type test pattern</a> syntax, which allows you to match against types instead of values at runtime.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>server </span><span style="color:#c594c5;">=</span><span> spawn system </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">listener</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;"><| fun (</span><span style="color:#f99157;">mailbox</span><span style="color:#c594c5;">: Actor<</span><span>obj</span><span style="color:#c594c5;">>) ->
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Bound as bound </span><span style="color:#c594c5;">->
</span><span> printf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Listening on </span><span style="color:#c594c5;">%O</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> bound.LocalAddress
</span><span> </span><span style="color:#c594c5;">| _ -></span><span> mailbox.Unhandled</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> mailbox.Context.System.Tcp</span><span style="color:#f99157;">() </span><span style="color:#c594c5;"><!</span><span> Tcp.Bind</span><span style="color:#c594c5;">(</span><span>mailbox.Self</span><span style="color:#c594c5;">,</span><span> IPEndPoint</span><span style="color:#c594c5;">(</span><span>IPAddress.Any</span><span style="color:#c594c5;">, </span><span style="color:#f99157;">9090</span><span style="color:#c594c5;">))
</span><span> loop</span><span style="color:#f99157;">()
</span></code></pre>
<p>Note that we have to provide a type annotation for the mailbox, otherwise we get a compile error. Also, since our match expression isn't exhaustive, it's good practice to have a catch-all arm that signals if a message wasn't handled. We'll be able to log this out later.</p>
<p>If you run the program now, you should see the socket get bound successfully! Next, we need to define what happens when someone connects.</p>
<h3 id="the-handler">The Handler</h3>
<p><small><a href="https://github.com/17cupsofcoffee/AkkaMUD/blob/part1c-the-handler/AkkaMUD/Program.fs"><i class="fa fa-github" aria-hidden="true"></i> View the code for this section</a></small></p>
<p>Above the server actor, let's start to define our connection handler.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>handler</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">(</span><span style="color:#f99157;">mailbox</span><span style="color:#c594c5;">: </span><span>Actor</span><span style="color:#c594c5;"><</span><span>obj</span><span style="color:#c594c5;">>) =
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Received as received </span><span style="color:#c594c5;">-> ()
</span><span> </span><span style="color:#c594c5;">| _ -></span><span> mailbox.Unhandled</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop connection
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> loop connection
</span></code></pre>
<p>There's a couple of things to note here:</p>
<ul>
<li>We're not spawning this actor yet - we're going to have our server actor make a copy as a child once for every connection.</li>
<li>Rather than storing the connection as mutable state, we thread it through the loop. You could use a mutable variable here, but this feels more functional to me.</li>
</ul>
<p>So, how are we going to translate incoming data into something our greeter actor can understand? We'll need to come up with a protocol - I'm going to keep this as basic as possible and just use this for now:</p>
<pre data-lang="bash" style="background-color:#2b2c2f;color:#cccece;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#5fb3b3;"><</span><span>command</span><span style="color:#5fb3b3;">> <</span><span>name</span><span style="color:#5fb3b3;">>
</span></code></pre>
<p>And here's our code updated to parse data into a <code>GreeterMsg</code>, passing it on to the greeter actor:</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>handler</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">(</span><span style="color:#f99157;">mailbox</span><span style="color:#c594c5;">: </span><span>Actor</span><span style="color:#c594c5;"><</span><span>obj</span><span style="color:#c594c5;">>) =
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Received as received </span><span style="color:#c594c5;">->
</span><span> </span><span style="color:#c594c5;">let </span><span>data </span><span style="color:#c594c5;">= (</span><span>Encoding.ASCII.GetString </span><span style="color:#c594c5;">(</span><span>received.Data.ToArray</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">))</span><span>.Trim</span><span style="color:#f99157;">()</span><span>.Split</span><span style="color:#c594c5;">([|</span><span style="color:#99c794;">' '</span><span style="color:#c594c5;">|], </span><span style="color:#f99157;">2</span><span style="color:#c594c5;">)
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> data </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| [| </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">hello</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">;</span><span> name </span><span style="color:#c594c5;">|] -></span><span> greeter </span><span style="color:#c594c5;"><!</span><span> Hello </span><span style="color:#c594c5;">(</span><span>name.Trim</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| [| </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">goodbye</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">;</span><span> name </span><span style="color:#c594c5;">|] -></span><span> greeter </span><span style="color:#c594c5;"><!</span><span> Goodbye </span><span style="color:#c594c5;">(</span><span>name.Trim</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| _ -> </span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">| _ -></span><span> mailbox.Unhandled</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop connection
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> loop connection
</span></code></pre>
<p>If the input was more complex, you'd probably be better served using something like <a href="http://www.quanttec.com/fparsec/">FParsec</a> to parse it - if this code starts getting too unwieldy in later posts, I might take a detour into that, but for now, this'll do.</p>
<p>Now we need to update our server actor to create handlers for each new connection. Rather than adding them directly to the root system, we can create a hierarchy by passing the server's mailbox to the spawn function.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>server </span><span style="color:#c594c5;">=</span><span> spawn system </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">server</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;"><| fun (</span><span style="color:#f99157;">mailbox</span><span style="color:#c594c5;">: Actor<</span><span>obj</span><span style="color:#c594c5;">>) ->
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">let </span><span>sender </span><span style="color:#c594c5;">=</span><span> mailbox.Sender</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Bound as bound </span><span style="color:#c594c5;">->
</span><span> printf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Listening on </span><span style="color:#c594c5;">%O</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> bound.LocalAddress
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Connected as connected </span><span style="color:#c594c5;">->
</span><span> printf </span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">%O</span><span style="color:#99c794;"> connected to the server</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> connected.RemoteAddress
</span><span> </span><span style="color:#c594c5;">let </span><span>handlerName </span><span style="color:#c594c5;">= </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">handler_</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;">+</span><span> connected.RemoteAddress.ToString</span><span style="color:#f99157;">()</span><span>.Replace</span><span style="color:#c594c5;">(</span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">[</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">, </span><span style="color:#5fb3b3;">""</span><span style="color:#c594c5;">)</span><span>.Replace</span><span style="color:#c594c5;">(</span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">]</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">, </span><span style="color:#5fb3b3;">""</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">let </span><span>handlerRef </span><span style="color:#c594c5;">=</span><span> spawn mailbox handlerName </span><span style="color:#c594c5;">(</span><span>handler sender</span><span style="color:#c594c5;">)
</span><span> sender </span><span style="color:#c594c5;"><!</span><span> Tcp.Register handlerRef
</span><span> </span><span style="color:#c594c5;">| _ -> </span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> mailbox.Context.System.Tcp</span><span style="color:#f99157;">() </span><span style="color:#c594c5;"><!</span><span> Tcp.Bind</span><span style="color:#c594c5;">(</span><span>mailbox.Self</span><span style="color:#c594c5;">,</span><span> IPEndPoint</span><span style="color:#c594c5;">(</span><span>IPAddress.Any</span><span style="color:#c594c5;">, </span><span style="color:#f99157;">9090</span><span style="color:#c594c5;">))
</span><span> loop</span><span style="color:#f99157;">()
</span></code></pre>
<p>Note that we have to give each actor a unique name - here, I'm using the user's IP address and port to generate one. Akka doesn't like the square brackets in IPv6 addresses, so don't forget to strip those out!</p>
<p>With that, all the pieces are in place - if you run your program, and then use <code>telnet</code> to send some messages, you should see the greeter printing out messages to the server console!</p>
<h2 id="responding">Responding</h2>
<p><small><a href="https://github.com/17cupsofcoffee/AkkaMUD/blob/part1d-responding/AkkaMUD/Program.fs"><i class="fa fa-github" aria-hidden="true"></i> View the code for this section</a></small></p>
<p>But that's no fun for our user, is it? Let's make a few more changes so our greeter can talk back to them.</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>greeter </span><span style="color:#c594c5;">=</span><span> spawn system </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">greeter</span><span style="color:#5fb3b3;">" </span><span style="color:#c594c5;"><| fun</span><span style="color:#f99157;"> mailbox </span><span style="color:#c594c5;">->
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;">() </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">let </span><span>sender </span><span style="color:#c594c5;">=</span><span> mailbox.Sender</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">|</span><span> Hello name </span><span style="color:#c594c5;">-></span><span> sender </span><span style="color:#c594c5;"><!</span><span> sprintf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Hello, </span><span style="color:#c594c5;">%s</span><span style="color:#99c794;">!</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> name
</span><span> </span><span style="color:#c594c5;">|</span><span> Goodbye name </span><span style="color:#c594c5;">-></span><span> sender </span><span style="color:#c594c5;"><!</span><span> sprintf </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Goodbye, </span><span style="color:#c594c5;">%s</span><span style="color:#99c794;">!</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span> name
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop</span><span style="color:#f99157;">()
</span><span> </span><span style="color:#c594c5;">}
</span><span> loop</span><span style="color:#f99157;">()
</span></code></pre>
<p>Pretty simple - instead of printing the text to stdout, we send it back to the actor which sent us the message. If you recall, that's our connection handler, so we need to teach that how to handle the new message type:</p>
<pre data-lang="fs" style="background-color:#2b2c2f;color:#cccece;" class="language-fs "><code class="language-fs" data-lang="fs"><span style="color:#c594c5;">let </span><span>handler</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">(</span><span style="color:#f99157;">mailbox</span><span style="color:#c594c5;">: </span><span>Actor</span><span style="color:#c594c5;"><</span><span>obj</span><span style="color:#c594c5;">>) =
</span><span> </span><span style="color:#c594c5;">let rec </span><span>loop</span><span style="color:#f99157;"> connection </span><span style="color:#c594c5;">=</span><span> actor </span><span style="color:#c594c5;">{
</span><span> </span><span style="color:#c594c5;">let! </span><span>msg </span><span style="color:#c594c5;">=</span><span> mailbox.Receive</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> msg </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| :</span><span>? Tcp.Received as received </span><span style="color:#c594c5;">->
</span><span> </span><span style="color:#c594c5;">let </span><span>data </span><span style="color:#c594c5;">= (</span><span>Encoding.ASCII.GetString </span><span style="color:#c594c5;">(</span><span>received.Data.ToArray</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">))</span><span>.Trim</span><span style="color:#f99157;">()</span><span>.Split</span><span style="color:#c594c5;">([|</span><span style="color:#99c794;">' '</span><span style="color:#c594c5;">|], </span><span style="color:#f99157;">2</span><span style="color:#c594c5;">)
</span><span>
</span><span> </span><span style="color:#c594c5;">match</span><span> data </span><span style="color:#c594c5;">with
</span><span> </span><span style="color:#c594c5;">| [| </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">hello</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">;</span><span> name </span><span style="color:#c594c5;">|] -></span><span> greeter </span><span style="color:#c594c5;"><!</span><span> Hello </span><span style="color:#c594c5;">(</span><span>name.Trim</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| [| </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">goodbye</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">;</span><span> name </span><span style="color:#c594c5;">|] -></span><span> greeter </span><span style="color:#c594c5;"><!</span><span> Goodbye </span><span style="color:#c594c5;">(</span><span>name.Trim</span><span style="color:#f99157;">()</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| _ -></span><span> connection </span><span style="color:#c594c5;"><!</span><span> Tcp.Write.Create </span><span style="color:#c594c5;">(</span><span>ByteString.FromString </span><span style="color:#5fb3b3;">"</span><span style="color:#99c794;">Invalid request.</span><span style="color:#f99157;">\n</span><span style="color:#5fb3b3;">"</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| :</span><span>? string as response </span><span style="color:#c594c5;">->
</span><span> connection </span><span style="color:#c594c5;"><!</span><span> Tcp.Write.Create </span><span style="color:#c594c5;">(</span><span>ByteString.FromString response</span><span style="color:#c594c5;">)
</span><span> </span><span style="color:#c594c5;">| _ -></span><span> mailbox.Unhandled</span><span style="color:#f99157;">()
</span><span>
</span><span> </span><span style="color:#c594c5;">return!</span><span> loop connection
</span><span> </span><span style="color:#c594c5;">}
</span><span>
</span><span> loop connection
</span></code></pre>
<p>Again, a really simple change! We just write any strings we receive out to the socket. I also added an error message for invalid commands, for completeness' sake.</p>
<p>If you try accessing your server through <code>telnet</code> again, you should now get the greetings returned straight to your console! It's hardly a MUD, but it's a start.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>We've now effectively got the 'gateway' into our actor system set up - the server listens for connections, the handlers parse data into strongly-typed messages, and then our greeter handles the actual logic. The important thing to note is that the greeter is totally unaware that it's part of a networked application - if we wanted, we could write a WebSockets endpoint or a unit test that sends messages into it, and we wouldn't have to modify the greeter code at all.</p>
<p>Next time, we'll start implementing some of the actual game on top of these foundations. I look forward to hearing people's feedback - I'm still very much a beginner with Akka, so don't hesitate to let me know if you think bits of the code could have been done better!</p>