<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>bytekeeper.org</title>
      <link>https://www.bytekeeper.org</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.bytekeeper.org/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 28 Feb 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>BASIL broke</title>
          <pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/basil-breakdown/</link>
          <guid>https://www.bytekeeper.org/posts/basil-breakdown/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/basil-breakdown/">&lt;p&gt;So, in the almost 8 years (insane!) it was running, BASIL had few hiccups. Recently it failed hard and was down for a few days. First: It&#x27;s back up again, but the rabbit whole was deep.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-game-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-game-server&quot; aria-label=&quot;Anchor link for: the-game-server&quot;&gt;🔗&lt;&#x2F;a&gt;The Game Server&lt;&#x2F;h1&gt;
&lt;p&gt;The first few years the machine running the games had issues with the network drivers (Linux). It first started loosing the connection then either hang or Kernel-panicking. A workaround was to restart it regularly. Mind you: this strategy is used by professional business software as well --- I&#x27;m in good company.&lt;&#x2F;p&gt;
&lt;p&gt;After a few years the issue suddenly was fixed. I can only assume some Kernel dev had the same issue an was motivated enough to fix it --- THANK YOU for that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basic-setup-of-basil&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#basic-setup-of-basil&quot; aria-label=&quot;Anchor link for: basic-setup-of-basil&quot;&gt;🔗&lt;&#x2F;a&gt;Basic Setup of BASIL&lt;&#x2F;h2&gt;
&lt;p&gt;The &quot;game&quot; machine was kind of running fine after that point --- without any scheduled restarts. The file serving machine started to get problems soon after. It&#x27;s a VPS and has limited disk space. And it started to fill up. It serves replays, game logs and all sorts of aggregated JSON data. Everything is pretty small, but multiplied by a few thousand it gets large very quickly.&lt;&#x2F;p&gt;
&lt;p&gt;Using two machines has several benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A VPS running games would be very expensive (good CPU, a lot of memory)&lt;&#x2F;li&gt;
&lt;li&gt;Hosting a web-server at home, even with Cloudflare &quot;buffer&quot; might get hammered&lt;&#x2F;li&gt;
&lt;li&gt;Using one machine for both:
&lt;ul&gt;
&lt;li&gt;Could be abused to influence the games being played&lt;&#x2F;li&gt;
&lt;li&gt;My internet being down would mean: no more public data&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;woes-of-producing-a-lot-of-data&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#woes-of-producing-a-lot-of-data&quot; aria-label=&quot;Anchor link for: woes-of-producing-a-lot-of-data&quot;&gt;🔗&lt;&#x2F;a&gt;Woes of Producing a lot of Data&lt;&#x2F;h2&gt;
&lt;p&gt;The fix here was simple. Archive all old files and sync only recent ones. That way the VPS should never be filled beyond capacity. This worked fine for a few years. I will eventually have to manually move the archives to another place as disk slowly fills up.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s head back to more recent days. On Feb. 19th - Dan destroyed BASIL with his custom CherryPi build. Okay, maybe it just made me check the machine and restart after installing security fixes. After that I did the usual check: Did the service start, are games being played. BUT: I did not check if games were actually running successfully.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, they did not. I tried some quick fixes but to no avail.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;down-the-rabbit-hole&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#down-the-rabbit-hole&quot; aria-label=&quot;Anchor link for: down-the-rabbit-hole&quot;&gt;🔗&lt;&#x2F;a&gt;Down the Rabbit Hole&lt;&#x2F;h1&gt;
&lt;p&gt;So the journey began:&lt;&#x2F;p&gt;
&lt;p&gt;I suspected a security update broke something. I found it had upgraded from Ubuntu 24.4.3 to 24.4.4. There was nothing extremely suspicious about that. So I started a headful game. I suspected a crash. But it ran. The other bot just did not join. Observing that one I saw why: No hosted games were listed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-you-not-join-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-you-not-join-game&quot; aria-label=&quot;Anchor link for: why-you-not-join-game&quot;&gt;🔗&lt;&#x2F;a&gt;Why You Not Join Game&lt;&#x2F;h2&gt;
&lt;p&gt;Why though? Either the host did not &quot;send&quot; its game or the client did not receive it. So I fired up &lt;code&gt;tcpdump&lt;&#x2F;code&gt; inside the docker network used by &lt;code&gt;sc-docker&lt;&#x2F;code&gt;. I did not remember the UDP port so a quick internet search revealed 6112. Nothing showed up when I tried to start a game. So I (falsely) assumed that nothing was being sent. I tried &lt;code&gt;WINEDEBUG&lt;&#x2F;code&gt; - figuring out the potential params with some ChatGPT help. It did not help much. So I decided to add &lt;code&gt;strace&lt;&#x2F;code&gt; to the sc-docker image and ran wine with strace (only the network related calls). It did call &lt;code&gt;sendto()&lt;&#x2F;code&gt; --- which is the Linux syscall for sending on a socket.&lt;&#x2F;p&gt;
&lt;p&gt;So it &lt;em&gt;was&lt;&#x2F;em&gt; sending. But why was it not receiving anything. I decided to &lt;code&gt;tcpdump&lt;&#x2F;code&gt; on the network bridge of docker. Thankfully, without port range. Because DAMN YOU internet, it announced games on port 6111 and &lt;strong&gt;not&lt;&#x2F;strong&gt; 6112.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;udp-broadcasting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#udp-broadcasting&quot; aria-label=&quot;Anchor link for: udp-broadcasting&quot;&gt;🔗&lt;&#x2F;a&gt;UDP Broadcasting&lt;&#x2F;h2&gt;
&lt;p&gt;So I ran &lt;code&gt;tcpdump&lt;&#x2F;code&gt; inside a docker container, and lo and behold... wait, again - nothing!&lt;&#x2F;p&gt;
&lt;p&gt;So I tried to manually broadcast UDP packets on 6111. And they got received. What the actual ... But there &lt;em&gt;was&lt;&#x2F;em&gt; a difference compared to my broadcast. It did use the subnet&#x27;s broadcast address. Starcraft uses 255.255.255.255. This in itself should not be a problem, and it worked before.&lt;&#x2F;p&gt;
&lt;p&gt;At this point I tried all kinds of shenanigans, checking netfilter, and iptables. All looked fine. At some point I decided to want to see &quot;more&quot; data about UDP packets being sent by Starcraft. With some more help ChatGPT figuring out the obscure and cryptic arguments of tcpdump, I finally got more info. In case you want to try: &lt;code&gt;tcpdump -i br-&amp;lt;yourbride&amp;gt; -nnve udp port 6111&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-s-not-only-the-ip&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-s-not-only-the-ip&quot; aria-label=&quot;Anchor link for: it-s-not-only-the-ip&quot;&gt;🔗&lt;&#x2F;a&gt;It&#x27;s Not Only the IP&lt;&#x2F;h2&gt;
&lt;p&gt;While packets were sent to 255.255.255.255, they were &lt;em&gt;not&lt;&#x2F;em&gt; sent to the MAC address FF:FF:..:FF. But to a concrete one. Whereas my manually sent packets &lt;em&gt;were&lt;&#x2F;em&gt; sent to that MAC address.&lt;&#x2F;p&gt;
&lt;p&gt;So some new code started to decide to route differently. I suspect a kernel &quot;fix&quot;. Figuring out the 255...255 address it might have decided that &lt;code&gt;lo&lt;&#x2F;code&gt; was a good choice - but I don&#x27;t know. I did not check. Whatever the cause is, packets were not broadcast - because it&#x27;s not a broadcast address.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixing-the-problem&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fixing-the-problem&quot; aria-label=&quot;Anchor link for: fixing-the-problem&quot;&gt;🔗&lt;&#x2F;a&gt;Fixing the Problem&lt;&#x2F;h2&gt;
&lt;p&gt;Because, in that case the fix might have meant to remove &lt;code&gt;lo&lt;&#x2F;code&gt; --- nah. Another option would have been to patch Starcraft. Kind of overkill.&lt;&#x2F;p&gt;
&lt;p&gt;A &quot;good&quot; solution might be to write a tool that captures those packets and re-broadcasts them properly.&lt;&#x2F;p&gt;
&lt;p&gt;So, nobody would actually try an ugly hack like rewriting UDP packets via netfilter. Or would they? Hint: Yes.
My netfilter foo is pretty bad, so ChatGPT helped once again - the basic idea being pretty simple: Create a priority prerouting rule that captures packets targeting 6111 and set the target MAC address to ff:...:ff. Like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;table inet filter {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	chain input {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		type filter hook input priority filter;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	chain forward {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		type filter hook forward priority filter;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	chain output {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		type filter hook output priority filter;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;table bridge starcraft_fix {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    chain prerouting {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        type filter hook prerouting priority -200;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        udp dport 6111 ether daddr set ff:ff:ff:ff:ff:ff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Making a 28-year-old game run reliably on a OS (Linux) that is wasn&#x27;t meant to run on, in a setting (headless) it was not meant to run on, with hooks (BWAPI) it was not supposed to have --- is a challenge.&lt;&#x2F;p&gt;
&lt;p&gt;Most bot authors use Windows or macOS - so neither this problem nor the solution might apply. But if you sc-docker games suddenly stop working it &quot;might&quot; be this issue. If you&#x27;re not on Linux - the fix might be different, but maybe I saved you a few hours finding the problem.&lt;&#x2F;p&gt;
&lt;p&gt;(PS: I used enough em dashes to trigger an LLM &quot;alarm&quot;. I let ChatGPT proof-read but all the text is mine.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why You Don&#x27;t Finish</title>
          <pubDate>Mon, 08 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/flight/</link>
          <guid>https://www.bytekeeper.org/posts/flight/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/flight/">&lt;p&gt;Do you start projects only to abandon them soon after? You &lt;em&gt;might&lt;&#x2F;em&gt; be fleeing.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t get me wrong, trying and failing is absolutely acceptable. How else could you learn, unless you make mistakes. But you have to allow yourself to make them.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes it is difficult to decide, but a project that clearly is going nowhere is fine to abort. Don&#x27;t push through if it makes no sense. As long as a project will teach you something useful, it is probably worth pursuing. What is useful? That is a decision only you can make. Listen to others, but don&#x27;t let someone else make that decision for you.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;abandoning-a-project&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#abandoning-a-project&quot; aria-label=&quot;Anchor link for: abandoning-a-project&quot;&gt;🔗&lt;&#x2F;a&gt;Abandoning a Project&lt;&#x2F;h1&gt;
&lt;p&gt;If you are about to abandon a project, you need to reflect on the reasons. Is there some new thing you are more interested in? If you follow that path, the next shiny idea will make you abandon that one next. An endless spiral of broken projects. You will never see their potential. Unless you finish something, everything will be a guaranteed failure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;flight&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#flight&quot; aria-label=&quot;Anchor link for: flight&quot;&gt;🔗&lt;&#x2F;a&gt;Flight&lt;&#x2F;h2&gt;
&lt;p&gt;What if your novel idea is just coping, or you have no new idea, but are just bored? The real reason might be fear. Whatever you create:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Will it be good enough?&lt;&#x2F;li&gt;
&lt;li&gt;What will people think of it?&lt;&#x2F;li&gt;
&lt;li&gt;Will I disappoint myself?&lt;&#x2F;li&gt;
&lt;li&gt;Will anyone use it?&lt;&#x2F;li&gt;
&lt;li&gt;Are you wasting your time?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That is the trap of vanity. A harmful trait that will prevent you from developing further. You cannot make progress without stumbling along the way.&lt;&#x2F;p&gt;
&lt;p&gt;Regarding the topic of no one using your result:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A few is enough for me; so is one; and so is none&lt;&#x2F;p&gt;
&lt;p&gt;--- Seneca&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If it is a waste of time, why did you start it in the first place? There must have been a reason. Learning something at the very least. Producing something of value as a bonus.&lt;&#x2F;p&gt;
&lt;p&gt;But avoiding adversity by chasing new projects will never show you what you are capable of, or what you are  not yet capable of. Maybe, that was why you diverted yourself in the first place, wasn&#x27;t it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;volatility&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#volatility&quot; aria-label=&quot;Anchor link for: volatility&quot;&gt;🔗&lt;&#x2F;a&gt;Volatility&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s assume you really found something new and so it&#x27;s not fear that drives you. If you jump ship, you only proved that you get distracted by the tiniest of things. Instead, write the idea down. Let it simmer for three days. If it is still &lt;em&gt;hot&lt;&#x2F;em&gt;, you may try it out. If not, pursue it at a later point --- after finishing your current project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;learning-to-learn&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#learning-to-learn&quot; aria-label=&quot;Anchor link for: learning-to-learn&quot;&gt;🔗&lt;&#x2F;a&gt;Learning to Learn&lt;&#x2F;h2&gt;
&lt;p&gt;Abandoning a project will waste a great opportunity: Learning what doesn&#x27;t work --- and why. Not finishing means you don&#x27;t have to think about failure. If you endure, not only will you hone your skills. You can analyze your flaws reflecting on a finished project. It could be a success, but you will never know.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;🔗&lt;&#x2F;a&gt;Summary&lt;&#x2F;h1&gt;
&lt;p&gt;You are not finishing projects because you fear that it is not perfect. You fear that others&#x27; opinions --- and your own --- will cast it as a failure.&lt;&#x2F;p&gt;
&lt;p&gt;So if you want to start finishing projects:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Accept that you lack the skills, but are willing to learn them.&lt;&#x2F;li&gt;
&lt;li&gt;When being distracted by a new shiny idea, write it down --- but tell yourself: &quot;I understand that this diversion is an exit provided by my mind to protect my ego&quot;. Don&#x27;t even consider it until some time has passed.&lt;&#x2F;li&gt;
&lt;li&gt;Think of what your project could be. If you don&#x27;t do it, nobody will. Does it really deserve to be abandoned, or does it deserve a chance to succeed?&lt;&#x2F;li&gt;
&lt;li&gt;Think of what you could learn by continuing. All those skills learned will make every further step better.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The only real failure is never to finish.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A Rant on Software Development Bloat</title>
          <pubDate>Fri, 05 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/devbloat/</link>
          <guid>https://www.bytekeeper.org/posts/devbloat/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/devbloat/">&lt;p&gt;In my opinion, software development has become rather bloated. If that is not the case for you, feel free to skip - or continue and enjoy some schadenfreude. This is just me ranting, don&#x27;t mind me. Much of this I experienced at some points in my career. Some of it I heard from friends. Some of it from social media.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer&lt;&#x2F;em&gt;: As with most &lt;del&gt;developers&lt;&#x2F;del&gt; people, I am in a bubble.&lt;&#x2F;p&gt;
&lt;p&gt;We got pretty good at pushing out MVPs. A reasonably good-looking UI, some backend scaffolding - that is done in days instead of months. How did we get there? Frameworks! We got so many, the start is basically plug-and-play. In some areas we got so many frameworks, it is overwhelming. JavaScript frameworks anyone?&lt;&#x2F;p&gt;
&lt;p&gt;Frameworks are nice. But many frameworks start to create an ecosystem after a while. You get more tools, but also more restrictions. This can be good and bad. On the one hand, working with the framework becomes more productive. On the other hand, learning the framework gets harder - so is integration with other libraries&#x2F;frameworks.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;dependencies&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependencies&quot; aria-label=&quot;Anchor link for: dependencies&quot;&gt;🔗&lt;&#x2F;a&gt;Dependencies&lt;&#x2F;h1&gt;
&lt;p&gt;Adding a new library or framework to your project? Are you thinking about the implications as well?
Your co-workers will also use them. &lt;strong&gt;You&lt;&#x2F;strong&gt; will use them. There should be a cost-benefit analysis for adding dependencies --- every time.&lt;&#x2F;p&gt;
&lt;p&gt;We mostly see the benefits while developing. Yes, we don&#x27;t have to write code. Yes, someone else might have solved it better than you could. And yes, if there&#x27;s a community --- you don&#x27;t have to support the code (as much).&lt;&#x2F;p&gt;
&lt;p&gt;What it costs us?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Google might suddenly drop the project, and you&#x27;re left with a huge chunk of software --- heavily intertwined with a framework that is no longer maintained.&lt;&#x2F;li&gt;
&lt;li&gt;Complexity of your software increases, because you opted for the &quot;common&quot; solution --- instead of the simple one.&lt;&#x2F;li&gt;
&lt;li&gt;You add bugs and security holes, which you probably cannot fix quickly --- other than asking someone else to do it, most likely a volunteer in an open-source project.&lt;&#x2F;li&gt;
&lt;li&gt;You don&#x27;t fully understand the framework, so your misuse adds more bugs. Thankfully, you can fix those yourself.&lt;&#x2F;li&gt;
&lt;li&gt;Updating a dependency can lead to conflicts if transitive dependencies don&#x27;t line up.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;dependencies-part-2&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependencies-part-2&quot; aria-label=&quot;Anchor link for: dependencies-part-2&quot;&gt;🔗&lt;&#x2F;a&gt;Dependencies Part 2&lt;&#x2F;h1&gt;
&lt;p&gt;Actually, there&#x27;s a hidden gem. Tool dependencies are often overlooked. They&#x27;re quite useful, until they&#x27;re not.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;your-ide&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#your-ide&quot; aria-label=&quot;Anchor link for: your-ide&quot;&gt;🔗&lt;&#x2F;a&gt;Your IDE&lt;&#x2F;h2&gt;
&lt;p&gt;Dev Containers, pre-configured IDEs, IDE setup guides? All useful. But again, nothing is free. For smaller projects, this part is usually not problematic. But for long-running products and projects...&lt;&#x2F;p&gt;
&lt;p&gt;As with all software, IDEs get updated, abandoned, and have bugs. You can enforce specific IDE setups to prevent this, but you will also make enemies among your co-workers on the way. Most of us customize their IDE experience. If you enforce your specific setup, you&#x27;re just evil. If you enforce the common ground it&#x27;s fair, but everyone will be unhappy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enforced-formatting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enforced-formatting&quot; aria-label=&quot;Anchor link for: enforced-formatting&quot;&gt;🔗&lt;&#x2F;a&gt;Enforced Formatting&lt;&#x2F;h2&gt;
&lt;p&gt;Yes, it&#x27;s nice once your team agrees on formatting. And that can be pretty unpleasant. But is your formatting enforced? Git pre-commit hooks? IDE integration? What happens if the formatter fails? It happens more often than you would expect. Or the IDE plugin differs from the plugin in the build tool, or the CLI tool you use.&lt;&#x2F;p&gt;
&lt;p&gt;You use more than one tool? That can happen if one tool only handles a part of what you want. But what happens if both tools have overlapping responsibilities. And they disagree? (Oh yes, they will.).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;build-tool&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#build-tool&quot; aria-label=&quot;Anchor link for: build-tool&quot;&gt;🔗&lt;&#x2F;a&gt;Build Tool&lt;&#x2F;h2&gt;
&lt;p&gt;Depending on the programming language, the number of build tools is ... enormous. They can be configured to death and bring all sorts of &lt;em&gt;joy&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Oh, your software is polyglot? Multiple build tools? Sounds like fun. Works fine on your continuous integration (CI), but building it on your machine is a chore? Scripted steps on your CI, manual steps locally --- nice!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;automated-testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#automated-testing&quot; aria-label=&quot;Anchor link for: automated-testing&quot;&gt;🔗&lt;&#x2F;a&gt;Automated Testing&lt;&#x2F;h2&gt;
&lt;p&gt;Unit tests take a second each, and there are 8,000 of them? Integration tests (for brevity&#x27;s sake, I&#x27;ll only distinguish between unit and integration tests here) are so slow you extracted them into separate build steps. Even your CI won&#x27;t run them unless you ask it to, if you know how to ask. And if the CI is even on same system and not split across multiple systems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;continuous-integration&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#continuous-integration&quot; aria-label=&quot;Anchor link for: continuous-integration&quot;&gt;🔗&lt;&#x2F;a&gt;Continuous Integration&lt;&#x2F;h2&gt;
&lt;p&gt;I mentioned it above, but CI is not something that magically appears. It has its own scripts and systems. It &lt;em&gt;can&lt;&#x2F;em&gt; be documented quite well, but often is not. It &lt;em&gt;can&lt;&#x2F;em&gt; be on one platform, but can be spread across platforms. Since your build and tests run in up to four hours there might be a slight problem if multiple developers are doing builds. It crashes, it fails randomly, it costs a ton of money. And you got one person who knows the configuration, two if you are lucky.&lt;&#x2F;p&gt;
&lt;p&gt;I mean, at least you have got CI and are not building the latest release on an individual developer&#x27;s machine.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;verdict&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#verdict&quot; aria-label=&quot;Anchor link for: verdict&quot;&gt;🔗&lt;&#x2F;a&gt;Verdict&lt;&#x2F;h1&gt;
&lt;p&gt;You will manage. If you can influence things, try to keep the environment simple --- not only the code (bloated architectures and design are topics for another time). If it&#x27;s out of your control, don&#x27;t get infuriated. You cannot change it, so accept it. Either way, it&#x27;s always a good time to learn the good and the bad stuff here.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t get angry at your co-workers; most of them want to do what they think is right. They might be wrong, you might be wrong. Especially on that point, reflect on your opinions. If they seem wrong, change them. If not, help the other person if possible --- otherwise, don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;A few suggestions that might help:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Require a short cost‑benefit note for new dependencies.&lt;&#x2F;li&gt;
&lt;li&gt;Add automated dependency update tests in CI.&lt;&#x2F;li&gt;
&lt;li&gt;Enforce one authoritative formatter.&lt;&#x2F;li&gt;
&lt;li&gt;Make builds reproducible locally (container or script).&lt;&#x2F;li&gt;
&lt;li&gt;Refactor tests to run fast.&lt;&#x2F;li&gt;
&lt;li&gt;Try to make builds as fast as possible locally.&lt;&#x2F;li&gt;
&lt;li&gt;Assign and rotate CI&#x2F;dependency owners.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>GameDev Review: Star Renegades</title>
          <pubDate>Tue, 09 Jan 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/review-star-renegades/</link>
          <guid>https://www.bytekeeper.org/posts/review-star-renegades/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/review-star-renegades/">&lt;p&gt;I promised a review &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;&quot;&gt;before&lt;&#x2F;a&gt;. I did not &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;shogun-showdown&#x2F;&quot;&gt;start&lt;&#x2F;a&gt; with it, oops. So let me catch up!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-came-before&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-came-before&quot; aria-label=&quot;Anchor link for: what-came-before&quot;&gt;🔗&lt;&#x2F;a&gt;What Came Before&lt;&#x2F;h1&gt;
&lt;p&gt;I was a bit surprised to find that the game was developed by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;developer&#x2F;massivedamage?snr=1_5_9__2000&quot;&gt;Massive Damage, Inc.&lt;&#x2F;a&gt;. They are the makers of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;651660&#x2F;Halcyon_6_Starbase_Commander_LIGHTSPEED_EDITION&#x2F;&quot;&gt;Halcyon 6&lt;&#x2F;a&gt; - which gained some small hype 2017&#x2F;2018. I bought the game and found it to be Okish. I played it a few hours and it seemed to lack focus. There were only a few updates, I&#x27;m not sure they &quot;fixed&quot; that. It could be a &quot;me&quot; problem. This however, is not a review of Halcyon.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;presentation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#presentation&quot; aria-label=&quot;Anchor link for: presentation&quot;&gt;🔗&lt;&#x2F;a&gt;Presentation&lt;&#x2F;h1&gt;
&lt;p&gt;The game looks great. At least if you like pixel art. That mixture of 3D and pixel art - its just working great for me. The anime sequences are good enough. They don&#x27;t seem to fit too well, but it must have been a ton of work, I can appreciate that.&lt;&#x2F;p&gt;
&lt;p&gt;In combat, characters don&#x27;t just aim and shoot, they get a real cool animation - and most of the time its not too long to be annoying in the long run.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;core-game-mechanics&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#core-game-mechanics&quot; aria-label=&quot;Anchor link for: core-game-mechanics&quot;&gt;🔗&lt;&#x2F;a&gt;Core Game Mechanics&lt;&#x2F;h1&gt;
&lt;p&gt;Clearly, combat is the main mechanic of the game. But there are other aspects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;walking-around&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#walking-around&quot; aria-label=&quot;Anchor link for: walking-around&quot;&gt;🔗&lt;&#x2F;a&gt;Walking Around&lt;&#x2F;h2&gt;
&lt;p&gt;Between battles you run around maps, unlocking regions and finding (some) items. This is a strange beast. While it could add to the overall game by being a freely roamable map, it is extremely linear. Its also not polished, sometimes you need to click around a lot to find a spot the characters will move to. It looks nice, and the concept of finding loot on it is also cool. But that also means little, since you just click on anything that could have loot in the area you&#x27;re in.&lt;&#x2F;p&gt;
&lt;p&gt;For me, this part of the game could very well be left out, resp. replaced with some narrative. Or, improved to be of value - or even be fun.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;socializing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#socializing&quot; aria-label=&quot;Anchor link for: socializing&quot;&gt;🔗&lt;&#x2F;a&gt;Socializing&lt;&#x2F;h2&gt;
&lt;p&gt;Also between battles you get to socialize with your companions. This is done by &quot;cards&quot; that you can let a character play targeting some other character. This idea is often found in the big RPGs, like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dragon_Age%3A_Origins&quot;&gt;Dragon Age: Origins&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But its more deeply integrated in those (yes, the sex aspect could be a major point - but it requires non-pixel art graphics I guess). Its carried through the narrative. After you gather you party you play the game with them, learning about theme and getting to like them.&lt;&#x2F;p&gt;
&lt;p&gt;Star Renegades tries to be a roguelike-like, your party might differ for each play-through. The narrative is slim, you don&#x27;t really build a connection with them. The socializing (camp) aspect seem extremely &quot;added&quot; - and while you reap some benefits (if two characters like each other, they can do combos), it feels a bit awkward.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;combat&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#combat&quot; aria-label=&quot;Anchor link for: combat&quot;&gt;🔗&lt;&#x2F;a&gt;Combat&lt;&#x2F;h2&gt;
&lt;p&gt;Now we&#x27;re getting down to the nitty-gritty core gameplay. The unique feature of combat seems to be the ability to &quot;push&quot; initiative of the enemies. You can even push them to the point they won&#x27;t be able to take their &quot;turn&quot;. As you have ample ability to push, you could win without taking damage. So the devs limited this. Enemies already pushed to the next turn (I know they use another term, but I&#x27;ll stick with turn here) cannot be pushed further.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe this mechanic can be made to work. They seemed to have tried and gave up. &quot;Why?&quot; you ask. Because they loaded up the game with buffs, stats and actions. But most of those things are not relevant at all - or at least appear to be irrelevant. The initiative manipulating mechanic seems to target more tactical players. But the added noise seems to try to cater to the RPG crowd. But since customization of characters is fairly limited, that seems a bit weak as well.&lt;&#x2F;p&gt;
&lt;p&gt;Also, there are some quality of live bugs: Enemies are previewed to blow up, yet they don&#x27;t. Enemies are shown to be pushed into the next turn, only to find they sometimes still get to shoot at you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;respecting-the-player-s-time&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#respecting-the-player-s-time&quot; aria-label=&quot;Anchor link for: respecting-the-player-s-time&quot;&gt;🔗&lt;&#x2F;a&gt;Respecting the Player&#x27;s Time&lt;&#x2F;h2&gt;
&lt;p&gt;After a few hours of easy to a bit less easy combat, I encountered a very strong enemy. I lost. But why? I could not figure out a reason. Maybe I was stupid? But all the enemies before were so darn easy. So it felt like a punishment. Or a &quot;hey we&#x27;re a roguelike-like&quot; message - in case you did not get it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-nemesis-system&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-nemesis-system&quot; aria-label=&quot;Anchor link for: a-nemesis-system&quot;&gt;🔗&lt;&#x2F;a&gt;A Nemesis System&lt;&#x2F;h2&gt;
&lt;p&gt;It has one. I&#x27;m not sure it works. It works well for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;241930&#x2F;Middleearth_Shadow_of_Mordor&#x2F;&quot;&gt;Shadow of Mordor&lt;&#x2F;a&gt; - but that is really kind of an open world game. You can choose to engage or to ignore your nemesis. And your enemies would climb ranks. However, in Star Renegades - it&#x27;s more of a level up system for them.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;🔗&lt;&#x2F;a&gt;Summary&lt;&#x2F;h1&gt;
&lt;p&gt;Don&#x27;t get me wrong, I actually enjoyed playing Star Renegades. I might play it a bit more, it just looks fantastic. Its weakness is the gameplay which swings from too easy to too hard with not much in-between. And they suffer from the &quot;one more feature&quot; syndrome. Less would have definitely be more here.&lt;&#x2F;p&gt;
&lt;p&gt;What I learned for my game? That I need to reevaluate all &quot;features&quot; later on and drop those that don&#x27;t make the game better.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll be diving into some market analysis!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Remnants of the Vanquished</title>
          <pubDate>Tue, 19 Dec 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/rotv/</link>
          <guid>https://www.bytekeeper.org/posts/rotv/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/rotv/">&lt;p&gt;Now, let me reveal the name of the &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;&quot;&gt;Unnamed Game&lt;&#x2F;a&gt;. This time, we will focus on some more technical aspects of the game. As a disclaimer: I use Bevy, which more or less enforces the use of an Entity Component System (ECS). ECS is kind of a hype (micro-services anyone?), of course it can be used for everything - but does it fit everything? Still, I am not yet turning my back on Bevy just yet, so I&#x27;ll have to make do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;short-recap&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#short-recap&quot; aria-label=&quot;Anchor link for: short-recap&quot;&gt;🔗&lt;&#x2F;a&gt;Short Recap&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;#ecs-systems&quot;&gt;Last time&lt;&#x2F;a&gt; I was babbling about model and visual components. I laid out some rules I &quot;found&quot; for turn based games using ECS:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Only core game logic systems may modify game logic components.&lt;&#x2F;li&gt;
&lt;li&gt;Only core game logic systems may remove game logic entities. They should send events in that case.&lt;&#x2F;li&gt;
&lt;li&gt;Non core game logic systems may add and modify their components to game logic entities. They must also ensure to clean up after themselves by removing components when systems go out of scope.&lt;&#x2F;li&gt;
&lt;li&gt;Non core game logic systems must handle entities suddenly disappearing and appearing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Alas, while I still think those are valid - a more restrictive approach might be necessary. Let&#x27;s take a step back and re-evaluate the current design approach.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;different-representation-of-what-the-player-sees-and-what-the-game-thinks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#different-representation-of-what-the-player-sees-and-what-the-game-thinks&quot; aria-label=&quot;Anchor link for: different-representation-of-what-the-player-sees-and-what-the-game-thinks&quot;&gt;🔗&lt;&#x2F;a&gt;Different Representation of What the Player Sees and What the Game &quot;Thinks&quot;&lt;&#x2F;h1&gt;
&lt;p&gt;I mentioned &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;#how-to-design-entities-and-systems-for-a-turn-based-game&quot;&gt;before&lt;&#x2F;a&gt;, that its harder for turn-based game to work with entities. ECS&#x27; are designed to run continuously, so for &quot;discrete&quot; components (discrete in time) - the result is stuttering.&lt;&#x2F;p&gt;
&lt;p&gt;But what does that mean? Let&#x27;s dive into the problem, by looking at several approaches on how to model game entities. For the sake of understandability, we&#x27;ll limit ourselves to one discrete event: A ship getting destroyed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;visual-components-and-model-components-share-entity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#visual-components-and-model-components-share-entity&quot; aria-label=&quot;Anchor link for: visual-components-and-model-components-share-entity&quot;&gt;🔗&lt;&#x2F;a&gt;Visual Components and Model Components Share Entity&lt;&#x2F;h2&gt;
&lt;p&gt;In this scenario, what the game &quot;thinks&quot; and what the player sees are the same. For the most part. Sounds simple enough. But, it has some repercussions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;immediate-model-updates-a-no-go&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#immediate-model-updates-a-no-go&quot; aria-label=&quot;Anchor link for: immediate-model-updates-a-no-go&quot;&gt;🔗&lt;&#x2F;a&gt;Immediate Model Updates: A No-Go&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotv&#x2F;shared_entities_immediate_model.png&quot; alt=&quot;Immediate Updates&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To the left is a representative entity. It contains hit points as a model component and a sprite as a visual component. The entity is in the process of being destroyed. To the right is the game state, separated by UI (visuals) and Model (what the game thinks). The UI is red, because the entity suddenly disappears which is inconsistent with what the player expects to happen (things going boooom). But the model is consistent (in a more computer sciency term - all invariants are held).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;&#x2F;strong&gt;: This is essentially a bad option, the game is for the player - not for the CPU or my peace of mind.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;delayed-model-updates-so-so&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#delayed-model-updates-so-so&quot; aria-label=&quot;Anchor link for: delayed-model-updates-so-so&quot;&gt;🔗&lt;&#x2F;a&gt;Delayed Model Updates: So-So&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotv&#x2F;shared_entities_delayed_model.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A similar representation as before, but this time the entity is not destroyed immediately. Instead, the &quot;planned&quot; destruction is communicated by an event or a flag in some component.&lt;&#x2F;p&gt;
&lt;p&gt;The UI has plenty of time to show explosions and whatnot. But, as indicated by the red model state, there&#x27;s a period where the model&#x27;s invariants don&#x27;t hold: a &quot;dead&quot; ship shouldn&#x27;t be present anymore. They will have to be removed later, by game model systems mind you, if the non-game-logic systems start meddling around things will get messy).&lt;&#x2F;p&gt;
&lt;p&gt;This will obviously work, but is a bit &quot;ugly&quot; from a technical point of view. The game logic systems will have to &quot;mark&quot; things other game logic systems will &quot;later&quot; do. In-between the game model is in a meh state. If, for example, the player was to leave the game (saving it in the process), loading it later would start by playing some animation - or not. It depends on whether the UI systems recreate the UI components for the already &quot;marked as dead&quot; ship.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;&#x2F;strong&gt;: This approach works but carry the stench of potential problems when restoring the game state.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;separate-entities-good-but-more-effort&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#separate-entities-good-but-more-effort&quot; aria-label=&quot;Anchor link for: separate-entities-good-but-more-effort&quot;&gt;🔗&lt;&#x2F;a&gt;Separate Entities: Good, but More Effort&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotv&#x2F;separate_entities.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this design, there are no &quot;reds&quot;. There is an entity for the model and an entity for the UI. The UI will try to keep entities in sync. If an entity in the model is destroyed, the UI can keep its entity around and start the destruction animation.&lt;&#x2F;p&gt;
&lt;p&gt;The model&#x27;s invariants are held, and the player won&#x27;t be confused. But we pay a price, we need to sync the relevant data. However, it offers the choice to diverge more freely from ECS in the model. The core game model does not need to be based on ECS at all.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;&#x2F;strong&gt;: This will work, but will also be more effort to implement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summary&quot; aria-label=&quot;Anchor link for: summary&quot;&gt;🔗&lt;&#x2F;a&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;As always, there is no single best solution - all have draw-backs. Coming from a business development perspective, it often pays to separate domain models:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotv&#x2F;domain_models.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Sourcing&quot; is the term for where data is coming from. Basically, that is, what I will be doing. That means &quot;Separate Entities&quot; is the way to go from here.&lt;&#x2F;p&gt;
&lt;p&gt;It also means I do not have to use ECS for the core game. Which means I can use a separate package (crate in Rust terms). That means Bevy will &quot;just&quot; be the frontend - which I could replace without touching the game rules at all.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;Having designed and implemented multiple smaller games using ECS, there is a challenge in using it for turn-based games. Using a separate &lt;code&gt;World&lt;&#x2F;code&gt; and schedule is something that can be used to make it work. But the gains are small in my opinion. As shown above I faced basic problems with my approach. I want to avoid wasting time later on, trying to synchronize all the data for entities that are a mixed bag of real-time components and turn-based components.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for sticking around, maybe you learned something - I sure did. See you next time...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Shogun Showdown Core Mechanics</title>
          <pubDate>Wed, 06 Dec 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/shogun-showdown/</link>
          <guid>https://www.bytekeeper.org/posts/shogun-showdown/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/shogun-showdown/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;&quot;&gt;As promised&lt;&#x2F;a&gt;, in this post, we will take a look at the game &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;2084000&#x2F;Shogun_Showdown&#x2F;&quot;&gt;Shogun Showdown&lt;&#x2F;a&gt;. It is still in early access, but has very good review scores and a sizeable amount too.
Its Steam page tells it&#x27;s &quot;inspired by great games like Into the Breach, Darkest Dungeon, Crypt of the Necrodancer, Slay the Spire and so much more&quot;. That is similar to the inspirations behind my own game. As my in-development game shares some of these traits, this game certainly is one I need to take a closer look.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-quick-detour-my-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-quick-detour-my-game&quot; aria-label=&quot;Anchor link for: a-quick-detour-my-game&quot;&gt;🔗&lt;&#x2F;a&gt;A Quick Detour: My Game&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;m on a &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;&quot;&gt;journey&lt;&#x2F;a&gt; to create and publish a game. The plan is to create a rogue-like with turn based combat, as mentioned just above. To not fumble blindly in game design, I want to check out and review similar games. But, mostly from a mechanics perspective. So let&#x27;s get back to Shogun Showdown...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-is-the-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-the-game&quot; aria-label=&quot;Anchor link for: what-is-the-game&quot;&gt;🔗&lt;&#x2F;a&gt;What is the Game?&lt;&#x2F;h1&gt;
&lt;p&gt;It&#x27;s a turn-based, rogue-like battler set in a Japanese-inspired setting. It features nice pixel art and a good soundtrack. I have not yet beaten the game, but from my current knowledge it basically has a few scenes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a Shop scene&lt;&#x2F;li&gt;
&lt;li&gt;a Combat scene&lt;&#x2F;li&gt;
&lt;li&gt;an Upgrade-only scene&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And while there are some random elements, the combat is pretty much deterministic. Since players will spent most of their time in that scene, let&#x27;s dive deeper:&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-combat-scene&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-combat-scene&quot; aria-label=&quot;Anchor link for: the-combat-scene&quot;&gt;🔗&lt;&#x2F;a&gt;The Combat Scene&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s start with a look on a typical combat scene:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;shogun-showdown&#x2F;combat_screen.jpg&quot; alt=&quot;Combat&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned, there&#x27;s not much randomness going on. The player has a set of actions, they can perform. Most will &quot;end the turn&quot; for the player and the enemies get to act. The game, like mine, tries to take elements from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;590380&#x2F;Into_the_Breach&#x2F;&quot;&gt;Into the Breach&lt;&#x2F;a&gt;. Reduced to 1 dimension, they left out units to protect. Its feasible to add those, so they might show up in a future update. The game also uses different &quot;field&quot; sizes, depending on the scenario.&lt;&#x2F;p&gt;
&lt;p&gt;Also, like in my imagined game, there are no decks. You get actions, and that&#x27;s all you get (mostly). Besides movement, the actions are similar to cards in other games. You can upgrade them, &quot;drawing&quot; is emulated by cool-downs. A very clever idea is the stacking of actions. It allows delaying attacks, unleashing multiple attacks in quick succession, damaging multiple enemies. It&#x27;s neatly embedded, and required to defeat certain game stages.&lt;&#x2F;p&gt;
&lt;p&gt;Another nice mechanic is the movement of the player and enemies. That includes actions that the player and enemies can use to move multiple tiles. It requires careful planning to avoid taking damage when moving to close to an enemy.&lt;&#x2F;p&gt;
&lt;p&gt;Enemies behave pretty deterministic and their chosen actions and action executions are at all times visible to the player. As with Into the Breach, this adds a nice puzzle-like feeling to the game: &quot;How can I solve this without taking any or much damage?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes (at least for me) it backfires a bit. Some enemies can attack from afar. If you focussed on close combat your only option is to try to get another enemy in-between. It feels a bit like &quot;iterative deepening&quot;: You make a bit of progress in the game, suddenly an enemy shows up that thwarts your choices. Maybe it&#x27;s still possible to win, and it&#x27;s just my skill (or lack thereof). I did not get that feeling in Into the Breach, it always seemed fair. Alas, it was not in early access, so I&#x27;ll cut Shogun Showdown some slack.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summarized&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#summarized&quot; aria-label=&quot;Anchor link for: summarized&quot;&gt;🔗&lt;&#x2F;a&gt;Summarized&lt;&#x2F;h2&gt;
&lt;p&gt;Movement and attack ranges give of a nice &quot;chess&quot;-like feeling. Enemies with no line-of-sight generally will just idle. But the &quot;initiative&quot; (i.e. the order in which units are taking turns) of the enemy is hidden. So, when you manage to get enemies to attack each other (which is a really nice mechanic), you don&#x27;t know which one will attack first.&lt;&#x2F;p&gt;
&lt;p&gt;The game is pretty forgiving, for a rogue-like. Health flasks and other consumables help, when a bad choice makes for a dire situation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compared-to-my-game-idea&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#compared-to-my-game-idea&quot; aria-label=&quot;Anchor link for: compared-to-my-game-idea&quot;&gt;🔗&lt;&#x2F;a&gt;Compared to My Game Idea&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;stacks-vs-board&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#stacks-vs-board&quot; aria-label=&quot;Anchor link for: stacks-vs-board&quot;&gt;🔗&lt;&#x2F;a&gt;Stacks vs Board&lt;&#x2F;h3&gt;
&lt;p&gt;The stacks in my prototype are similar to the one dimensional &quot;board&quot; in this game. This choice naturally leads to a reduced set of actions. The player can not attack any random enemy, but will have to select one of a few viable targets. Same for the enemies, many just can&#x27;t target the player due to other units being in the way. Its more natural than the stacks in my prototype, which allow targeting the top ship only, most of the time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;damage-management&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#damage-management&quot; aria-label=&quot;Anchor link for: damage-management&quot;&gt;🔗&lt;&#x2F;a&gt;Damage Management&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;shogun-showdown&#x2F;damage-management.png&quot; alt=&quot;A real artist&quot; &#x2F;&gt;
Another thing is the damage management. Its basically the core of Into the Breach, and also of Shogun Showdown. My prototype does things differently. You can almost never avoid taking damage. Instead, something that fits the lore of a science fiction game, I added shields. Shields get replenished fully at the start of each combat. Since damage cannot be avoided, the goal is to distribute the damage across all player ships to avoid actual hull damage.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;movement&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#movement&quot; aria-label=&quot;Anchor link for: movement&quot;&gt;🔗&lt;&#x2F;a&gt;Movement&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;shogun-showdown&#x2F;movement.png&quot; alt=&quot;Shake it&quot; &#x2F;&gt;
There is no actual movement per se in my game idea. Actions will usually put a ship to the bottom of the stack. Some can actions allow changing the player stack. Some modify the enemy stack. This differs a lot from Into the Breach and Shogun Showdown. In both games the player can actively move unit(s). For Shogun Showdown, this means an actual turn. For Into the Breach, the player still can perform an action. Not diving into this too much for now, it essentially means I might need to re-design movement in my game.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-other-scenes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-other-scenes&quot; aria-label=&quot;Anchor link for: the-other-scenes&quot;&gt;🔗&lt;&#x2F;a&gt;The Other Scenes&lt;&#x2F;h1&gt;
&lt;p&gt;There are shopping, upgrade and path selection scenes. The least important seems to be the path selection one. It has not struck me as very important on what benefits selecting different paths might have. I guess one could just remove it and tell a linear tale, for now.&lt;&#x2F;p&gt;
&lt;p&gt;Upgrading seems kinda important, but most of the time its &quot;you gain some you lose some&quot;. Even upgrades that cost &quot;money&quot; are sometimes not game-changers. I should play a bit more (almost certainly not because its a fun game) to check this further.&lt;&#x2F;p&gt;
&lt;p&gt;Shopping... I mean, it allows adding more tiles and has actions and such. But it feels a bit superfluous. I&#x27;m also pretty sure the sense of &quot;puzzle combat&quot; will be gone once I have a lot of actions available. Into the Breach managed to get superb gameplay by having about 6-8 actions (besides moving around) in a whole session.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;verdict&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#verdict&quot; aria-label=&quot;Anchor link for: verdict&quot;&gt;🔗&lt;&#x2F;a&gt;Verdict&lt;&#x2F;h1&gt;
&lt;p&gt;Its a fun game. Combat feels nice for the most part and when I am surprised, its mostly through my own lack of planning. There&#x27;s no observable narrative, the setting is very cool - the game really needs one. The world map serves no other purpose than to click a random next location. Upgrading and shopping seems a bit superfluous. BUT - this is an Early Access game. The actual combat is so much fun, I am pretty sure the devs can improve the other aspects. It could stand right there, next to Into the Breach - with some work.&lt;&#x2F;p&gt;
&lt;p&gt;For my journey, it shows some the flaws in my game design I need to address. Its such a wonderful time in which I can validate ideas also by looking at what other people did. Not that this approach did not work many years ago, but the amount of good quality (and bad quality) games is huge. Thanks for reading - next time I will write about my prototype again. Stay tuned.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Yet to be Named Game - Continued</title>
          <pubDate>Sat, 25 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/unnamed-game-2/</link>
          <guid>https://www.bytekeeper.org/posts/unnamed-game-2/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/unnamed-game-2/">&lt;p&gt;In the &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;&quot;&gt;last&lt;&#x2F;a&gt; post, I showed some of the goals I want to achieve with my game. Including some early screen shots. I decided to replace the AI generated ships with some procedural generated ships - lets have a look:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;procedural-ships.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;While not exactly beautiful, and not even in the style I want the game to be - it&#x27;s a better placeholder. It should not agitate people as much, as AI generated art would.&lt;&#x2F;p&gt;
&lt;p&gt;Other than that, due to me being sick all week - not much changed. I made some small progress on the &quot;star travelling map&quot; (fancy), were a player can choose their path through the game.&lt;&#x2F;p&gt;
&lt;p&gt;So instead of game design, this time we&#x27;ll take a look at a technical aspect.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-engine-am-i-using&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-engine-am-i-using&quot; aria-label=&quot;Anchor link for: what-engine-am-i-using&quot;&gt;🔗&lt;&#x2F;a&gt;What Engine am I using&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bevyengine.org&#x2F;&quot;&gt;Bevy&lt;&#x2F;a&gt;. Wait... I said wait!&lt;&#x2F;p&gt;
&lt;p&gt;I know its in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; and I understand that is a hype topic. It is my current language of choice, and while Bevy might be far from complete, doing a prototype in it should be fine.&lt;&#x2F;p&gt;
&lt;p&gt;That inevitably means one thing: I am using an Entity Component System (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Entity_component_system&quot;&gt;ECS&lt;&#x2F;a&gt;). And this is a turn based game.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;how-to-design-entities-and-systems-for-a-turn-based-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-design-entities-and-systems-for-a-turn-based-game&quot; aria-label=&quot;Anchor link for: how-to-design-entities-and-systems-for-a-turn-based-game&quot;&gt;🔗&lt;&#x2F;a&gt;How to Design Entities and Systems for a Turn Based Game&lt;&#x2F;h1&gt;
&lt;p&gt;It might not be obvious, but real-time games are far easier to do with ECS. System could be executed slightly out of order, or not at all for some frames. It&#x27;s not something that a player will notice, or at least care much. If an enemy dies one frame too late it can always be excused. Visible glitches like projectiles passing through enemies are a lot more noticeable, so you&#x27;re not free to ignore the effects.&lt;&#x2F;p&gt;
&lt;p&gt;In turn based games however, things get dicier. Systems running out of order are immediately apparent.&lt;&#x2F;p&gt;
&lt;p&gt;Attacking an enemy which stands around with 0 health until the next turn? No-go.&lt;&#x2F;p&gt;
&lt;p&gt;Ordering an attack move and the game first attacks and then moves? Not good.&lt;&#x2F;p&gt;
&lt;p&gt;Another problem: Usually all systems run every frame. You cannot design systems that run once per turn. At least, not as easy. While I do have some experience in turn based games &lt;em&gt;and&lt;&#x2F;em&gt; in ECS games, I did not do both at the same time yet. Turns out, the real question to ask is&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-a-turn&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-a-turn&quot; aria-label=&quot;Anchor link for: what-is-a-turn&quot;&gt;🔗&lt;&#x2F;a&gt;What is a Turn?&lt;&#x2F;h2&gt;
&lt;p&gt;In chess its after you moved your piece. In a game like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;646570&#x2F;Slay_the_Spire&#x2F;&quot;&gt;Slay the Spire&lt;&#x2F;a&gt; its when you hit that &lt;em&gt;end turn&lt;&#x2F;em&gt; button.&lt;&#x2F;p&gt;
&lt;p&gt;But how should systems react to that? In real-time games its pretty easy: You assign the player input to some component and some system will use that and manipulate other components. You can do that in a turn-based game too. For example have a &lt;code&gt;Attack&lt;&#x2F;code&gt; component that has the &lt;code&gt;target&lt;&#x2F;code&gt; and the &lt;code&gt;weapon&lt;&#x2F;code&gt; in it. And once the system handling that is done, remove or clear that component. Oh, why do you get that strange feeling of &quot;wrongness&quot;?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-handle-input&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-handle-input&quot; aria-label=&quot;Anchor link for: how-to-handle-input&quot;&gt;🔗&lt;&#x2F;a&gt;How to Handle Input&lt;&#x2F;h2&gt;
&lt;p&gt;I cannot tell for you, but I can tell for me: Components are there to capture the state of the application&#x2F;game. But attacking in a turn based game is not a state change itself. The state of the game should reflect the action, but not necessarily contain the action as part of the state. (Of course there are exceptions to that rule, this is just the general advice of avoiding littering your entities with input components)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;trigger-vs-state.png&quot; alt=&quot;That is why your mouse button has that depression&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Events&lt;&#x2F;code&gt; (or Signals) are part of most ECS&#x27;. And a good fit for the discrete nature of input in a turn based game. You would not want to use it for real-time games mostly. For example, pulling the trigger to fire non-stop in an action game does not lend itself well for events. The player holding the trigger is &lt;em&gt;part&lt;&#x2F;em&gt; of the state of the game. On the other hand, the player pressing &lt;code&gt;jump&lt;&#x2F;code&gt; should only be part of a component, if they can vary the height of the jump by pressing longer or shorter. If not, an event will most likely suffice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ecs-systems&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ecs-systems&quot; aria-label=&quot;Anchor link for: ecs-systems&quot;&gt;🔗&lt;&#x2F;a&gt;ECS Systems&lt;&#x2F;h2&gt;
&lt;p&gt;Now for my game that means, every system can run continuously (and if I wasn&#x27;t a pedantic, even ignore the order partially - one frame more or less won&#x27;t hurt). Most will only do something if they receive an event. This helped also in determining the solution for another problem: When should the AI execute its turn?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-ai-s-turn&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-ai-s-turn&quot; aria-label=&quot;Anchor link for: the-ai-s-turn&quot;&gt;🔗&lt;&#x2F;a&gt;The AI&#x27;s Turn&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s not much of an AI in the game. Still, it has to perform its turn execution at some point. Previously it executed immediately after the player did. That is a problem. For the player, it looks as if the AI did nothing - because all units just &quot;jumped&quot; and were done.&lt;&#x2F;p&gt;
&lt;p&gt;So I added a hacky &lt;code&gt;ai_can_move&lt;&#x2F;code&gt; field that was read and modified by multiple systems. That way I could animate the player action, wait a short moment and execute the AI actions. It works, but it was not nice having that flag and the toggling logic.&lt;&#x2F;p&gt;
&lt;p&gt;What I really wanted ... and &quot;now its ok for the AI to execute its moves&quot; event. Far simpler than the flag.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;game-model-components-and-visual-components&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#game-model-components-and-visual-components&quot; aria-label=&quot;Anchor link for: game-model-components-and-visual-components&quot;&gt;🔗&lt;&#x2F;a&gt;Game &quot;Model Components&quot; and &quot;Visual Components&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;With that all &quot;solved&quot; for now, I still had to figure out how to represent all the entities. I wanted the game to work without any graphics. So I created a &quot;game model&quot;, based on entities. They would just contain the components required to run the turn based systems. Systems that rendered things should use their own entities and keep them in sync with the model entities.&lt;&#x2F;p&gt;
&lt;p&gt;And I immediately ignored that and added Sprite components to model entities (in a system outside of the core game systems mind you). This made implementing the prototype faster, but I would have to refactor the code to have a separation later.&lt;&#x2F;p&gt;
&lt;p&gt;But I noticed, even for my game - that was not true. At least not completely. As long as I would not touch model components in other parts of my game - I could add and remove any components I liked. Same for the other way around, model systems should not touch other components. With one exception, model systems are allowed to remove entities, and therefore delete components of other systems. Removing an entity should therefore be accompanied by an event, so other systems can react. This does not have to be explicit, Bevy for example sends a &lt;code&gt;RemovedComponent&lt;&#x2F;code&gt; event. But if you need more data, a custom event would be the choice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-handle-mixing-visual-and-game-logic-components&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-handle-mixing-visual-and-game-logic-components&quot; aria-label=&quot;Anchor link for: how-to-handle-mixing-visual-and-game-logic-components&quot;&gt;🔗&lt;&#x2F;a&gt;How to Handle Mixing Visual and Game Logic Components&lt;&#x2F;h2&gt;
&lt;p&gt;This is, of course, dangerous to some degree. If you are careless, this might leave core game components in a desolate state. But it saves time and performs (not really important here) better. So lets summarize the rules to make it work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Only core game logic systems may modify game logic components.&lt;&#x2F;li&gt;
&lt;li&gt;Only core game logic systems may remove game logic entities. They should send events in that case.&lt;&#x2F;li&gt;
&lt;li&gt;Non core game logic systems may add and modify their components to game logic entities. They must also ensure to clean up after themselves by removing components when systems go out of scope.&lt;&#x2F;li&gt;
&lt;li&gt;Non core game logic systems must handle entities suddenly disappearing and appearing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game-2&#x2F;data-model.png&quot; alt=&quot;Image&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;This time we have taken a look at how to design entities to work for the core game and one or more scenes. Events are a key element to keep things simpler and loosely coupled. ECS obviously is not the one-size-fits-all solution, but with some thinking the shoe fits quite good.&lt;&#x2F;p&gt;
&lt;p&gt;I played some &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;2084000&#x2F;Shogun_Showdown&#x2F;&quot;&gt;Shogun Showdown&lt;&#x2F;a&gt; (purely for research purposes of course). I did promise to do a review of another game, but that will have to wait. So next time, really: A review of shogun showdown, from a core mechanics point of view.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Yet to be Named Game</title>
          <pubDate>Thu, 16 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/unnamed-game/</link>
          <guid>https://www.bytekeeper.org/posts/unnamed-game/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/unnamed-game/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;game-dev-process&#x2F;&quot;&gt;Previously&lt;&#x2F;a&gt; we took a birds eye view on the topic of the game development process.
Or, to be more precise, the model in my mind. I plan on refining and correcting it as I go and learn.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;current-state-of-my-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#current-state-of-my-game&quot; aria-label=&quot;Anchor link for: current-state-of-my-game&quot;&gt;🔗&lt;&#x2F;a&gt;Current State of My Game&lt;&#x2F;h2&gt;
&lt;p&gt;I can actually show a screenshot of my prototype:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;prototype-screenshot.png&quot; alt=&quot;Not much to see yet&quot; &#x2F;&gt;
It contains a background I made and some ships an AI model created (for the prototype only, I will create or commission the real art). To the left are the player ships, to the right the enemy ships (I did not even bother to make the &quot;look&quot; toward the player ships).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;game-of-stacks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#game-of-stacks&quot; aria-label=&quot;Anchor link for: game-of-stacks&quot;&gt;🔗&lt;&#x2F;a&gt;Game of Stacks&lt;&#x2F;h3&gt;
&lt;p&gt;What is shown above contains the basic idea of the game:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You and the enemy have a stack of ships.&lt;&#x2F;li&gt;
&lt;li&gt;Either &quot;player&quot; can only perform actions with the ship on top&lt;&#x2F;li&gt;
&lt;li&gt;Actions generally evolve around stack manipulation&lt;&#x2F;li&gt;
&lt;li&gt;Actions doing damage will do so only to the top enemy ship for the most part
I intend to not change these, if possible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Things that are not set in stone, but shown above:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The action the enemy ship &quot;would&quot; take if it was on top. The reason it shows up for all enemies is that a player action could move an enemy to the top.&lt;&#x2F;li&gt;
&lt;li&gt;Health &lt;em&gt;and&lt;&#x2F;em&gt; shield. Not only does it work for a sci-fi setting, it adds a tactical layer - shields will be replenished at the start of combat.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here are the current actions of the top ship (it is taking the role of a &quot;tank&quot; in classical RPG terms):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;actions.png&quot; alt=&quot;Example Actions&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Lets take a look at the actions (keep in mind this is a very early prototype and things can and will change here):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Skip&lt;&#x2F;strong&gt;: all ships can do this, reasons for using it: Ship is on cooldown, all relevant actions are on cooldown, or the player wants to avoid cooldown. It will just move the ship to the bottom of the stack, ending the players turn.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Relinquish&lt;&#x2F;strong&gt;: The ship will move down one spot in the stack, the new top ship will immediately be ready for action. This allows the player to perform an action of another ship. This will mostly move the original ship back up on the top, which will take the response (its the tank after all).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Taunt&lt;&#x2F;strong&gt;: The enemy ship will take some small damage and the current ship will not be moved away. This allows the player to letting the current ship take a hit that would otherwise target the next player ship.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Attack&lt;&#x2F;strong&gt;: A light attack, after which the ship will move to the bottom of the stack. This is one of the simpler damage dealing actions, with a predictable stack positioning.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As you might have guessed, predicting the position of enemy and player ships is required to make effective use of the players fleet. In my last post I commented on the inspiration for the game. Funnily enough, its main source is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;590380&#x2F;Into_the_Breach&#x2F;&quot;&gt;Into the Breach&lt;&#x2F;a&gt; and not &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;646570&#x2F;Slay_the_Spire&#x2F;&quot;&gt;Slay the Spire&lt;&#x2F;a&gt;. But it differs a lot:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The obvious difference is, its not played on a grid.&lt;&#x2F;li&gt;
&lt;li&gt;And the player doesn&#x27;t get to freely move around ships in the stack.&lt;&#x2F;li&gt;
&lt;li&gt;Its also hard to avoid taking damage.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some of these limit the choice a player has each turn. One thing I try to compensate that is the addition of shields. The amount of health and shields is still very low, I want to make each turn as decisive as possible. It wouldn&#x27;t work if players and enemies would have at it for hours.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;challenges&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#challenges&quot; aria-label=&quot;Anchor link for: challenges&quot;&gt;🔗&lt;&#x2F;a&gt;Challenges&lt;&#x2F;h3&gt;
&lt;p&gt;Besides using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bevyengine.org&#x2F;&quot;&gt;bevy&lt;&#x2F;a&gt;, which might be a questionable decision - creating a turn based game in real-time engine poses some challenges.
Since systems run all the time, they need to &quot;not do anything&quot; as long as neither the player, nor the AI makes moves. There&#x27;s also the problem that AI actions should be delayed - or appear delayed to avoid confusing the player.&lt;&#x2F;p&gt;
&lt;p&gt;But the harder challenge was and is making the core mechanics &quot;fun&quot;. I am a firm believer of &quot;less is more&quot; - so I am trying to add as little as possible at a time to test if I can improve the prototype. It kind of boils down towards the target audience, which in turn is also determined by the number of choices a player has at each &quot;turn&quot; when &quot;playing&quot; something:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;unnamed-game&#x2F;player-choice.png&quot; alt=&quot;Object might appear larger than they are&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Obviously this plot has no indication of &quot;fun&quot; in it. There&#x27;s always a &quot;player&quot; having fun at any point. Depending on the person, almost everything on that plot might &quot;fit&quot; at some point.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Why did I show it?&lt;&#x2F;em&gt;
All human interaction can be placed somewhere on there. To the left your influence is miniscule (only few people can influence book authors). To the right your choice is abundant. Think hiking. For games it has additional consequences: To the left you will basically make an interactive movie. That means extremely high production value or an artistic product. To the right its also very high production value. Or so flexible it&#x27;s almost a content creation tool. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.factorio.com&#x2F;&quot;&gt;Factorio&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.minecraft.net&#x2F;&quot;&gt;Minecraft&lt;&#x2F;a&gt;, ...&lt;&#x2F;p&gt;
&lt;p&gt;I want my game to be in the middle. Enough choice to make every decision count. Not so extremely limited that the game is pretty much deterministic. Games tend to look simpler there, but the mechanics are polished to death. It will be tough and I expect the game to suck, but me a huge learning experience.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;short-term-goals&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#short-term-goals&quot; aria-label=&quot;Anchor link for: short-term-goals&quot;&gt;🔗&lt;&#x2F;a&gt;Short-Term Goals&lt;&#x2F;h3&gt;
&lt;p&gt;Tweaking the core mechanics. Implementing the &quot;paths&quot; a player can take. Adding environmental effects, like a solar flares of a local star damaging shields of all ships. Adding energy that can be used to upgrade ships. And repair cores to heal ships after combat.&lt;&#x2F;p&gt;
&lt;p&gt;And in-between that: Testing testing testing, finding people who will play-test such an early prototype.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;long-term-vision&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#long-term-vision&quot; aria-label=&quot;Anchor link for: long-term-vision&quot;&gt;🔗&lt;&#x2F;a&gt;Long-Term Vision&lt;&#x2F;h3&gt;
&lt;p&gt;I do have a nice narrative, and hope to add it in the game without boring players. I also would like to add side-quests, that can force players to take certain paths but will give rewards. For example, taking a friendly ship along and bring it to some destination.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;outlook&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#outlook&quot; aria-label=&quot;Anchor link for: outlook&quot;&gt;🔗&lt;&#x2F;a&gt;Outlook&lt;&#x2F;h3&gt;
&lt;p&gt;I hope to have the core mechanics basics done in time for the next update. I also want to do some reviews of similar games I played to get inspiration. The reviews will be focussed on the core mechanics mostly. I&#x27;ll start with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;651670&#x2F;Star_Renegades&#x2F;&quot;&gt;Star Renegades&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to suggest topics you want me to dive into. I&#x27;ll do so even when I have no clue what I am talking about, I&#x27;ll just fake it. Seems to work well enough.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Game Development: A Personal Journey</title>
          <pubDate>Thu, 09 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/game-dev-process/</link>
          <guid>https://www.bytekeeper.org/posts/game-dev-process/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/game-dev-process/">&lt;p&gt;Today, let&#x27;s take a peek at game development in general. Similar to what I already did &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotpr-clone&#x2F;&quot;&gt;a while ago&lt;&#x2F;a&gt;. A few months ago I picked it up again and to flesh out the design more and do some prototyping. It was nice and all, but I made the classical mistake of having a scope that would take years to implement.&lt;&#x2F;p&gt;
&lt;p&gt;Depending on what you want to achieve, that could be okay - or not. In my case, I want to learn the whole process:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Game Design&lt;&#x2F;li&gt;
&lt;li&gt;Game Development&lt;&#x2F;li&gt;
&lt;li&gt;Game Marketing&lt;&#x2F;li&gt;
&lt;li&gt;Game Self Publishing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ve been reading books and listening to audiobooks on the topic for a few years now. Its not all wasted time, but as an experienced software developer I can say: Only practising the art will make you really learn it. I am very certain that is true for almost anything that exists.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-game-development-process&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-game-development-process&quot; aria-label=&quot;Anchor link for: the-game-development-process&quot;&gt;🔗&lt;&#x2F;a&gt;The Game Development Process&lt;&#x2F;h1&gt;
&lt;p&gt;Disclaimer: I did not yet publish or finish any game. This is what I learned from various sources I read.&lt;&#x2F;p&gt;
&lt;p&gt;This is a rough diagram on how I imagine the development process generally works:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;game-dev-process&#x2F;game-development-process.png&quot; alt=&quot;Game Development Process Diagram&quot; &#x2F;&gt;
The arced arrows represent loops on the timeline of development. I also think the span of the loops are essentially &quot;true&quot; in relation to each other. The prototyping phase will be shorter than the release phase. It, in turn, will be shorter than the implementation phase. This is not a reflection of my experience, but of the things I learned, reading from other gamedev blogs and post mortems.&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, I believe marketing should start even before the design or the prototype is done. There&#x27;s no point were you should stop doing it.&lt;&#x2F;p&gt;
&lt;p&gt;Change design as much as you like in the prototyping phase. Be more conservative in the implementation phase, unless play-testing shows your game sucks, then - uh - &quot;pivot&quot;. Don&#x27;t really change the design in the release phase, you probably will regret it - not having enough time to flesh it out &lt;em&gt;and&lt;&#x2F;em&gt; implement it.&lt;&#x2F;p&gt;
&lt;p&gt;Content creation is pretty simple. Once you get your prototype running, keep the game playable as much as possible. If not for yourself, do it to have the opportunity to have people test it. Even better if you get a streamer to test an early version.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;my-game-development-experience-up-to-now&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-game-development-experience-up-to-now&quot; aria-label=&quot;Anchor link for: my-game-development-experience-up-to-now&quot;&gt;🔗&lt;&#x2F;a&gt;My Game Development Experience Up to Now&lt;&#x2F;h1&gt;
&lt;p&gt;I guess I should have started with the drawing above, and then make a plan for my game. I might have noticed earlier that I cannot create a large game &lt;em&gt;and&lt;&#x2F;em&gt; learn the rest of the development process in a reasonable time.&lt;&#x2F;p&gt;
&lt;p&gt;To be able to practise anything besides the development itself, I needed to shrink my scope severely. I decided on a game that would take a few months to develop. Going even further, like a one-month project, would be sufficient to help my learn self publishing. Maybe even game design. But marketing would be lacking.&lt;&#x2F;p&gt;
&lt;p&gt;According to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;howtomarketagame.com&#x2F;2021&#x2F;04&#x2F;19&#x2F;yes-you-need-to-create-a-steam-page-right-now&#x2F;&quot;&gt;some sources&lt;&#x2F;a&gt;, you need at least 6 months for things to get rolling. So my plan was to create a game in 3-4 months. I understand, that it will take longer. The plan was based on the untrue assumption, that I could work on the game for roughly 2 hours every day of the week. It doesn&#x27;t matter that it will take longer, what matters is that it prevents feature creep due to a tighter schedule.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-learned-so-far&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-learned-so-far&quot; aria-label=&quot;Anchor link for: what-i-learned-so-far&quot;&gt;🔗&lt;&#x2F;a&gt;What I learned so far&lt;&#x2F;h2&gt;
&lt;p&gt;I had a basic idea, and decided to dive right into prototyping. This strategy usually serves me well, since most daily programming problems don&#x27;t require much thought or planning. To be fair, I think programming for over 30 years helps a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out, with games you pay a high price doing that. I started my prototype and then tried to iterate with different mechanics which lead to a lot of refactoring. Don&#x27;t get me wrong, this is certainly a good idea once you have some basic mechanics settled. But at that time, I had barely a grasp. So my first lesson learned:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-skip-the-design-part&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#don-t-skip-the-design-part&quot; aria-label=&quot;Anchor link for: don-t-skip-the-design-part&quot;&gt;🔗&lt;&#x2F;a&gt;Don&#x27;t skip the design part&lt;&#x2F;h2&gt;
&lt;p&gt;I did not want to waste more time, so I did something strange. I took a notebook, left the computer and started scribbling ideas. I drew the &quot;scenes&quot; I had in mind already. Then started sketching gameplay mechanics, and wrote down what came to mind.&lt;&#x2F;p&gt;
&lt;p&gt;I did not fully flesh out the design on paper, but I had some nice narrative going. And, more importantly, I found the base mechanics I want to adhere to.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-i-am-at&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-i-am-at&quot; aria-label=&quot;Anchor link for: where-i-am-at&quot;&gt;🔗&lt;&#x2F;a&gt;Where I am at&lt;&#x2F;h2&gt;
&lt;p&gt;So, I am still in the early stages. I don&#x27;t want to reveal the current game design. Not for fear of someone stealing my ideas, ideas are available in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.escapistmagazine.com&#x2F;Why-Your-Game-Idea-Sucks&#x2F;&quot;&gt;abundance&lt;&#x2F;a&gt;. Let&#x27;s just say I got my inspiration from games like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;646570&#x2F;Slay_the_Spire&#x2F;&quot;&gt;Slay the Spire&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;app&#x2F;590380&#x2F;Into_the_Breach&#x2F;&quot;&gt;Into the Breach&lt;&#x2F;a&gt;. And yet, it is neither a card battler, nor has a chess-board like map.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m currently iterating, trying to flesh out my idea. I don&#x27;t yet see my prototype or core gameplay loop being fun. But I am convinced that I can change it, to be fun:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;game-dev-process&#x2F;current-position-in-process.png&quot; alt=&quot;You are here&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The goal now, and my main focus of work, is to &quot;fix&quot; this problem. If I can make it &lt;em&gt;click&lt;&#x2F;em&gt; - the rest will follow.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Comments - Take 3 - On Zola</title>
          <pubDate>Fri, 03 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/comments-self-hosted/</link>
          <guid>https://www.bytekeeper.org/posts/comments-self-hosted/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/comments-self-hosted/">&lt;p&gt;A &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;comments-on-jekyll&#x2F;&quot;&gt;while ago&lt;&#x2F;a&gt; I added comments for my Jekyll base blog. Since then, I switched to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;zola&lt;&#x2F;a&gt;.
Its basically a Rust based &quot;clone&quot; of the static site generator (SSG) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;. Compared to Jekyll, it is a lot faster, and I found it to be a lot simpler.
However, Zola has its own set of challenges. For instance, changing themes is nearly impossible and theme extension can get messy. That is the story for a future post.
At some point I might consider creating my own SSG. Building a small one should be easy enough (and due to GitHub&#x27;s Actions it could be used immediately).&lt;&#x2F;p&gt;
&lt;p&gt;Why did I move? Jekyll was basically the go-to SSG back when GitHub only supported it. And it always supported an outdated version. And Jekyll is generally pretty slow.
And after some small update I could not get it to run on my machine in a reasonable amount of time (minutes is reasonable here). Searching for a new led me to Hugo, and then in turn to its Rust &quot;clone&quot; Zola.
I tend to prefer applications written in Rust nowadays, but only slightly - due to the fact that I could contribute code.&lt;&#x2F;p&gt;
&lt;p&gt;Oh and, while migrating, I trashed the comment functionality - oops. Now its time to restore it back to its old (and pretty empty) glory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;comments-on-jekyll&#x2F;#step-1-create-a-service-that-accepts-comments&quot;&gt;Previously&lt;&#x2F;a&gt; I used CGI. It&#x27;s a bit archaic and cumbersome.
So here&#x27;s what we&#x27;ll do:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;First, we&#x27;ll transform it to a web service&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll switch from our custom (and now obsolete) GitHub client to a working one I don&#x27;t have to maintain&lt;&#x2F;li&gt;
&lt;li&gt;Our Javascript client needs to be re-added to the new Zola generated site&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here&#x27;s a sequence diagram of how it is supposed to work:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;comments-self-hosted&#x2F;Sequence%20Diagram%20Comments.png&quot; alt=&quot;Comment Sequence Diagram&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;our-own-web-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#our-own-web-server&quot; aria-label=&quot;Anchor link for: our-own-web-server&quot;&gt;🔗&lt;&#x2F;a&gt;Our own Web Server&lt;&#x2F;h1&gt;
&lt;p&gt;Now, first, we need to implement our own TCP stack... J&#x2F;k we&#x27;ll use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hyper.rs&#x2F;&quot;&gt;hyper&lt;&#x2F;a&gt;. We could use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;axum&#x2F;&quot;&gt;Axum&lt;&#x2F;a&gt; but we won&#x27;t be needing any full blown web server anytime soon.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s fire up a simple server at &lt;code&gt;127.0.0.0:3000&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; core&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;convert&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Infallible&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; hyper&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;make_service_fn&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; service_fn&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; hyper&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Body&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Request&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Response&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Server&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;net&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;SocketAddr&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; This is the actual endpoint:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; It should take a comment from a user by POST request and convert it to instructions on GitHub.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The result should be a branch which includes the comment. And a PR for the branch for easy review and merge-ability.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;async&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; post_comment_service&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;_req&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Request&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Response&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Body&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Infallible&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Response&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Hello World&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;tokio&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; addr&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; SocketAddr&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;127&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 3000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; post_comment_service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        make_service_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;_conn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;_&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Infallible&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;service_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;post_comment_service&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;bind&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;addr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;serve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;post_comment_service&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Simple enough.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;choosing-a-github-client-for-rust&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#choosing-a-github-client-for-rust&quot; aria-label=&quot;Anchor link for: choosing-a-github-client-for-rust&quot;&gt;🔗&lt;&#x2F;a&gt;Choosing a GitHub Client for Rust&lt;&#x2F;h1&gt;
&lt;p&gt;According to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;rest&#x2F;overview&#x2F;libraries-for-the-rest-api?apiVersion=2022-11-28#rust&quot;&gt;GitHub&lt;&#x2F;a&gt; - there&#x27;s no official client for Rust. But there are two third party clients. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;octocat-rs&#x2F;octocat-rs&quot;&gt;Octocat&lt;&#x2F;a&gt; seems to be on the decline. I&#x27;m sorry if I misjudged here, I just compared the activity to the alternative &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;XAMPPRocky&#x2F;octocrab&quot;&gt;Octocrab&lt;&#x2F;a&gt; which has a lot more &quot;everything&quot;. A quick check reveals it seems to be able to create branches, commits and PRs - everything we need. So we&#x27;re using that, just a quick &lt;code&gt;cargo add octocrab&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;representing-comment-requests-and-comment-data&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#representing-comment-requests-and-comment-data&quot; aria-label=&quot;Anchor link for: representing-comment-requests-and-comment-data&quot;&gt;🔗&lt;&#x2F;a&gt;Representing Comment Requests and Comment Data&lt;&#x2F;h1&gt;
&lt;p&gt;Most of the post handling code is the same as before, for brevities sake I will only skim through here:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; This is the request from a client&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Deserialize&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Debug&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Post&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    path&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; This will be serialized into a comment file on GitHub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Debug&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Comment&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, let&#x27;s modify our &lt;code&gt;post_comment_service&lt;&#x2F;code&gt; to actually do something. First we need to grab ourselves the content and then deserialize it to a &lt;code&gt;Post&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;post_request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; hyper&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_bytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;post&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Post&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; serde_json&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from_slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;post_request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Response&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;builder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;StatusCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;BAD_REQUEST&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Invalid JSON&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Quick note: Using Axum, this would have been a lot shorter.&lt;&#x2F;p&gt;
&lt;p&gt;The next steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a branch&lt;&#x2F;li&gt;
&lt;li&gt;Add the comment to the correct &lt;code&gt;comments.yaml&lt;&#x2F;code&gt; file&lt;&#x2F;li&gt;
&lt;li&gt;Create a pull request&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Creating a branch requires a commit SHA it is based on, so we grab that and create a branch of it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; master_sha&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt; repo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;get_ref&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Reference&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Branch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;master&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not get master ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Object&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Commit&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; sha&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; ..&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; sha&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; ..&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; sha&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; unreachable!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    debug!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Creating branch &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; from &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; branch_name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; master_sha&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;create_ref&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Reference&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Branch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;branch_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; master_sha&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not create branch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, due to technical limitations (Zola can read a yaml file, but not a set of files) we need to have all comments of a blog in one file. This will be called &lt;code&gt;comments.yaml&lt;&#x2F;code&gt; as stated above.&lt;&#x2F;p&gt;
&lt;p&gt;A new comment should not just overwrite all other comments, so we first need to grab the file if it exists.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; content_items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; match&lt;&#x2F;span&gt;&lt;span&gt; repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;get_content&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;content_items&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; content_items&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;_&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            info!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Assuming no comments present yet at &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            ContentItems&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt; items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; There can&amp;#39;t be more than one file with the same name:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    assert!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;content_items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; content&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; content_items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now lets create or append to the existing comment file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; new_comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        serde_yaml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;comment&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not convert comment to yaml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Yes, this name is rubbish &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; author&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CommitAuthor&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Comment0r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        email&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;none@example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; let&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;content&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; content&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; GitHub API requires the SHA of the old file to update it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; content&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sha&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;content&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;decoded_content&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; content&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sha&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        writeln!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; content&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; new_comment&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not add comment to file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;update_file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;            &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Added comment from &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            content&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            sha&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;branch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;branch_name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;commiter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;author&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not update file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        debug!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Creating new file at &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;create_file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;            &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Added comment from &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            new_comment&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;branch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;branch_name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;commiter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;author&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not create file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I decided against parsing the existing file. It should be fine. If it&#x27;s not, I can fix it manually, its a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;yaml.org&quot;&gt;YAML&lt;&#x2F;a&gt; file after all.
Instead, just a &quot;grab the existing content&quot; and &quot;append new comment&quot; in YAML format, if nothing is there &quot;create a new file&quot; and &quot;write the comment as initial content&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Last but not least, I don&#x27;t want to manually merge or create pull requests, so let&#x27;s do that as well:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    oc&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;pulls&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;owner&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;repo&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;create&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;New comment from &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            branch_name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;master&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Could not create PR&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will create a PR with the title &quot;New comment from &lt;user&gt;&quot; trying to merge the new branch into master.&lt;&#x2F;p&gt;
&lt;p&gt;Now, this service serves no purpose, if no-one ever calls it. That would be...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-client-part&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-client-part&quot; aria-label=&quot;Anchor link for: the-client-part&quot;&gt;🔗&lt;&#x2F;a&gt;The Client Part&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;ll omit the html part for now, it&#x27;s mostly a boring form with a name, an URL and a message field - and a submit button.
The &quot;interesting&quot; part is the JavaScript client, that will do the following:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Take the comment&lt;&#x2F;li&gt;
&lt;li&gt;Send it to an endpoint&lt;&#x2F;li&gt;
&lt;li&gt;Show the user a success or error page&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here is the first part:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; form&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; document&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;getElementById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;commentForm&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;form&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; function&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E36209, #FFAB70);&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Don&amp;#39;t submit actually&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;preventDefault&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; formData&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; FormData&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;form&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; commentData&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; formData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        url&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; formData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; formData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        path&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{{ current_path | safe }}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t get confused by the &lt;code&gt;{{ ... }}&lt;&#x2F;code&gt; stuff, that&#x27;s part of the template engine Zola uses (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;keats.github.io&#x2F;tera&#x2F;&quot;&gt;Tera&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;With the data firm in our grasp, onwards to step 2:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Now post the comment to the service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{{ config.extra.api_url | safe }}&#x2F;comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    method&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    headers&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    body&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;commentData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And finally step 3:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E36209, #FFAB70);&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        form&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;reset&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        window&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;pages&#x2F;submitted&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        window&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;pages&#x2F;submission-failed&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E36209, #FFAB70);&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    window&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;pages&#x2F;submission-failed&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also clear the form when the submit succeeded. Otherwise, returning to the page would keep the form content. On error, I let the content stay, maybe it was just a hiccup after all?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;security&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#security&quot; aria-label=&quot;Anchor link for: security&quot;&gt;🔗&lt;&#x2F;a&gt;Security&lt;&#x2F;h1&gt;
&lt;p&gt;The service itself is pretty small. Due to Rust&#x27;s nature, I consider buffer under&#x2F;overflow attacks to be pretty unlikely. Not impossible mind you.
Rust could have some memory safety bug. Hyper uses some &lt;code&gt;unsafe&lt;&#x2F;code&gt; and could have a bug there. Same with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tower-rs&#x2F;tower&quot;&gt;tower&lt;&#x2F;a&gt; which I will use below.
I pass much of the user entered data to Octocrab, which in turn relies on other libraries and interacts with GitHubs API. I expect their API to refuse any malformed requests, otherwise its their problem (unless..).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;threat-model&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#threat-model&quot; aria-label=&quot;Anchor link for: threat-model&quot;&gt;🔗&lt;&#x2F;a&gt;Threat Model&lt;&#x2F;h2&gt;
&lt;p&gt;The service will run on a trusted machine (due to access to a personal access token from GitHub). It is meant to run behind a reverse proxy, but should also work as a public service.
It will not guarantee to work under a DOS attack, but it will guarantee not to let an remote attacker DOS the whole host system.&lt;&#x2F;p&gt;
&lt;p&gt;The main DOS vectors against the host system are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sending large chunks of data in a post request which would then be sent to GitHub (clogging up up- and downstream)&lt;&#x2F;li&gt;
&lt;li&gt;Sending many small requests which would result in a multiple of API calls to GitHub&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;changes-required-by-the-threat-model&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#changes-required-by-the-threat-model&quot; aria-label=&quot;Anchor link for: changes-required-by-the-threat-model&quot;&gt;🔗&lt;&#x2F;a&gt;Changes Required by the Threat Model&lt;&#x2F;h2&gt;
&lt;p&gt;First, lets limit the input our services receives to have some basic DOS protection:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; body&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Limited&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;req&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;into_body&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1024&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s also do some basic rate limiting:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; post_comment_service&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; make_service_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;_conn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        Ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;_&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Infallible&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;RateLimit&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            service_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;post_comment_service&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;            Rate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Duration&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from_secs&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It would certainly be better to limit per IP. But the intention here was not to prevent DOS attacks to the service itself, just the host system.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-s-next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;What&#x27;s Next?&lt;&#x2F;h1&gt;
&lt;p&gt;Obviously, the code could be improved - it was basically almost completely rewritten in about 2 days. This will have to wait for another time...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;It was a fun and sometimes tedious exercise. Hyper&#x27;s API is &quot;strange&quot; - services return a &lt;code&gt;Result&amp;lt;.., Infallible&amp;gt;&lt;&#x2F;code&gt;. Why not just the real result. I guess I could read up on the reason, but its certainly hidden very well.
Octocrab is pretty decent. It&#x27;s a bit cumbersome at times (i.e. &lt;code&gt;send()&lt;&#x2F;code&gt; for API that retrieves things). Authenication is really annoying and lacks documentation - but is easy enough once figured out.
I ran into an issue where I configured the wrong URL to access GitHub - I swear I saw it somewhere in the Octocrab repository.
This was made even worse by the API suddenly trying to deserialize incomplete JSON objects from GitHub (like cut off after 21 characters, without any reason why).&lt;&#x2F;p&gt;
&lt;p&gt;It took a fair bit longer than expected.&lt;&#x2F;p&gt;
&lt;p&gt;The parts I did not present:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The cloudflare config to route the &lt;code&gt;api&lt;&#x2F;code&gt; sub-domain to my VPS&lt;&#x2F;li&gt;
&lt;li&gt;My VPS&#x27; Apache2 config for a secure reverse proxy&lt;&#x2F;li&gt;
&lt;li&gt;Getting a signed certificate&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I hope you enjoyed this post of mine, feel free to comment. It should work now. If not, I&#x27;ll just assume you&#x27;re happy and live in blissful ignorance.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>4X Game Loop</title>
          <pubDate>Fri, 05 Aug 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/game-loop/</link>
          <guid>https://www.bytekeeper.org/posts/game-loop/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/game-loop/">&lt;p&gt;I think I don&#x27;t really need to explain what a 4X game is, maybe not even what the 4X stand for.
But, I&#x27;ll do it anyway - the gameplay loop of a typical 4X game is:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;game-loop&#x2F;4x_game.dot.png&quot; alt=&quot;The 4 Xs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;eXplore&lt;&#x2F;li&gt;
&lt;li&gt;eXpand&lt;&#x2F;li&gt;
&lt;li&gt;eXploit&lt;&#x2F;li&gt;
&lt;li&gt;eXterminate&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Much of the fun of this simple formula comes from the fact that none of those factors stands on its own.
You usually cannot expand without exploring first. Sometimes to expand by exterminating an enemy at a location first.
What is there to exploit, if you did not expand before?
And how will you exterminate, if you lack the resources you ought to have exploited first.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s dive deeper into the &lt;del&gt;abyss&lt;&#x2F;del&gt; parts of such a game:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exploration&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#exploration&quot; aria-label=&quot;Anchor link for: exploration&quot;&gt;🔗&lt;&#x2F;a&gt;Exploration&lt;&#x2F;h2&gt;
&lt;p&gt;The most basic approach is sending out scouts. An alternative would be spying.
Maybe diplomacy uncovers some secrets as well.&lt;&#x2F;p&gt;
&lt;p&gt;Keywords:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Scouts&lt;&#x2F;li&gt;
&lt;li&gt;Diplomacy&lt;&#x2F;li&gt;
&lt;li&gt;Special events&lt;&#x2F;li&gt;
&lt;li&gt;Spies&lt;&#x2F;li&gt;
&lt;li&gt;Racial abilities&lt;&#x2F;li&gt;
&lt;li&gt;Tech abilities&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;expansion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#expansion&quot; aria-label=&quot;Anchor link for: expansion&quot;&gt;🔗&lt;&#x2F;a&gt;Expansion&lt;&#x2F;h2&gt;
&lt;p&gt;Again, there&#x27;s the direct way of sending a colony ship (settler). Alternatively you can take over an enemy colony.
A random encounter could grant you a new colony.&lt;&#x2F;p&gt;
&lt;p&gt;Keywords:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Colony ships&lt;&#x2F;li&gt;
&lt;li&gt;Outposts&lt;&#x2F;li&gt;
&lt;li&gt;Diplomacy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;exploitation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#exploitation&quot; aria-label=&quot;Anchor link for: exploitation&quot;&gt;🔗&lt;&#x2F;a&gt;Exploitation&lt;&#x2F;h2&gt;
&lt;p&gt;Pumping up the population. Building factories. Gather food. There could be a lot of resources to be gathered.
Also things like research are part of this.
There are also hidden resources. A large army could incline neighbours for that large tribute.&lt;&#x2F;p&gt;
&lt;p&gt;Keywords:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Research&lt;&#x2F;li&gt;
&lt;li&gt;Mining&lt;&#x2F;li&gt;
&lt;li&gt;Taxation&lt;&#x2F;li&gt;
&lt;li&gt;Diplomacy&lt;&#x2F;li&gt;
&lt;li&gt;Spies&lt;&#x2F;li&gt;
&lt;li&gt;Production&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;extermination&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#extermination&quot; aria-label=&quot;Anchor link for: extermination&quot;&gt;🔗&lt;&#x2F;a&gt;Extermination&lt;&#x2F;h2&gt;
&lt;p&gt;Send your force and crush your foes. Or let your spies sabotage the heck out of them. Or let some allies or vassals handle it.&lt;&#x2F;p&gt;
&lt;p&gt;Keywords:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Battle ships&lt;&#x2F;li&gt;
&lt;li&gt;Planetary defense&lt;&#x2F;li&gt;
&lt;li&gt;Spies&lt;&#x2F;li&gt;
&lt;li&gt;Diplomacy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;design-for-my-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#design-for-my-game&quot; aria-label=&quot;Anchor link for: design-for-my-game&quot;&gt;🔗&lt;&#x2F;a&gt;Design for my Game&lt;&#x2F;h1&gt;
&lt;p&gt;I planned on covering all topics above, but that is basically to large for a single post.
In the last post I covered a few basics of research.
I will try a breadth-first approach in design and switch to another topic.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s tackle...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exploration-1&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#exploration-1&quot; aria-label=&quot;Anchor link for: exploration-1&quot;&gt;🔗&lt;&#x2F;a&gt;Exploration&lt;&#x2F;h2&gt;
&lt;p&gt;The basis for almost all games here is using some sort of scout.
It would not make sense to change that, it is pretty much what players expect to find.&lt;&#x2F;p&gt;
&lt;p&gt;But, what is a scout? A special ship (surveyor anyone)? A player designed ship?
Pre-designed units have a large benefit: They can be balanced pretty well.
The player chooses which ship to build, but not what it is composed of.&lt;&#x2F;p&gt;
&lt;p&gt;Player designed ships are way more interesting in concept.
In practice, it boils down to a few ships with a specific purpose (like scouting).
Remnants of the Precursors for example allows only a few designs, which emphasizes that.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ship-design&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ship-design&quot; aria-label=&quot;Anchor link for: ship-design&quot;&gt;🔗&lt;&#x2F;a&gt;Ship Design&lt;&#x2F;h3&gt;
&lt;p&gt;I do think, limiting the number of designs is good idea.
But for the actual design of a ship I have another idea. As usual, ships are composed of different modules.
Each module modifies some attributes of a ship:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Attribute&lt;&#x2F;th&gt;&lt;th&gt;Effect&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Cost&lt;&#x2F;td&gt;&lt;td&gt;Makes your ship more expensive (but shiny and pretty as well)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Space&lt;&#x2F;td&gt;&lt;td&gt;Most modules take up some precious space on a ship, but some can extend it&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Range Bonus&lt;&#x2F;td&gt;&lt;td&gt;Increases&#x2F;Decreases the range a ship can distance itself to the nearest colony&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Interstellar Speed Bonus&lt;&#x2F;td&gt;&lt;td&gt;Increases&#x2F;Decreases speed for long range travel&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Interstellar Stealth Bonus&lt;&#x2F;td&gt;&lt;td&gt;Makes ships more&#x2F;less detectable while in flight&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Hit point Bonus&lt;&#x2F;td&gt;&lt;td&gt;Increases&#x2F;Decreases the hull of a ship to take more of a punch&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Shield Bonus&lt;&#x2F;td&gt;&lt;td&gt;Similar to hit points, but weak against energy weapons - strong against physical weapons&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Combat Speed Bonus&lt;&#x2F;td&gt;&lt;td&gt;Increases&#x2F;Decreases speed for combat&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Combat Repair&lt;&#x2F;td&gt;&lt;td&gt;Repairs ships by a small amount each combat turn&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Particle Damage&lt;&#x2F;td&gt;&lt;td&gt;Adds particle beams with some strength as combat and point defense weapons&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Projectile Damage&lt;&#x2F;td&gt;&lt;td&gt;Adds projectile weapons with some strength as combat weapon&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Missile Damage&lt;&#x2F;td&gt;&lt;td&gt;Adds missiles with some strength as combat weapons&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Combat Stealth&lt;&#x2F;td&gt;&lt;td&gt;Makes ships harder to hit or easier to hit in combat&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Colonists&lt;&#x2F;td&gt;&lt;td&gt;This allows a ship to colonize an empty planet&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;A ship, by itself is just a hull. The collection of modules selected will determine its purpose.
But, there&#x27;s no free lunch. All modules will be balanced, one way or another.&lt;&#x2F;p&gt;
&lt;p&gt;For example, a missile launcher weapon will most likely take up a lot of space and will not be cheap.
A particle beam weapon on the other hand could be smaller and cheaper.
But at the cost of reduced combat stealth, and a weakness in damaging the hull of enemy ships.&lt;&#x2F;p&gt;
&lt;p&gt;Once again this reminds of RPG attributes on items:
&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;game-loop&#x2F;Simple%20Weapon.png&quot; alt=&quot;Simple Weapon&quot; title=&quot;Impressive&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That is intentional.&lt;&#x2F;p&gt;
&lt;p&gt;I really want to enforce that ships are good at some things and bad at others.
Otherwise you will end up with the typical &quot;high tech jack of all trades&quot; ships.
Also, the number of designs should be limited, as with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rayfowler.itch.io&#x2F;remnants-of-the-precursors&quot;&gt;ROTP&lt;&#x2F;a&gt;.
I would like the player to see each design as a &quot;character&quot;. Although with some serious multiple personality disorder.&lt;&#x2F;p&gt;
&lt;p&gt;So modifying a design should be possible. What happens with the existing ships?
They could keep their original design. But then, the game could just have unlimited designs.
They could be scrapped. They could be retrofitted. Or something in-between.&lt;&#x2F;p&gt;
&lt;p&gt;No code was &lt;del&gt;harmed&lt;&#x2F;del&gt; written for this post. As mentioned before, I already have a small code base.
But exploring (no pun intended) the design space is more of an issue for now.
So next time we take a look at &lt;em&gt;Expanding&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>MOO resp. ROTPR inspired 4X game</title>
          <pubDate>Sat, 30 Jul 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/rotpr-clone/</link>
          <guid>https://www.bytekeeper.org/posts/rotpr-clone/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/rotpr-clone/">&lt;p&gt;So &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;makingcomputerdothings.com&#x2F;a-new-project-untitled-space-mercenary-game&#x2F;&quot;&gt;Sonko&lt;&#x2F;a&gt; started a new project.
Following suit, I jumped back in time to already have a game project on-going for quite some time now.
A bad habit of mine is to reach some milestone and then &quot;pause&quot; (drop) the project.
But now I&#x27;ll return to it, and as a small incentive to keep on it, I&#x27;ll start this blog.&lt;&#x2F;p&gt;
&lt;p&gt;As with Sonko, this means my StarCraft AI related projects are on hold (don&#x27;t worry, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.basil-ladder.net&#x2F;&quot;&gt;basil ladder&lt;&#x2F;a&gt; will keep running).&lt;&#x2F;p&gt;
&lt;p&gt;Also, on a rather technical note: I dropped Jekyll as a static site generator and moved to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;zola&lt;&#x2F;a&gt;. Every Jekyll upgrade wasted a lot of time to get it running again. Zola is just one binary, and it should run fine on github actions as well.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;introduction&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;🔗&lt;&#x2F;a&gt;Introduction&lt;&#x2F;h1&gt;
&lt;p&gt;I played &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Master_of_Orion_II%3A_Battle_at_Antares&quot;&gt;Master of Orion 2&lt;&#x2F;a&gt; (MOO for now) a lot when I was younger.
Even back then I tried to create my own clone of the game.
But the scale of the game was a bit to much for me back then - maybe even today.&lt;&#x2F;p&gt;
&lt;p&gt;But not all thing were good. It irked me a bit how much the game slowed down in the later phases due to the massive amount of micro-management.&lt;&#x2F;p&gt;
&lt;p&gt;Fast forward some 30 years and some more MOO games. I always was a fan of easy to learn hard to master games.
Almost none of the games inspired by it (or being successor to) seem to do that. They just upped the complexity.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Antoine de Saint-Exupéry, Airman&#x27;s Odyssey&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotpr-clone&#x2F;rotpr.png&quot; alt=&quot;Remnants of the Precursors&quot; &#x2F;&gt;
Remnants of the Precursors (ROTPR for now) has a simple interface. Maybe even a bit to bland. But it gets the job done. I did not really play the original &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Master_of_Orion&quot;&gt;Master of Orion&lt;&#x2F;a&gt;, I was still &lt;del&gt;wasting&lt;&#x2F;del&gt; enjoying my time with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Civilization_(video_game)&quot;&gt;Civilization&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I do like the simpler colony management, which avoids going through every colony in the mid-late game. Spying is a bit strange, it is not really obvious what multiple networks and&#x2F;or high spendings are doing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotpr-clone&#x2F;moo2.jpg&quot; alt=&quot;Master of Orion 2&quot; &#x2F;&gt;
Master of Orion 2 (not 1) is a bit different, more complex in some aspects (colony management) and less so in others (less diplomacy&#x2F;spying).&lt;&#x2F;p&gt;
&lt;p&gt;The UI is a bit dated, and looks only good when shrunk down. Still looks nice though.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;designing-my-game&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#designing-my-game&quot; aria-label=&quot;Anchor link for: designing-my-game&quot;&gt;🔗&lt;&#x2F;a&gt;Designing My Game&lt;&#x2F;h1&gt;
&lt;p&gt;My game already has quite a lot of code, but barely anything to show.
I decided to create the game abstraction in code as a first step. This was a mistake in my opinion.
While its clear that the inner game model should be sound, only testing it via unit tests is rather dull.&lt;&#x2F;p&gt;
&lt;p&gt;It currently covers the basics of building ships and colonizing other systems.
Like any software architecture, it is best to delay the choices which &quot;cost&quot; the most - as long as possible.
This might sound unintuitive, but help not wasting a huge amount of time due to some bad &quot;large&quot; initial decisions.&lt;&#x2F;p&gt;
&lt;p&gt;Although I got some basics covered, I want to explore design space a bit further before continuing&#x2F;refactoring.&lt;&#x2F;p&gt;
&lt;p&gt;I know a few things I &lt;em&gt;want&lt;&#x2F;em&gt; in the game design:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Colonization should be per star system (like ROTPR). Colonizing individual planets really takes its toll on usability. Multiple different factions owning each a planet in a system? A separate UI for planets in a system? It just makes things more tedious.&lt;&#x2F;li&gt;
&lt;li&gt;Research based on a RPG like skill tree. Even ROTPR, which tries to make research meaningful succumbs to the &quot;unimportant research item&quot; problem.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;rotpr-clone&#x2F;tech_tree.dot.png&quot; alt=&quot;Tech tree like skill tree&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Tech items above are just examples (for some I don&#x27;t have an idea on what they do), I do want to make it unconventional - yet useful. Every tech has 3 upgrades which will improve on the previous state in one way or the other.&lt;&#x2F;p&gt;
&lt;p&gt;Ideas for unconventional techs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fanatics - For every upgrade, establish a colony on any unoccupied star system&lt;&#x2F;li&gt;
&lt;li&gt;Boost - Increase productivity of a single colony for a short while&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ideas for more conventional techs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;XYZ Armor - Unlocks a type of armor, further upgrades improve efficiency&lt;&#x2F;li&gt;
&lt;li&gt;XYZ Weapons - Unlocks a type of weapon, further upgrades improve efficiency&lt;&#x2F;li&gt;
&lt;li&gt;Productive - Increases productivity per upgrade level&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now the real change comes with the way you unlock techs. Usually, you designate a research topic and after some turns you&#x27;ll get it. This is not the case here.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, you build up research points - and once you have enough you can immediately unlock or upgrade a tech.
This allows for fast reaction to new circumstances. There are also only a very limited amount of techs to unlock. With either, MOO2 or ROTPR I always feel like the actual decision of what to research matters little. Yes, some do - but you can always replace them later with something else. I want the player to be able to focus on a certain play style, stick to it, but still be able to win.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s all for today!&lt;&#x2F;p&gt;
&lt;p&gt;Next Time: Gameplay Loop&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A son was born</title>
          <pubDate>Sat, 16 May 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/2020-05-16/</link>
          <guid>https://www.bytekeeper.org/posts/2020-05-16/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/2020-05-16/">&lt;p&gt;This was meant to be a tech related blog, mostly for Broodwar AI related stuff and private projects. Since private projects are included, I guess one could call my son a private project.&lt;&#x2F;p&gt;
&lt;p&gt;Screaming into the light, he was born in late February. His name is Benjamin, which I am certain means horse-dung in some weird language. I do not care, go away! And due to Corona, we have been left alone for most of the time since then.&lt;&#x2F;p&gt;
&lt;p&gt;My last blog entry was in January, and up until now I really did not have the energy to write a new article. But since most of my free time is now reserved for serving the little demon, I&#x27;ll just write about what
I know best now: My spawn. Prepare for many picture which may or may not include my son (hint: nope, no pix from him - maybe in a later post).&lt;&#x2F;p&gt;
&lt;p&gt;As with all babies, he is cute (actually I believe mostly parents find most babies cute, for my part they look like french baguettes mostly...). If you remember Tamagochi, it is really its human form.&lt;&#x2F;p&gt;
&lt;p&gt;Lucky for me, we have a spare room - otherwise working would be very hard due to missing shut-eye. Which means shift 1 is work, shift 2 is work, shift 3 is sleep. And things like &lt;em&gt;this&lt;&#x2F;em&gt; mostly eat into shift 3.&lt;&#x2F;p&gt;
&lt;p&gt;A little human goes through a lot of development phases. There is the cute phase...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;2020-05-16&#x2F;Cute-Baby.jpg&quot; alt=&quot;Not mine&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then there is the crying phase...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;2020-05-16&#x2F;crying_baby.jpg&quot; alt=&quot;Still not mine&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And there is the default happy phase&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;2020-05-16&#x2F;screaming-yoda.jpg&quot; alt=&quot;Definitely not mine&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;OK, the last one might be slightly exaggerated. And it is not the default. At least for my son, most of the time he is pretty happy.&lt;&#x2F;p&gt;
&lt;p&gt;You are supposed to react to the baby&#x27;s needs. Ignore that at your own peril (eardrums). Really, a lot of time I look at my son and this is what I see:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;2020-05-16&#x2F;mischievous-baby.jpeg&quot; alt=&quot;Same smirk, but not mine&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I believe the general baby package works like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You receive it&lt;&#x2F;li&gt;
&lt;li&gt;It comes un-boxed, without manual - W.T.H.?&lt;&#x2F;li&gt;
&lt;li&gt;It must already be defect, or is it supposed to behave this way&lt;&#x2F;li&gt;
&lt;li&gt;Your midwife (its common to have one here) tells you a lot of good info, and a lot of mumbo-jumbo.&lt;&#x2F;li&gt;
&lt;li&gt;She tells you what homeopathy &quot;stuff&quot; (I refuse to say medicine here) you can give you baby - that is ok, since placebo also works for babies. (Btw did you know, there is also a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Nocebo&quot;&gt;nocebo&lt;&#x2F;a&gt; effect? Cool!)&lt;&#x2F;li&gt;
&lt;li&gt;She tells you to see an osteopath (that is so much dumbfuckery). Uuuuhhhh magic energy waves uuuuhh&lt;&#x2F;li&gt;
&lt;li&gt;You start to get a grip on it... or do you?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Thing is, the first few weeks you could care less. Your baby can see like 30cm (about 12in you strange people who stopped in time). It can&#x27;t really focus or look at you.
After that it actually can focus you, which is good - it can now target practice with its sound-wave gun &lt;em&gt;while looking at you&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Then it starts to smile at you. First for short periods of time, then more. It might also randomly switch from crying, screaming, and laughing.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re currently within the Stan phase. What that is?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;2020-05-16&#x2F;stan.gif&quot; alt=&quot;Stan&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And with that, I will conclude today&#x27;s &lt;em&gt;rant&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Comments for static pages? - Part 2</title>
          <pubDate>Sat, 25 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/comments-part-2/</link>
          <guid>https://www.bytekeeper.org/posts/comments-part-2/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/comments-part-2/">&lt;p&gt;Looking &lt;a href=&quot;https:&#x2F;&#x2F;www.bytekeeper.org&#x2F;posts&#x2F;comments-on-jekyll&#x2F;&quot;&gt;back&lt;&#x2F;a&gt;... I need a bit of DIY GitHub API for Rust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;still-step-2-create-a-branch-and-pr-automatically&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#still-step-2-create-a-branch-and-pr-automatically&quot; aria-label=&quot;Anchor link for: still-step-2-create-a-branch-and-pr-automatically&quot;&gt;🔗&lt;&#x2F;a&gt;Still step 2: Create a branch and PR automatically&lt;&#x2F;h2&gt;
&lt;p&gt;Thankfully, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.github.com&#x2F;v3&#x2F;&quot;&gt;V3 API&lt;&#x2F;a&gt; is really simple.&lt;&#x2F;p&gt;
&lt;p&gt;Using our tRusty language, we can define some structs to be serialized to JSON for requests:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CreateRef&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r#ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The name to be used&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sha&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The commit SHA to point to&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CreateFile&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    content&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    branch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    committer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; UserRef&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; UserRef&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    email&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CreatePR&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    title&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    head&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The branch to be merged&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    base&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; The target branch for the merge (ie. master)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The basic steps we want to do are described by these:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Create a branch, using &lt;code&gt;CreateRef&lt;&#x2F;code&gt;. Git uses refs for things that &quot;have&quot; commits. A branch is just that!&lt;&#x2F;li&gt;
&lt;li&gt;Create a file on the branch with &lt;code&gt;CreateFile&lt;&#x2F;code&gt;. &lt;code&gt;UserRef&lt;&#x2F;code&gt; will be used as committer (me in this case).&lt;&#x2F;li&gt;
&lt;li&gt;The last step is to create a PR with &lt;code&gt;CreatePR&lt;&#x2F;code&gt;  (quelle surprise), to merge the branch back.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The file we want to create has to be converted to base64 to be used by &lt;code&gt;CreateFile&lt;&#x2F;code&gt;. The content itself should be YAML and contain this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Serialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Comment&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r#ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Luckily &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;serde&lt;&#x2F;a&gt; comes to our rescue once again and happily creates a serializer to yaml here.
Another helpful small library provides base64 encoding. The result is this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CreateFile&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;Comment&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    content&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;serde_yaml&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;comment&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    branch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; branch_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    committer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; UserRef&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; owner&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        email&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; owner_email&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The requests itself look quite similar. As an example, here&#x27;s the &lt;code&gt;CreateFile&lt;&#x2F;code&gt; one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; create_file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    client&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;reqwest&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;blocking&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    owner&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    path&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    create_file&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;CreateFile&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; reqwest&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;reqwest&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;blocking&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Response&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;        format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&#x2F;contents&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            owner&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; repo&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; path&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;as_str&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    client&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;url&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;create_file&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No, &lt;code&gt;reqwest&lt;&#x2F;code&gt; is not a typo! It&#x27;s actually a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;seanmonstar&#x2F;reqwest&quot;&gt;popular library&lt;&#x2F;a&gt; (I don&#x27;t mean the javascript one).
I don&#x27;t really need non-blocking I&#x2F;O here, so the &lt;code&gt;blocking&lt;&#x2F;code&gt; client is put to good use.&lt;&#x2F;p&gt;
&lt;p&gt;The whole Rust project can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Bytekeeper&#x2F;github_comment_rs&quot;&gt;in my GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I tried to solve this &quot;problem&quot; with some constraints:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It should be inexpensive&lt;&#x2F;li&gt;
&lt;li&gt;It should respect the privacy of my readers&lt;&#x2F;li&gt;
&lt;li&gt;It should be simple enough to fix or port&lt;&#x2F;li&gt;
&lt;li&gt;It should not depend too much on third parties&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Using GitHub for the blog and for comments is very inexpensive. I use no third party commentary service, as such the privacy is protected as much as is possible here.
The HTML part is very simple and only depends on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jekyllrb.com&#x2F;&quot;&gt;Jekyll&lt;&#x2F;a&gt;. The Rust part is also simple and fits in less than 300 lines of code of Rust.&lt;&#x2F;p&gt;
&lt;p&gt;Success ✓&lt;&#x2F;p&gt;
&lt;p&gt;If you have any comments, please leave them down below - since you are actually able to do so now!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Comments for static pages? - Part 1</title>
          <pubDate>Tue, 21 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/comments-on-jekyll/</link>
          <guid>https://www.bytekeeper.org/posts/comments-on-jekyll/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/comments-on-jekyll/">&lt;p&gt;&lt;a href=&quot;&#x2F;&quot;&gt;This&lt;&#x2F;a&gt; is actually a static page hosted by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.github.com&#x2F;&quot;&gt;GitHub&lt;&#x2F;a&gt;, powered by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jekyllrb.com&#x2F;&quot;&gt;Jekyll&lt;&#x2F;a&gt;.
But how to get a comment section?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;give-me-comments&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#give-me-comments&quot; aria-label=&quot;Anchor link for: give-me-comments&quot;&gt;🔗&lt;&#x2F;a&gt;Give Me Comments&lt;&#x2F;h1&gt;
&lt;p&gt;Weeeelll....
You could use a service like Disqus.&lt;&#x2F;p&gt;
&lt;p&gt;Or, you use the idea from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;damieng.com&#x2F;blog&#x2F;2018&#x2F;05&#x2F;28&#x2F;wordpress-to-jekyll-comments&quot;&gt;damieng&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Jekyll has a neat feature: you can place data files under &lt;code&gt;_data&lt;&#x2F;code&gt; and access in Liquid templates.&lt;&#x2F;p&gt;
&lt;p&gt;And that&#x27;s more or less the starting point. Add comments as data and render them via Jekyll (utilizing Liquid).&lt;&#x2F;p&gt;
&lt;p&gt;Using the folder &lt;code&gt;_data&#x2F;comments&#x2F;{slug}&#x2F;&lt;&#x2F;code&gt; to place comment files in. (&lt;code&gt;slug&lt;&#x2F;code&gt; being the &quot;slugified&quot; post name.)
The actual files can be in a few formats, but I used yaml.
Here is an example comment:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;---&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;d&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1579711318_617889634&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ef&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;asil-timeouts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;essage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; T&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;esting comments&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; B&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ytekeeper&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;u&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;rl&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;d&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#22863A, #85E89D);&quot;&gt;ate&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1579711318&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;id&lt;&#x2F;code&gt; is the id of the comment &lt;em&gt;and&lt;&#x2F;em&gt; the base name of the file: &lt;code&gt;1579711318_617889634.yml&lt;&#x2F;code&gt;.
&lt;code&gt;ref&lt;&#x2F;code&gt; is a reference for what the comment was for. Ie. a slug or the id of another comment (not yet working here).
&lt;code&gt;message&lt;&#x2F;code&gt; is too complicated to explain... really. So is &lt;code&gt;name&lt;&#x2F;code&gt;. &lt;code&gt;url&lt;&#x2F;code&gt; is the website a user gave and will be used to render the commenter&#x27;s name as a link to the site.
&lt;code&gt;date&lt;&#x2F;code&gt; a simple timestamp and, as you might have noticed, part of the &lt;code&gt;id&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But having the comments stored as data will not be enough, unless I can convince people to check out the GitHub repo for comments.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;render-me-comments&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#render-me-comments&quot; aria-label=&quot;Anchor link for: render-me-comments&quot;&gt;🔗&lt;&#x2F;a&gt;Render Me Comments&lt;&#x2F;h1&gt;
&lt;p&gt;This one will be quick.
I use a modified version of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;damieng&#x2F;jekyll-blog-comments&#x2F;tree&#x2F;master&#x2F;jekyll&#x2F;_includes&quot;&gt;damieng&#x27;s _includes&lt;&#x2F;a&gt;.
Since I modified the structure a bit I had to adapt it. I also changed the layout bit, the final version can be seen down here (unless it does not work in which case: bummer).&lt;&#x2F;p&gt;
&lt;p&gt;For now they look ok but will certainly need more fine-tuning, especially once it&#x27;s possible to reply to individual comments as well.&lt;&#x2F;p&gt;
&lt;p&gt;Storage place for comments ✓&lt;&#x2F;p&gt;
&lt;p&gt;Rendering comments ✓&lt;&#x2F;p&gt;
&lt;p&gt;Adding comments: ❌&lt;&#x2F;p&gt;
&lt;h1 id=&quot;add-me-commentse&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#add-me-commentse&quot; aria-label=&quot;Anchor link for: add-me-commentse&quot;&gt;🔗&lt;&#x2F;a&gt;Add Me Commentse&lt;&#x2F;h1&gt;
&lt;p&gt;This could be as simple as people wanting to comment grabbing a fork of my repository and adding a comment file in data and creating a PR for it.
I&#x27;m pretty sure this avoids all comment spamming bots and requires almost no moderation.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t want to fork? Ok, I&#x27;ll give you commit r... waaait.&lt;&#x2F;p&gt;
&lt;p&gt;So basically, using damieng&#x27;s idea:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I want a PR for each comment&lt;&#x2F;li&gt;
&lt;li&gt;I want branch and PR to be automatically created&lt;&#x2F;li&gt;
&lt;li&gt;Once I merge it, GitHub will automatically update and voilà&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;step-1-create-a-service-that-accepts-comments&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-1-create-a-service-that-accepts-comments&quot; aria-label=&quot;Anchor link for: step-1-create-a-service-that-accepts-comments&quot;&gt;🔗&lt;&#x2F;a&gt;Step 1: Create a service that accepts comments&lt;&#x2F;h2&gt;
&lt;p&gt;This could be any form of (micro) web service. Damieng used Azure Functions. But Amazon Lambda or Google Functions would be fine too.
But I&#x27;m oldschool... I already have a VPS, so I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Common_Gateway_Interface&quot;&gt;CGI&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m also a experienced Java developer, so I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kotlinlang.org&#x2F;&quot;&gt;Kotlin&lt;&#x2F;a&gt;. J&#x2F;K of course I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I learn Rust as I go (I already tried that once but the burrow checker tried to murder me in my sleep), so take everything here with a grain of salt.&lt;&#x2F;p&gt;
&lt;p&gt;So, let&#x27;s start with some Rust code. There is this really nice library called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;serde&lt;&#x2F;a&gt; which has a ton of serializers and deserializers for various formats.
Like &lt;code&gt;urlencoded&lt;&#x2F;code&gt; which is what a simple form post will send.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s write the code to process a &lt;code&gt;POST&lt;&#x2F;code&gt;ed comment:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Deserialize&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Post&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r#ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    redirect&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For those who have never seen C, a &lt;code&gt;struct&lt;&#x2F;code&gt; is like a &lt;code&gt;class&lt;&#x2F;code&gt;: A container for fields.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s it! The &lt;code&gt;derive&lt;&#x2F;code&gt; attribute with the &lt;code&gt;Deserialize&lt;&#x2F;code&gt; argument will create the code to dump in a &lt;code&gt;urlendcoded&lt;&#x2F;code&gt; string and get out an instance of &lt;code&gt;Post&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; post&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Post&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; serde_urlencoded&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from_bytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;as_slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I omitted the boilerplate code to read the data from &lt;code&gt;stdin&lt;&#x2F;code&gt; - which is how you get data with &lt;code&gt;CGI&lt;&#x2F;code&gt;. The full code can be seen in my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Bytekeeper&#x2F;github_comment_rs&#x2F;blob&#x2F;master&#x2F;src&#x2F;main.rs&quot;&gt;GitHub repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-create-a-branch-and-pr-automatically&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-2-create-a-branch-and-pr-automatically&quot; aria-label=&quot;Anchor link for: step-2-create-a-branch-and-pr-automatically&quot;&gt;🔗&lt;&#x2F;a&gt;Step 2: Create a branch and PR automatically&lt;&#x2F;h2&gt;
&lt;p&gt;Creating a branch and pushing it should be fairly simply with &lt;code&gt;GIT&lt;&#x2F;code&gt;&#x27;s CLI. But for the PR, the GitHub API will have to be used.&lt;&#x2F;p&gt;
&lt;p&gt;It can also be used to create a branch and (!) even create a file in there. So I just have to use a GitHub API Rust implementation to solve this.
Actually, there are a few - but they are mostly unmaintained.
They also don&#x27;t allow the shenanigans I need.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;To be continued&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Slow bots on Basil</title>
          <pubDate>Mon, 13 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.bytekeeper.org/posts/basil-timeouts/</link>
          <guid>https://www.bytekeeper.org/posts/basil-timeouts/</guid>
          <description xml:base="https://www.bytekeeper.org/posts/basil-timeouts/">&lt;p&gt;If you haven&#x27;t yet, check out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;basil.bytekeeper.org&#x2F;&quot;&gt;BASIL&lt;&#x2F;a&gt;. It&#x27;s a 24&#x2F;7 Starcraft league, but for bots!
It usually runs fine nowadays. But sometimes an external hiccup occurs. One of those is the problem of detecting who to blame if a game times out.
It works fine if the game ends with the in-game timeout of 60min, since the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;basil-ladder&#x2F;sc-tm&quot;&gt;Tournament Module&lt;&#x2F;a&gt; BASIL uses
will make both bots leave and select the winner based on the score (That might be unfair, but that&#x27;s a topic for another post).&lt;&#x2F;p&gt;
&lt;p&gt;But if a game gets killed because it took to long (wall-clock time), what then? There are no scores, no replays - nothing essentially.
Besides a bot just hanging, most bots just take their time. So I wanted to penalize the slower bot in that case. This looked like a good idea back
in the beginning. For &lt;code&gt;dll&lt;&#x2F;code&gt; bots this works fine, but for &lt;code&gt;client&lt;&#x2F;code&gt; bots, it didn&#x27;t work up until &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bwapi&#x2F;bwapi&#x2F;commit&#x2F;4a984290cf6aa6f05ebcbdcd4a094ffceb57f6e9&quot;&gt;BWAPI 4.4&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As of today that means that games timing out in that fashion will not be counted. One could abuse this by timing out intentionally for imminent losses.
I don&#x27;t expect this to happen, the bot community is generally competitive, but also friendly and fair. (Totally unrelated: I can actually disable bots permanently)&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
