<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts on SuperUserDone</title><link>https://superuserdone.com/posts/</link><description>Recent posts on SuperUserDone</description><generator>Hugo</generator><language>en-US</language><managingEditor>louis@superuserdone.com (Louis van der Walt)</managingEditor><webMaster>louis@superuserdone.com (Louis van der Walt)</webMaster><lastBuildDate>Thu, 18 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://superuserdone.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>LLMs: Don't use a sledgehammer when tweezers will do</title><link>https://superuserdone.com/posts/2026-06-18-dont-use-a-sledgehammer/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2026-06-18-dont-use-a-sledgehammer/</guid><category>AI</category><category>LLMs</category><category>Data Engineering</category><description>LLMs are not always the right tool for the job.</description><content:encoded><![CDATA[
<p>Consider this sorting function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> llm_api_of_your_choice <span style="color:#f92672">import</span> setup_llm_api
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>llm <span style="color:#f92672">=</span> setup_llm_api(API_KEY)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">llm_sort</span>(words):
</span></span><span style="display:flex;"><span>    prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Given these words: </span><span style="color:#e6db74">{</span><span style="color:#e6db74">&#39; &#39;</span><span style="color:#f92672">.</span>join(words)<span style="color:#e6db74">}</span><span style="color:#e6db74">, sort them alphabetically. Reply only with the words separated by spaces.&#34;</span>
</span></span><span style="display:flex;"><span>    reply <span style="color:#f92672">=</span> llm(prompt)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> reply<span style="color:#f92672">.</span>split()
</span></span></code></pre></div><p>No sane person would ever use this, right? Even bubble sort would outperform an LLM on this task in speed, accuracy and reliability. It&rsquo;s quite simple to see why. An LLM burns through a massive amount of compute to do something a first-year algorithms course solved decades ago. Worse: it might even get the answer wrong! Even worse: the API might be down! Not to mention if you are not self-hosting your model your data is now being transmitted to a random third party with a vague clause in their privacy policy.</p>
<p>Now tell me: <strong>WHY REACH FOR AN LLM FOR EVERY DATA PROBLEM, YOU BOZO!?!</strong></p>
<p>The sort function above is a joke. Don&rsquo;t turn your production system into the same joke with a higher AWS bill just because your data is slightly messy.</p>
<p>Okay. Rant over.</p>
<h2 id="the-rot">The Rot</h2>
<p>In the age of LLMs, a wicked temptation has taken hold. Instead of reaching for proper engineering judgment, the default has become reaching for an LLM. A dataset needs cleaning? Ask the model. Two records need matching? Ask the model. Some text needs extracting? Ask the model. Something needs validating, routing, deduplicating, normalising, clustering or searching?</p>
<p>Ask the model.</p>
<p>Have you considered that maybe, perhaps, perchance, the magical text box is not the right tool for every job?</p>
<p>LLMs are not useless, quite the contrary. They are amazing pieces of technology when applied to the right problems. What happens though is that they are flexible enough to look like the right solution in places where they are the slow, unpredictable, hard-to-debug option. Likewise, they make bad solutions look sophisticated because they are great at making demos look good.</p>
<p>But production systems do not care about your demo in your AI-generated notebook on five cherry-picked examples that only needs to work once in your presentation to the CIO.</p>
<p>They care about cost, latency, throughput, repeatability, inspectability, observability, reliability, edge cases, failure modes, and if the same input gives the same output <a href="https://www.anthropic.com/engineering/april-23-postmortem">tomorrow morning</a>.</p>
<p>LLMs do not understand your business logic, edge-cases, your taxonomy, your customers. You do. You might say: &ldquo;Just fine-tune it, bro.&rdquo; Sure, &ldquo;bro&rdquo;, but if the plan starts and ends at &ldquo;fine-tune it,&rdquo; and nobody in the room can explain what data you need, what labels actually mean, what a loss function is, and what failure modes you are willing to tolerate, then you do not have a solution. You have an even more expensive demo.</p>
<h2 id="the-what-before-the-how">The What Before the How</h2>
<p>Next time you are faced with a choice, don&rsquo;t ask: How can we use an LLM for this? Rather, try to understand your problem deeply and ask:</p>
<blockquote>
<p>What&rsquo;s the smallest, most reliable tool that solves this problem?</p>
</blockquote>
<p>You will end up with something that outperforms an LLM in speed and accuracy, is more inspectable and reliable, and can be built on a laptop in a week.</p>
<p>Sometimes that tool is a regular expression. Sometimes it&rsquo;s a lookup table or a join. Sometimes it&rsquo;s a text-distance algorithm, a sparse retrieval model, a dense embedding model, a search index, classifier, rules engine, parser or better data. Sometimes it is doing it by hand.</p>
<p>And yes, sometimes it <em>is</em> an LLM.</p>
<p>Knowing which of these to reach for is the job. That judgment is what separates engineering from autocomplete. Please just don&rsquo;t let LLMs become a <em>default</em>.</p>
<p>Sure, nobody is raising money by saying: &ldquo;we used a join.&rdquo; Have you considered that boring and correct might be enough? You work for an insurance company. Don&rsquo;t turn it into <em>Big Data Startup Enterprises Incorporated Limited</em>.</p>
<p>Don&rsquo;t use a sledgehammer when tweezers will do.</p>

]]></content:encoded></item><item><title>Code in my life: A chronicle part 3</title><link>https://superuserdone.com/posts/2026-06-13-code-in-my-life-3/</link><pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2026-06-13-code-in-my-life-3/</guid><category>code</category><category>nostalgia</category><category>chronicle</category><description>A chronicle of the code I have written in my life and never shared: a friend</description><content:encoded><![CDATA[
<p><em>This post is a continuation of <a href="https://superuserdone.com/posts/2026-06-06-code-in-my-life-2/">part 2</a></em></p>
<p>After my computer broke, I was devastated. But the devastation did not last that long. In 2014, for my 10th birthday, I received a small Android tablet as part of a bundle deal my parents got. Unlike my previous computer, this one came with a SIM card and a small monthly data allowance.</p>
<p>With that access, I almost immediately got my parents to purchase and install the original Minecraft Pocket Edition for me.</p>
<p>Initially I was content with just playing it, but soon my friends started talking about &ldquo;mods&rdquo;. While they played computer minecraft, I wanted in on the action. I soon discovered Block Launcher, and MCPE Universe, and I played with mods for the first time.</p>
<p>I was not content with just that, though, and soon curiosity took over. Realizing that the files I downloaded were .zip files I could extract was my catalyst.</p>
<p>Soon I was decompressing every mod I could find, trying to make sense of the code, and Frankensteining it together to make my own mods.</p>
<p>My first mod took months to create, but I was incredibly proud of it. The idea was to create a mod that made TNT more powerful, but I only knew how to make blocks that did nothing. It was a simple mod, adding a new block that looks exactly like TNT, but is not. I thought it would be the perfect prank, placing TNT in front of your friend, lighting it and nothing happens. In late 2014, I posted it on MCPE Universe with a screenshot of myself holding flint and steel next to a burning TNT block.</p>
<p>Soon thereafter, someone reached out. Another young coder asked in the comment section of my TNT mod if I&rsquo;d be interested in working together.</p>


  
  
  
  
  
  
  <a href="/posts/2026-06-13-code-in-my-life-3/disqus.png">
    <img
      src="/posts/2026-06-13-code-in-my-life-3/disqus_hu_d2d977dfcb1eaf5d.webp"
      srcset="/posts/2026-06-13-code-in-my-life-3/disqus_hu_c852cfe9cca407a6.webp 400w, /posts/2026-06-13-code-in-my-life-3/disqus_hu_10f0c014597a38e4.webp 800w, /posts/2026-06-13-code-in-my-life-3/disqus_hu_d2d977dfcb1eaf5d.webp 1200w, /posts/2026-06-13-code-in-my-life-3/disqus_hu_d4c7557ddffec3.webp 1720w, /posts/2026-06-13-code-in-my-life-3/disqus_hu_76355598d553d696.webp 2580w"
      sizes="(max-width: 860px) 100vw, 860px"
      width="800"
      height="288"
      alt="A more than a decade old interaction between a young me and another young coder"
      loading="lazy"
      decoding="async"
    >
  </a>


<p>What I find fascinating is that our security mechanism was that he would email me the name of my mod, “so I know it’s him.” Someone could just as easily impersonate him reading the exchange&hellip; If only I knew about <a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie-Hellman Key exchange</a> back then!</p>
<p>We started communicating via email, and started making mods together. The pinnacle of our modding career had us making a mod called CrAZy MoD. Here is the full original code, preserved as-is in its entirety for your enjoyment!</p>
<p>(I have changed the name of the other person to Wolf42 to preserve their privacy, the rest of the code remains as-is.)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#75715e">//Mod By Wolf42 and Louis999
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//Do Not Distribute Without Wolf42&#39;s and Louis999&#39;s Permistion
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//Have Fun and be Crazy!
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*Mod History:
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">V1-The &#34;megga&#34;, &#34;lol&#34;, and &#34;dumie&#34; blocks were added by Louis999!
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">V2-Louis999 added Wolf42 to the mod helper list and they became friends!
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">Also Wolf42 added &#34;The Stick Of Truth&#34; item and the Beggining Chat when you enter a world, also he added the commands!
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">V3-Louis999 added the &#34;Cow&#34; command!
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">   Also Wolf42 Did a full mod Rewrite! and added a load more commands!*/</span>
</span></span><span style="display:flex;"><span>   
</span></span><span style="display:flex;"><span><span style="color:#75715e">//Variables
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">PX</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">getX</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">PY</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">getY</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">PZ</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">getZ</span>();
</span></span><span style="display:flex;"><span>   
</span></span><span style="display:flex;"><span><span style="color:#75715e">//newLevel
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">newLevel</span>(){
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Welcome Its Time to be CrAzY!&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Type /help for help!&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;or Type /creator for The Crazy Mod Creators Information!&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>   
</span></span><span style="display:flex;"><span><span style="color:#75715e">//Blocks
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//megga
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">defineBlock</span>(<span style="color:#ae81ff">252</span>, <span style="color:#e6db74">&#34;megga &#34;</span>,[[<span style="color:#e6db74">&#34;gold_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;gold_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;iron_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;iron_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;iron_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;iron_block&#34;</span>, <span style="color:#ae81ff">0</span>]]);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">setDestroyTime</span>(<span style="color:#ae81ff">252</span>, <span style="color:#ae81ff">1.0</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Item</span>.<span style="color:#a6e22e">addCraftRecipe</span>(<span style="color:#ae81ff">252</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>, [<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>,  <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">46</span>]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//lol
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">defineBlock</span>(<span style="color:#ae81ff">254</span>,  <span style="color:#e6db74">&#34;lol&#34;</span>,[[<span style="color:#e6db74">&#34;cauldron_inner&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;piston_inner&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;mob_spawner&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;piston_top_normal&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;destroy&#34;</span>, <span style="color:#ae81ff">9</span>], [<span style="color:#e6db74">&#34;cauldron_top&#34;</span>, <span style="color:#ae81ff">0</span>]]);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">setDestroyTime</span>(<span style="color:#ae81ff">254</span>, <span style="color:#ae81ff">1.0</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Item</span>.<span style="color:#a6e22e">addCraftRecipe</span>(<span style="color:#ae81ff">254</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>, [<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>,  <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//dumie
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">defineBlock</span>(<span style="color:#ae81ff">255</span>, <span style="color:#e6db74">&#34;dummie&#34;</span>,[[<span style="color:#e6db74">&#34;diamond_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;mob_spawner&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;diamond_block&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;piston_top_sticky&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;tnt&#34;</span>, <span style="color:#ae81ff">0</span>], [<span style="color:#e6db74">&#34;destroy&#34;</span>, <span style="color:#ae81ff">0</span>]]);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Block</span>.<span style="color:#a6e22e">setDestroyTime</span>(<span style="color:#ae81ff">255</span>, <span style="color:#ae81ff">1.0</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Item</span>.<span style="color:#a6e22e">addCraftRecipe</span>(<span style="color:#ae81ff">255</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span> , [<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//Items
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//The Stick Of Truth
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">ModPE</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#ae81ff">511</span>,<span style="color:#e6db74">&#34;blaze_rod&#34;</span>,<span style="color:#ae81ff">0</span>,<span style="color:#e6db74">&#34;The Stick Of Truth&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ModPE</span>.<span style="color:#a6e22e">setFoodItem</span>(<span style="color:#ae81ff">510</span>,<span style="color:#e6db74">&#34;blaze_powder&#34;</span>,<span style="color:#ae81ff">0</span>,<span style="color:#f92672">-</span><span style="color:#ae81ff">20</span>,<span style="color:#e6db74">&#34;Pizza&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//Comands
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">procCmd</span>(<span style="color:#a6e22e">command</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">cmd</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">command</span>.<span style="color:#a6e22e">split</span>(<span style="color:#e6db74">&#34; &#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;help&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Item IDs 500&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Block IDs 252, 254, and 255&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;type /lol for more help&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;or type /cow for page 2!&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;lol&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">setHealth</span>(<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;LOL!&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;You Should of Got the Hint With the word lol&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;troll&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">addItemInventory</span>(<span style="color:#ae81ff">510</span>, <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;TROLL!&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">setHealth</span>(<span style="color:#ae81ff">10</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;info&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Player</span>.<span style="color:#a6e22e">setHealth</span>(<span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;WE LIKE TROLLING!&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;cow&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Type /troll for A Great big Troll!&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Type /info for info about this mod!&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;creator&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Type /cw23 to learn about Wolf42&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Type /l999 to learn about Louis999&#34;</span>); 
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;cw23&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Wolf42 is a YouTuber and a modder, as you see here.&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;He Also is the one who wrote what your reading right here, lol&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">cmd</span>[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;l999&#34;</span>)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">clientMessage</span>(<span style="color:#e6db74">&#34;Louis999 is a basic modder and mod creator, he thought of this mod and created it!&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//Wont Work Properly
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">/*else{
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">clientMessage(&#34;That Command Isnt Real D:&#34;);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">}*/</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The humor of a 10-year-old me captured in full glory! What I find fascinating is the copyright notice at the top and the chronicle of our modding history together. I loved how I called myself a &ldquo;basic&rdquo; modder. Another interesting detail is that we clearly wanted a fallback if commands fell through, but we did not know how <code>else</code> and <code>if else</code> worked.</p>
<p>A few other funny things:</p>
<ul>
<li>Typing <code>/lol</code> kills you</li>
<li><code>/info</code> sets your health to 0</li>
<li>Blocks with basically random faces</li>
<li>Eating the <code>pizza</code> item would subtract 20 from your hunger, meaning it would effectively set your hunger to 0. Looking at the texture, I guess that makes sense!</li>
</ul>


  
  
  
  
  
  
  <a href="/posts/2026-06-13-code-in-my-life-3/pizza.png">
    <img
      src="/posts/2026-06-13-code-in-my-life-3/pizza_hu_7ad30566d315577.webp"
      srcset="/posts/2026-06-13-code-in-my-life-3/pizza_hu_567c5dbe15669780.webp 400w, /posts/2026-06-13-code-in-my-life-3/pizza_hu_dd212a9946a6639b.webp 800w, /posts/2026-06-13-code-in-my-life-3/pizza_hu_7ad30566d315577.webp 1200w, /posts/2026-06-13-code-in-my-life-3/pizza_hu_63b3fb8c72b68454.webp 1720w, /posts/2026-06-13-code-in-my-life-3/pizza_hu_e0d9b3a77eb03f22.webp 2580w"
      sizes="(max-width: 860px) 100vw, 860px"
      width="800"
      height="768"
      alt="The pizza item texture, badly drawn pixel art."
      loading="lazy"
      decoding="async"
    >
  </a>


<p>But, as with all good things, we both moved on quite quickly. The tablet era was short, chaotic, and full of half-understood JavaScript. But when I got my dad’s old work laptop in early 2015, things changed. That was when I really started getting into programming&hellip;</p>
<p><em>This post will continue in part 4</em></p>

]]></content:encoded></item><item><title>Code in my life: A chronicle part 2</title><link>https://superuserdone.com/posts/2026-06-06-code-in-my-life-2/</link><pubDate>Sat, 06 Jun 2026 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2026-06-06-code-in-my-life-2/</guid><category>code</category><category>nostalgia</category><category>chronicle</category><description>A chronicle of the code I have written in my life and never shared: Hello World</description><content:encoded><![CDATA[
<p><em>This post is a continuation of <a href="https://superuserdone.com/posts/2026-05-30-code-in-my-life-a-cronicle/">part 1</a></em></p>
<p>During my childhood, I was obsessed with mazes. I would spend hours drawing mazes with increasingly complex rules and I would force my parents and friends to solve them. I wanted to translate this to the computer.</p>
<p>Every so often, I would return to the challenge. Most of my mazes were built in Minecraft. Soon though, my mind started to connect the dots. Minecraft had convinced me that computers could be bent to my will. If I could understand how the game worked, maybe I could make my own world. I was convinced understanding the <code>.bat</code> script was the key.</p>
<p>Initially, I experimented. I vividly remember trying to rename the script, after which windows would yell out a scary warning about how changing an extension might make the file unusable. I quickly pressed cancel, hoping I did not break my game.</p>
<p>The first time I tried opening the <code>minecraft.bat</code> file in notepad, I was overwhelmed. A bunch of nonsensical garbage filled my screen. I did not know it at the time, but the people who shipped the cracked version obfuscated the script. It was not meant to be understood.</p>
<p>I borrowed more and more books on programming and computers from the library, slowly building an understanding of how mysterious lines of text made the computer do stuff.</p>
<p>I attempted many times to apply my knowledge. Most of my &ldquo;programming&rdquo; was copying code from books in notepad, trying to run it but not having the compilers or knowledge to execute my code. My computer was littered with hundreds of little files. <code>maze.py</code>, <code>maze.php</code>, <code>maze.java</code>. Each an attempt to achieve my dream of creating a maze. Each time, I faced disappointment when I double-clicked, and the computer told me that the file I just worked on was unrecognised.</p>
<p>However, there was one piece of code I wrote that actually worked. A small little batch script - welcoming me to a new world.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;Hello World&#34;</span>
</span></span></code></pre></div><p>A short while later the computer I was working on died. Being locked out of a world I had only just started exploring, I had to find other ways to scratch that itch&hellip;</p>
<p><em>This post continues in <a href="https://superuserdone.com/posts/2026-06-13-code-in-my-life-3/">part 3</a></em></p>

]]></content:encoded></item><item><title>Code in my life: A chronicle part 1</title><link>https://superuserdone.com/posts/2026-05-30-code-in-my-life-a-cronicle/</link><pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2026-05-30-code-in-my-life-a-cronicle/</guid><category>code</category><category>nostalgia</category><category>chronicle</category><description>A chronicle of the code I have written in my life and never shared</description><content:encoded><![CDATA[
<p>Curiosity has always been part of my wiring. Before I got into computers I would take apart everything I could (most of the time irreparably).</p>
<p>My first ever digital device was an old Nokia phone we had in the house. It had no internet, no SIM card, just a snake game. My favourite part of it? The settings. I would sneak it into bed at night and play around in the settings at age 7&hellip; Until my dad caught me staying up one night after bedtime and took it away.</p>
<p>As many computer lovers, I started young. The first computer I ever used was our family computer that I played games on. It was a horribly slow computer running Windows XP. It took a whole 20 minutes to start up, and a further 10 to load into the desktop.</p>
<p>Around the time I turned 8, my parents got a new family machine, and the old computer became mine. <a href="https://en.wikipedia.org/wiki/Buggy_(video_game)">Buggy</a>, <a href="https://en.wikipedia.org/wiki/Caterpillar_Construction_Tycoon">Caterpillar Construction Tycoon</a> and <a href="https://en.wikipedia.org/wiki/SimCopter">Sim Copter</a> were the staples of my childhood.</p>
<p>I have a fondly held memory where I wondered how someone would create a game. In my 8-year-old brain, I thought games were made by drawing every possible screen and your keyboard and mouse inputs would swap between frames in an interactive slideshow type fashion.</p>
<p>Around the time I turned 8, Minecraft started gaining popularity. All my friends were playing it, so I begged my parents to get it. After enough begging, they gave in and sourced a free version from a totally reputable online source and put it on my computer.</p>
<p>I have no idea what version of the game I had. Now I know it had to be one of the classic versions, but not sure exactly what version. All I knew is that my friends had cool features like survival mode, infinite worlds and redstone, while I had a finite world and basic features.</p>
<p>The version of the game I had contained 2 files. One was called <code>minecraft_(random number)_xp.jar</code>, and there was another file: <code>minecraft.bat</code>. Both on my desktop. To play the game I had to double-click the <code>.bat</code> file, a console would open up, it would stay blank for a good while, suddenly print a massive amount to the console, and I would be in the game.</p>
<p>As a kid, my mom would religiously take us to the library, and I would frustrate her with my book choice. I never went for the story books. I always darted for the non-fiction and natural sciences section. Around age 9, one of the books I took out was a full-blown Windows XP reference book. I did not fully understand what I read, but somewhere in it was a page on batch commands, and suddenly that <code>.bat</code> file started to make a bit of sense&hellip;</p>
<p><em>This post continues in <a href="https://superuserdone.com/posts/2026-06-06-code-in-my-life-2/">part 2</a></em></p>

]]></content:encoded></item><item><title>AI Ruined CTFs</title><link>https://superuserdone.com/posts/2026-05-10-ai-ruined-ctfs/</link><pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2026-05-10-ai-ruined-ctfs/</guid><category>ctf</category><category>hacking</category><category>AI</category><description>A reflection on how AI has transformed CTF competitions, shifting challenge design away from educational value and toward obscurity and whether the relationship can be repaired.</description><content:encoded><![CDATA[
<p>I love Capture-The-Flag competitions.</p>
<p>Or - Should I rather say: I used to. Now we decided maybe it is best to spend some time apart, you know? With AI entering the picture&hellip; I don&rsquo;t want to fall into a love triangle. CTFs made it clear, their heart belongs to someone else.</p>
<h2 id="love-at-first-sight">Love at first sight</h2>
<p>CTF competitions hooked me in 2019, after I participated in PicoCTF. Immediately I was entranced. Soon after, Covid19 struck, and I was stuck at home with nothing to do, so instead of doing boring school work, I participated in every CTF I could find. The most notable one I participated in was the CSCG 2020, where I placed 21st in the global category.</p>
<p>In 2022, I pivoted toward competitive programming, but soon thereafter returned to CTFs.</p>
<p>In 2023, I jumped into CTFs again. I founded our university&rsquo;s cybersecurity society in 2024, and our team ranked number one in South Africa in 2024 and 2025, closely followed by&hellip; myself! During this time I also won the BSides Joburg 2025 CTF.</p>
<h2 id="what-makes-a-good-ctf-challenge">What makes a good CTF challenge</h2>
<p>Good CTF challenges teach through friction. The struggle is the point.</p>
<p>One of my first encounters with software security puzzles was this challenge from <a href="https://www.securify.nl/blog/spot-the-bug-challenge-2018-warm-up/">Securify</a>. This is one of the best examples of a well designed CTF challenge. Simple, the goal is right there. The only thing keeping you from solving the problem is a lack of knowledge. The great thing is, you can find the information with a bit of reading and experimentation and eventually solve the challenge. The depth of knowledge you gain from bashing your head against such a challenge is incredible.</p>
<p>Contrast it with a bad challenge. A challenge like, for example, crack this hash.</p>
<pre tabindex="0"><code>955b322ab45819879603fe24584dc4604a1a412681a0884b34a573e1a21a217b
</code></pre><p>While a well designed crypto challenge might give you a nice python script with an implementation flaw that you have to exploit mathematically to derive the original string, a challenge like this does not teach anything. It becomes a question of who can paste the hash into CrackStation first. Not very helpful.</p>
<h2 id="the-cracks">The cracks</h2>
<p>During the start of 2025, I started to notice AI creeping in. First it was one of my team mates using his ChatGPT pro subscription to farm challenges in a friendly competition, then it was other teams slowly but surely catching on. I even experimented a bit, by building my own harness called <code>tertullian</code>. Overnight, an arms race started.</p>
<p>At first, challenge authors could keep up. AI solved the beginner challenges. The ones nobody really cared about. But eventually, the strongest oak must fall, AI models could soon solve close to 80% of challenges in a typical CTF. Agentic tools exploded onto the scene. Suddenly, AI companies made hacking a benchmark, and CTF challenges became training material. Most CTF challenges can be solved in half an hour with the right prompt. Success no longer correlates with a team&rsquo;s problem-solving skills, but rather the amount of tokens they can afford.</p>
<p>Authors responded, by adding a layer of obscurity. For instance, they might remove whitespace and rename the variables of the above PHP script in an attempt to confuse the AI. I personally am not fond of this style of challenge, but understanding a piece of code with a level of obfuscation is still a skill, I do not object to this style of challenge.</p>
<p>The problem is not bad CTF challenges. Bad challenges have existed since the dawn of CTFs, rather the transformation of CTF challenges with excellent educational value into bad ones.</p>
<h2 id="the-breakup">The breakup</h2>
<p>AI teams again recognised this trend, and models started to become better at solving these obfuscated challenges. Authors had to adapt, again. This time by adding another level of indirection.</p>
<p>The straw that broke the camel&rsquo;s back for me was when I encountered this challenge: Instead of just putting your SQLi payload into the application, you should now first XOR encrypt it with the challenge author&rsquo;s name, and a value posted in the Discord. Other authors further obfuscate the challenge with more guessing layers that do not reward skill. Luck becomes the defining factor, or teams that can run 40 agents on a single challenge to find the top secret method this string is encoded in.</p>
<h2 id="could-we-get-back-together-again">Could we get back together again?</h2>
<p>The real problem is not that AI can solve challenges, rather that its presence changes what challenge authors design for.</p>
<p>This is an incredibly hard challenge to solve. How do you reward teams who use human effort to solve challenges, while keeping challenges fun? It is not realistic to try and ban AI usage in CTFs. How do you showcase something cool without an AI tool bypassing the learning opportunity? How do you satisfy competitive people (like myself) who want to compete, but cant compete against a machine that automatically solves your opponents challenges for them?</p>
<p>These are all my unanswered questions. For now, until we have a proper solution, I&rsquo;m retiring from CTF competitions with the exception of small local challenges and private competitions where I trust people to not spoil the fun.</p>

]]></content:encoded></item><item><title>Pyro Engine: Modular Premake Based Build System</title><link>https://superuserdone.com/posts/2023-12-01-pyro-premake/</link><pubDate>Fri, 01 Dec 2023 00:00:00 +0000</pubDate><author>louis@superuserdone.com (Louis van der Walt)</author><guid isPermaLink="true">https://superuserdone.com/posts/2023-12-01-pyro-premake/</guid><category>code</category><category>game-engine</category><category>pyro</category><description>How I built a custom Premake-based build system for the Pyro game engine, with automatic module dependency management, a declarative uses API, and DLL/static lib switching.</description><content:encoded><![CDATA[
<p>When I started with Pyro engine, I knew that I would need to create a clean and
robust build system without a complicated mess of scripts. Initially I wanted to
build upon CMAKE, but most of my projects up to this point used CMAKE and no
matter how hard I tried, it always turns into a mess after a while. So I started
looking into alternatives.</p>
<p>I immediately ruled out making my own system as I think it would be a waste of
time that I could use to work on my engine. In the end I decided to look at the
following systems.</p>
<table>
  <thead>
      <tr>
          <th>Buildsystem</th>
          <th>Why I chose to look at it</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Keep using CMAKE</td>
          <td>This was my failsafe if I did not find something I liked more.</td>
      </tr>
      <tr>
          <td>Bazel</td>
          <td>Google</td>
      </tr>
      <tr>
          <td>Premake</td>
          <td>Used by many large studios, lua scripting, easy to hack</td>
      </tr>
      <tr>
          <td>SCons</td>
          <td>I knew Godot used it effectively</td>
      </tr>
  </tbody>
</table>
<p>I did not add Meson to the list as I am scared of its warp package manager and
the visual studio generator sucks.</p>
<h2 id="bazel">Bazel</h2>
<p>Bazel is the build tool for Google and is used for all their stuff. I decided to
reject it as I could not get it working with Visual Studio, and it needs msys.</p>
<h2 id="scons">SCons</h2>
<p>Scons is the build system the Godot Engine uses, and rather effectively I must
add. I wanted to use a similar code layout as Godot, so it seemed like a great
choice.</p>
<h2 id="premake">Premake</h2>
<p>Premake is used by a large number of AAA game studios, and I think I know why.
Initialy I did not like the design, but it grew on me as it is super hackable
and has everything I need.</p>
<h1 id="hacking-premake">Hacking Premake</h1>
<p>The more I use premake the more I believe that it is designed to be hacked into
the perfect build system for your needs, so that is what I did. The first thing
I did was designing the architecture of my engine and building everything around
that.</p>
<p>The architecture I decided on was loosely based on the design of Godot, where
the engine is split into many modules that are mostly independent of each other.
What I is modules can depend on e.g. the memory system, but physics should not
depend on rendering or input. I also decided that each module must be able to
compile to either an DLL or a Static library and linked into the engine at
the flip of a switch. The reason I decided to do it this way was because it
lowers link time if everything can be dlls for debug, and the final build can
then be statically linked. Theoretically I could use incremental linking with
visual studio, but I ran into issues before with that, so I would rather KISS.</p>
<p>I wanted to be able to do something like this in the main file.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span> <span style="color:#75715e">-- MODULE_DEFS</span>
</span></span><span style="display:flex;"><span>	include_modules({
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;pyro_memory&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;pyro_containers&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;pyro_reflection&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;pyro_rendering&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- ...</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- RUNTIME_DEFS</span>
</span></span><span style="display:flex;"><span>	include_runtimes({
</span></span><span style="display:flex;"><span>		{<span style="color:#e6db74">&#34;launch_win32&#34;</span>, <span style="color:#e6db74">&#34;WindowedApp&#34;</span>}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- TOOL_DEFS</span>
</span></span><span style="display:flex;"><span>	include_tools({
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;pyro_asset_tool&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- TEST_DEFS</span>
</span></span><span style="display:flex;"><span>	include_tests({
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;pyro_memory_test&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- ...</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- EXTERNAL_DEFS</span>
</span></span><span style="display:flex;"><span>	include_externals({
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;gtest&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- ...</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">-- BENCHMARK_DEFS</span>
</span></span><span style="display:flex;"><span>  include_benchmarks({
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- you get the idea</span>
</span></span><span style="display:flex;"><span>  })
</span></span></code></pre></div><p>This system uses a template to auto create modules, runtimes, etc if the
declared type is not found.</p>
<p>The other thing I wanted was to use a custom premake command to add dependencies
to a module, as the default way to do it in premake you need to manually add the
include directory of a dependency to the target.</p>
<p>What I came up with is the <code>uses</code> command. In the
module/pyro_modulename/pemake5.lua file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span>pyro_module <span style="color:#e6db74">&#34;pyro_modulename&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    uses {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;pyro_other_module&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">-- ...</span>
</span></span></code></pre></div><p>The process for other types will be similar.</p>
<p>I also added some utility functions to map file vpath filter arrays to flat file
lists but that is just standard boilerplate.</p>
<h2 id="implementing-the-system">Implementing the system</h2>
<p>Not comes the big challenge, actually implementing this system. For the part
that includes modules and creates them if they are missing, it is easy.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">include_modules</span>(table)
</span></span><span style="display:flex;"><span>	group <span style="color:#e6db74">&#34;engine/modules&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> i, name <span style="color:#66d9ef">in</span> pairs(table) <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>	    <span style="color:#75715e">-- Create the module if not found</span>
</span></span><span style="display:flex;"><span>	    <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> os.isfile(<span style="color:#e6db74">&#34;engine/modules/&#34;</span><span style="color:#f92672">..</span>name<span style="color:#f92672">..</span><span style="color:#e6db74">&#34;/premake5.lua&#34;</span>) <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>            create_module(name)
</span></span><span style="display:flex;"><span>	    <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	    include(<span style="color:#e6db74">&#34;engine/modules/&#34;</span><span style="color:#f92672">..</span>name)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>	group <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>The <code>create_module()</code> function obviously needs an implementation, but that is
the gist of it. The hard code is the using declaration, used for both external
and internal(other modules)</p>
<p>The way I decided to implement this system is by creating an additional set of
fields for each config, under projects, <code>publicincludedirs</code> and <code>publiclinks</code>.
Here is an example of this in the gtest script.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span>pyro_external_project <span style="color:#e6db74">&#34;gtest&#34;</span>
</span></span><span style="display:flex;"><span>    kind <span style="color:#e6db74">&#34;StaticLib&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    publiclinks {<span style="color:#e6db74">&#34;gtest&#34;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    publicincludedirs {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;gtest/googlemock&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;gtest/googlemock/include&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;gtest/googletest&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;gtest/googletest/include&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    includedirs {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- Ommited for brevity</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    files {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- Ommited for brevity</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    removefiles {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">-- Ommited for brevity</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>The system then recursively iterates through all the items declared in the uses
declaration for each project, caches the project dependencies and inserts all
the dependencies as <code>includedirs</code> and <code>links</code> into the project. Every project is
only touched once with this system to speed things up, and it auto-detects
dependency cycles.</p>
<p>The <code>pyro_module()</code> function is used in the module premakes, and they auto
declare the<code>publicincludedirs</code> and <code>publiclinks</code> for the project so you can
immediately do <code>uses</code> with the name of the module.</p>
<p>There are many other things it also does, like auto generating a documentation
folder for each project and some other minor things, but that should be the gist
of it all.</p>
<p>The code for this part is a bit of a mess, so I am not including it, but any
decent programmer should be able to create a similar system in a weekend or
two. The only hurdle is you should be comfortable reading the premake code as
the API to do this is not documented, though it is used internally for
everything.</p>
<p>That concludes the build-system of Pyro engine! As you can see some simple
utilities can easily make life much easier. I hope this inspires you to create
your own build system with Premake. Happy hacking!</p>

]]></content:encoded></item></channel></rss>