<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>allejo.io | interstellar space pirate extraordinaire</title>
    <link href="https://allejo.io/atom.xml" rel="self"/>
    <link href="https://allejo.io/"/>
    <updated>2026-02-17T06:38:26+00:00</updated>
    <id>https://allejo.io/</id>
    <author>
        <name>Vladimir Jimenez</name>
        <email>me@allejo.io</email>
    </author>

            <entry>
            <title>How and why I jailbroke my Kindle</title>
            <link href="https://allejo.io/blog/how-and-why-i-jailbroke-my-kindle/" rel="alternate" type="text/html" title="How&#x20;and&#x20;why&#x20;I&#x20;jailbroke&#x20;my&#x20;Kindle" />
            <updated>2025-07-14T08:00:00+00:00</updated>
            <id>eb637502715aeaff48286324b41cf601d6b5978a</id>
            <content type="html" xml:base="https://allejo.io/blog/how-and-why-i-jailbroke-my-kindle/">&lt;p&gt;For years, I had been searching for the &amp;quot;perfect e-ink reader&amp;quot; that met all of my niche needs and wants. I wanted something with integration to &lt;a href=&quot;https://calibre-ebook.com/&quot;&gt;calibre&lt;/a&gt;, the ability to download and read RSS feeds, and, lastly, the ability to connect to cloud storage, such as Google Drive, where I host my PDFs. These seem like a niche set of features, and honestly, they are. However, this is what I&#039;ve always wanted. I conducted my research and evaluated numerous e-ink readers, including Boox, Kobo, the new Kindle, Nook, ReMarkable, and others. None of them checked all the boxes, and all came with extreme vendor lock-in or had virtually no developer community behind them. Sure, I&#039;ve got an iPad that can accomplish all those features and more, but it has a lot more functionality than I need when I want to read without distractions.&lt;/p&gt;
&lt;p&gt;I recently discovered the Kindle jailbreaking community and was reminded of my teen years when I was active in the iOS jailbreaking community. The level of freedom I had over my device at the time was something I would love to have over my Kindle. With Amazon recently running &lt;a href=&quot;https://techcrunch.com/2025/04/26/amazons-big-book-sale-just-happens-to-overlap-with-independent-bookstore-day/?guccounter=1&quot;&gt;a big book sale that &amp;quot;coincidentally&amp;quot; overlapped with Independent Bookstore Day&lt;/a&gt;, I decided it was time to say &amp;quot;fuck you&amp;quot; to Amazon and part ways as much as I can.&lt;/p&gt;
&lt;h2 id=&quot;why-am-i-jailbreaking-my-kindle&quot;&gt;Why am I jailbreaking my Kindle?&lt;/h2&gt;
&lt;p&gt;I have recently worked long hours organizing all my ebooks into a single library using Calibre, with the goal of eventually syncing them to my Kindle. Here&#039;s the tricky part, for years, I had been buying ebooks from Amazon, and while they did make the ecosystem incredibly user-friendly for the majority of the population, I felt limited due to the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As of February 26, 2025, Amazon has discontinued support for the &amp;quot;Download &amp;amp; Transfer via USB&amp;quot; functionality. This means it is now significantly more tedious for you to download copies of the ebooks &lt;strong&gt;you&lt;/strong&gt; have bought. In the past, I have had Amazon delist ebooks from their online stores, and as a result, I lost access to them on my Kindle, as I can no longer search for them. Thankfully, I was in the habit of always downloading a copy of those ebooks, but what if I hadn&#039;t? I might have lost that digital book even though I &lt;strong&gt;bought&lt;/strong&gt; it.&lt;/li&gt;
&lt;li&gt;I never thought I would be writing this, but with the direction my country, the United States, is heading regarding censorship, I did not want to remain at the mercy of Amazon to dictate which books I&#039;ve &lt;strong&gt;purchased&lt;/strong&gt;, and still be allowed to read.&lt;/li&gt;
&lt;li&gt;The only way I can transfer books to my Kindle wirelessly is by emailing the file to my Kindle email address, which Amazon manages. What&#039;s stopping Amazon from censoring the books I send to them to deliver to my Kindle?&lt;/li&gt;
&lt;li&gt;My Kindle Paperwhite, 3rd generation, from 2015, is considered &amp;quot;end of life,&amp;quot; meaning I no longer receive updates from Amazon. According to Amazon, my Kindle is considered no different from a paperweight. Why would I trade in, my perfectly functional and in good condition device for a new device that has even more restrictions placed on it? It&#039;s wasteful consumerism. If Amazon has stopped developing for my Kindle, let&#039;s turn to the open source community of nerds that hasn&#039;t.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fuck Amazon! Let&#039;s jailbreak my 7th generation 2015 Kindle Paperwhite (aka &lt;code&gt;PW3&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;the-jailbreaking-process&quot;&gt;The Jailbreaking Process&lt;/h2&gt;
&lt;p&gt;First off, the jailbreaking process is nowhere near as simple as it was in my iOS days, where you could download an app on your computer, press a single button, and wait. That&#039;s not to say it&#039;s difficult or impossible; it&#039;s just a more involved process. A lot of very smart nerds who share the same sentiment about breathing new life into their abandoned devices have invested considerable effort in simplifying the process as much as possible.&lt;/p&gt;
&lt;p&gt;Thank you to the awesome community behind &lt;a href=&quot;https://kindlemodding.org/&quot;&gt;Kindle Modding&lt;/a&gt; for writing all the tutorials and documentation I used to jailbreak my Kindle. This would not have been possible without &lt;a href=&quot;https://www.mobileread.com/forums/member.php?u=330416&quot;&gt;HackerDude&lt;/a&gt;&#039;s WinterBreak exploit that I was able to get working.&lt;/p&gt;
&lt;h3 id=&quot;the-prerequisites&quot;&gt;The Prerequisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A computer; it&#039;s not possible to jailbreak your Kindle just from the device itself&lt;/li&gt;
&lt;li&gt;Your Kindle &lt;strong&gt;must&lt;/strong&gt; be registered with Amazon (unfortunately)&lt;/li&gt;
&lt;li&gt;You must have a saved Wi-Fi connection on your Kindle, and be ready to connect to that Wi-Fi during the jailbreak process&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;run-the-jailbreak-exploit&quot;&gt;Run the Jailbreak Exploit&lt;/h3&gt;
&lt;p&gt;The first step is to run an exploit, which allows us to remove security restrictions on the Kindle; this is known as &amp;quot;jailbreaking.&amp;quot;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the &lt;a href=&quot;https://github.com/KindleModding/WinterBreak/releases/latest/download/WinterBreak.tar.gz&quot;&gt;latest release of the WinterBreak jailbreak&lt;/a&gt; on your computer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn on airplane mode on your Kindle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reboot your Kindle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After rebooting, connect the Kindle to the computer via USB. Extract WinterBreak and copy over the contents onto the Kindle (including the hidden dot files!). Replace any files that may already exist.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Eject your Kindle from the computer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Kindle Store by clicking on the cart icon. It&#039;ll prompt you to turn off Airplane mode, do it. Once the store loads, the Mesquito hack will be displayed. Click to run it.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
      &lt;figure&gt;
    &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./winterbreak-click-to-run.png&quot;&gt;&lt;img src=&quot;./winterbreak-click-to-run.png&quot; alt=&quot;Kindle screenshot of WinterBreak&amp;#039;s &amp;quot;click to run&amp;quot; prompt&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
    &lt;figcaption&gt;
      The Kindle Store serves as our environment for executing code, so we run the Mesquito exploit from here.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wait for a few seconds, and then console text will appear in the upper left corner of the display. At this point, turn airplane mode back on and install the hotfix to persist this jailbreak across reboots.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
      &lt;figure&gt;
    &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./jailbreak-successful.png&quot;&gt;&lt;img src=&quot;./jailbreak-successful.png&quot; alt=&quot;Jailbreak process log text displaying on the upper left part of the screen&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
    &lt;figcaption&gt;
      When the jailbreak process is complete, the top left of your Kindle will display Terminal-like log message.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;persisting-the-jailbreak-aka-installing-the-hotfix&quot;&gt;Persisting the Jailbreak (aka &amp;quot;Installing the Hotfix&amp;quot;)&lt;/h3&gt;
&lt;p&gt;The next step is to persist this jailbreak across reboots and updates; a Kindle update referred to as a &amp;quot;hotfix&amp;quot; is needed to accomplish this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the &lt;a href=&quot;https://github.com/KindleModding/Hotfix/releases/latest/download/Update\_hotfix\_universal.bin&quot;&gt;update &lt;code&gt;.bin&lt;/code&gt; file from GitHub&lt;/a&gt;, this file is what&#039;s known as the &amp;quot;hotfix.&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Plug in the Kindle and copy the &lt;code&gt;.bin&lt;/code&gt; file to the device&#039;s root directory. If there are any other &lt;code&gt;.bin&lt;/code&gt; files on the device already, delete them first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to your Kindle settings, then click the three-dot menu, and select &amp;quot;Update your Kindle.&amp;quot; Confirm the installation.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
      &lt;figure&gt;
    &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;update-your-kindle.png&quot;&gt;&lt;img src=&quot;update-your-kindle.png&quot; alt=&quot;Dropdown menu with &amp;quot;Update your Kindle&amp;quot; option&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
    &lt;figcaption&gt;
      From your Kindle settings, you will apply the hotfix as an &quot;update&quot; to your Kindle software.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the Kindle has rebooted, the hotfix is now installed and needs to be executed on the device. A new &amp;quot;ebook&amp;quot; is displayed in your device&#039;s library called &amp;quot;Run Hotfix.&amp;quot; Open that book to run the process.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; delete the hotfix ebook, it is necessary to run it every time there&#039;s an update.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;install-kual-and-mrpi&quot;&gt;Install KUAL and MRPI&lt;/h3&gt;
&lt;p&gt;Now that our Kindle is fully jailbroken, we can run whatever code or applications we want. Similarly to how the hotfix in the previous step created a fake ebook that, when opened, executed code, we need to do the same for our other applications so that we can launch them from the Kindle UI. This is where the Kindle Unified Application Launcher, or KUAL, comes in to solve that need.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download &lt;a href=&quot;https://kindlemodding.org/jailbreaking/post-jailbreak/installing-kual-mrpi/Update\_KUALBooklet\_ALLDEVICES\_KS2\_install.bin&quot;&gt;KUAL for devices released in 2012 or after&lt;/a&gt;, or &lt;a href=&quot;https://storage.gra.cloud.ovh.net/v1/AUTH\_2ac4bfee353948ec8ea7fd1710574097/mr-public/KUAL/KUAL-v2.7.37-gfcb45b5-20250419.tar.xz&quot;&gt;KUAL for legacy devices (pre-2012)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download &lt;a href=&quot;https://fw.notmarek.com/khf/kual-mrinstaller-khf.tar.xz&quot;&gt;the MobileRead Package Installer (MRPI)&lt;/a&gt; to simplify the installation of update packages (remember the .bin file from the previous step?).&lt;/li&gt;
&lt;li&gt;Connect your Kindle to your computer.&lt;/li&gt;
&lt;li&gt;Extract the MRPI contents and copy over/merge the &lt;code&gt;extensions&lt;/code&gt; and &lt;code&gt;mrpackages&lt;/code&gt; folders to your Kindle.&lt;/li&gt;
&lt;li&gt;Copy over the KUAL &lt;code&gt;.bin&lt;/code&gt; file to the root of the device.&lt;/li&gt;
&lt;li&gt;Eject and unplug your Kindle.&lt;/li&gt;
&lt;li&gt;In the Kindle&#039;s search bar, type &lt;code&gt;;log mrpi&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to your Kindle settings, then click the three dot menu, and select &amp;quot;Update your Kindle.&amp;quot; Confirm the installation.&lt;/li&gt;
&lt;li&gt;After your Kindle reboots, a KUAL ebook should be located in your library.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;disable-ota-updates&quot;&gt;Disable OTA Updates&lt;/h3&gt;
&lt;p&gt;My Kindle Paperwhite 3rd gen is EOL and no longer receives updates, but let&#039;s disable OTA anyway to be safe. My Kindle is stuck at firmware 5.16.2.1.1, so I need to install &lt;code&gt;renameotabin&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download &lt;a href=&quot;https://www.mobileread.com/forums/showpost.php?p=4076733\&amp;amp;postcount=25&quot;&gt;&lt;code&gt;renameotabin&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Connect the Kindle to your computer and copy the &lt;code&gt;renameotabin&lt;/code&gt; folder into the &lt;code&gt;extensions&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Eject and unplug the Kindle.&lt;/li&gt;
&lt;li&gt;Open the KUAL ebook and select &amp;quot;Rename OTA Binaries&amp;quot; from the menu. Then, proceed to select &amp;quot;Rename.&amp;quot;
&lt;ol&gt;
&lt;li&gt;If you need to restore, downgrade, or otherwise manage your Kindle through official methods, select &lt;code&gt;Restore&lt;/code&gt; before proceeding.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;restoring-the-store&quot;&gt;Restoring the Store&lt;/h3&gt;
&lt;p&gt;I&#039;m not sure how I feel about having the Kindle Store enabled again, but I&#039;ve re-enabled its access, just in case I need it. I may turn it off at a later point.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delete &lt;code&gt;.active_content_sandbox&lt;/code&gt; from the root of the device.&lt;/li&gt;
&lt;li&gt;Eject and unplug.&lt;/li&gt;
&lt;li&gt;Reboot your device.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;installing-koreader&quot;&gt;Installing KOReader&lt;/h3&gt;
&lt;p&gt;KOReader is a replacement for Kindle&#039;s default e-Reader software, and honestly, it has so much more. This is the biggest reason why I jailbroke my Kindle.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download &lt;a href=&quot;https://github.com/koreader/koreader/releases&quot;&gt;the latest version of KOReader from GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If your Kindle was released before 2012, use the &amp;quot;legacy&amp;quot; Kindle download. Otherwise, download the non-&amp;quot;legacy&amp;quot; version.&lt;/li&gt;
&lt;li&gt;Connect the Kindle to the computer.&lt;/li&gt;
&lt;li&gt;Extract the contents of the KOReader archive and copy/merge over all the folders onto the Kindle&#039;s root.&lt;/li&gt;
&lt;li&gt;Launch KOReader from KUAL; prefer to use &amp;quot;Start KOReader&amp;quot; because the &amp;quot;no framework&amp;quot; version is largely intended for older devices. The ASAP variant is a faster startup time for KOReader, but I see no need to use it since it loads fast for me.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;setting-up-ssh&quot;&gt;Setting Up SSH&lt;/h2&gt;
&lt;p&gt;I&#039;m trying to avoid having a micro-USB cable on my desk; I already have one at my charging station. Every other device that&#039;s on my desk supports either wireless charging or USB-C; I like to avoid having too many cables. I enabled SSH on my Kindle so that I could mount it wirelessly on my Mac; i.e., I can drag and drop files from my SFTP client wirelessly.&lt;/p&gt;
&lt;p&gt;Within KOReader, I navigate to the gear icon in the top menu bar, then select &lt;code&gt;Network&lt;/code&gt;, followed by &lt;code&gt;SSH Server&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-tools.png&quot;&gt;&lt;img src=&quot;./menu-tools.png&quot; alt=&quot;KOReader&amp;#039;s Gear menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          In KOReader&#039;s Gear menu, select the &quot;Network&quot; submenu that is the third item from the top.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-tools-network.png&quot;&gt;&lt;img src=&quot;./menu-tools-network.png&quot; alt=&quot;KOReader&amp;#039;s Network menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          Within the Network menu, select the &quot;SSH server&quot; submenu that is the last item of the menu.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;p&gt;I&#039;m setting up my SSH key on the Kindle so that I can connect to it securely.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I first need to temporarily enable &amp;quot;Login without password (DANGEROUS)&amp;quot; on my Kindle. This will allow me to connect to the Kindle via SSH with a blank password.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, I will start the SSH server by clicking on the &amp;quot;SSH Server&amp;quot; menu item. You will get a pop-up on your Kindle that displays its IP address; I need it for the next step.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
      &lt;figure&gt;
    &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./ssh-server-started.png&quot;&gt;&lt;img src=&quot;./ssh-server-started.png&quot; alt=&quot;Pop-up notification confirming KOReader&amp;#039;s SSH server started successfully&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
    &lt;figcaption&gt;
      When the SSH server has started, you&#039;ll receive a success notification with two bits of important information: the Kindle&#039;s IP address and KOReader&#039;s SSH port.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I can now connect via SSH. The connection details are as follows,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Host: Your Kindle&#039;s IP address from step 2, for me, it was 192.168.12.120.&lt;/li&gt;
&lt;li&gt;Username: root&lt;/li&gt;
&lt;li&gt;Password: &amp;lt;leave empty&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My SSH command looked like so,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh root@192.168.12.120 -p 2222
#################################################
#  N O T I C E  *  N O T I C E  *  N O T I C E  #
#################################################
Rootfs is mounted read-only. Invoke mntroot rw to
switch back to a writable rootfs.
#################################################
[root@kindle root]# mntroot rw&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Upon connecting, I&#039;m greeted by this notice that essentially tells me, &amp;quot;consider your current session as read-only, if you want to make edits to any files, (which we do), run this command.&amp;quot; I want to be able to save my public key on the Kindle so that we can use it to SSH in; therefore we need to make the filesystem writable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# mntroot rw
system: I mntroot:def:Making root filesystem writeable&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I need to find my Kindle&#039;s &lt;code&gt;authorized_keys&lt;/code&gt; file, which is located at, &lt;code&gt;/mnt/us/koreader/settings/SSH/authorized_keys&lt;/code&gt;. Like most light-weight systems, the only text editor available is &lt;code&gt;vi&lt;/code&gt; so be sure to know how to exit once you&#039;re done.&lt;/p&gt;
&lt;p&gt;Paste your public key into your &lt;code&gt;authorized_keys&lt;/code&gt; and exit your SSH session. Go back to your Kindle and shut down your SSH server. Earlier, I enabled &amp;quot;Login without password.&amp;quot; Now, it&#039;s time for me to turn it off, and I should be able to log in with my SSH key.&lt;/p&gt;
&lt;p&gt;To verify that updating my &lt;code&gt;authorized_keys&lt;/code&gt; worked correctly, I&#039;ll add a &lt;code&gt;-v&lt;/code&gt; to my SSH command and read the logs of which public key is accepted by my Kindle (I have several keys on my computer).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh root@192.168.12.120 -p 2222 -v

... lots of output ...

debug1: Offering public key: /Users/allejo/.ssh/id_ed25519 ED25519 SHA256:9S2ld7G9EZXqIbqXKvosAKA74Xj1rDaOP9Gpx4pvdIQ
debug1: Server accepts key: /Users/allejo/.ssh/id_ed25519 ED25519 SHA256:9S2ld7G9EZXqIbqXKvosAKA74Xj1rDaOP9Gpx4pvdIQ
Enter passphrase for key &#039;/Users/allejo/.ssh/id_ed25519&#039;:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &amp;quot;Server accepts key:&amp;quot; line is what we&#039;re looking for. This means that my Kindle recognizes my SSH key and will allow me to log in once I unlock my key. Now, I can use my favorite SFTP client to connect to my Kindle securely. If I want to see the equivalent of my Kindle&#039;s &amp;quot;root,&amp;quot; i.e., the filesystem I see when I connect my Kindle to my computer with a micro-USB cable, that&#039;s mounted at &lt;code&gt;/mnt/us/&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;screenshots&quot;&gt;Screenshots&lt;/h2&gt;
&lt;p&gt;Another feature I was oddly excited for was the ability to take screenshots! Okay, so Kindles have been able to &lt;a href=&quot;https://www.toptrix.net/2020/05/most-useful-kindle-tools-services.html&quot;&gt;take screenshots since at least 2020&lt;/a&gt;, but it&#039;s such a bad experience. The gesture isn&#039;t tricky, but for the life of me, I cannot tap both corners at the same time. And let&#039;s say I do manage to do so by chance; the screen flashes white, and that&#039;s it. But you know what else that looks like? Your Kindle repainting its entire screen. There&#039;s no notification at all that my screenshot was successful.&lt;/p&gt;
&lt;p&gt;But KOReader gets it right! Its default gesture for screenshots is a long one-finger diagonal swipe. I swipe from a top corner to its diagonal bottom corner, and I receive a notification that my screenshot was taken.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./screenshot-notification.png&quot;&gt;&lt;img src=&quot;./screenshot-notification.png&quot; alt=&quot;Screenshot notification on KOReader displayed as a popup&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          KOReader displays a notification when you successfully take a screenshot, confirming your action was recorded.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;p&gt;And since I can SFTP to my Kindle at any time, I can easily view those screenshots on my computer. And since KOReader provides a file browser, I can go find my screenshots at &lt;code&gt;/mnt/us/koreader/screenshots/&lt;/code&gt; and view them on my Kindle itself!&lt;/p&gt;
&lt;h2 id=&quot;add-a-dictionary&quot;&gt;Add a Dictionary&lt;/h2&gt;
&lt;p&gt;My KOReader installation did not have a dictionary installed by default; I&#039;m unsure if I configured it that way or if this is expected. There are two methods for installing dictionaries: through KOReader&#039;s top menu and by manually transferring dictionaries.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search.png&quot;&gt;&lt;img src=&quot;./menu-search.png&quot; alt=&quot;KOReader&amp;#039;s Search menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          In KOReader, tap the magnifying glass icon to access the Search menu.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search-settings.png&quot;&gt;&lt;img src=&quot;./menu-search-settings.png&quot; alt=&quot;KOReader&amp;#039;s Search Settings menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          From the Search menu, select the &quot;Settings&quot; option to configure dictionary and search preferences.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search-settings-dictionary.png&quot;&gt;&lt;img src=&quot;./menu-search-settings-dictionary.png&quot; alt=&quot;KOReader&amp;#039;s Dictionary Settings menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          The &quot;Dictionary Settings&quot; menu allows you to manage, download, and configure how dictionaries work in KOReader.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;h3 id=&quot;through-koreader-s-top-menu&quot;&gt;Through KOReader&#039;s Top Menu&lt;/h3&gt;
&lt;p&gt;The first option is to install a dictionary from inside KOReader. Navigate to the Search menu (represented by a magnifying glass), then select Settings, and finally, Download Dictionaries. The annoying part about this method is that there are 20 pages worth of dictionaries that you need to flip through. &amp;quot;E&amp;quot; for &amp;quot;English&amp;quot; is early on in the alphabet, so it&#039;s on page 4 for me.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search-settings-dictionary-download.png&quot;&gt;&lt;img src=&quot;./menu-search-settings-dictionary-download.png&quot; alt=&quot;KOReader&amp;#039;s Download Dictionaries menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          The &quot;Download Dictionaries&quot; submenu shows a paginated list of available dictionaries to install.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search-settings-dictionary-download-english.png&quot;&gt;&lt;img src=&quot;./menu-search-settings-dictionary-download-english.png&quot; alt=&quot;English dictionary selection in KOReader&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          The English dictionary can be found on page 4 of the &quot;Download Dictionaries&quot; submenu.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;h3 id=&quot;manually-via-sftp-or-wired&quot;&gt;Manually via SFTP (or wired)&lt;/h3&gt;
&lt;p&gt;My preferred approach is to download dictionary bundles manually and copy them to my Kindle via SFTP (or wired, if you prefer). The dictionaries I downloaded and installed are based on daily dumps from &lt;a href=&quot;https://www.wiktionary.org/&quot;&gt;Wiktionary&lt;/a&gt;; I got the English-only dictionary and an English-Spanish dictionary.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reader-dict.com/en/download/en&quot;&gt;English Dictionary from Wiktionary&lt;/a&gt; (i.e. &lt;code&gt;dict-en-en.zip&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://download.wikdict.com/dictionaries/stardict/&quot;&gt;Bilingual Dictionaries from Wiktionary&lt;/a&gt; (i.e. &lt;code&gt;wikdict-en-es.zip&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;KOReader supports dictionaries that are in the &lt;a href=&quot;https://en.wikipedia.org/wiki/StarDict&quot;&gt;StarDict&lt;/a&gt; format, which consists of extensions such as &lt;code&gt;*.idx&lt;/code&gt;, &lt;code&gt;*.ifo&lt;/code&gt;, or &lt;code&gt;*.ifo.gz&lt;/code&gt;, as well as &lt;code&gt;*.dict&lt;/code&gt; or &lt;code&gt;*.dict.dz&lt;/code&gt;. The above links will download a ZIP file, respectively, that, when extracted, gives you folders with their StarDict files inside. Extracting these ZIP files gave me the &lt;code&gt;dict-en-en&lt;/code&gt; and &lt;code&gt;wikdict-en-es&lt;/code&gt; folders.&lt;/p&gt;
&lt;p&gt;KOReader requires dictionaries to be stored in &lt;code&gt;/mnt/us/koreader/data/dict&lt;/code&gt; (or &lt;code&gt;/koreader/data/dict&lt;/code&gt; when connected with a cable). Each dictionary requires two levels of folders, so I created two folders to store my downloaded dictionary: &amp;quot;WikDict Bilingual&amp;quot; and &amp;quot;Ebook Reader Dict;&amp;quot; I then put &lt;code&gt;wikdict-en-es&lt;/code&gt; and &lt;code&gt;dict-en-en&lt;/code&gt; in those folders, respectively.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
  |- GNU Collaborative International Dictionary of English
  |  |- gcide
  |  |  |- stardict.idx
  |  |  |- stardict.dict.dz
  |  |  |- stardict.syn
  |  |  |- stardict.ifo
  |  |  |- stardict.idx.oft
  |- WikDict Bilingual
  |  |- wikdict-en-es
  |  |  |- stardict.dict.dz
  |  |  |- stardict.idx
  |  |  |- stardict.ifo
  |  |  |- stardict.idx.oft
  |- Ebook Reader Dict
  |  |- dict-en-en
  |  |  |- dict-data.ifo
  |  |  |- dict-data.dict.dz
  |  |  |- dict-data.syn.dz
  |  |  |- res/
  |  |  |- dict-data.idx
  |  |  |- dict-data.idx.oft&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After installing dictionaries, the &lt;code&gt;data/dict/&lt;/code&gt; folder looks like the file tree shown above. The &amp;quot;GNU Collaborative International Dictionary of English&amp;quot; folder was automatically created for me by KOReader when I installed the English dictionary through the KOReader UI (as discussed in the previous section).&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-search-settings-dictionary-manage.png&quot;&gt;&lt;img src=&quot;./menu-search-settings-dictionary-manage.png&quot; alt=&quot;KOReader&amp;#039;s Dictionary Management interface&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          The &quot;Dictionary Management&quot; menu allows you to view and manage all installed dictionaries on your Kindle.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;h2 id=&quot;exiting-koreader&quot;&gt;Exiting KOReader&lt;/h2&gt;
&lt;p&gt;One important thing to note is that while KOReader is running, it is not possible to connect your Kindle to your computer using a USB cable and transfer data to it. For me, this isn&#039;t an issue since I&#039;ve set up SSH, and I&#039;ve been wanting the ability to manage my Kindle wirelessly from my desk.&lt;/p&gt;
&lt;p&gt;Now, I may not need to exit KOReader to transfer any books to it, but it is helpful to know how to exit an application; I&#039;m looking at you, Vim. Exiting KOReader will return me to my stock Kindle UI; this will turn off any of the features I mentioned, such as screenshots or SSH. The way I see it is KOReader is your enhanced &amp;quot;operating system&amp;quot; providing many useful services and integrations/apps. I intend on running KOReader 24/7 on my Kindle and treat it as a better OS.&lt;/p&gt;
&lt;div class=&quot;c-image-gallery&quot;&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-main.png&quot;&gt;&lt;img src=&quot;./menu-main.png&quot; alt=&quot;KOReader&amp;#039;s main menu&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          Access the main menu by tapping the hamburger icon to find the Exit submenu.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
          &lt;figure&gt;
        &lt;div&gt;
&lt;p&gt;&lt;a href=&quot;./menu-main-exit.png&quot;&gt;&lt;img src=&quot;./menu-main-exit.png&quot; alt=&quot;KOReader&amp;#039;s exit confirmation dialog&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;figcaption&gt;
          Select &quot;Exit&quot; once more to close KOReader and return to the stock Kindle interface.
        &lt;/figcaption&gt;
      &lt;/figure&gt;
      &lt;/div&gt;
&lt;p&gt;Click on the Main menu (the hamburger icon), then click on Exit, and choose Exit again to shut down KOReader entirely.&lt;/p&gt;
&lt;h2 id=&quot;next-free-weekend-s-task-integrating-calibre&quot;&gt;Next free weekend&#039;s task: integrating calibre&lt;/h2&gt;
&lt;p&gt;One of the most powerful features that I&#039;m excited about is its &lt;strong&gt;wireless&lt;/strong&gt; integration with calibre! My tags, genres, and collections—everything I&#039;ve worked on—will finally integrate smoothly with my Kindle. Something Amazon would never support with its shit excuse for a library UI on its website or within its apps. However, I have yet to fully learn about this feature, and it will be a project for another weekend.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Being Promoted to Customer at Stack Overflow</title>
            <link href="https://allejo.io/blog/being-promoted-to-customer-at-stack-overflow/" rel="alternate" type="text/html" title="Being&#x20;Promoted&#x20;to&#x20;Customer&#x20;at&#x20;Stack&#x20;Overflow" />
            <updated>2023-10-21T08:00:00+00:00</updated>
            <id>41e6575ed7967e9c4bb9ce6e7ea31c474b310791</id>
            <content type="html" xml:base="https://allejo.io/blog/being-promoted-to-customer-at-stack-overflow/">&lt;p&gt;All good things must come to an end, am I right? And with Stack Overflow&#039;s &lt;a href=&quot;https://meta.stackexchange.com/questions/393806/practical-effects-of-the-october-2023-layoff&quot;&gt;&lt;strong&gt;second&lt;/strong&gt; round of layoffs this past week&lt;/a&gt;, I figured I&#039;d finally write something. My tenure at Stack Overflow ended prematurely. Unfortunately, I was affected by &lt;a href=&quot;https://meta.stackexchange.com/questions/389060/2023-layoff-impacting-58-so-se-staff-2023-05-10&quot;&gt;its May 2023 layoff&lt;/a&gt;, and truth be told, I was heartbroken. No workplace is ever perfect, but I loved working at Stack Overflow. I was proud of the impact I was making, an impact on the lives of millions of developers around the world. To say I was blindsided by the layoff is an understatement.&lt;/p&gt;
&lt;p&gt;The night before I got laid off, I had just finished merging four different PRs that were the culmination of about three months&#039; worth of my work to restructure the front-end codebase of Stack Overflow. Finally! I had gotten the first handful of reusable and accessible Stimulus components built, tested, shipped, and integrated with our build system. I had read stories about other companies having layoffs and their employees losing access to their work accounts overnight, but I never thought it would happen to me. I was wrong. Unlike some other companies, I at least met with my manager, who told me the news. After the meeting, I immediately sent one message to my team&#039;s channel saying I had fun working with them and that I had gotten laid off. I saw the &lt;em&gt;&amp;quot;Jane Doe is typing...&amp;quot;&lt;/em&gt; message, and then Slack signed me out. That fucking hurt. I couldn&#039;t see what anybody responded. I couldn&#039;t say bye to my friends, some of whom don&#039;t have or aren&#039;t active on social media.&lt;/p&gt;
&lt;h2 id=&quot;the-job-hunt&quot;&gt;The Job Hunt&lt;/h2&gt;
&lt;p&gt;It is no secret that I loathe capitalism. If you&#039;re in tech, you probably know how awful the job market is right now, as with many other fields. This poor job market could have been avoided if more large companies hadn&#039;t done so much hiring to the point where they couldn&#039;t sustain themselves past the pandemic slowdown.&lt;/p&gt;
&lt;p&gt;I like statistics, so as I&#039;ve been applying to jobs, I&#039;ve been doing my best to keep a spreadsheet of the jobs I&#039;ve applied to, responses I&#039;ve gotten, etc. At first, I thought it would be fun to keep track of these numbers, but now it&#039;s harming my mental health and self-confidence more than anything else.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Submitted ~400 applications&lt;/li&gt;
&lt;li&gt;Received ~120 rejections&lt;/li&gt;
&lt;li&gt;Never received a status update (aka ghosted) ~260 times&lt;/li&gt;
&lt;li&gt;Got 15 interviews
&lt;ul&gt;
&lt;li&gt;4 rejections after interviewing&lt;/li&gt;
&lt;li&gt;Ghosted 11 times after interviewing (i.e., never heard back after interviewing; yes, I did try reaching out)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#039;re a hiring manager that has interviewed someone and hasn&#039;t gotten back to them despite them directly emailing you: Go fuck yourself. It doesn&#039;t matter how many applicants you have or how busy you are; sending a quick, templated message of &amp;quot;Sorry, we&#039;re not going with you&amp;quot; is much better than ignoring folks who set time aside to interview with you. If you have too many applicants, then limit how many applications you can accept before a job automatically closes. Stop fucking wasting other people&#039;s time.&lt;/p&gt;
&lt;h2 id=&quot;things-i-m-tired-of-hearing&quot;&gt;Things I&#039;m Tired of Hearing&lt;/h2&gt;
&lt;p&gt;I have plenty of very kind and supportive folks in my life. While they all have good intentions, the most common things I&#039;ve been told that I&#039;m honestly tired of hearing are,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You&#039;re a great developer! It&#039;ll be easy for you to get another job.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Clearly, it&#039;s not been &amp;quot;easy&amp;quot; considering the stats above.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You should consider this a vacation and enjoy your time off!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There&#039;s a difference between taking a vacation and being unemployed. Being on vacation means you are still getting paid and have a job to return to after your vacation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Have you been applying for jobs?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yea, no shit.&lt;/p&gt;
&lt;p&gt;I have &lt;a href=&quot;https://www.linkedin.com/posts/allejo_top-three-things-you-can-say-to-your-unemployed-activity-7095507866777776129-q8aH&quot;&gt;complained about this before on social media&lt;/a&gt;, and I&#039;ve had a few folks reach out to me to apologize, but honestly, I take no offense to it. I know folks mean well, but I&#039;m just exhausted from hearing the same things repeatedly. That&#039;s all.&lt;/p&gt;
&lt;h2 id=&quot;what-am-i-up-to-now&quot;&gt;What am I up to now?&lt;/h2&gt;
&lt;p&gt;I&#039;m currently doing some contracting work for the awesome folks at &lt;a href=&quot;https://compiler.la/&quot;&gt;Compiler&lt;/a&gt;, catching up with some open source work, returning to the BZFlag community with gifts, and finishing my thesis! It&#039;s only taken me five months of being unemployed to finally improve my mental health slightly and start tackling life again. However, that doesn&#039;t mean I&#039;d consider myself as being back on my feet. That will only happen once I have more security of being employed full-time with benefits and not worrying about what&#039;s next after a contract ends.&lt;/p&gt;
&lt;p&gt;Oh, and I&#039;ve also been shit posting on LinkedIn recently with a surprising amount of impressions. For someone who never posts on LinkedIn, I&#039;ve been getting more activity lately than I have in years. So, follow me on LinkedIn to read up on my stupid posts that have a hint of professionalism. Or don&#039;t; I&#039;m not your boss. I can&#039;t tell you what to do.&lt;/p&gt;
&lt;p&gt;There&#039;s a lot more I would love to say and speak about related to Stack Overflow, but I&#039;m legally prohibited from doing so; I needed that severance!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Joining Stack Overflow</title>
            <link href="https://allejo.io/blog/joining-stack-overflow/" rel="alternate" type="text/html" title="Joining&#x20;Stack&#x20;Overflow" />
            <updated>2022-11-21T08:00:00+00:00</updated>
            <id>6fe75d452efe93a61c7a5e930a5c1f56abc6a6e4</id>
            <content type="html" xml:base="https://allejo.io/blog/joining-stack-overflow/">&lt;p&gt;It’s been radio silence on my site for almost two years. Since my last post, I got hired by one of my dream companies: Stack Overflow. I have always enjoyed building tools to make the developer experience easier for everyone, so it should be no surprise that working at Stack Overflow has always been a dream of mine. I have actually applied to Stack Overflow twice in my life; the first time, I didn’t even make it to the phone screening. The second time, I got hired. If at first you don’t succeed, give up. Or something like that, right?&lt;/p&gt;
&lt;h2 id=&quot;leaving-the-public-sector&quot;&gt;Leaving the Public Sector&lt;/h2&gt;
&lt;p&gt;Since I started working professionally at 19, I have always been working in the public sector: local government (City of Santa Monica) then state government (California State University, Northridge). I enjoyed it, I loved the thought of contributing to building things that would benefit the public and not be done for a profit. I loved working for a university because I wanted to build things for students and make their lives better. I had hired student developers and was mentoring them to build production level applications. Despite upper management thinking I was unreasonable for expecting good code from students, I had faith that with a bit of direction they’d be able to write clean code. Turns out, I was right. I felt like I was making a difference in the lives of others and I was ecstatic. But I wasn’t feeling fulfilled or appreciated.&lt;/p&gt;
&lt;p&gt;Any time I put forward ideas to improve the lives of the student population, I’d get told that it wasn’t a priority. Employee appreciation and fun was in the form of seasonal parties where each employee would pitch in ~$20 so that we could have a budget. As a taxpayer-funded entity, we had to be mindful of how we spent our money. However, there was no shortage of sessions with “job coaches” for our directors to help them improve their management skills. We had monthly birthday celebrations with a giant cake from Costco, where we were reminded of the directors’ generousity by pitching in to get us this cake.&lt;/p&gt;
&lt;p&gt;So I left.&lt;/p&gt;
&lt;h2 id=&quot;interviewing-at-stack-overflow&quot;&gt;Interviewing at Stack Overflow&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.blog/2020/07/03/how-stack-overflow-hires-engineers/&quot;&gt;Stack Overflow is incredibly open about its hiring process&lt;/a&gt; and following in that spirit, I’d like to talk about my experience. Firstly, my interviewing experience was incredibly nerve-racking at almost every step of the way and I’ll explain why.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The very first time I applied to Stack Overflow was on July 3rd, 2020 and I promptly received a rejection email on July 10, 2020. I got rejected in 7 days. Thank you for letting me know! There’s nothing worse than receiving a rejection email months after you even remember applying. I was disheartened by the rejection email but thankful they told me I had gotten rejected.&lt;/li&gt;
&lt;li&gt;As I mentioned, it was my second time applying to Stack Overflow. I applied on January 1st, 2021. New year, new job? Am I right? Ha. Because the first time I applied I got an update on my application status after a week, I had hoped the same would be true this time around. A week passed. Two weeks. Three... I just assumed I had gotten rejected. It wasn’t until March 8th that I got an email from Stack Overflow apologizing that they hadn’t been able to process all their applications in a timely manner and that if I was still interested in applying, to click a link to resubmit. My dream company? Apologizing? Yes, of course I forgive you! (Sell-out, I know). On March 10th, a recruiter from Stack Overflow reached out to me asking to schedule an interview. It’s happening, y’all!&lt;/li&gt;
&lt;li&gt;The initial screening happened and at this point, the recruiter asked me some basic questions about my experience, my salary expectations, what I was looking for, etc. The recruiter and I ended up bonding over the GIS Stack Exchange; what are the odds? But, I did it! I had &lt;strong&gt;an&lt;/strong&gt; interview at my dream company! I was feeling pretty happy at this moment.&lt;/li&gt;
&lt;li&gt;The recruiter reached out to schedule a code screen interview! AKA “the hiring manager interview” because as the name suggests, he became my manager. It started off being a terrifying interview as he asked me about an algorithm that I had never heard of. I guess he saw the panic in my face as he proceeded to tell me he hadn’t heard of it either until he joined Stack. He described to me what the algorithm had to do and then I proceeded to write the code. At the end of the interview, I felt pretty good with my implementation but of course, I had to confirm. So I rewrote my code and tested to see if it would work and... it didn’t! I was horrified. I was close but made a mistake with my logic and I thought I had failed that step of the interview.&lt;/li&gt;
&lt;li&gt;You can imagine my surprise when I got an email from the recruiter telling me I had moved on to the next step and to schedule my Algorithm and Architecture interviews with Stack developers. After each of these interviews, I felt the exact same feeling where I thought I had failed each time because I would work through the problem a second time after the interview and see how I did. Each time, I made minor mistakes in my code and my solution would be off since during these interviews, we didn’t run the code I wrote (spoiler: I wrote my code in Google Docs). I was honestly expecting to receive an email from the recruiter thanking me for my time and better luck next time.&lt;/li&gt;
&lt;li&gt;I couldn’t believe it. I had received an email from the recruiter asking to schedule an interview with a VP. Despite me making mistakes in my code during the interviews, I still passed and moved forward. This would turn out to be my last interview before I received an offer. In actuality, this wasn’t so much an interview than a casual chat with the VP about Stack Overflow as a company and what it’s like working there.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then on April 13, 2021, I received an email from the recruiter. It was an update, “the fun kind.” It was an email to schedule a call for an official offer, which obviously, I accepted.&lt;/p&gt;
&lt;h2 id=&quot;about-the-interviews&quot;&gt;About the Interviews&lt;/h2&gt;
&lt;p&gt;A few months after getting hired, I was shadowing my colleagues performing interviews and shortly after, I was interviewing other candidates myself. The way interviews are done at Stack Overflow are a bit different from other companies, I feel. I’ve seen companies ask Leet Code style questions or CS heavy questions (e.g. invert a binary tree). Most statistics on the internet are made up but I think it’s safe to say that unless you’d be working on low-level stuff like a language/compiler, you’ll rarely ever need in-depth knowledge of algorithms that haven’t already been implemented in an existing library or language runtime. Asking these types of questions introduces a huge disadvantage to self-taught developers, career switchers, bootcamp participants, and many others that never had a college CS education.&lt;/p&gt;
&lt;p&gt;So what’s different about the interviews we give at Stack? We focus largely on realistic problem solving; we give candidates real life scenarios and have them write code to solve those problems. For example, a completely hypothetical interview prompt we’d give is — given a CSV file you should write code that will accept the extracted contents, aggregate some stuff, and finally output the contents of the new data to standard out. In my opinion, it’s realistic problem solving rather than memorizing certain algorithms that you’ll never use in your day to day.&lt;/p&gt;
&lt;h2 id=&quot;what-s-it-like&quot;&gt;What’s it like?&lt;/h2&gt;
&lt;p&gt;Amazing and lots of fun. Despite being in open source for years, I am very early on in my professional career and still lack a lot of experience for things at the scale of Stack Overflow. Since starting there, I can honestly say that there hasn’t been a day where I haven’t learned something new. It’s an absolutely amazing feeling to have so many brilliant people be my coworkers, teachers, and mentors.&lt;/p&gt;
&lt;p&gt;I started on the Community Products - Engagement team, which was originally tasked with maintaining the Stack Overflow Jobs board and Developer Story. We were later tasked with killing it, much to the disappointment of several in our community.&lt;/p&gt;
&lt;p&gt;Recently, I have moved to the Community Enablement team, which focuses largely on building, improving, and managing moderator tools. The Stack Exchange network could not exist without the community members and its moderators keeping everything alive and in good shape. We’re a new team and I’m looking forward to the things that will be coming out of this team.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Wait, We Have Customers?</title>
            <link href="https://allejo.io/blog/wait-we-have-customers/" rel="alternate" type="text/html" title="Wait,&#x20;We&#x20;Have&#x20;Customers&#x3F;" />
            <updated>2020-09-13T08:00:00+00:00</updated>
            <id>57cdbdd2ef51c7fbea5d7a00a191b88ad7bfc4f3</id>
            <content type="html" xml:base="https://allejo.io/blog/wait-we-have-customers/">&lt;p&gt;I have never worked retail in my life and for that, I am very grateful. Trust me, with my sarcasm and attitude, you do not want me working in retail. If you work in retail, you have my full respect; I could never do what you do. Working on a college campus, something I see very often is that employees forget who we&#039;re working for and who our customers are.&lt;/p&gt;
&lt;p&gt;For those of you who know me, I&#039;m excruciatingly honest and rarely ever filter myself; this trait of mine has gotten me in trouble at work plenty of times. This post isn&#039;t meant to be philosophical but I believe we&#039;re in our current state because no one spoke up early on when important decisions were being made and things just became the norm.&lt;/p&gt;
&lt;h2 id=&quot;describing-a-college-campus&quot;&gt;Describing a College Campus&lt;/h2&gt;
&lt;p&gt;As much as I would love to air all of the dirty laundry I&#039;m privy to about the college campus I work on, that&#039;s considered taboo. I&#039;m sorry, let&#039;s call it &amp;quot;whistleblowing&amp;quot; instead. Sounds worse, am I right? Everyone has different opinions about what&#039;s ethical and what&#039;s not. Is telling someone that whistleblowing is the same as &amp;quot;career suicide&amp;quot; ethical? Depends on who you ask. But I digress.&lt;/p&gt;
&lt;p&gt;The college campus I work at is huge; it&#039;s got enough employees to populate a small town. I&#039;m a developer, so of course, I work for the IT department. But wait. Which one? Practically every department on campus has its own IT team and then, of course, we have the central IT department in charge of maintaining the core infrastructure for the campus (that&#039;s where I work). Our separate IT teams on campus work together as well as the &amp;quot;checks and balances&amp;quot; system of the US government: it&#039;s nice in theory but terrible in practice. Sorry, third graders, that was a spoiler.&lt;/p&gt;
&lt;h2 id=&quot;customers-on-a-campus&quot;&gt;Customers on a Campus&lt;/h2&gt;
&lt;p&gt;Who are the customers for a college campus? If you guessed, &amp;quot;students,&amp;quot; then you&#039;re right! Actually, in theory, you&#039;re right. When it comes down to it, I genuinely do not believe that the employees of a college campus remember that students are our &amp;quot;customers.&amp;quot; They&#039;re literally paying thousands of dollars and funding the salaries of employees and yet... They&#039;re not treated as customers? How does that make sense?&lt;/p&gt;
&lt;p&gt;The battles students need to fight against the administration amazes me. Some students can get through their college careers with no problems but others, face a plethora of obstacles. Have you ever tried transferring credits between colleges? Let&#039;s say you&#039;re transferring from College A to College B. You are literally at the mercy of College B&#039;s discretion on whether or not College A&#039;s teachings live up to College B&#039;s standards. What happens if your credits are rejected? Then you have to take the classes again at College B.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But there are charts published by colleges stating which credits they will and will not accept!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Listen to yourself. Let that sink in. Colleges are literally publishing what they think about other college&#039;s coursework. This is a battle between colleges and students are caught in the middle of it. This is a fundamental problem with the education system not being standardized.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Those published charts are &lt;em&gt;literally&lt;/em&gt; standardization of what is being taught. That&#039;s why colleges have those charts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&#039;s cute you think that. Have you ever read a syllabus at the beginning of a semester and thought to yourself, &amp;quot;wow! That sounds so cool! I&#039;m going to learn so much in this class,&amp;quot; but by the end of the semester, you realize that everything on the syllabus was either a lie or blown out of proportion?&lt;/p&gt;
&lt;p&gt;It&#039;s these syllabi that are agreed upon between campuses. This is why making changes to them is very painful because they need to be &amp;quot;reevaluated&amp;quot; by other colleges and other colleges need to change their own syllabi to teach the same material or reject it and no longer accept transfer credits.&lt;/p&gt;
&lt;p&gt;Why do people accept it as &amp;quot;the norm&amp;quot; for colleges treating their students like they don&#039;t matter? Students are the reason we are getting paid. We should not be making their lives incredibly difficult and wasting their time.&lt;/p&gt;
&lt;h2 id=&quot;a-sharing-development-culture&quot;&gt;A Sharing Development Culture&lt;/h2&gt;
&lt;p&gt;&amp;quot;Culture&amp;quot; is the word that every managerial figure loves to hear and boasts about. I agree that we have a culture on campus, however, I would add an adjective to it: toxic.&lt;/p&gt;
&lt;p&gt;Why did I originally bring up working in IT and other IT teams on campus? One of the biggest problems I see is that people in IT forget that students are our customers. Well, not directly. The IT teams I&#039;m talking about build and manage things for faculty and administration that then help those users provide services for students. This is how people forget we&#039;re ultimately serving students.&lt;/p&gt;
&lt;p&gt;The vast majority of the tools I work on are intended to be used internally by campus employees to make their lives easier so that &lt;em&gt;they&lt;/em&gt; can make things better for students. If you have ever worked with me or seen my work on GitHub, you probably know that I absolutely love building things in a modular way. &lt;em&gt;Incredibly&lt;/em&gt; modular; but not a &amp;quot;dependency hell&amp;quot; type of modular either. Please don&#039;t question my judgment, that&#039;s what my parents are for. As an example of something modular that I&#039;ve built, let&#039;s look at my &amp;quot;BZFlag Plug-in Starter&amp;quot; that consists of three separate projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/aclovis&quot;&gt;aclovis&lt;/a&gt;: a library for generating C++ code via JavaScript. This project has enough interfaces where it can be expanded to generating other languages too if I wanted to.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/bzfPluginGen&quot;&gt;bzfPluginGen&lt;/a&gt;: a library that uses &lt;em&gt;aclovis&lt;/em&gt; to generate the source code for a BZFlag plug-in.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/bzflagPluginStarter3&quot;&gt;BZFlag Plug-in Starter&lt;/a&gt;: a React website that uses &lt;em&gt;bzfPluginGen&lt;/em&gt; to let users generate plug-ins by clicking buttons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these tools build on top of each other and can be expanded on their own! aclovis can be expanded to support other languages. bzfPluginGen can be updated to generate a new C++ syntax. BZFlag Plug-in Starter can be updated to have an entirely different design. It may seem like overkill but they all live in their own separate projects with a single purpose in mind. I have plenty more examples on GitHub but the point is, many of my tools at work are this modular.&lt;/p&gt;
&lt;p&gt;There are plenty of tools that my colleagues have built as well that can be useful to other departments if shared! It&#039;s this type of sharing that is currently discouraged. Wait what? &lt;em&gt;Dis&lt;/em&gt;-couraged? Yes. This is one of many reasons why I consider our current culture is toxic. We are forcing everyone to work in silos because nobody wants to take responsibility for maintaining shared tools.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we release it to other teams and they find a bug, do we have to drop everything and fix it for them?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No! Who the hell makes those types of guarantees? Even Microsoft doesn&#039;t fix that &lt;a href=&quot;https://www.theverge.com/2020/8/6/21355674/human-genes-rename-microsoft-excel-misreading-dates&quot;&gt;one bug in Microsoft Excel that drives your industry insane&lt;/a&gt;. So why should you? You are not making any guarantees by sharing your code. What you are doing is saving other people time. In the future, their teams may work on a feature of a shared tool that you end up needing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But what if people expect us to make guarantees?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use an open source license! For example, the &lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT license&lt;/a&gt; clearly states, &#039;THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED...&#039; The open source world has got these things figured out already. Where have you been?&lt;/p&gt;
&lt;p&gt;Wait. If everyone is using the same tool, doesn&#039;t that mean there are more people who can fix bugs? If someone wants a feature, doesn&#039;t that mean there are more people who can work on the feature? You mean, someone else has already laid out the foundation for a project and all you have to do is add this one small feature that you need and not spend weeks building the same thing from scratch?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(insert mind blown gif)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Yes! Yes! Yes! Share your code with other teams on campus and release it as &lt;a href=&quot;https://en.wikipedia.org/wiki/Inner_source&quot;&gt;inner source&lt;/a&gt;. Start a culture on your campus of sharing your code with other teams! Once you&#039;re comfortable with that, start sharing with &lt;em&gt;other&lt;/em&gt; campuses! That&#039;s when you&#039;re ready for the big boy world of open source.&lt;/p&gt;
&lt;h2 id=&quot;but-law-is-scary&quot;&gt;But Law is Scary!&lt;/h2&gt;
&lt;p&gt;Clearly, you have not seen the thousands of popular open source projects started by random users with absolutely no legal experience. I&#039;m recommending you use an open source license because it&#039;s quick and easy. As I said, the open source world has got it figured out.&lt;/p&gt;
&lt;p&gt;I&#039;m not a lawyer. What do I know? Allow me to point you to &lt;a href=&quot;https://ben.balter.com/2017/11/28/everything-an-open-source-maintainer-might-need-to-know-about-open-source-licensing/&quot;&gt;Ben Balter&#039;s amazing post about open source licensing&lt;/a&gt;. If the average high schooler can create something on GitHub and open source it, I&#039;m sure you can too. Developers should be focusing their time on developing &lt;em&gt;new&lt;/em&gt; things, not worry about licensing, or reinventing the wheel.&lt;/p&gt;
&lt;p&gt;Encourage developers to share. We love creating stuff but not stuff that&#039;s already been invented. Remember college employees, students are the reason you still have jobs; you should be building things for them in the most efficient way possible.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>2019 Year-in-Review</title>
            <link href="https://allejo.io/blog/2019-year-in-review/" rel="alternate" type="text/html" title="2019&#x20;Year-in-Review" />
            <updated>2019-12-21T08:00:00+00:00</updated>
            <id>91230d332cf279a7ec8bd12886a938ccc13c6f5a</id>
            <content type="html" xml:base="https://allejo.io/blog/2019-year-in-review/">&lt;p&gt;This year had a really rough start and had a bumpy ride everywhere in between due to personal problems. BUT! It’s okay, there’s a lot of other stuff that happened this year that made things interesting and enjoyable for me. Well, I’m sure a lot more stuff happened but these are the only things I remember or am willing to share.&lt;/p&gt;
&lt;h2 id=&quot;grad-school&quot;&gt;Grad School?!&lt;/h2&gt;
&lt;p&gt;It’s no secret that I really despise the American education system and I was always disgruntled about being forced to attend college to get a degree simply to be taken seriously (i.e. getting a job with benefits). Knowing that, I shocked myself when I started thinking about going for a Master’s degree. As a campus employee, I am able to take classes and get a master’s degree at a heavily discounted rate. So the option was always available to me but I refused to pursue a master’s from the computer science department based on principal; their teaching ideals are heavily dated and obsolete, in my opinion. So I considered what other fields I could get a master&#039;s in and I started thinking about my minor during undergrad: GIS. Funny enough, as I was thinking about getting a master’s in GIS, I started consistently running into my one of my favorite professors on campus, Dr. Soheil Boroushaki.  We grabbed lunch together and we discussed the GIS master’s program. After our lunch together, he had me sold on the program. But of course, I couldn’t tell him that immediately. I needed to pretend to think about it and I needed to figure out how to get my job to pay for my degree…&lt;/p&gt;
&lt;p&gt;One of the things I hate the most is people complaining about a problem and doing nothing about it. Am I a hypocrite? I’ve been complaining about the education system for years and I have done nothing about it. Allow me to correct any misunderstandings. I am pursuing my master’s degree so that I may begin teaching at the college level and start doing my part, however small, in changing the education people receive. My current goal is to start teaching programming related GIS classes as soon as I get my degree.&lt;/p&gt;
&lt;h2 id=&quot;the-rest&quot;&gt;The Rest&lt;/h2&gt;
&lt;p&gt;My high school has an event every year for its graduating seniors where alumni from the school will return for dinner and talk to seniors about their respective fields. Now, when I was a high school senior I was really interested in programming and signed up for the dinner with hopes of talking to a programmer. To my disappointment, I was paired up with an architect (I think) because there weren&#039;t any programmers available. Remember how I said I hated people who complain but don’t work towards change? Well this year, I reached out to my high school and told them that if they had any students interested in programming/technology, I’d be more than happy to come. Sure enough, they invited me to the dinner and I had a whopping two students interested! I was actually more surprised there were even two students who signed up with an interest in programming. I really hope I was able to give them some insight during our dinner. Also, I was one of the youngest alumni who was there. I was woefully under qualified to give them any life advice like the other alumni, but I at least knew my field! I hope to be able to continue being active in my high school&#039;s alumni community and participate in events like these in the future.&lt;/p&gt;
&lt;p&gt;I’m a huge fan of &lt;a href=&quot;https://www.leeandlie.com/&quot;&gt;Amanda Lee (aka AmaLee, aka Leeandlie)&lt;/a&gt; and her music and voice acting. I wasn’t able to find her original tweet but she mentioned a while back that someone was squatting on the domain she wanted: leeandlie.com; so she had to register leeandlieofficial.com. Well long story short, one day I was checking up on the domain out of boredom and found that it was able for me to acquire. And so I did. As soon as I had control of it, I redirected it to her (now) old domain and reached out to her to hand it over. Conclusion: &lt;a href=&quot;https://twitter.com/LeeandLie/status/1121569017754542080&quot;&gt;she finally owns leeandlie.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I dragged my father to &lt;a href=&quot;https://music.mxdwn.com/2019/10/21/reviews/show-reviews/babymetal-live-at-the-forum-review-setlist-photos/&quot;&gt;BABYMETAL’s first US arena show at The Forum&lt;/a&gt; and boy, was that an amazingly fun adventure; I’d definitely do it again and surprisingly enough, my dad would too.&lt;/p&gt;
&lt;p&gt;I was invited to the GitHub Sponsors beta before it was released to the general public and I got &lt;a href=&quot;https://github.com/sponsors/allejo&quot;&gt;my first two sponsors this year!&lt;/a&gt; I am really hoping that sponsoring open source developers becomes more common and that I may start working on projects that other individuals feel like sponsoring. Hint: I’ll gladly sell my soul for someone to sponsor me.&lt;/p&gt;
&lt;h2 id=&quot;software-accomplishments&quot;&gt;Software Accomplishments&lt;/h2&gt;
&lt;p&gt;What type of obsessive programmer would I be if I didn’t have a section dedicated to all my work in the open source community? Every year, there’s always something new and fun for me in the open source world and this year did not let me down. Here’s a summary of the most notable things that happened!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;allejo/php-vcr-sanitizer&lt;/code&gt; &lt;a href=&quot;https://github.com/allejo/php-vcr-sanitizer/issues/6&quot;&gt;reached 1.0.0&lt;/a&gt; after learning that people were actually using it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scrivo/highlight.php&lt;/code&gt; had &lt;strong&gt;14&lt;/strong&gt; releases this year; &lt;a href=&quot;https://github.com/scrivo/highlight.php/compare/v9.13.1.1...v9.17.1.0&quot;&gt;&lt;code&gt;v9.13.1.1&lt;/code&gt; through &lt;code&gt;v9.17.1.0&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag.org/pull/25&quot;&gt;bzflag.org redesign was launched on April 1st&lt;/a&gt; after ~9 months of work; here&#039;s &lt;a href=&quot;/blog/the-building-of-the-new-bzflag.org/&quot;&gt;how we built it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/stakx-io/stakx/releases/tag/v0.2.0&quot;&gt;stakx reached v0.2.0&lt;/a&gt; after it was battle tested with the new BZFlag website&lt;/li&gt;
&lt;li&gt;I joined the &lt;a href=&quot;https://github.com/bovigo/vfsStream&quot;&gt;vfsStream team&lt;/a&gt; as a maintainer&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forums.bzflag.org/viewtopic.php?f=2&amp;amp;t=20337&quot;&gt;Launched BZFlag Postgame&lt;/a&gt; and tagged &lt;a href=&quot;https://github.com/allejo/postgame&quot;&gt;9 alpha versions&lt;/a&gt; for that project&lt;/li&gt;
&lt;li&gt;Built &lt;a href=&quot;https://github.com/allejo/bzflag-networking.php&quot;&gt;&lt;code&gt;allejo/bzflag-networking.php&lt;/code&gt;&lt;/a&gt; and released 9 versions of it&lt;/li&gt;
&lt;li&gt;Refactored my personal website to use Bootstrap instead of Foundation&lt;/li&gt;
&lt;li&gt;Open-sourced my &lt;a href=&quot;https://github.com/allejo/VPNBlocker&quot;&gt;VPNBlocker BZFlag plug-in&lt;/a&gt; and released v2.0.0&lt;/li&gt;
&lt;li&gt;Contributed &lt;a href=&quot;https://github.com/OpenAPITools/openapi-generator/pull/3919&quot;&gt;my first PR to OpenAPI Generator&lt;/a&gt;, fixing some TypeScript errors&lt;/li&gt;
&lt;li&gt;Touched WordPress development again after years by contributing to &lt;a href=&quot;https://github.com/westonruter/syntax-highlighting-code-block/pulls?utf8=%E2%9C%93&amp;amp;q=is%3Apr+sort%3Aupdated-desc+author%3Aallejo+&quot;&gt;Weston Router&#039;s Syntax Highlighting plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/bzflagPluginStarter3&quot;&gt;BZFlag Plug-in Starter hits version 3&lt;/a&gt; with support for &lt;strong&gt;a lot&lt;/strong&gt; of new options&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/bzfPluginGen&quot;&gt;bzfPluginGen&lt;/a&gt; was released and served as the core for v3 of my plug-in starter&lt;/li&gt;
&lt;li&gt;Released &lt;a href=&quot;https://www.npmjs.com/package/@allejo/react-a11y-dialog&quot;&gt;&lt;code&gt;@allejo/react-a11y-dialog&lt;/code&gt;&lt;/a&gt; as an NPM package forked from of Hugo Giraudel&#039;s project&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;&lt;code&gt;jekyll-toc&lt;/code&gt;&lt;/a&gt; was adopted by more websites: City of Amsterdam, Intuit, Duality&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/jekyll-anchor-headings&quot;&gt;&lt;code&gt;jekyll-anchor-headings&lt;/code&gt;&lt;/a&gt; was adopted by awesome websites like: Bitrise, sitespeed.io, Duality, mlpack.org, Riot.js, &amp;quot;Just the Dogs&amp;quot; Jekyll theme, Microsoft’s TypeScript website, VMWare’s Octant documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#039;s next?&lt;/h2&gt;
&lt;p&gt;As I&#039;ve mentioned before, I don&#039;t believe in whole concept of &amp;quot;new year new me&amp;quot; or doing something new in the new year. I&#039;m content with where I&#039;m at and I will continue to move forward with the same projects I&#039;ve been working on.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>The Unsolicited Job "Offer"</title>
            <link href="https://allejo.io/blog/the-unsolicited-job-offer/" rel="alternate" type="text/html" title="The&#x20;Unsolicited&#x20;Job&#x20;&quot;Offer&quot;" />
            <updated>2019-07-19T08:00:00+00:00</updated>
            <id>a3ee1c7a6129b8efb2e7b15b9ed8bd449b76b7e8</id>
            <content type="html" xml:base="https://allejo.io/blog/the-unsolicited-job-offer/">&lt;p&gt;When I was much younger, I&#039;d get excited about receiving messages from recruiters on LinkedIn. I thought to myself that I was actually in demand. The ignorance of youth, am I right? Truthfully, I&#039;m not popular enough or patient enough to get anything out of LinkedIn; so to me, it&#039;s mostly a joke and I treat it as such.&lt;/p&gt;
&lt;p&gt;I accepted the job offer for my current position end of May 2018 and I updated my LinkedIn accordingly the same day so I wouldn&#039;t get recruiters sending me messages any more. Little did I know, that meant absolutely nothing to recruiters. I kept receiving unsolicited messages regularly from recruiters until mid August 2018 when I disabled InMails. Yes. I had to completely disable a feature on LinkedIn, because despite my happily starting a new job, apparently I was still wanted for &amp;quot;a great full-time opportunity with a profitable and long-standing successful company.&amp;quot; That&#039;s right everyone, I got a job offer at—wait, what company is this that supposedly wants me? I was never told!&lt;/p&gt;
&lt;p&gt;Did disabling InMails solve my problem? If you think so, you are horribly mistaken. Instead of InMails, I was now receiving connection requests from recruiters sending me their messages via friend requests. Why would anyone think that&#039;s a good idea?&lt;/p&gt;
&lt;p&gt;Don&#039;t get me wrong, being a recruiter must be a tough job and I respect it. Every job is a respectable job. But please put effort into your job and don&#039;t just use the same generic and templated messages for everyone. One InMail I received included this gem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We work using Paired Programming and Extreme Programming methods to build Next-Gen Tech from AI, Machine Learning to drones and we offer some amazing perks. No other company is doing what we are!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every company is building &amp;quot;next generation technology&amp;quot; nowadays. If this were true, there would be a lot more real R&amp;amp;D going on for the betterment of society. Remember blockchain and the all hype it got? I&#039;m sure that was &amp;quot;next-gen&amp;quot; too. Also, what is &amp;quot;extreme programming?&amp;quot;&lt;/p&gt;
&lt;p&gt;Why am I giving recruiters such a hard time? Why am I hating on their methods? Because it&#039;s only fair to do so, in my opinion. As an employer, how would you feel if you got a resume that consisted of &lt;em&gt;just&lt;/em&gt; the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rock star programming&lt;/li&gt;
&lt;li&gt;Worked on next-gen tech at a profitable and long-standing successful company&lt;/li&gt;
&lt;li&gt;No other developer does what I do!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Would you call this individual in for an interview? I wouldn&#039;t. Not even if I was absolutely desperate, because it shows a lack of effort on the applicant&#039;s part. So when recruiters do essentially the same thing, it shows a lack of effort.&lt;/p&gt;
&lt;p&gt;How do I solve this problem? Well, in my &amp;quot;about&amp;quot; section I have a little message hidden away. If you&#039;re truly interested in someone as a person, you should do them the courtesy of at least skimming their about section.&lt;/p&gt;
&lt;p&gt;The way I see InMails is that it&#039;s all just a game. The way InMails work is that if the recipient responds to them within 90 days, the recruiter gets credit back for the InMail they sent you&lt;sup&gt;&lt;a href=&quot;https://www.linkedin.com/help/linkedin/answer/75&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. As long as they receive some sort of response, even a rejection, they&#039;ll get their credit back; isn&#039;t that great? When I was receiving InMails the same week as I updated my new position, I simply stopped responding entirely to the InMails. If you&#039;re not going to do your due diligence of seeing that I had just started a new job, then you don&#039;t deserve your InMail back.&lt;/p&gt;
&lt;p&gt;Fast-forward a year, I&#039;ve reenabled InMail. I keep getting these ridiculous messages and connection requests. Wait. If I don&#039;t like LinkedIn, why do I still have one and advertise the link with my other social media links? Because it&#039;s blasphemy for someone in my age group not have one.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>The Building of the New BZFlag.org</title>
            <link href="https://allejo.io/blog/the-building-of-the-new-bzflag.org/" rel="alternate" type="text/html" title="The&#x20;Building&#x20;of&#x20;the&#x20;New&#x20;BZFlag.org" />
            <updated>2019-04-13T08:00:00+00:00</updated>
            <id>74edfd01fbf46469b922f1d8effdfaa9973352e9</id>
            <content type="html" xml:base="https://allejo.io/blog/the-building-of-the-new-bzflag.org/">&lt;p&gt;For roughly 9 months, I had been working on the new website for &lt;a href=&quot;https://bzflag.org&quot;&gt;the BZFlag project&lt;/a&gt; with the help of some &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag.org/graphs/contributors&quot;&gt;awesome contributors&lt;/a&gt;; the planning and early work started in July 2018 but full speed development didn&#039;t really start until November 2018. On April 1st, 2019, the site was made live! I love a good and elaborate April Fools&#039; joke, so what better way of participating this year than by launching a completely redesigned website for a project close to my heart? I mean, I also finally released my &lt;a href=&quot;https://forums.bzflag.org/viewtopic.php?f=79&amp;amp;t=20200&quot;&gt;AOLer plug-in&lt;/a&gt; this year to troll but that&#039;s beside the point.&lt;/p&gt;
&lt;p&gt;So what&#039;s the purpose of this blog post? Well, I wanted to write about everything I learned as I was building the new BZFlag site using my beloved &lt;a href=&quot;https://stakx.io/&quot;&gt;stakx&lt;/a&gt;. This is a completely biased opinion but I must say, I built stakx to be pretty well balanced when it came to features.&lt;/p&gt;
&lt;h2 id=&quot;the-good-and-bad-of-css&quot;&gt;The Good and Bad of CSS&lt;/h2&gt;
&lt;p&gt;As I was writing the page templates for the new site, I needed to decide on which CSS framework to use and &lt;em&gt;how&lt;/em&gt; to use it. There are enough CSS frameworks out there as there are JS dependencies but the typical contenders are Bootstrap and Foundation. My site uses Foundation and I regret that decision, so I decided to use Bootstrap. Well, &lt;strong&gt;parts&lt;/strong&gt; of Bootstrap that is. I used their grid system and some of their utility classes because I did not want their opinionated design nor was I going to use any of their components. I wanted full control of this design and I didn&#039;t want to bother reseting properties just to undo Bootstrap&#039;s UI.&lt;/p&gt;
&lt;p&gt;Utility based CSS classes are great for prototyping and they saved me so much time by not forcing me to create unique classes for every single element. I want to add left and right padding for tablets and desktops? Cool! Let me just add &lt;code&gt;px-md-3&lt;/code&gt; to the element. But, what happens when I start having to create multiple copies of the same components or start having to add multiple rules or need custom rules?&lt;/p&gt;
&lt;p&gt;Using Tailwind as an example, I&#039;ll build the same component with and without utility classes. Mind you, this is as close as I can get to the same components using Tailwind&#039;s default classes; once I need a more custom design, I have to do one of the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the styles via the inline &lt;code&gt;style&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;Create a custom Tailwind build, which means I now I have to bring in a &lt;code&gt;node_modules&lt;/code&gt; folder with &lt;em&gt;at least&lt;/em&gt; &lt;a href=&quot;https://npm.anvaka.com/#/view/2d/tailwindcss&quot;&gt;80 dependencies&lt;/a&gt; and require all developers working on the site to have Node installed in addition to PHP.&lt;/li&gt;
&lt;li&gt;Create my own custom classes; at which point, why the hell am I using Tailwind to begin with?&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;#&quot;&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;block relative overflow-hidden no-underline&quot;&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;box-shadow: 0px 2px 8px 1px #000; transition: box-shadow 0.2s;&quot;&lt;/span&gt;
&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;absolute min-h-full min-w-full&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;left: 50%; top: 50%; transform: translate(-50%, -50%); transition: min-height 0.2s, min-width 0.2s; z-index:-1;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;#&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
  &amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;flex flex-col p-4&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;style&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;background-color: rgba(0, 0, 0, 0.6); height: 300px; transition: background-color 0.2s;&quot;&lt;/span&gt;
  &amp;gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#039;m aware of all the hype behind &lt;a href=&quot;https://github.com/css-modules/css-modules&quot;&gt;CSS Modules&lt;/a&gt;, &lt;a href=&quot;https://cssinjs.org/&quot;&gt;CSS in JS&lt;/a&gt;, &lt;a href=&quot;https://www.styled-components.com/&quot;&gt;Styled Components&lt;/a&gt;, and hundreds more. Please. Fucking stop. There are times it&#039;s better just to do things &amp;quot;old school.&amp;quot; Stop following the trends and &lt;strong&gt;think&lt;/strong&gt;. Think about your project and requirements. Do you really need to bring in hundreds of dependencies &lt;em&gt;just&lt;/em&gt; to build a website? The answer is a resounding no.&lt;/p&gt;
&lt;p&gt;With the above example, I followed BEM with my CSS naming convention and all I&#039;m using is good old Sass to generate whatever classes I need.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;c-hover-card&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;c-hover-card__thumbnail&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;#&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;c-hover-card__body&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;...&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have Sass variables and mixins to keep consistent values throughout my classes such as margin and padding.  I have &lt;code&gt;margin()&lt;/code&gt; and &lt;code&gt;padding()&lt;/code&gt; mixins just so I can use them in the same way I would in HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-scss&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;@include&lt;/span&gt; padding(x, &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;);     &lt;span class=&quot;hljs-comment&quot;&gt;// px-3&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;@include&lt;/span&gt; padding(y, &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt;, md); &lt;span class=&quot;hljs-comment&quot;&gt;// py-md-4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, don&#039;t get me wrong. I have utility classes in my Sass but only the ones I need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Borders&lt;/li&gt;
&lt;li&gt;Display&lt;/li&gt;
&lt;li&gt;Flexbox settings&lt;/li&gt;
&lt;li&gt;Font sizes&lt;/li&gt;
&lt;li&gt;Screenreader utilities&lt;/li&gt;
&lt;li&gt;Spacing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I designed and built stakx to be the only executable you need to get a full-fledged static site built and I have accomplished just that. Despite some of the shortcomings of &lt;a href=&quot;https://github.com/leafo/scssphp&quot;&gt;the PHP implementation of Sass&lt;/a&gt;, such as slowness and some bugs, it&#039;s a solid library and it&#039;s what stakx uses at its core.&lt;/p&gt;
&lt;p&gt;Conclusion: There is absolutely no need to bring in Node as a dependency for the majority of cases. So, please. Stop doing so.&lt;/p&gt;
&lt;h2 id=&quot;collections-searching&quot;&gt;Collections + Searching&lt;/h2&gt;
&lt;p&gt;Just like a certain copyrighted animated individual, you want to collect them all! At its core, collections are one of stakx&#039;s most powerful feature. Some static site generators (SSGs) are designed for blogging, so collections for blog posts exist by default. Some SSGs have an opinion on &lt;em&gt;where&lt;/em&gt; your collections should live and how they should be named. In stakx, there&#039;s no such thing. You create the collections you want, named whatever you want, wherever you want, and nothing more; the BZFlag site consists of 7 collections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generic Documentation&lt;/li&gt;
&lt;li&gt;Flags&lt;/li&gt;
&lt;li&gt;Slash Commands&lt;/li&gt;
&lt;li&gt;Developer Documentation&lt;/li&gt;
&lt;li&gt;API Events&lt;/li&gt;
&lt;li&gt;API Functions&lt;/li&gt;
&lt;li&gt;API Objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having collections in a site is no innovative concept to stakx, but what makes them so great? It&#039;s all Front Matter (YAML) + markdown; both are pretty standard across the Internet. That means any content within these collections could be parsed and reused anywhere else. Because all of our site&#039;s content is inside of collections, it was incredibly easy to &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag.org/pull/21&quot;&gt;build a search index by generating JavaScript with our Twig templates&lt;/a&gt; that would be read in by &lt;a href=&quot;https://github.com/nextapps-de/flexsearch&quot;&gt;FlexSearch.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let me just say. FlexSearch.js is amazing! It&#039;s stupidly fast and powerful. It&#039;s relatively new to the scene and for our purposes, it outshined lunr.js tremendously.&lt;/p&gt;
&lt;p&gt;Due to &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag.org/pull/21#issuecomment-471822614&quot;&gt;problems we had with lunr.js not being consistent with searching&lt;/a&gt;, we had to explore alternatives. We even considered having a self-hosted search engine on a separate server. But. Why?! That means it&#039;s another service for us to maintain, theme, and host. If I haven&#039;t made it clear already, if there&#039;s no need to bring in unneeded dependencies, then why do so?&lt;/p&gt;
&lt;p&gt;FlexSearch.js came to the rescue being the search engine we were looking for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#039;s client-side, so nothing extra to maintain&lt;/li&gt;
&lt;li&gt;It&#039;s stupid fast and efficient, so it&#039;s a reasonable burden on the browser&lt;/li&gt;
&lt;li&gt;No dependencies! A single 6 KB minified file is all you need for the full feature set&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, I know I&#039;ve been going on about not having unneeded dependencies but a full-blown search engine with indexing, operators, partial matching, etc. is a necessary dependency. I don&#039;t have the time, patience, or knowledge to implement one myself.&lt;/p&gt;
&lt;p&gt;So how did we get to building &lt;a href=&quot;https://www.bzflag.org/search/index.js&quot;&gt;the search index&lt;/a&gt; source? We created a static PageView in stakx that will loop through all of our collections and build a JavaScript array of objects for FlexSearch.js to index.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-twig&quot;&gt;&lt;span class=&quot;xml&quot;&gt;---
title: Search Index
permalink: /search/index.js
---

const SearchIndex = [
    &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;set&lt;/span&gt;&lt;/span&gt; uid = 0 %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt;&lt;/span&gt; collection in site.collections %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt;&lt;/span&gt; item in collections[collection.name] %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
            {
                id: &lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ uid }}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;,
                title: &#039;&lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ item.title }}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;&#039;,
                content: &quot;&lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ item.content | striptags | replace({&#039;\n&#039;: &#039; &#039;}) | escape(&#039;js&#039;) }}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;&quot;,
                permalink: &#039;&lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ url(item) }}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;&#039;
            },
            &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{%- &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;set&lt;/span&gt;&lt;/span&gt; uid = uid + 1 -%}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;endfor&lt;/span&gt;&lt;/span&gt; %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;endfor&lt;/span&gt;&lt;/span&gt; %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember old school web development when you needed to have multiple &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags and global variables? Yup! So long as you don&#039;t go to extremes, it&#039;s still an acceptable practice in my opinion.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;{{ url(pages[&#039;Search Index&#039;]) }}&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;{{ url(&#039;/assets/js/flexsearch.min.js&#039;) }}&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;{{ url(&#039;/assets/js/search.js&#039;) }}&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, in our &lt;code&gt;search.js&lt;/code&gt;, all we need to do is create the actual FlexSearch.js object and we&#039;re all set! We then wrote &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag.org/pull/21/files#diff-4be111b67af9a1593260ba8451ad3653&quot;&gt;the rest of our vanilla JavaScript&lt;/a&gt; to read from an HTML &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and call &lt;code&gt;.search()&lt;/code&gt; when a form is submitted.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; idx = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; FlexSearch({
    &lt;span class=&quot;hljs-attr&quot;&gt;doc&lt;/span&gt;: {
        &lt;span class=&quot;hljs-attr&quot;&gt;id&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&#039;id&#039;&lt;/span&gt;,
        &lt;span class=&quot;hljs-attr&quot;&gt;field&lt;/span&gt;: [
            &lt;span class=&quot;hljs-string&quot;&gt;&#039;title&#039;&lt;/span&gt;,
            &lt;span class=&quot;hljs-string&quot;&gt;&#039;content&#039;&lt;/span&gt;
        ]
    }
});
idx.add(SearchIndex);

&lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
idx.search(&lt;span class=&quot;hljs-string&quot;&gt;&quot;your query&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#039;s it! We have the majority of our site&#039;s content in appropriately nested folders, an entire client-side search engine, and only a single external dependency. Seriously, why the hell would you want to bring in Node.js, npm, Webpack, and all of those other hyped up tools when they&#039;re not actually needed? Please stop overcomplicating the Internet!&lt;/p&gt;
&lt;p&gt;Conclusion: Save the bandwidth for Netflix binging.&lt;/p&gt;
&lt;h2 id=&quot;datasets&quot;&gt;Datasets&lt;/h2&gt;
&lt;p&gt;Datasets in stakx behave identically to collections with the exception that items in the datasets do not have bodies of content. The biggest benefit to having raw data is the fact that it can be reused anywhere else. On this site, we used a dataset to store release notes for &lt;em&gt;all&lt;/em&gt; of our releases dating back to 1.7c, which was released on January 1st, 2000, in a parseable format. Moving forward, we essentially now have an authoritative change log in a maintainable and parseable format meaning we can build whatever we want with this data.&lt;/p&gt;
&lt;p&gt;When it comes to stakx behavior, datasets behave similarly to collections meaning we can create a dynamic PageView and it&#039;ll render dedicated pages for each release from this data. And since all of the files in these datasets are parseable, if we need to manipulate the structure of the data, then we can write scripts to manipulate the data in memory and write back the changes to the files.&lt;/p&gt;
&lt;h2 id=&quot;javascript-less-syntax-highlighting&quot;&gt;JavaScript-less Syntax Highlighting&lt;/h2&gt;
&lt;p&gt;Yup, you read that heading correctly. All of the syntax highlighting for the API docs on the site are handled entirely by stakx at compile time; that means no dependency on highlight.js or Prism.js. Want to load a page without JavaScript enabled? Sure! Go for it! Our code samples will still be highlighted for you.&lt;/p&gt;
&lt;p&gt;This is all handled by the awesome  &lt;a href=&quot;https://github.com/scrivo/highlight.php&quot;&gt;highlight.php&lt;/a&gt;! A project I&#039;ve adopted and now maintain. It&#039;s a loyal port of highlight.js written in PHP. This means two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all of the language definitions are being maintained by &lt;a href=&quot;https://github.com/highlightjs/highlight.js/pulse&quot;&gt;a mature and dedicated JS community&lt;/a&gt;; there&#039;s no need to rewrite or update language definitions separately&lt;/li&gt;
&lt;li&gt;any stylesheet you find for highlight.js will work with highlight.php and therefore, stakx&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pageviews&quot;&gt;PageViews&lt;/h2&gt;
&lt;p&gt;stakx&#039;s design philosophy is powered entirely by what are called PageViews. PageViews are essentially views in a traditional MVC pattern and stakx itself is the controller. However, these views are slightly more powerful than traditional views in which it can configure how the controller behaves.&lt;/p&gt;
&lt;h3 id=&quot;collection-management-static-dynamic-pageviews&quot;&gt;Collection Management (Static + Dynamic PageViews)&lt;/h3&gt;
&lt;p&gt;There are three different types of PageViews: static, dynamic, and repeater. In the BZFlag site, we only made use of static and dynamic. How did we use these two types for managing collections and datasets?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_pages/../&amp;lt;collection&amp;gt;/
|- list.html.twig  # Static PageView
\- show.html.twig  # Dynamic PageView&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#039;s take the collection that contains Slash Commands docs as an example. The directory would be called &lt;code&gt;slash-commands&lt;/code&gt; and there would be two files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list.html.twig&lt;/code&gt; - A page to list all of the available slash commands organized how we wanted them, grouped by category in alphabetical order&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show.html.twig&lt;/code&gt; - This is the dynamic PageView (or template, if you will) that will be used to display information for &lt;em&gt;each&lt;/em&gt; slash command; e.g. parameters, description, version it was added, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;static-pageviews&quot;&gt;Static PageViews&lt;/h3&gt;
&lt;p&gt;Every other page that we need for the site worked out to be static PageViews. These are pages that just need markup and won&#039;t have its content or structure reused; e.g. the homepage. Sure the homepage will have reused components, but the page structure itself won&#039;t be reused for another page. Unlike collections, where the pages for slash command details all have the same sections.&lt;/p&gt;
&lt;h2 id=&quot;development-web-server&quot;&gt;Development Web Server&lt;/h2&gt;
&lt;p&gt;Now, I haven&#039;t explored how all the SSGs out there work, so this section is based on Jekyll&#039;s behavior. The way Jekyll works for a development server is it builds the entire site and then serves up those HTML files. I&#039;m not sure how other SSGs handle this but stakx only parses the site&#039;s content and starts its web server. Nothing is built.&lt;/p&gt;
&lt;p&gt;That&#039;s right. Absolutely nothing is built when you run &lt;code&gt;stakx serve&lt;/code&gt; because why should it build the entire site? The way stakx works is it starts a web server that has an internal router. Then depending on the URL you go to, that&#039;s the single page it will build. Yup! If you go to &lt;code&gt;localhost:8000/getting-started/&lt;/code&gt; it will &lt;strong&gt;only&lt;/strong&gt; build that page and its dependencies (e.g. CSS if Sass is used) and serve it to you. What if I change the base template of the theme that every page in the site uses? Will I have to wait for every single page to rebuild because I touched a core theme file? Don&#039;t be silly. How does that make sense? Oh wait. Jekyll does that. Yea… stakx only builds pages as you request them. That means I can change a core theme file and the next page load will probably be a few milliseconds slower because it&#039;s no longer cached but that&#039;s hardly noticeable.&lt;/p&gt;
&lt;p&gt;Is it not fair that I&#039;m hating on Jekyll alone? Ok, fine. Come hither, JavaScript based SSGs. Let me hate on you too. Alright, let&#039;s see. Why do I have to wait for the whole Webpack process to complete just for my development server to start? A Vuepress instance at my job takes 8 seconds to start. Seriously, how many dependencies do I need to wait to complete just to get a static site?&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Why should you listen to me and what I wrote in this post? You shouldn&#039;t. After all, I graduated college less than a year ago and according to all the jobs who rejected me, I&#039;m too young, don&#039;t know anything, and don&#039;t have the required amount of years of &amp;quot;full-time experience.&amp;quot; Everything that I&#039;ve learned on my own clearly means nothing, so please ignore everything you&#039;ve read up until now.&lt;/p&gt;
&lt;p&gt;On a less bitter note, here&#039;s something for you to wrap your head around:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the full BZFlag site is built by a single executable that&#039;s less than 2MB and that you can drag and drop anywhere
&lt;ul&gt;
&lt;li&gt;the final BZFlag site consists of over 600 flat HTML files&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;the BZFlag website will load perfectly fine and will still work without JavaScript enabled in the browser (expect for the search)&lt;/li&gt;
&lt;li&gt;there is a full client-side search engine that only relies on a single dependency and is built with vanilla JavaScript and the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag
&lt;ul&gt;
&lt;li&gt;no Vue, React, jQuery, Webpack, etc.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;there is absolutely no use of Node or npm; with modern websites, this is blasphemy!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please. Stop and think whether or not that framework or dependency is &lt;em&gt;really&lt;/em&gt; necessary for your project. The Internet needs less overcomplicated things.&lt;/p&gt;
&lt;p&gt;Now, in all seriousness, despite how simple or straightforward the BZFlag site seems to be built, there was a lot of thought that went into the structure and organization of it. Trust me, a lot of thought went into stakx&#039;s behavior too.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>The Importance of a Community Manager</title>
            <link href="https://allejo.io/blog/the-importance-of-a-community-manager/" rel="alternate" type="text/html" title="The&#x20;Importance&#x20;of&#x20;a&#x20;Community&#x20;Manager" />
            <updated>2019-01-27T08:00:00+00:00</updated>
            <id>b0bd26d7fbf2e938d9fee37bc974c06d95f4c396</id>
            <content type="html" xml:base="https://allejo.io/blog/the-importance-of-a-community-manager/">&lt;p&gt;A month ago, I went on a rant about &lt;a href=&quot;/blog/show-your-os-devs-some-love/&quot;&gt;showing your contributors some love&lt;/a&gt; and while I stand by that sentiment, I&#039;ve learned something new in the past month: the importance of a community manager/advocate. Or just community engagement, in general.&lt;/p&gt;
&lt;p&gt;What&#039;s happened in the past month that has led me to this new rant?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;my &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/6433&quot;&gt;PR to Docker to take down their GitHub hosted site&lt;/a&gt; (the one I submitted in April 2018) was finally merged in 🎉&lt;/li&gt;
&lt;li&gt;I submitted PRs &lt;a href=&quot;https://github.com/Kong/docs.konghq.com/pull/1109&quot;&gt;#1109&lt;/a&gt;, &lt;a href=&quot;https://github.com/Kong/docs.konghq.com/pull/1111&quot;&gt;#1111&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Kong/docs.konghq.com/pull/1112&quot;&gt;#1112&lt;/a&gt; to the Kong Docs fixing issues they had reported and have received silence&lt;/li&gt;
&lt;li&gt;Docker Docs &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/7464&quot;&gt;#7464&lt;/a&gt; was closed with no real information from the team&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So my great experiences with submitting PRs to Docker Docs and Kong Docs were due to team members who were very engaging with the community. In my last post, I mentioned &lt;a href=&quot;https://github.com/coopr&quot;&gt;@coopr&lt;/a&gt; and how enjoyable it was to work him. Since my last PR, it seems like he&#039;s parted ways with Kong and has started on a new adventure. Best of luck, Cooper!&lt;/p&gt;
&lt;p&gt;What&#039;s so important about having someone engaging with the community? It&#039;s someone whose main focus will be engaging with the community, not someone volunteering their own time. Ideally, it will alleviate the burden on your developers so they can focus on writing code instead of balancing that with community engagement. Since Cooper&#039;s departure, it&#039;s felt to me like the existing PRs in that repo have received little to no attention unless the PRs were submitted by core developers themselves.&lt;/p&gt;
&lt;p&gt;Another aspect of engaging with the community is communication and some transparency. I bring up &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/7464&quot;&gt;PR #7464&lt;/a&gt; as an example for lack of transparency and communication. The tl;dr of that PR is that Docker is forcing you to create an account to download Docker, when it&#039;s not actually necessary to do so. Originally Docker had allowed users to download without an account but the docs team changed this with no discussion with the community and then closed the PR reverting this with the following message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This generated a lively discussion for the docs team; thanks for that.&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/7464#issuecomment-452457837&quot;&gt;L-Hudson&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When members of the community seem pretty adamant about an issue, I feel there should be more of a discussion and explanation when the maintainers have reached a conclusion. Don&#039;t just close something and hope people will shut up about it.&lt;/p&gt;
&lt;p&gt;It wasn&#039;t until I recently hopped on a community call organized by a vendor we use at work that it finally hit me, whoever is leading the community engagement is really important. That call was actually enjoyable and I was taking the work of their community manager to granted. The fact that I had enjoyable interactions with my PRs before were because of the people who took responsibility for PRs from start to finish.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Show Your OS Devs Some Love</title>
            <link href="https://allejo.io/blog/show-your-os-devs-some-love/" rel="alternate" type="text/html" title="Show&#x20;Your&#x20;OS&#x20;Devs&#x20;Some&#x20;Love" />
            <updated>2018-12-28T08:00:00+00:00</updated>
            <id>6a701db59c77d144688eafc7e301d598e73f2dc6</id>
            <content type="html" xml:base="https://allejo.io/blog/show-your-os-devs-some-love/">&lt;p&gt;I&#039;ve been lurking around the open source community for about a decade now. I say lurking because I feel like I lurk more than I actually contribute. Though, judging by the little squares on &lt;a href=&quot;https://github.com/allejo&quot;&gt;my GitHub profile&lt;/a&gt;, I am by definition: fairly active. My favorite time of the year is Hacktoberfest because I do my best to find a community I don&#039;t typically hang around and see if I can contribute. Sure, I could always go to those &amp;quot;we&#039;ll accept any PR during Hacktoberfest&amp;quot; repositories, but where&#039;s the fun in that?&lt;/p&gt;
&lt;p&gt;In the past, I really enjoyed making random appearances in the &lt;a href=&quot;https://github.com/docker/docker.github.io&quot;&gt;Docker Docs repository&lt;/a&gt; in unexpected ways.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I wrote an &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/1474&quot;&gt;insane Liquid-only TOC generator for them&lt;/a&gt; that later became &lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;its own project&lt;/a&gt; that&#039;s now become my legacy...&lt;/li&gt;
&lt;li&gt;I won &lt;a href=&quot;https://docs.docker.com/hackathon/#overall-winners&quot;&gt;second place in their 2017 Docker Docs Hackathon&lt;/a&gt; by contributing a &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/2860&quot;&gt;complete rewrite of the site sidebars&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;This PR alone fixed four issues: &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/2002&quot;&gt;#2002&lt;/a&gt;, &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/2232&quot;&gt;#2232&lt;/a&gt;, &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/2403&quot;&gt;#2403&lt;/a&gt;, and &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/2692&quot;&gt;#2692&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Additionally, it also fixed &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/2860#issuecomment-295872145&quot;&gt;not one&lt;/a&gt;,  &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/2860/commits/ef16138cfb471b8659e8e76661508740d1bb36da&quot;&gt;not two&lt;/a&gt;, but &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/2860/commits/b307c2c8f1bd33f0bb0af9cdeb41cd9df3d8dd41&quot;&gt;three&lt;/a&gt; additional findings that didn&#039;t have respective issues yet&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So if you&#039;re browsing the Docker Docs site and enjoy how smoothly the sidebars work with scrolling, how that hamburger actually appears at a reasonable breakpoint, or if you&#039;re maintaining the source and you like how the navigation for the &lt;em&gt;entire&lt;/em&gt; site is now in a single include... You&#039;re welcome.&lt;/p&gt;
&lt;p&gt;I loved the Docker Docs team at the time. I wasn&#039;t in any way affiliated with Docker, but they&#039;d reach out to me from time to time with questions. I loved it. I was in college, so I needed something to keep me busy during my mundane classes and they gave me just that. Heck, they even &lt;a href=&quot;https://www.instagram.com/p/BSClaRZAfwU/&quot;&gt;sent me some free swag&lt;/a&gt;; thanks Misty!&lt;/p&gt;
&lt;p&gt;As with anything in life, things change. My favorite docs team members left to other companies; good for them! I figured I&#039;d continue to make appearances regardless so back in April 2018, I submitted &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/6433&quot;&gt;a PR to take down the GitHub pages hosted version of the site&lt;/a&gt;; something &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/6101&quot;&gt;they&#039;d been asking for help with&lt;/a&gt;. At the time of writing this, the PR remains open and mostly untouched.&lt;/p&gt;
&lt;p&gt;For Hacktoberfest, I looked through the PRs to see if the one I had created in April had finally been merged or closed. Maybe I missed the notification? Nope. Untouched since April. A few days later, it was bumped on the 4th. I thought to myself, &amp;quot;Ok, I guess taking down the GH version of the site might take some &lt;em&gt;long&lt;/em&gt; internal discussion.&amp;quot; I work for the government, I&#039;m used to slowness in decision making. In the spirit of Hacktoberfest, I decided I&#039;d try fixing some site bugs. I submitted &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/7496&quot;&gt;PR #7496&lt;/a&gt;, which would fix the &lt;a href=&quot;https://github.com/docker/docker.github.io/issues/6299&quot;&gt;browser history spam you&#039;d get as you scrolled through &lt;strong&gt;any&lt;/strong&gt; page&lt;/a&gt;. Fast forward 2 months. Untouched. No comments. No labels. No assignments.&lt;/p&gt;
&lt;p&gt;&amp;lt;sarcasm&amp;gt;Man, I&#039;m really feeling the love from the Docker Docs team...&amp;lt;/sarcasm&amp;gt;&lt;/p&gt;
&lt;p&gt;Since I make it a personal goal to contribute to new communities during Hacktoberfest, I found Kong Inc. who was &lt;a href=&quot;https://github.com/Kong/docs.konghq.com/issues/894&quot;&gt;looking for a way to automate creation of Table of Contents on their GitHub pages hosted site&lt;/a&gt;. Doesn&#039;t that sound familiar to another community I once helped out? I submitted &lt;a href=&quot;https://github.com/Kong/docs.konghq.com/pull/920&quot;&gt;PR #920&lt;/a&gt; and I had such an enjoyable experience in doing so! Working with &lt;a href=&quot;https://github.com/coopr&quot;&gt;@coopr&lt;/a&gt; was so productive! I had questions on how they wanted something implemented, they had answers and feedback quickly! It took about 13 days to iron out this PR and get things &amp;quot;just right&amp;quot; but it was well worth it. Not only did Kong get a new feature on their documentation site, it was discovered that there were many improvements and updates needed throughout. These were tackled by other members of the community in the coming days. Now that&#039;s what I call collaboration.&lt;/p&gt;
&lt;p&gt;A few days later after my PR was merged, I get pinged on Twitter by the Kong account thanking me for adding the auto-TOC feature to their site. Then much much later, I get pinged once again telling me about how &lt;a href=&quot;https://konghq.com/blog/hacktoberfest-kong-community-delivers/&quot;&gt;they mentioned me in their blog post&lt;/a&gt;. Kong gave me more attention than my parents ever did; it&#039;s such a nice feeling to be loved.&lt;/p&gt;
&lt;div class=&quot;row&quot;&gt;
  &lt;div class=&quot;col-md-6&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Huge thank you to &lt;a href=&quot;https://twitter.com/allejome?ref_src=twsrc%5Etfw&quot;&gt;@allejome&lt;/a&gt;, who added automatic table-of-contents generation to our documentation as part of &lt;a href=&quot;https://twitter.com/hashtag/Hacktoberfest?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Hacktoberfest&lt;/a&gt;! Contributors like you are the stars of &lt;a href=&quot;https://twitter.com/hashtag/OpenSource?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#OpenSource&lt;/a&gt;! Wondering what to work on to earn a contributor t-shirt?👕🎃🦍&lt;a href=&quot;https://t.co/Rt1JBSl7kP&quot;&gt;https://t.co/Rt1JBSl7kP&lt;/a&gt;&lt;/p&gt;— Kong (@thekonginc) &lt;a href=&quot;https://twitter.com/thekonginc/status/1053738625807081473?ref_src=twsrc%5Etfw&quot;&gt;October 20, 2018&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;

  &lt;div class=&quot;col-md-6&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Really feeling the love, &lt;a href=&quot;https://twitter.com/thekonginc?ref_src=twsrc%5Etfw&quot;&gt;@thekonginc&lt;/a&gt; 🥰 thanks for the mention in this! &lt;a href=&quot;https://t.co/hoVNFuTZI9&quot;&gt;https://t.co/hoVNFuTZI9&lt;/a&gt;&lt;/p&gt;— Vladimir Jimenez 🇨🇦🧐 (@allejome) &lt;a href=&quot;https://twitter.com/allejome/status/1070503389543321600?ref_src=twsrc%5Etfw&quot;&gt;December 6, 2018&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So what&#039;s the moral of this story?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thank your open source contributors! A simple &amp;quot;thank you&amp;quot; when your merge their PR goes a long way. Tag them in release notes. Give them credit!&lt;/li&gt;
&lt;li&gt;Be responsive and kind to PRs. Remember, people are taking time out of their day to contribute to your project. So be responsive and give them feedback. A PR will likely not be mergeable on the first try, so give them feedback on what to do to make it mergeable. There&#039;s nothing more satisfying to me than getting feedback from a team of a project I care enough about to contribute to.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#039;s really that simple. In this story, Kong Inc. went above and beyond with showing me so much love in everything they mentioned me in and I appreciate all of that! I&#039;m really feeling the love from Kong. There&#039;s not a doubt in my mind that I&#039;d return to the Kong community far quicker and more willingly than I would the Docker one.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Why I Built stakx</title>
            <link href="https://allejo.io/blog/why-i-built-stakx/" rel="alternate" type="text/html" title="Why&#x20;I&#x20;Built&#x20;stakx" />
            <updated>2018-09-01T08:00:00+00:00</updated>
            <id>0c81132293fcc5ec144205a02942931d6050dc60</id>
            <content type="html" xml:base="https://allejo.io/blog/why-i-built-stakx/">&lt;p&gt;I&#039;ve been wanting to write something on my blog because I haven&#039;t written anything since May and for me, that&#039;s a long time. So I decided I&#039;d write about why I decided to create stakx.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Arrogance: I was/am arrogant enough to believe that I could do a better job with features and design choices for how a site should be built&lt;/li&gt;
&lt;li&gt;Challenge: I wanted to see how difficult it would be to create my own&lt;/li&gt;
&lt;li&gt;Learning: I simply wanted to learn&lt;/li&gt;
&lt;li&gt;Burger King: I wanted to have things my way&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;static-site-generator-ssg-comparisions&quot;&gt;Static Site Generator (SSG) Comparisions&lt;/h2&gt;
&lt;p&gt;Any other sane developer would ask, &amp;quot;why don&#039;t you just use an existing SSG?&amp;quot; My answer to that is: I have used other SSGs and I found that they all fell short for my needs and wants.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jekyll
&lt;ul&gt;
&lt;li&gt;The Liquid template engine leaves &lt;em&gt;a lot&lt;/em&gt; to be desired when compared to an engine like Twig&lt;/li&gt;
&lt;li&gt;You &lt;em&gt;shouldn&#039;t&lt;/em&gt; be using Jekyll globally and instead need to install Bundler&lt;/li&gt;
&lt;li&gt;You&#039;re not supposed to modify your system installation of Ruby so you can&#039;t install Bundler without first configuring and using rbenv&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Literally any JavaScript based SSG
&lt;ul&gt;
&lt;li&gt;Please. No. Make it stop! As diverse as the JavaScript community may be, boy is it a dependency hell. Setting up a very simple website requires one or more of the following:
&lt;ul&gt;
&lt;li&gt;Install a binary &amp;quot;globally&amp;quot; with its own &lt;code&gt;node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install it via &lt;code&gt;package.json&lt;/code&gt; and with it, an unreasoably large &lt;code&gt;node_modules&lt;/code&gt; folder per site&lt;/li&gt;
&lt;li&gt;Some type of bundler/builder/task runner (Webpack, Grunt, Gulp, and friends)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Sculpin
&lt;ul&gt;
&lt;li&gt;A step in the right direction, uses a powerful template engine&lt;/li&gt;
&lt;li&gt;Practically a PHP based version of Jekyll but with Composer needed instead of Bundler&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Hugo
&lt;ul&gt;
&lt;li&gt;Hugo got things right. Single binary that you can drop anywhere and run it.&lt;/li&gt;
&lt;li&gt;Their template engine? Could be improved.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;looking-back&quot;&gt;Looking Back&lt;/h2&gt;
&lt;p&gt;There are a number of things I would design differently, a lot of which I&#039;ve been able to rectify in stakx 0.2.0 development. There are a number of things that I&#039;d have a hard time changing now, so I&#039;m stuck with them. But looking back, would I still build my own SSG if I was given the chance?&lt;/p&gt;
&lt;p&gt;Definitely.&lt;/p&gt;
&lt;p&gt;stakx has been one of my largest open source projects that I&#039;ve worked on my own that hasn&#039;t been related to an existing community. It&#039;s been a fun experience thus far creating something like this mainly for myself.&lt;/p&gt;
&lt;h2 id=&quot;is-stakx-for-the-masses&quot;&gt;Is stakx for the masses?&lt;/h2&gt;
&lt;p&gt;Despite my most ambitious and obscure projects that all scream &amp;quot;what the hell are you doing?!&amp;quot; getting a lot of unexpected attention...&lt;/p&gt;
&lt;p&gt;No. Not at all. stakx isn&#039;t meant for the masses.&lt;/p&gt;
&lt;p&gt;While I&#039;m designing stakx with backwards compatability in mind during breaking releases, I no longer have any dreams of stakx becoming a new hyped up SSG competitor. I&#039;d honestly be more disappointed that my project got taken away from me by a community. It&#039;s fun making decisions without thinking of how it&#039;d affect a lot of other people.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Jekyll Heading Anchors without JavaScript</title>
            <link href="https://allejo.io/blog/jekyll-heading-anchors-without-javascript/" rel="alternate" type="text/html" title="Jekyll&#x20;Heading&#x20;Anchors&#x20;without&#x20;JavaScript" />
            <updated>2018-05-29T08:00:00+00:00</updated>
            <id>00cf5b28c2e702f2de9bdd3946cb316f77e33735</id>
            <content type="html" xml:base="https://allejo.io/blog/jekyll-heading-anchors-without-javascript/">&lt;p&gt;Despite all my efforts for developing the next major version of stakx, I come bearing gifts for the Jekyll community. When building websites with long pages, it has become the standard to include a table of contents that link to headings. Up until &lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;jekyll-toc&lt;/a&gt;, the only way to achieve that was through a Jekyll plugin or JavaScript. Now, the current solution for adding clickable links next to headings is to use JavaScript. Let&#039;s change that.&lt;/p&gt;
&lt;p&gt;There are too many &lt;em&gt;static&lt;/em&gt; websites out there that load a lot of JavaScript to the point where it slows down. Why? Seriously. Stahp that. I mean, I&#039;m even planning on moving away from Disqus solely because it loads too much. Anyhow, websites like these cause me to navigate it with JavaScript disabled but that often leads to me not being able to click on a permalink for a heading. Taking the same behavior and concept from &lt;strong&gt;jekyll-toc&lt;/strong&gt;, I&#039;ve built &lt;a href=&quot;https://github.com/allejo/jekyll-anchor-headings&quot;&gt;jekyll-anchor-headings&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If things can be achieved without resorting to JavaScript, why not do it?&lt;/p&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;The code is available on GitHub: &lt;a href=&quot;https://github.com/allejo/jekyll-anchor-headings&quot;&gt;allejo/jekyll-anchor-headings&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;
&lt;p&gt;Usage is simple, instead of using &lt;code&gt;{{ content }}&lt;/code&gt; to display your page&#039;s content (your rendered markdown, i.e.) you would feed &lt;code&gt;{{ content }}&lt;/code&gt; into the &lt;a href=&quot;https://github.com/allejo/jekyll-anchor-headings/blob/master/_includes/anchor_headings.html&quot;&gt;&lt;code&gt;anchor_headings.html&lt;/code&gt;&lt;/a&gt; include.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-twig&quot;&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;include&lt;/span&gt;&lt;/span&gt; anchor_headings.html html=content %}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snippet is available in the GitHub repo and has unit tests as well. We have to be sure that things continue working as expected with variations of HTML.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it Works&lt;/h2&gt;
&lt;p&gt;Since &lt;strong&gt;jekyll-toc&lt;/strong&gt; has worked out pretty robustly, I&#039;ve taken a lot of &lt;a href=&quot;/blog/a-jekyll-toc-without-plugins-or-javascript/#how-it-works&quot;&gt;the same concepts&lt;/a&gt; and code from that project and adapted it to work with this new project.&lt;/p&gt;
&lt;h3 id=&quot;step-0&quot;&gt;Step 0&lt;/h3&gt;
&lt;p&gt;Taking steps 1 through 6 from the &lt;a href=&quot;/blog/a-jekyll-toc-without-plugins-or-javascript/#how-it-works&quot;&gt;previous post&lt;/a&gt;, we currently have access to the &lt;code&gt;id&lt;/code&gt; attribute and the contents of the actual headings (e.g. &lt;code&gt;h1&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;First, we build an anchor tag manually as a string by setting the &lt;code&gt;href&lt;/code&gt; attribute along with &lt;code&gt;class&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt;. So at this point, we&#039;ll have the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;href=&quot;#some-id&quot; class=&quot;my-class permalink&quot; title=&quot;click me!&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Now, we&#039;ll take that string and simple prefix &lt;code&gt;&amp;lt;a&lt;/code&gt; and append an &lt;code&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt; with possibly some content in between the anchors.&lt;/p&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;We do some stupid string replacements to change the current heading tags (&lt;code&gt;h1&lt;/code&gt; - &lt;code&gt;h6&lt;/code&gt;) with some that we build on our own with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The custom anchor we built in steps 1 and 2&lt;/li&gt;
&lt;li&gt;The heading content we have from step 0&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/allejo/jekyll-anchor-headings&quot;&gt;project on GitHub&lt;/a&gt; and give it a shot on your own website! There&#039;s no need for you to understand the craziness of manipulating HTML as strings, just &lt;code&gt;{% include %}&lt;/code&gt; in your layouts and you&#039;re set!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>One Year of jekyll-toc</title>
            <link href="https://allejo.io/blog/one-year-of-jekyll-toc/" rel="alternate" type="text/html" title="One&#x20;Year&#x20;of&#x20;jekyll-toc" />
            <updated>2018-04-21T08:00:00+00:00</updated>
            <id>e49a4dd7c30e1fcbc0592acfe46441c30e83677e</id>
            <content type="html" xml:base="https://allejo.io/blog/one-year-of-jekyll-toc/">&lt;p&gt;Over a year ago, I &lt;a href=&quot;/blog/a-jekyll-toc-without-plugins-or-javascript/&quot;&gt;wrote a table of contents generator for Jekyll websites, on a whim, using nothing but Liquid&lt;/a&gt;. There was a need for being able to generate table of contents for pages from a layout without relying on a plugin or JavaScript and without having to include &lt;code&gt;{:toc}&lt;/code&gt; in every single markdown file. I never had a need for this feature, it was one of Docker&#039;s employees who was asking if a solution existed. I was intrigued by the problem and so I wrote my solution on a whim as a proof of concept; I really just wanted to know if I could achieve the same thing by using &lt;em&gt;just&lt;/em&gt; Liquid.&lt;/p&gt;
&lt;p&gt;It took me &lt;a href=&quot;https://gist.github.com/allejo/a83bcef99a9e0a6f481fce01e492efff/revisions&quot;&gt;a few tries&lt;/a&gt; to actually figure out a robust solution to the problem but I succeeded; it got Docker&#039;s docs team&#039;s seal of approval. 🎉 I felt proud that I had done a thing! So I wrote up a blog post to feel important and that was the end of it. Or so I thought. A few months later, a developer from the UK Ministry of Justice showed up on IRC asking about this same problem. I linked him to my blog post and that&#039;s how &lt;a href=&quot;https://github.com/ministryofjustice/technical-guidance/blob/f8378bdce3682c1b68a726ab32c88124e27a42a0/LICENCE#L31&quot;&gt;I got my name in the UK Ministry of Justice&#039;s License file&lt;/a&gt;. Pretty damn awesome, am I right?&lt;/p&gt;
&lt;p&gt;I got curious and looked into my site&#039;s analytics and did some Googling to see how common this problem was. Turns out, there were enough people having this problem to motivate me to move the snippet I wrote into &lt;a href=&quot;https://github.com/allejo/jekyll-toc/commit/ed3838d29bac9dc64bdb5cf45eac092ace228fde&quot;&gt;its own GitHub repository&lt;/a&gt;. Heck, I even &lt;a href=&quot;https://github.com/allejo/jekyll-toc/commit/2a53d7c8c03792c2b24969c5520afc58e4a8bd0c&quot;&gt;added unit tests&lt;/a&gt; to the repository and &lt;a href=&quot;https://travis-ci.org/allejo/jekyll-toc&quot;&gt;connected Travis CI&lt;/a&gt; with notifications to ensure that if I made changes to the Liquid snippet, that it&#039;d still work.&lt;/p&gt;
&lt;p&gt;I wanted to make sure that my effort of moving this into a repository wasn&#039;t wasted, so I did some searching on GitHub to see if I could shamelessly advertise my project; I found &lt;a href=&quot;https://github.com/mmistakes/minimal-mistakes/issues/1222&quot;&gt;issue #1222 on the minimal-mistakes repository&lt;/a&gt;. Minimal Mistakes is one of the more popular Jekyll themes out there and I was able to get my project incorporated into that theme. How cool is that?! Thanks &lt;a href=&quot;https://mademistakes.com/&quot;&gt;Michael Rose (@mmistakes)&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I was satisfied with this venture. It sated my desire for spreading my project. A few months passed and I had started getting a steady amount of traffic to my GitHub repository from my blog post and Google. I even got some feature requests and bug reports! Heck, even &lt;a href=&quot;https://github.com/twbs/bootstrap/pull/25838&quot;&gt;Bootstrap want(ed/s) to use it&lt;/a&gt;! (&lt;em&gt;There have been a number of separate attempts for using this snippet on the official Bootstrap website but none of them have been merged, yet.&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Fast forward to now. I get @mentioned in issues/PRs every so often giving me credit for jekyll-toc and that&#039;s freaking awesome! Don&#039;t stop! I enjoy seeing where you&#039;re all using my project. Besides getting mentioned, I search GitHub on occasion to see how far my snippet has gone and I&#039;m never disappointed. Just last month, I discovered that Apache is using it in some of their Jekyll sites. And just a few days ago, I discovered that &lt;a href=&quot;https://github.com/JetBrains/oss-site-jekyll-theme/commit/ff779cfa2ebc2c34f0d1e194a1d6a27a748f0c96&quot;&gt;JetBrains is using it in their new Jekyll theme&lt;/a&gt;! JetBrains has always had a special place in my heart because of their awesome IDEs and now, they&#039;re using something of mine! If only you could see my inner Jonah Hill fangirl squeeing right now.&lt;/p&gt;
&lt;p&gt;Besides my code seeing the light of day in the United Kingdom, it has even &lt;a href=&quot;https://blog.kotet.jp/2018/04/toc-on-github-pages/&quot;&gt;made it to Japan as well&lt;/a&gt;! This is honestly a lot of fun for me and I&#039;m really proud; the repository doesn&#039;t have hundreds of stars, nor does it need to have that many. I&#039;m flattered by the reach this project has gotten. Thank you to everyone who has used or shared my little jekyll-toc project! Don&#039;t stop @mentioning me in your issues, PRs, or commit messages! Like I said, I love to see where jekyll-toc goes next!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 9</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-9/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;9" />
            <updated>2018-03-02T08:00:00+00:00</updated>
            <id>45519ede6c06cd6e5c21fbb17bde10e86d5a4ad8</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-9/">&lt;p&gt;Happy new year! ...in March. It&#039;s a new year and I come bearing gifts as your resident BZFlag Santa Claus from beautiful Southern California: the place with a Starbucks in every corner and misspelled names on coffee cups in every trashcan. I&#039;m here to bring you a surprise new chapter for &lt;em&gt;BZFlag Plug-ins for Dummies&lt;/em&gt; covering world weapons, custom flags, and the latest improvements to the world weapon API.&lt;/p&gt;
&lt;p&gt;What are world weapons? They are shots that are fired by the server instead of by tanks. Technically, the world and server are separate so they should be called &amp;quot;server shots&amp;quot; because it&#039;s actually the server shooting them but... They were dubbed world weapons so the name stuck. If you&#039;ve played on Urban Jungle, Clay Hills, or Planet MoFo&#039;s Apocalypse, you&#039;ve seen world weapons. They are those shock waves that happen when you drive over a mine and they are all those extra shots that exist on MoFo with flags like &amp;quot;Death Barrel&amp;quot; or &amp;quot;Triple Barrel.&amp;quot;&lt;/p&gt;
&lt;p&gt;The world weapon API has always been wonky, counterintuitive to use, not easy to work with, and synonyms. Whenever a tank or the server fires a shot, it is assigned two IDs: a local ID and a global ID. Since this series focuses on plug-in development and not BZFlag&#039;s protocol, we only care about a shot&#039;s GUID. Up until recent changes, the only way to know a shot&#039;s GUID was to assign it yourself, which doesn&#039;t sound too hard. It&#039;s not. The problem arises when you have multiple plug-ins shooting world weapons and they&#039;re each setting their own GUIDs; there are bound to be conflicts. Planet MoFo has run into this issue several times in the past and we&#039;ve had to be meticulous about picking these shot GUIDs. The new functions that were introduced in the API for BZFlag 2.4.14 solves this issue by reworking how world weapons are handled and making use of GUIDs exclusively. The &lt;strong&gt;only&lt;/strong&gt; API function you should be using to fire world weapons is &lt;code&gt;bz_fireServerShot()&lt;/code&gt;; the other &lt;code&gt;bz_fireWorldWep()&lt;/code&gt; and &lt;code&gt;bz_fireWorldGM()&lt;/code&gt; functions are now deprecated and will be removed in the next major release.&lt;/p&gt;
&lt;p&gt;I think that&#039;s enough of an explanation, so let&#039;s build a plug-in instead!&lt;/p&gt;
&lt;h2 id=&quot;plug-in-specification&quot;&gt;Plug-in Specification&lt;/h2&gt;
&lt;p&gt;In this chapter, we&#039;ll be building a plug-in that introduces a custom flag. When a player carrying this flag shoots another tank, the victim will die and a shockwave will explode at their location. If that shockwave hits any other enemy tanks, then those kills be credited to the original flag carrier. Let&#039;s call this flag, &amp;quot;Cascade.&amp;quot;&lt;/p&gt;
&lt;h2 id=&quot;registering-custom-flags&quot;&gt;Registering Custom Flags&lt;/h2&gt;
&lt;p&gt;Planet MoFo is known for its custom flags, so let&#039;s first explain how we create those custom flags. The BZFS API has a &lt;code&gt;bz_RegisterCustomFlag()&lt;/code&gt; function, which is used for defining our flag&#039;s definition. Once the flag is defined and the plug-in is loaded, map makers and server owners can simply use &lt;code&gt;+f&lt;/code&gt; to add those flags to a map.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_RegisterCustomFlag(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* abbr, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* name, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* helpString, bz_eShotType shotType, bz_eFlagQuality quality)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#039;s create our own Cascade flag and of course, this will go in our plug-in&#039;s &lt;code&gt;Init()&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void CascadeFlag::Init (const char* config)
{
    bz_RegisterCustomFlag(&quot;CA&quot;, &quot;Cascade&quot;, &quot;All of your victims will trigger a shockwave at their death location&quot;, 0, eGoodFlag);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#039;ve registered our custom flag, let&#039;s figure out what this actually means.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;abbr&lt;/code&gt; is the abbreviation of the flag name; i.e. one or two letter abbreviation of the flag name. Try to be unique and don&#039;t cause conflicts with existing flags&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; is the full name of the flag&lt;/li&gt;
&lt;li&gt;&lt;code&gt;helpString&lt;/code&gt; is the message that appears under your mouse box in your HUD, which explains what the flag does&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shotType&lt;/code&gt; is an enum but it was never implemented in the API, so just use &lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;quality&lt;/code&gt; is whether the flag is an &lt;code&gt;eGoodFlag&lt;/code&gt; or an &lt;code&gt;eBadFlag&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, our brand new Cascade flag can be added to maps! However, it&#039;s as useful as the Useless flag. We will now need to implement the behavior we want. Going back to our specification, we want something to happen if someone shoots a tank with the flag and then shoot a shockwave at their location of death. If you guessed that we&#039;re going to listen to the player death event, you&#039;re right!&lt;/p&gt;
&lt;h2 id=&quot;implementing-custom-flags-behavior&quot;&gt;Implementing Custom Flags&#039; Behavior&lt;/h2&gt;
&lt;p&gt;It&#039;s important that you implement custom flag behavior however &lt;strong&gt;you&lt;/strong&gt; want, this example requires that we perform an action on a player&#039;s death. The first step is to register the player death event to notify BZFS that we want to listen to this event.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_ePlayerDieEvent);

    bz_RegisterCustomFlag(&lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Cascade&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;All of your victims will trigger a shockwave at their death location&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, eGoodFlag);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to implement what&#039;s going to happen when a player actually dies. Our goal is shoot a shockwave &lt;strong&gt;only&lt;/strong&gt; when the Cascade flag is used, meaning we have to be explicit about it in our implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerDieEvent:
        {
            bz_PlayerDieEventData_V1 *data = (bz_PlayerDieEventData_V1*)eventData;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (data-&amp;gt;flagKilledWith == &lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-comment&quot;&gt;// Step 1&lt;/span&gt;
            {
                &lt;span class=&quot;hljs-comment&quot;&gt;// Step 2&lt;/span&gt;
                &lt;span class=&quot;hljs-keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;[&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;] = {&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;};
                bz_fireServerShot(&lt;span class=&quot;hljs-string&quot;&gt;&quot;SW&quot;&lt;/span&gt;, data-&amp;gt;state.pos, &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;, data-&amp;gt;killerTeam);
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;Make sure we only fire a world weapon shockwave when a player was actually killed with the Cascade flag. &lt;code&gt;data-&amp;gt;flagKilledWith&lt;/code&gt; will hold the abbreviation of the flag used to kill the player, so we compare it against our own abbreviation: CA.&lt;/p&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;We have to create a vector for the direction our shot needs to take. However, because we&#039;re using a shockwave, there&#039;s no need to calculate the vector so we can set it to &lt;code&gt;0, 0, 0&lt;/code&gt;. However, this vector is calculated with the &lt;a href=&quot;http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm&quot;&gt;lookAt function&lt;/a&gt;. There are far more qualified people on Google to cover the maths behind this subject, so I&#039;ll let them do that. Thanks to JeffM for helping with all the maths used internally in BZFS for calculating world weapon directions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;bz_fireServerShot&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* shotType, &lt;span class=&quot;hljs-keyword&quot;&gt;float&lt;/span&gt; origin[&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;], &lt;span class=&quot;hljs-keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;[&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;], bz_eTeamType color = eRogueTeam, &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; targetPlayerId = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;destroyed-by-server&quot;&gt;Destroyed by Server&lt;/h2&gt;
&lt;p&gt;Awesome! We&#039;re done, right? Build the plug-in and try it out. Let&#039;s look at the specification once again, did we get everything? The cascading affect of the flag doesn&#039;t quite work correctly... It shows that the those tanks in the shockwave radius were &amp;quot;destroyed by server.&amp;quot; Why?&lt;/p&gt;
&lt;p&gt;As I mentioned earlier, these shots are in fact fired by the server, so the death message actually makes sense. Can we make the owner of the &lt;em&gt;server&lt;/em&gt; shot a player instead? It&#039;s called a server shot for a reason, so no, it&#039;s not possible. If only there were a way to reassign the killer in the death event...&lt;/p&gt;
&lt;h2 id=&quot;world-weapon-management&quot;&gt;World Weapon Management&lt;/h2&gt;
&lt;p&gt;Server shots will always be owned by the server and should remain as such. However, manipulating death data is something supported by the API so let&#039;s take advantage of that fact.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bz_fireServerShot()&lt;/code&gt; function will return the GUID of the shot as a &lt;code&gt;uint32_t&lt;/code&gt; so we should definitely store it because we&#039;ll be using it. Shots in BZFS are just objects stored in a fancy array and these objects support storing arbitrary information for each individual shot. Let&#039;s take advantage of this as well!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerDieEvent:
        {
            bz_PlayerDieEventData_V1 *data = (bz_PlayerDieEventData_V1*)eventData;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (data-&amp;gt;flagKilledWith == &lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;)
            {
                &lt;span class=&quot;hljs-keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;[&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;] = {&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;};
                &lt;span class=&quot;hljs-keyword&quot;&gt;uint32_t&lt;/span&gt; shotGUID = bz_fireServerShot(&lt;span class=&quot;hljs-string&quot;&gt;&quot;SW&quot;&lt;/span&gt;, data-&amp;gt;state.pos, &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;, data-&amp;gt;killerTeam);

                &lt;span class=&quot;hljs-comment&quot;&gt;// Step 3&lt;/span&gt;
                bz_setShotMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;, data-&amp;gt;flagKilledWith.c_str());
                bz_setShotMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;, data-&amp;gt;killerID);

                &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
            }

            &lt;span class=&quot;hljs-comment&quot;&gt;// Step 4&lt;/span&gt;
            &lt;span class=&quot;hljs-keyword&quot;&gt;uint32_t&lt;/span&gt; shotGUID = bz_getShotGUID(data-&amp;gt;killerID, data-&amp;gt;shotID);

            &lt;span class=&quot;hljs-comment&quot;&gt;// Step 5&lt;/span&gt;
            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (bz_shotHasMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;) &amp;amp;&amp;amp; bz_shotHasMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;))
            {
                &lt;span class=&quot;hljs-comment&quot;&gt;// Step 6&lt;/span&gt;
                &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; flagType = bz_getShotMetaDataS(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// &#039;s&#039; for string&lt;/span&gt;

                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flagType == &lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;)
                {
                    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 7&lt;/span&gt;
                    data-&amp;gt;killerID = bz_getShotMetaDataI(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;); &lt;span class=&quot;hljs-comment&quot;&gt;// &#039;i&#039; for integer&lt;/span&gt;
                }
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Let&#039;s make use of the fact that shots can store arbitrary information. Let&#039;s store two pieces of information: the &amp;quot;owner&amp;quot; of the shot (the player carrying the Cascade flag) and the &amp;quot;type&amp;quot; of flag. By storing these two pieces of information, we&#039;ll be able to know who to credit for the kill and avoid any conflicts with multiple world weapon shots from different flag types.&lt;/p&gt;
&lt;h3 id=&quot;step-4&quot;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;Remember how I mentioned local IDs and global IDs at the beginning? Well, we have to use the local IDs to get the GUID. Let&#039;s just leave it at that because the GUID is all we really care about.&lt;/p&gt;
&lt;h3 id=&quot;step-5&quot;&gt;Step 5&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Every&lt;/em&gt; shot on a server has a GUID and is a shot object in a fancy array; this means that we need to make sure the shot fired is a world weapon that we triggered and control. We confirm that the &amp;quot;type&amp;quot; and &amp;quot;owner&amp;quot; metadata exists for the shot. By doing so, we know that this shot has the structure that this plug-in uses and we&#039;re not interfering other shots.&lt;/p&gt;
&lt;h3 id=&quot;step-6&quot;&gt;Step 6&lt;/h3&gt;
&lt;p&gt;We now confirm that the shot is actually a Cascade shot by getting the &amp;quot;type&amp;quot; metadata. If another plug-in uses the &amp;quot;name&amp;quot; and &amp;quot;type&amp;quot; combination or this plug-in has multiple special flags, we&#039;ll know which shots we should actually work with.&lt;/p&gt;
&lt;h3 id=&quot;step-7&quot;&gt;Step 7&lt;/h3&gt;
&lt;p&gt;We reassign &lt;code&gt;data-&amp;gt;killerID&lt;/code&gt; to the &amp;quot;owner&amp;quot; of the world weapon so that they may take credit for the kill instead of the server.&lt;/p&gt;
&lt;h2 id=&quot;final-plug-in&quot;&gt;Final Plug-in&lt;/h2&gt;
&lt;p&gt;That&#039;s it! The plug-in is now ready for action on your new map!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;
};

BZ_PLUGIN(CascadeFlag)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;CascadeFlag&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_ePlayerDieEvent);

    bz_RegisterCustomFlag(&lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Cascade&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;All of your victims will trigger a shockwave at their death location&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, eGoodFlag);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;CascadeFlag::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerDieEvent:
        {
            bz_PlayerDieEventData_V1 *data = (bz_PlayerDieEventData_V1*)eventData;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (data-&amp;gt;flagKilledWith == &lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;)
            {
                &lt;span class=&quot;hljs-keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;[&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;] = {&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;};
                &lt;span class=&quot;hljs-keyword&quot;&gt;uint32_t&lt;/span&gt; shotGUID = bz_fireServerShot(&lt;span class=&quot;hljs-string&quot;&gt;&quot;SW&quot;&lt;/span&gt;, data-&amp;gt;state.pos, &lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;, data-&amp;gt;killerTeam);

                bz_setShotMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;, data-&amp;gt;flagKilledWith.c_str());
                bz_setShotMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;, data-&amp;gt;killerID);

                &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
            }

            &lt;span class=&quot;hljs-keyword&quot;&gt;uint32_t&lt;/span&gt; shotGUID = bz_getShotGUID(data-&amp;gt;killerID, data-&amp;gt;shotID);

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (bz_shotHasMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;) &amp;amp;&amp;amp; bz_shotHasMetaData(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;))
            {
                &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; flagType = bz_getShotMetaDataS(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;);

                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flagType == &lt;span class=&quot;hljs-string&quot;&gt;&quot;CA&quot;&lt;/span&gt;)
                {
                    data-&amp;gt;killerID = bz_getShotMetaDataI(shotGUID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;owner&quot;&lt;/span&gt;);
                }
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Awesome job! You&#039;ve just completed your first plug-in that makes use of world weapons, custom flags, AND the new API that was just implemented this last weekend. World weapons aren&#039;t always easy to work with and can get pretty complicated so I&#039;d highly recommend that you play around with this on your own.&lt;/p&gt;
&lt;p&gt;This new chapter was solely inspired by the &lt;a href=&quot;https://github.com/BZFlag-Dev/bzflag/pull/99&quot;&gt;latest API changes that I&#039;ve been working on with JeffM&lt;/a&gt; meaning this series is still on a hiatus. Once I am able to better explain the maths behind these calculations, I&#039;ll write another chapter dedicated to just that but until then, thanks for reading!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Recap of 2017</title>
            <link href="https://allejo.io/blog/recap-of-2017/" rel="alternate" type="text/html" title="Recap&#x20;of&#x20;2017" />
            <updated>2017-12-13T08:00:00+00:00</updated>
            <id>158463649888f3facc35ab999d2c84a5255c504a</id>
            <content type="html" xml:base="https://allejo.io/blog/recap-of-2017/">&lt;p&gt;It&#039;s been a busy semester, I just completed my second to last semester of my college career. The end of 2017 is approaching and what I have accomplished in the past year? Looking back, I don&#039;t really feel like I accomplished anything significant but here are some highlights.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/bzion&quot;&gt;BZiON&lt;/a&gt; reached version 0.10.0&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/stakx-io/stakx&quot;&gt;stakx&lt;/a&gt; reached version 0.1.2 and development for the next major release has started&lt;/li&gt;
&lt;li&gt;Became a contributor to &lt;a href=&quot;https://github.com/scrivo/highlight.php&quot;&gt;highlight.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Came second in the Docker Docs Hackathon by improving their navigation significantly&lt;/li&gt;
&lt;li&gt;My &lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;jekyll-toc&lt;/a&gt; project has gotten some recognition
&lt;ul&gt;
&lt;li&gt;The UK Ministry of Justice is using it&lt;/li&gt;
&lt;li&gt;It&#039;s included in the &lt;a href=&quot;https://github.com/mmistakes/minimal-mistakes&quot;&gt;Minimal Mistakes Jekyll theme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It was &lt;a href=&quot;https://github.com/twbs/bootstrap/pull/24466&quot;&gt;&lt;em&gt;almost&lt;/em&gt; included in the Bootstrap website&lt;/a&gt;, however &lt;a href=&quot;https://github.com/allejo/jekyll-toc/issues/3&quot;&gt;certain limitations&lt;/a&gt; prevented that from happening&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Wrote 8 chapters about writing plug-ins for BZFlag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a number of projects that I&#039;m currently working on that I haven&#039;t open sourced yet because I&#039;m not content with the current state they&#039;re in; these are new projects that I&#039;m still experimenting with. I do all of my open source work so I can learn and practice; I also do it because I genuinely enjoy it. Because I do it for fun, I don&#039;t expect or want anything in return but I don&#039;t want to be demoralized either. Without going off on a tangent or whining, here&#039;s a friendly reminder to show some appreciation to developers however you deem fit. It can be as simple as star&#039;ing the repository on GitHub or a shout-out on Twitter or whatever platform. This has been the first year where I&#039;ve gotten random mentions and recognition for some of my projects outside my typical circles and it&#039;s awesome! It&#039;s very encouraging and motivating to get that type of feedback.&lt;/p&gt;
&lt;p&gt;What do I have planned for 2018? Well... The whole concept of doing something new in the new year seems rather ridiculous to me; I have goals and plans year around that rollover from year to year. I have at least 4 different projects that will overlap during my winter break from school. Is it crazy or unhealthy? Probably.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Breaking Columnar Transposition and Caesar Cipher</title>
            <link href="https://allejo.io/blog/breaking-columnar-transposition-and-caesar-cipher/" rel="alternate" type="text/html" title="Breaking&#x20;Columnar&#x20;Transposition&#x20;and&#x20;Caesar&#x20;Cipher" />
            <updated>2017-10-13T08:00:00+00:00</updated>
            <id>ff72df666e6f1aedfc203e6bb7f77390b3ca400b</id>
            <content type="html" xml:base="https://allejo.io/blog/breaking-columnar-transposition-and-caesar-cipher/">&lt;p&gt;It only took until I reached my final year in college to finally find a class I found challenging and actually learned from (excluding GIS classes). I&#039;ve always been fascinated by cryptography and have always wanted to learn but never found the time to do so on my own. Until I took this class, I had no knowledge of how crytography &lt;em&gt;actually&lt;/em&gt; worked. I found this assignment to be incredibly fun because it was challenging for me.&lt;/p&gt;
&lt;h2 id=&quot;heads-up&quot;&gt;Heads Up&lt;/h2&gt;
&lt;p&gt;When I was first given this assignment, I searched the internet to see if anyone had already solved this. That&#039;s how code works, right? (I&#039;m looking at you, &lt;a href=&quot;http://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm&quot;&gt;left-pad&lt;/a&gt;). Unfortunately, I wasn&#039;t able to find anything. I say &amp;quot;unfortunately&amp;quot; but looking back at this, I&#039;m really glad I didn&#039;t find anything or else I wouldn&#039;t have been able to challenge myself to figure this out.&lt;/p&gt;
&lt;p&gt;There were several in my class that couldn&#039;t solve this assignment so I&#039;ll be posting this for future readers. However, I highly recommend that if you find cryptography interesting, to skim through this post at most. It&#039;s more fun to do it on your own, trust me.&lt;/p&gt;
&lt;h2 id=&quot;let-s-get-started&quot;&gt;Let&#039;s Get Started&lt;/h2&gt;
&lt;p&gt;I was given a cipher and was tasked with finding the keyword used to encrypt the plain text, and in turn, find the plain text. The plain text was encrypted using columnar transposition and a simple shift substitution.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HUKUUEUUYREUYYKGRRGNZZKXNGNOLKXTSAQNZYEGNZTRGTOYALSSEHSYGRVSOOLEGIKVKNZOEJYXT&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of this code is reliant on the &lt;a href=&quot;https://pycipher.readthedocs.io/en/master/&quot;&gt;pycipher&lt;/a&gt; project and uses Python 2, however it can easily be upgraded to Python 3.&lt;/p&gt;
&lt;h2 id=&quot;breaking-the-simple-shift-substitution&quot;&gt;Breaking the Simple Shift Substitution&lt;/h2&gt;
&lt;p&gt;To find the shift used for this half, the approach I used was to use frequency analysis on the original cipher. Certain letters are used far more than others in any language, so we&#039;ll take advantage of that by finding the most suitable value. An example of this would be that the letter Q is used far less in English than the letter A.&lt;/p&gt;
&lt;p&gt;The frequency of letters in the English language can be found on the Internet, so these are the values that I used for this analysis. The following code is based on &lt;a href=&quot;https://code.activestate.com/recipes/142813-deciphering-caesar-code/&quot;&gt;ActiveState&#039;s recipe for deciphering a caesar cipher&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;ENGLISH = (&lt;span class=&quot;hljs-number&quot;&gt;0.0749&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0129&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0354&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0362&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.1400&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0218&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0174&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0422&lt;/span&gt;,
           &lt;span class=&quot;hljs-number&quot;&gt;0.0665&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0027&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0047&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0357&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0339&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0674&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0737&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0243&lt;/span&gt;,
           &lt;span class=&quot;hljs-number&quot;&gt;0.0026&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0614&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0695&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0985&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0300&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0116&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0169&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0028&lt;/span&gt;,
           &lt;span class=&quot;hljs-number&quot;&gt;0.0164&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0.0004&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to see how often each letter appears in our cipher text. To calculate the frequency of these letters we have a function called &lt;code&gt;letter_frequency()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;letter_frequency&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(text)&lt;/span&gt;:&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;# For ease, remove all non-letter characters and convert it to lowercase for consistency.&lt;/span&gt;
    text = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;&#039;[^a-z]&#039;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#039;&#039;&lt;/span&gt;, text.lower())

    &lt;span class=&quot;hljs-comment&quot;&gt;# A dictionary to keep count of how many times each letter is used inside of `text`. They key will be the letter&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;# being counted.&lt;/span&gt;
    alphabetCount = dict([(c,&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;) &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; c &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; string.lowercase])

    &lt;span class=&quot;hljs-comment&quot;&gt;# Cast to float&lt;/span&gt;
    totalLetters = len(text) * &lt;span class=&quot;hljs-number&quot;&gt;1.0&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; char &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; text:
        alphabetCount[char] += &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;# Convert the dictionary to a list of items and sort them by the letter&lt;/span&gt;
    textLetterCount = alphabetCount.items()
    textLetterCount.sort()

    &lt;span class=&quot;hljs-comment&quot;&gt;# Calculate the amount of times the letter has appeared in relation to the cipher&#039;s length&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; [ count/totalLetters &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (letter,count) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; textLetterCount ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we can calculate the frequency of letters in the cipher, we need to start calculating the deltas of each possible shift value (1 through 25). To first calculate the delta, we need a function that will calculate the difference between the frequency values we got from &lt;code&gt;letter_frequency()&lt;/code&gt; and the hard coded values defined in the &lt;code&gt;ENGLISH&lt;/code&gt; list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;frequency_delta&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(source, dest)&lt;/span&gt;:&lt;/span&gt;
    N = &lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; f1, f2 &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; zip(source, dest):
        N += abs(f1 - f2)

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; N&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value we get from this function will be the total change in frequencies from our potential shift and the English language letter frequencies. The lower the frequency, the better the chances of that shift being our offset value. Now, finally, we just need to loop through all 25 possible shifts and find the one with the lowest frequency.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;decipher_caesar&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(cipher)&lt;/span&gt;:&lt;/span&gt;
    lowestDelta = &lt;span class=&quot;hljs-number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# A ridiculously large initial delta in order to ensure a new lowest delta&lt;/span&gt;
    bestRotation = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;
    letterFrequencies = letter_frequency(cipher)

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; shift &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; range(&lt;span class=&quot;hljs-number&quot;&gt;26&lt;/span&gt;):
        currentDelta = frequency_delta(letterFrequencies[shift:] + letterFrequencies[:shift], ENGLISH)

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; currentDelta &amp;lt; lowestDelta:
            lowestDelta = currentDelta
            bestRotation = shift

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; {
        &lt;span class=&quot;hljs-string&quot;&gt;&#039;rotation&#039;&lt;/span&gt;: bestRotation,
        &lt;span class=&quot;hljs-string&quot;&gt;&#039;plain_text&#039;&lt;/span&gt;: Caesar(bestRotation).decipher(cipher)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After we run the &lt;code&gt;decipher_caesar()&lt;/code&gt; function on our initial cipher, we get the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lowest delta frequency: &lt;code&gt;0.478501298701&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Rotation: &lt;code&gt;6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Plain Text: &lt;code&gt;BOEOOYOOSLYOSSEALLAHTTERHAHIFERNMUKHTSYAHTNLANISUFMMYBMSALPMIIFYACEPEHTIYDSRN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;breaking-columnar-transposition&quot;&gt;Breaking Columnar Transposition&lt;/h2&gt;
&lt;p&gt;Breaking this isn&#039;t quick and easy like the Caesar cipher but that doesn&#039;t mean it&#039;s difficult either; it&#039;s just very time consuming once you get the concept of it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Transposition_cipher#Columnar_transposition&quot;&gt;Columnar transposition&lt;/a&gt; makes use of a keyword to encrypt text; however, two different keywords could possibly encrypt the same plain text the same way. For example, using CAT and BAT as keywords would encrypt to the same result since they&#039;d both order the columns as &lt;code&gt;213&lt;/code&gt;. We can use this fact to our advantage and just use numbers when trying to find possible keywords.&lt;/p&gt;
&lt;p&gt;The length of the keyword is important and without knowing this information, it makes finding the keyword take far longer since we&#039;ll have to try out different lengths. To represent our keyword, I&#039;ll be using numbers. Since I&#039;m writing this after I figured out the keyword, I know the length is 8 so we&#039;ll be using 8 numbers: &lt;code&gt;01234567&lt;/code&gt; (&lt;a href=&quot;https://www.reddit.com/r/ProgrammerHumor/comments/6m7z9o/arrays_start_at_one_police_edition/&quot;&gt;because arrays start at 0&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The first step we need to do is create all of the possible permutations of our potential keyword, meaning we&#039;d have a list looking like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;01234567
02134567
02314567
...
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&#039;t worry, we&#039;ll build them all with &lt;a href=&quot;https://docs.python.org/2/library/itertools.html#itertools.permutations&quot;&gt;&lt;code&gt;itertools.permutations&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; itertools

permutations = itertools.permutations(&lt;span class=&quot;hljs-string&quot;&gt;&#039;01234567&#039;&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have every possible permutation, 40,320 to be exact, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Factorial&quot;&gt;8!&lt;/a&gt;, we need to undo the columnar transposition of the cipher text with each permutation and save it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; pycipher &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; ColTrans

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;brute_columnar_transposition&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(text, keyword)&lt;/span&gt;:&lt;/span&gt;
    possible_orders = permutations(keyword)
    columnPositions = {}

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; order &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; possible_orders:
        key = &lt;span class=&quot;hljs-string&quot;&gt;&#039;&#039;&lt;/span&gt;.join(order)
        columnPositions[key] = ColTrans(key).decipher(text)

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; columnPositions&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this function, we&#039;ll be getting every possible arrangement of the cipher&#039;s columns. But that&#039;s still 40,320 combinations. Unless you are &lt;em&gt;really&lt;/em&gt; bored to do it manually, it&#039;s necessary to use a dictionary attack on all of these combinations and then find which version has the most English words.&lt;/p&gt;
&lt;p&gt;I highly recommend you do &lt;strong&gt;not&lt;/strong&gt; use your OS&#039; dictionary if you&#039;re on Unix. Why?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;allejo$ wc -l /usr/share/dict/words
235886 /usr/share/dict/words&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&#039;s worse is that these words are in alphabetical order, which will take forever to find a word such as &amp;quot;which&amp;quot; that is used frequently. To solve this issue, we need a shorter list of common English words (ideally sorted by frequency). Thankfully, someone on GitHub did just that and built a list containing the &lt;a href=&quot;https://github.com/first20hours/google-10000-english&quot;&gt;10,000 most common English words in order of frequency&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Build a list of English words from a file that&#039;s delimited by new lines&lt;/span&gt;
&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;build_dictionary&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;:&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; open(&lt;span class=&quot;hljs-string&quot;&gt;&#039;10k-english.txt&#039;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#039;r&#039;&lt;/span&gt;) &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; dict_file:
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; dict_file.read().split()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a dictionary and all of the possible ordering of cipher columns, we&#039;ll perform the dictionary attack and we will write all of this information to a text file because it&#039;ll be a lot.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-python&quot;&gt;combinations = brute_columnar_transposition(
    &lt;span class=&quot;hljs-string&quot;&gt;&#039;BOEOOYOOSLYOSSEALLAHTTERHAHIFERNMUKHTSYAHTNLANISUFMMYBMSALPMIIFYACEPEHTIYDSRN&#039;&lt;/span&gt;
)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;dictionary_attack&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(permutations, words, output = &lt;span class=&quot;hljs-string&quot;&gt;&#039;iambrute.txt&#039;&lt;/span&gt;)&lt;/span&gt;:&lt;/span&gt;
    results = {}

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; cols, string &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; permutations.iteritems():
        results[string] = {
            &lt;span class=&quot;hljs-string&quot;&gt;&#039;cols&#039;&lt;/span&gt;: cols,
            &lt;span class=&quot;hljs-string&quot;&gt;&#039;words&#039;&lt;/span&gt;: []
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; _word &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; words:
            word = _word.strip().upper()

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; len(word) &amp;lt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;:
                &lt;span class=&quot;hljs-keyword&quot;&gt;continue&lt;/span&gt;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; word &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; string:
                results[string][&lt;span class=&quot;hljs-string&quot;&gt;&#039;words&#039;&lt;/span&gt;].append(word)

    sortedResults = OrderedDict(sorted(
        results.iteritems(), 
        key=&lt;span class=&quot;hljs-keyword&quot;&gt;lambda&lt;/span&gt; x: len(x[&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;][&lt;span class=&quot;hljs-string&quot;&gt;&#039;words&#039;&lt;/span&gt;]), 
        reverse=&lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;
    ))

    &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; open(output, &lt;span class=&quot;hljs-string&quot;&gt;&#039;w&#039;&lt;/span&gt;) &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; data_file:
        &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; k &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; sortedResults:
            line = &lt;span class=&quot;hljs-string&quot;&gt;&quot;{}: [{}]\n&quot;&lt;/span&gt;.format(
                &lt;span class=&quot;hljs-string&quot;&gt;&#039;, &#039;&lt;/span&gt;.join([ sortedResults[k][&lt;span class=&quot;hljs-string&quot;&gt;&#039;cols&#039;&lt;/span&gt;], k ]),
                &lt;span class=&quot;hljs-string&quot;&gt;&#039;, &#039;&lt;/span&gt;.join(sortedResults[k][&lt;span class=&quot;hljs-string&quot;&gt;&#039;words&#039;&lt;/span&gt;])
            )
            data_file.write(line)

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; results&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the process of finding English words, we are ignoring any words less than 3 letters long and making a list of words that exist in each of the combinations. Ideally, you&#039;d want to ignore overlapping words so you don&#039;t find &amp;quot;THE&amp;quot; and &amp;quot;EAT&amp;quot; inside of &amp;quot;THEATER&amp;quot;. However, it never hurts to find more words within already existing words; it&#039;ll increase its ranking.&lt;/p&gt;
&lt;p&gt;Once we finish finding all of the words that exist in each of the combinations, we create an &lt;a href=&quot;https://docs.python.org/2/library/collections.html#collections.OrderedDict&quot;&gt;OrderedDict&lt;/a&gt; sorted by the amount of words that exist in the string in descending order so the string with the most words will be at the beginning of the file.&lt;/p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;
&lt;p&gt;I&#039;ve made &lt;a href=&quot;https://github.com/allejo/iambrute&quot;&gt;my code available on GitHub&lt;/a&gt; for you to explore and learn from. Since the code&#039;s public, it has likely been indexed by Google, meaning: if you found it, so can your professor. Don&#039;t get caught cheating.&lt;/p&gt;
&lt;p&gt;Here&#039;s the plain text I got from my cipher.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Column Order: &lt;code&gt;03247615&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Plain Text: &lt;code&gt;BEHAPPYFORTHEMOMENTTHISMOMENTISYOURLIFEBYKHAYYAMOHANDALSOTHISCLASSISREALLYFUN:&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Words Found: &lt;code&gt;[THE, AND, FOR, THIS, YOU, YOUR, ALL, OUR, HIS, ALSO, THEM, LIFE, REAL, CLASS, MEN, REALLY, HAND, FUN, FEB, URL, HAPPY, LAS, MOMENT, FORT, MOM, FORTH, APP, ENT, MENT, HAY]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies on Hiatus</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-on-hiatus/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x20;on&#x20;Hiatus" />
            <updated>2017-09-14T08:00:00+00:00</updated>
            <id>8c3f3b509e9115185c341409256b2f6ae5ca0018</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-on-hiatus/">&lt;p&gt;I never expected the &lt;em&gt;BZFlag Plug-ins for Dummies&lt;/em&gt; series to get much traffic due to its nature; a very small community, few to no developers, etc. With regards to the current status of the project, I feel that I have covered enough of the API and plug-in writing to be helpful to new developers. For this reason, I&#039;ll be putting this series on hold for a while; by doing so, it&#039;ll allow me to focus on other projects that require my attention.&lt;/p&gt;
&lt;p&gt;There is no estimate for how long this project will be on hold for. If you were following the series and would like to see it continued, please &lt;a href=&quot;/contact/&quot;&gt;let me know&lt;/a&gt; and tell me what you&#039;d like to see covered. Until next time, thanks for the support! &amp;hearts;&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 8</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-8/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;8" />
            <updated>2017-09-02T08:00:00+00:00</updated>
            <id>5e2dfcdd092801c77e70c6761fe0825de097e8e6</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-8/">&lt;p&gt;A common task to achieve in plug-ins is to have a countdown or time delayed functionality. As an example, a plug-in introducing a custom game mode such as Last Tank Standing will need a countdown for the last 5 seconds of each round and will need to kick a new player at a set interval. This is C++ so there&#039;s no magic &lt;code&gt;setTimeout()&lt;/code&gt; function that we can use and there&#039;s no API function; instead we&#039;ll have to make use of the &lt;code&gt;bz_eTickEvent&lt;/code&gt; and &lt;code&gt;time_t&lt;/code&gt; objects.&lt;/p&gt;
&lt;h2 id=&quot;the-tick-event&quot;&gt;The Tick Event&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;bz_eTickEvent&lt;/code&gt; is an event that occurs every tick in the main BZFS loop. So, what&#039;s a &amp;quot;tick?&amp;quot; A single tick does not mean a single second; typically there are more ticks per second and it&#039;s never a consistent amount. Because ticks aren&#039;t designed to be used as an accurate measurement of time, we use a &lt;code&gt;time_t&lt;/code&gt; object, which was designed to handle time.&lt;/p&gt;
&lt;h2 id=&quot;plug-in-specification&quot;&gt;Plug-in Specification&lt;/h2&gt;
&lt;p&gt;We&#039;ll be a building a plug-in that handles a timed game with a 5 second countdown after the &lt;code&gt;/start&lt;/code&gt; slash command is executed.&lt;/p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The Setup&lt;/h2&gt;
&lt;p&gt;We&#039;ll be needing to create 3 separate variables to handle the 5 second countdown.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isCountdownInProgress&lt;/code&gt; - Whether or not a countdown is progress (optional, but very handy)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;countdownTotal&lt;/code&gt; - The amount of seconds we will announce in our countdown&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastCountdownCheck&lt;/code&gt; - This &lt;code&gt;time_t&lt;/code&gt; object will be used to keep the timestamp of the last time we announced a second in our countdown.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; isCountdownInProgress;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; countdownTotal;
    &lt;span class=&quot;hljs-keyword&quot;&gt;time_t&lt;/span&gt; lastCountdownCheck;
};

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    isCountdownInProgress = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we&#039;ll be implementing our &lt;code&gt;/start&lt;/code&gt; slash command, which will start our countdown. What we need to do is the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set our boolean to true so we have easy access to check whether or not a countdown is progress&lt;/li&gt;
&lt;li&gt;Set the amount of seconds our countdown will go on for&lt;/li&gt;
&lt;li&gt;Set the timestamp of our last check to the current time&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;start&quot;&lt;/span&gt;)
    {
        isCountdownInProgress = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;; &lt;span class=&quot;hljs-comment&quot;&gt;// 1&lt;/span&gt;
        countdownTotal = &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;;           &lt;span class=&quot;hljs-comment&quot;&gt;// 2&lt;/span&gt;
        time(&amp;amp;lastCountdownCheck);    &lt;span class=&quot;hljs-comment&quot;&gt;// 3&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&#039;s take a look at implementing the actual countdown which will happen in the tick event. So what&#039;s actually happening? Imagine a cliché family road trip where the children are asking, &amp;quot;are we there yet?&amp;quot; repeatedly. Our plug-in will behave exactly the same way, except it&#039;ll be asking: &amp;quot;has it been &lt;em&gt;at least&lt;/em&gt; one second since my last announcement?&amp;quot;&lt;/p&gt;
&lt;p&gt;It&#039;s very important to notice the &amp;quot;at least one second&amp;quot; in the question. As mentioned, ticks happen at uneven intervals so it&#039;s quite possible that the last tick happened 1.6 seconds ago. If we check if it was exactly 1 second ago (with &lt;code&gt;==&lt;/code&gt;), chances are we&#039;ll miss a tick and we&#039;ll never continue in the countdown because the &lt;code&gt;difftime()&lt;/code&gt; value will never be exactly 1 again. By checking if it&#039;s been at least one second, we don&#039;t care if the last tick was 1.6 seconds ago, we&#039;ll still be able to continue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eTickEvent:
{
    &lt;span class=&quot;hljs-comment&quot;&gt;// We only want to care about checking timestamps if the countdown is in progress&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...remember we want to be efficient and quick in our plug-ins.&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (isCountdownInProgress)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;time_t&lt;/span&gt; currentTime;
        time(&amp;amp;currentTime);

        &lt;span class=&quot;hljs-comment&quot;&gt;// difftime() returns the difference in time objects in seconds&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (difftime(currentTime, lastCountdownCheck) &amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
        {
            bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, &lt;span class=&quot;hljs-string&quot;&gt;&quot;%d...&quot;&lt;/span&gt;, countdownProgress);

            time(&amp;amp;lastCountdownCheck); &lt;span class=&quot;hljs-comment&quot;&gt;// Update the time of the last number announced&lt;/span&gt;
            countdownProgress--; &lt;span class=&quot;hljs-comment&quot;&gt;// Decrease the amount of seconds we still have to go in our countdown&lt;/span&gt;
        }
    }
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks good, right? Yea. But we&#039;re not done. If you examine this example closer, you&#039;ll notice that we don&#039;t actually stop the countdown after it&#039;s been started; meaning we&#039;ll start announcing negative numbers and countdown indefinitely. Let&#039;s fix that.&lt;/p&gt;
&lt;p&gt;Once our countdown progress reaches zero after all the &lt;code&gt;countdownProgress--&lt;/code&gt; decrements, we set our &lt;code&gt;isCountdownInProgress&lt;/code&gt; boolean to false and we&#039;ll no longer enter this bit of code in the next tick event until another &lt;code&gt;/start&lt;/code&gt; is executed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eTickEvent:
{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (isCountdownInProgress)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;time_t&lt;/span&gt; currentTime;
        time(&amp;amp;currentTime);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (difftime(currentTime, lastCountdownCheck) &amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
        {
            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (countdownProgress &amp;lt;= &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
            {
                isCountdownInProgress = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
                bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, &lt;span class=&quot;hljs-string&quot;&gt;&quot;The countdown is over. Good luck and have fun!&quot;&lt;/span&gt;);
            }
            &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
            {
                bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, &lt;span class=&quot;hljs-string&quot;&gt;&quot;%d...&quot;&lt;/span&gt;, countdownProgress);

                time(&amp;amp;lastCountdownCheck);
                countdownProgress--;
            }
        }
    }
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;our-complete-plug-in&quot;&gt;Our Complete Plug-in&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomSlashCommandHandler
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; isCountdownInProgress;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; countdownTotal;
    &lt;span class=&quot;hljs-keyword&quot;&gt;time_t&lt;/span&gt; lastCountdownCheck;
};

BZ_PLUGIN(TimerPlugin)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Timer Plugin&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_eTickEvent);

    bz_registerCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;start&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();

    bz_removeCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;start&quot;&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eTickEvent:
        {
            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (isCountdownInProgress)
            {
                &lt;span class=&quot;hljs-keyword&quot;&gt;time_t&lt;/span&gt; currentTime;
                time(&amp;amp;currentTime);

                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (difftime(currentTime, lastCountdownCheck) &amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
                {
                    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (countdownProgress &amp;lt;= &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
                    {
                        isCountdownInProgress = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
                        bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, &lt;span class=&quot;hljs-string&quot;&gt;&quot;The countdown is over. Good luck and have fun!&quot;&lt;/span&gt;);
                    }
                    &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
                    {
                        bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, &lt;span class=&quot;hljs-string&quot;&gt;&quot;%d...&quot;&lt;/span&gt;, countdownProgress);

                        time(&amp;amp;lastCountdownCheck);
                        countdownProgress--;
                    }
                }
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;TimerPlugin::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;start&quot;&lt;/span&gt;)
    {
        isCountdownInProgress = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
        countdownTotal = &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;;
        time(&amp;amp;lastCountdownCheck);

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The BZFS API doesn&#039;t have a &lt;code&gt;setTimeout()&lt;/code&gt; function nor does it have any custom countdown functionality, so we&#039;re left to implement things ourselves. My &lt;a href=&quot;https://github.com/allejo/lastTankStanding&quot;&gt;Last Tank Standing&lt;/a&gt; plug-in makes use of this technique for the countdown at the beginning of each match and also to keep track of the duration of each round to kick a new player.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 7</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-7/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;7" />
            <updated>2017-08-19T08:00:00+00:00</updated>
            <id>f68a2eda58a22d3999223977f55f835406715ad8</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-7/">&lt;p&gt;The next topic I&#039;d like to cover regarding plug-in writing is how to create custom map objects. Have you ever played the King of the Hill (KOTH), &lt;a href=&quot;https://github.com/allejo/AllHandsOnDeck&quot;&gt;All Hands on Deck (AHOD)&lt;/a&gt;, or Jumping Skills? In this chapter we&#039;ll be building a plug-in that knows when players enter a specified zone to control the hill, capture the flag, or save at a check point. Prior to May 2015, you would have to write all of the logic and math yourself if you wanted to support custom zones. Thanks to the &lt;code&gt;bz_CustomZoneObject&lt;/code&gt; class, courtesy of yours truly, all of the math and logic has been moved to the API and requires no effort on your part.&lt;/p&gt;
&lt;p&gt;These custom map objects are not visible to players. Think of them as the same as a BZW zone object, which has no visible form.&lt;/p&gt;
&lt;h2 id=&quot;registering-your-map-object&quot;&gt;Registering Your Map Object&lt;/h2&gt;
&lt;p&gt;The first step is to make your plugin extend the &lt;code&gt;bz_CustomMapObjectHandler&lt;/code&gt; abstract class, which will require you to implement a virtual function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This virtual function is called during the process of reading the map file and will give you access to &lt;em&gt;every&lt;/em&gt; map object. Implementing this virtual function can also help you keep track of bases, teleporters, or any other map object you&#039;d like. We&#039;ll be using this function to parse the information of our custom map object.&lt;/p&gt;
&lt;p&gt;Similar to creating custom slash commands, you&#039;ll be making use of two functions in our &lt;code&gt;Init()&lt;/code&gt; and &lt;code&gt;Cleanup()&lt;/code&gt; methods, respectively. This is necessary, otherwise BZFS will not know that your custom map object is a valid object.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bz_registerCustomMapObject()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bz_removeCustomMapObject()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#039;s what our setup will look like so far. Now if we load a map file with our custom map object, nothing will happen but an error won&#039;t be thrown either.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomMapObjectHandler
{
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;:
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;&lt;/span&gt;;
};

BZ_PLUGIN(FlagTakeZonePlugin)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-comment&quot;&gt;/*commandLine*/&lt;/span&gt;)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_ePlayerUpdateEvent);
    bz_registerCustomMapObject(&lt;span class=&quot;hljs-string&quot;&gt;&quot;flagtakezone&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt;)&lt;/span&gt;
&lt;/span&gt;{
    Flush();

    bz_removeCustomMapObject(&lt;span class=&quot;hljs-string&quot;&gt;&quot;flagtakezone&quot;&lt;/span&gt;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;defining-your-map-object&quot;&gt;Defining Your Map Object&lt;/h2&gt;
&lt;p&gt;After registering your custom map object, you&#039;re going to want to create your own class to handle any of the logic necessary. Thanks to the &lt;code&gt;bz_CustomZoneObject&lt;/code&gt; class, we won&#039;t have to implement any logic for finding the position or rotation of our objects. We just have to implement our own custom logic, if any.&lt;/p&gt;
&lt;p&gt;We&#039;ll be building a custom zone where certain flags are forbidden and if a player enters the zone carrying that flag, we&#039;ll take it from them. We&#039;ll also have an optional setting where this zone will only affect rogues. The class for our map object will have a vector of the forbidden flags and will look like so.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagZone&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomZoneObject
{
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;:
    FlagZone() : bz_CustomZoneObject() {}

    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; rogueOnly;
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; forbiddenFlags;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;parsing-your-map-object-part-1&quot;&gt;Parsing Your Map Object (Part 1)&lt;/h2&gt;
&lt;p&gt;The next step in this plug-in is to actually parse the map object and save the details/configuration to our code. We do this in our implementation of the &lt;code&gt;MapObject()&lt;/code&gt; method of our plug-in.&lt;/p&gt;
&lt;p&gt;It&#039;s important that we only read the map objects that we want to be efficient. As I mentioned, this method will run for each map object in a map file, so we should be sure to quickly exit our &lt;code&gt;MapObject()&lt;/code&gt; implementation if the current map object isn&#039;t one that we want or it has no data.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// Note, this parameter will be in uppercase&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (object != &lt;span class=&quot;hljs-string&quot;&gt;&quot;FLAGTAKEZONE&quot;&lt;/span&gt; || !data)
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the rest of the method, we&#039;ll be handling the parsing and saving of the data. But we&#039;ll be needing a location to save our zones, so for that reason we&#039;ll be creating a vector in our class definition and saving our zones to that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomMapObjectHandler
{
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;:
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;&amp;lt;FlagZone&amp;gt; zones;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a location to save our zone data to, let&#039;s start parsing and saving the data from the map objects. Given a custom map object that follows this specification:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flagtakezone
    position 0 0 0
    size 10 10 10
    take US
    take WG
    rogueonly
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this specification, the &lt;code&gt;take&lt;/code&gt; key will take 1 value and that&#039;s the flag abbreviation. There will also be an optional &lt;code&gt;rogueonly&lt;/code&gt; key that takes no parameters. This will be reflected on our &lt;code&gt;MapObject()&lt;/code&gt; implementation.&lt;/p&gt;
&lt;p&gt;Let&#039;s take this slowly, so let&#039;s go over the first steps in handling our zones.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (object != &lt;span class=&quot;hljs-string&quot;&gt;&quot;FLAGTAKEZONE&quot;&lt;/span&gt; || !data)
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 1&lt;/span&gt;
    FlagZone newZone;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 2&lt;/span&gt;
    newZone.handleDefaultOptions(data);

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 3&lt;/span&gt;
    zones.push_back(newZone);

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;At this point, we know we&#039;re handling a &amp;quot;flagtakezone&amp;quot; so we&#039;ll have to create an instance of a &lt;code&gt;FlagZone&lt;/code&gt;. This instance will be the zone object we use while reading this map object.&lt;/p&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Remember all of the math and logic I mentioned that you didn&#039;t have to take care of? Well, this is it. The &lt;code&gt;handleDefaultOptions()&lt;/code&gt; function will automatically determine if the object is a box or a cylinder, calculate the position, sizing, and rotation. Later on, all we have to do is call the &lt;code&gt;pointInZone()&lt;/code&gt; method and we&#039;ll know whether a given point is inside this zone or not. Awesome, right?&lt;/p&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;After we&#039;ve parsed the data for our object, let&#039;s add it to our vector so our plug-in can know this zone exists and so it can handle more than one.&lt;/p&gt;
&lt;h2 id=&quot;parsing-your-map-object-part-2&quot;&gt;Parsing Your Map Object (Part 2)&lt;/h2&gt;
&lt;p&gt;Now that we&#039;ve take care of the &amp;quot;built-in&amp;quot; options for our map object, we&#039;ll need to take care of our custom options. In this step, we need to loop through the rest of the data in the map object, parse it, and save it. What data am I talking about? The custom &lt;code&gt;take&lt;/code&gt; and &lt;code&gt;rogueonly&lt;/code&gt; options that our map object supports. All of this information is stored in our &lt;code&gt;data&lt;/code&gt; argument, which we must loop through.&lt;/p&gt;
&lt;p&gt;The map object data stored is an array of the data in the object delimited by new lines. Basically, we have an array that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&quot;position 0 0 0&quot;, &quot;size 10 10 10&quot;, &quot;take US&quot;, &quot;take WG&quot;, &quot;rogueonly&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#039;ll be using a for loop to go through each of these lines and save them to an &lt;code&gt;std::string&lt;/code&gt; for convenience. By calling &lt;code&gt;handleDefaultOptions()&lt;/code&gt;, we don&#039;t have to worry about &lt;code&gt;position&lt;/code&gt; or &lt;code&gt;size&lt;/code&gt; or any of those other &amp;quot;built-in&amp;quot; options. We just need to handle our own additional options.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (object != &lt;span class=&quot;hljs-string&quot;&gt;&quot;FLAGTAKEZONE&quot;&lt;/span&gt; || !data)
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;

    FlagZone newZone;
    newZone.handleDefaultOptions(data);

    &lt;span class=&quot;hljs-comment&quot;&gt;// Loop through all of the data&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; i = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; i &amp;lt; data-&amp;gt;data.size(); i++)
    {
        &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; line = data-&amp;gt;data.get(i).c_str();
    }

    zones.push_back(newZone);
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#039;ll looping through our data, let&#039;s actually parse it and make sense of it. We&#039;ll make use of &lt;code&gt;bz_APIStringList&lt;/code&gt;&#039;s awesome tokenizing functionality so we don&#039;t have to worry about building our own tokenizer or using Boost.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; i = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; i &amp;lt; data-&amp;gt;data.size(); i++)
{
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; line = data-&amp;gt;data.get(i).c_str();

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 4&lt;/span&gt;
    bz_APIStringList nubs;
    nubs.tokenize(line.c_str(), &lt;span class=&quot;hljs-string&quot;&gt;&quot; &quot;&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;);

    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (nubs.size() &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 5&lt;/span&gt;
        &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; key = bz_toupper(nubs.get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str());

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 5.1&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (key == &lt;span class=&quot;hljs-string&quot;&gt;&quot;TAKE&quot;&lt;/span&gt; &amp;amp;&amp;amp; nubs.size() &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
        {
            newZone.forbiddenFlags.push_back(nubs.get(&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;).c_str());
        }
    }
    &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 6&lt;/span&gt;
        &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; key = bz_toupper(line.c_str());

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 6.1&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (line == &lt;span class=&quot;hljs-string&quot;&gt;&quot;ROGUEONLY&quot;&lt;/span&gt;)
        {
            newZone.rogueOnly = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-4&quot;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;Following the BZW language definition, we&#039;ll be tokenizing on a single space between values.&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; do your own custom definition here if you&#039;d like, but I highly recommend following the BZW convention and making it easy for map makers to stay consistent.&lt;/p&gt;
&lt;h3 id=&quot;step-5&quot;&gt;Step 5&lt;/h3&gt;
&lt;p&gt;If our tokenizer resulted in more than one value, that means we have a line with parameters. Because BZW is case-insensitive, I&#039;ll convert the first token to uppercase so we can compare it with our known options.&lt;/p&gt;
&lt;p&gt;You may also convert it to lowercase instead, but I prefer using uppercase since that&#039;s how the &lt;code&gt;object&lt;/code&gt; value is defined.&lt;/p&gt;
&lt;h3 id=&quot;step-5-1&quot;&gt;Step 5.1&lt;/h3&gt;
&lt;p&gt;Since we only have the &lt;code&gt;take&lt;/code&gt; option that accepts parameters, our if statement is pretty simple. However, the more options you support, the longer your if statement may be.&lt;/p&gt;
&lt;p&gt;Lastly, we&#039;ll take the second token, which we expect to be our flag abbreviation, and we&#039;ll push it into the vector of our &lt;code&gt;FlagZone&lt;/code&gt; instance.&lt;/p&gt;
&lt;h3 id=&quot;step-6&quot;&gt;Step 6&lt;/h3&gt;
&lt;p&gt;Since we don&#039;t have any tokens, that means it&#039;s a line without any parameters. Convert it into uppercase for the same reason as before.&lt;/p&gt;
&lt;h3 id=&quot;step-6-1&quot;&gt;Step 6.1&lt;/h3&gt;
&lt;p&gt;Because we only have a single option that takes no parameters, we just need to check for the &lt;code&gt;rogueonly&lt;/code&gt; option and set it accordingly in our &lt;code&gt;FlagZone&lt;/code&gt; instance.&lt;/p&gt;
&lt;h2 id=&quot;making-use-of-your-map-object&quot;&gt;Making Use of Your Map Object&lt;/h2&gt;
&lt;p&gt;Now that we&#039;ve defined our object, parsed it, and saved all of the data... Let&#039;s actually do something with it! We&#039;ll be hooking into &lt;code&gt;bz_ePlayerUpdateEvent&lt;/code&gt; so we can listen to player updates, which happens &lt;em&gt;a lot&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As mentioned earlier, all of our zones have a convenient &lt;code&gt;pointInZone()&lt;/code&gt; method, which we will be using to check each player update to see if they&#039;re in a zone.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Step 7&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; currentFlag = bz_getPlayerFlag(updateData-&amp;gt;playerID);

&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (currentFlag.empty())
{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
}

&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;auto&lt;/span&gt; &amp;amp;zone : zones)
{
    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 8&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (zone.rogueOnly &amp;amp;&amp;amp; bz_getPlayerTeam(updateData-&amp;gt;playerID) != eRogueTeam)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;continue&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 9&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (zone.pointInZone(updateData-&amp;gt;state.pos))
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 10&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;auto&lt;/span&gt; flag : zone.forbiddenFlags)
        {
            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (currentFlag == flag)
            {
                bz_sendTextMessagef(BZ_SERVER, update-&amp;gt;playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;This %s flag is forbidden here&quot;&lt;/span&gt;, currentFlag.c_str());
                bz_removePlayerFlag(update-&amp;gt;playerID);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-7&quot;&gt;Step 7&lt;/h3&gt;
&lt;p&gt;Get the current flag of the player and store it in an &lt;code&gt;std::string&lt;/code&gt; for convenience. If the player doesn&#039;t have a flag, we don&#039;t have to bother checking through any of the zones.&lt;/p&gt;
&lt;h3 id=&quot;step-8&quot;&gt;Step 8&lt;/h3&gt;
&lt;p&gt;We need to be able to short circuit if the current zone we&#039;re checking is &amp;quot;rogue only&amp;quot; and the player isn&#039;t a rogue. Checking a boolean and a team is faster than calculating whether or not a player is in a zone.&lt;/p&gt;
&lt;h3 id=&quot;step-9&quot;&gt;Step 9&lt;/h3&gt;
&lt;p&gt;By using our handy &lt;code&gt;pointInZone()&lt;/code&gt; method, we can check if the player is inside our zone.&lt;/p&gt;
&lt;h3 id=&quot;step-10&quot;&gt;Step 10&lt;/h3&gt;
&lt;p&gt;At this point, we&#039;ve determined that our player is inside our zone, is carrying a flag, and we need to check whether the flag is forbidden or not. We loop through the forbidden flags defined for this zone and we remove the flag if it&#039;s a match.&lt;/p&gt;
&lt;h2 id=&quot;our-complete-plug-in&quot;&gt;Our Complete Plug-in&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagZone&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomZoneObject
{
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt;:
    FlagZone() : bz_CustomZoneObject() {}

    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; rogueOnly;
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;vector&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;&amp;gt; forbiddenFlags;
};

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomMapObjectHandler
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;&lt;/span&gt;;
};

BZ_PLUGIN(FlagTakeZonePlugin)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Flag Take Zone Plugin&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-comment&quot;&gt;/*commandLine*/&lt;/span&gt;)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_ePlayerUpdateEvent);

    bz_registerCustomMapObject(&lt;span class=&quot;hljs-string&quot;&gt;&quot;flagtakezone&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt;)&lt;/span&gt;
&lt;/span&gt;{
    Flush();

    bz_removeCustomMapObject(&lt;span class=&quot;hljs-string&quot;&gt;&quot;flagtakezone&quot;&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::MapObject&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_ApiString object, bz_CustomMapObjectInfo *data)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (object != &lt;span class=&quot;hljs-string&quot;&gt;&quot;FLAGTAKEZONE&quot;&lt;/span&gt; || !data)
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;

    FlagZone newZone;
    newZone.handleDefaultOptions(data);

    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; i = &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;; i &amp;lt; data-&amp;gt;data.size(); i++)
    {
        &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; line = data-&amp;gt;data.get(i).c_str();

        bz_APIStringList nubs;
        nubs.tokenize(line.c_str(), &lt;span class=&quot;hljs-string&quot;&gt;&quot; &quot;&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (nubs.size() &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
        {
            &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; key = bz_toupper(nubs.get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str());

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (key == &lt;span class=&quot;hljs-string&quot;&gt;&quot;TAKE&quot;&lt;/span&gt; &amp;amp;&amp;amp; nubs.size() &amp;gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
            {
                newZone.forbiddenFlags.push_back(nubs.get(&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;).c_str());
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
        {
            &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; key = bz_toupper(line.c_str());

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (line == &lt;span class=&quot;hljs-string&quot;&gt;&quot;ROGUEONLY&quot;&lt;/span&gt;)
            {
                newZone.rogueOnly = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
            }
        }
    }

    zones.push_back(newZone);
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagTakeZonePlugin::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerUpdateEvent:
        {
            bz_PlayerUpdateEventData_V1 *updateData = (bz_PlayerUpdateEventData_V1*)eventData;

            &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; currentFlag = bz_getPlayerFlag(updateData-&amp;gt;playerID);

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (currentFlag.empty())
            {
                &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
            }

            &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;auto&lt;/span&gt; &amp;amp;zone : zones)
            {
                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (zone.rogueOnly &amp;amp;&amp;amp; bz_getPlayerTeam(updateData-&amp;gt;playerID) != eRogueTeam)
                {
                    &lt;span class=&quot;hljs-keyword&quot;&gt;continue&lt;/span&gt;;
                }

                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (zone.pointInZone(updateData-&amp;gt;state.pos))
                {
                    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;auto&lt;/span&gt; flag : zone.forbiddenFlags)
                    {
                        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (currentFlag == flag)
                        {
                            bz_sendTextMessagef(BZ_SERVER, update-&amp;gt;playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;This %s flag is forbidden here&quot;&lt;/span&gt;, currentFlag.c_str());
                            bz_removePlayerFlag(update-&amp;gt;playerID);
                        }
                    }
                }
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congrats on building your first plug-in that adds a custom map object! Supporting custom map objects may seem pretty inefficient and resource intensive but so long as you write your code responsibly, no extra lag will be introduced due to these zones.&lt;/p&gt;
&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;My apologies for this chapter being one week late! I had originally written it with less detail and decided to give myself another week to add more detail and rewrite parts of it. As an update, the release schedule for this series has changed to being every other Saturday. With school starting soon and topics requiring more detail, I won&#039;t be able to write them as quickly as I had originally intended. Thanks for understanding.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 6</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-6/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;6" />
            <updated>2017-07-29T08:00:00+00:00</updated>
            <id>2dcd2a3346308db64e553d4fe44788af7347b849</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-6/">&lt;p&gt;In honor of my latest commit, of adding a new parameter to &lt;code&gt;bz_addURLJob()&lt;/code&gt;, this chapter will be covering making URL requests and handling the return data. Why would a plug-in need to make URL calls? Well, a plug-in may need to communicate with an API but don&#039;t expect instant results, there&#039;ll be a small delay. A common plug-in that makes use of API calls would be my own League Overseer, which communicates with the league&#039;s API to report matches automatically. There are two different ways of doing URL jobs in a plug-in.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the URL job and forget about it, meaning you don&#039;t care about the results returned by the website you&#039;re calling&lt;/li&gt;
&lt;li&gt;Create the URL job and execute code based on the returned data from the website&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&#039;ll be handling both situations in this chapter. If your plug-in will be working with the returned data, you will need to extend the abstract class, &lt;code&gt;bz_URLHandler_V2&lt;/code&gt;. By extending this abstract class, you will have to implement 3 methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;URLDone()&lt;/code&gt; - This method is called whenever a URL job is completed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;URLTimeout()&lt;/code&gt; - This method is called when a URL job times out.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;URLError()&lt;/code&gt; - This method is called when a URL job results in an error.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_URLHandler_V2
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Virtual methods from `bz_URLHandler_V2`&lt;/span&gt;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;URLDone&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* URL, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt;* data, &lt;span class=&quot;hljs-keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; size, &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; complete)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;URLTimeout&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* URL, &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; errorCode)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;URLError&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* URL, &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; errorCode, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt; *errorString)&lt;/span&gt;&lt;/span&gt;;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;making-the-url-calls&quot;&gt;Making the URL Calls&lt;/h2&gt;
&lt;p&gt;When making URL calls, you&#039;ll be using the &lt;code&gt;bz_addURLJob()&lt;/code&gt; function anywhere in your plug-in but don&#039;t expect instant results. Oh, and don&#039;t worry about pausing execution while waiting for a response, the game and code will go on&lt;sup&gt;1&lt;/sup&gt;. Here&#039;s the definition of the function we&#039;ll be using and its parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;bz_addURLJob&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* URL, bz_URLHandler_V2* handler, &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt;* token, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* postData = &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, bz_APIStringList *headers = &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;The URL you&#039;ll be making the call to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;handler&lt;/td&gt;
&lt;td&gt;The class which implements the callbacks needed to handle returned data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;token&lt;/td&gt;
&lt;td&gt;A way to keep track of which job is which when running multiple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;postData&lt;/td&gt;
&lt;td&gt;An ampersand delimited and URL encoded list of data that will be used as POST data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;headers&lt;/td&gt;
&lt;td&gt;An array of HTTP headers that will be used in your URL call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This does not imply threads or asynchronous behavior. Your plug-in is still running in the main server loop so be efficient.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;url-calls-without-callbacks&quot;&gt;URL Calls without Callbacks&lt;/h2&gt;
&lt;p&gt;Let&#039;s consider a plug-in that will record player joins and send the data to a website to log players&#039; online status, similar to Strayer&#039;s bzstats but this&#039;d be real-time since it&#039;d report player activity as it happens. We won&#039;t care about the data that was returned in this particular situation so we won&#039;t worry about specifying a handler, we&#039;ll leave it as &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first parameter of the function is a URL, this is the URL we&#039;ll be sending the data to. The second important bit is the data that we&#039;ll be sending as POST data, as we typically would: delimited by ampersands and URL encoded values.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;argumentOne=hello%20world&amp;amp;argumentTwo=10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will have to build the POST data ourselves and give it to our function to send in the URL job. We will be providing all of the formatting ourselves (ampersands and equal signs) and we need to make sure we encode all of our values so nothing breaks. To encode our values, &lt;code&gt;bz_urlEncode()&lt;/code&gt; is provided but it expects a &lt;code&gt;const char*&lt;/code&gt; so we&#039;ll be doing a lot of casting so we can continue to work with objects instead of pointers.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerJoinEvent:
{
    bz_PlayerJoinPartEventData_V1 *data = (bz_PlayerJoinPartEventData_V1*)eventData;

    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; urlQuery;
    urlQuery += &lt;span class=&quot;hljs-string&quot;&gt;&quot;callsign=&quot;&lt;/span&gt; + &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;(bz_urlEncode(data-&amp;gt;record-&amp;gt;callsign.c_str()));
    urlQuery += &lt;span class=&quot;hljs-string&quot;&gt;&quot;&amp;amp;bzID=&quot;&lt;/span&gt; + &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;(bz_urlEncode(data-&amp;gt;record-&amp;gt;bzID.c_str()));

    &lt;span class=&quot;hljs-comment&quot;&gt;// urlQuery -&amp;gt; &quot;callsign=allejo%20bot&amp;amp;bzID=12345&quot;&lt;/span&gt;

    bz_addURLJob(&lt;span class=&quot;hljs-string&quot;&gt;&quot;http://localhost/api/?report=join&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, urlQuery.c_str());
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We leave both the &lt;code&gt;handler&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt; parameters as &lt;code&gt;NULL&lt;/code&gt; since we don&#039;t care about whatever comes back from the URL. It could fail and burn for all we care.&lt;/p&gt;
&lt;h2 id=&quot;url-calls-with-callbacks&quot;&gt;URL Calls with Callbacks&lt;/h2&gt;
&lt;p&gt;Ok, what about if we &lt;em&gt;do&lt;/em&gt; care about things failing and burning? Or what if we need the data returned from the URL? As an example, League Overseer expects to receive information back from the league website; it expects to get team information or the results of a match report. We work with the returned data in our &lt;code&gt;URLDone()&lt;/code&gt; implementation.&lt;/p&gt;
&lt;p&gt;Similar to our handling of slash commands, we&#039;ll comment out some of the parameters we won&#039;t actually use to prevent warnings about unused variables; we only really care about &lt;code&gt;*data&lt;/code&gt; and &lt;code&gt;complete&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this case, the data we&#039;ll be receiving from the URL we called will be text, so we will cast it to a &lt;code&gt;const char*&lt;/code&gt; and assign that value to an &lt;code&gt;std::string&lt;/code&gt; to allow for easier manipulation in the future. The logic is left up to you to implement. You can output it to all the users with &lt;code&gt;bz_sendTextMessage()&lt;/code&gt;, store it in an array, or &lt;a href=&quot;http://bash.org/?240849&quot;&gt;just discard it and drive into walls&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::URLDone&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-comment&quot;&gt;/*URL*/&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; *data, &lt;span class=&quot;hljs-keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;/*size*/&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; complete)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; webData = (&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;*)data;

    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (complete)
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// ...your own logic&lt;/span&gt;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most APIs nowadays supports returning data as JSON, so if you&#039;d like to use parse and work with JSON, I&#039;ve already set the ground work for it. If you use &lt;a href=&quot;https://github.com/json-c/json-c&quot;&gt;json-c&lt;/a&gt;, you&#039;ll likely have good support since it&#039;s available on most *nix distributions (&lt;code&gt;libjson0-dev&lt;/code&gt; in Debian/Ubuntu; &lt;code&gt;json-c-devel&lt;/code&gt; in Fedora Linux) and should already be installed by server owners who run league servers (League Overseer requires it). To make your life &lt;em&gt;even&lt;/em&gt; easier, I&#039;ve written a thin wrapper on top of json-c that&#039;ll save you the effort of using its complicated and tedious API: &lt;a href=&quot;https://github.com/allejo/JsonObject&quot;&gt;JSONObject&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&#039;re welcome.&lt;/p&gt;
&lt;h2 id=&quot;using-http-headers&quot;&gt;Using HTTP Headers&lt;/h2&gt;
&lt;p&gt;Some APIs require keys or tokens to be passed through HTTP headers, and as of today, the BZFS API supports sending headers in your URL jobs.&lt;/p&gt;
&lt;p&gt;The first step is to create a &lt;code&gt;bz_APIStringList&lt;/code&gt; pointer somewhere; I&#039;d recommend declaring it in the class definition, initialize it when you configure your headers, and free it in &lt;code&gt;URLDone()&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we free the list &lt;em&gt;before&lt;/em&gt; &lt;code&gt;URLDone()&lt;/code&gt; is called, you&#039;ll likely get a segmentation fault.&lt;/li&gt;
&lt;li&gt;If you never delete it, you&#039;ll likely get a memory leak.&lt;/li&gt;
&lt;li&gt;If you don&#039;t use &lt;code&gt;bz_newStringList()&lt;/code&gt; and use a reference instead, your plug-in won&#039;t work on Windows.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_APIStringList *headers = bz_newStringList(); &lt;span class=&quot;hljs-comment&quot;&gt;// Remember to free this!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we&#039;ve created the string list pointer, we&#039;ll be able to set and modify headers as we please. This functionality follows the same practice as libcurl&#039;s for simplicity and familiarity.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Remove a header curl would otherwise add by itself&lt;/span&gt;
headers-&amp;gt;push_back((&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;)&lt;span class=&quot;hljs-string&quot;&gt;&quot;Accept:&quot;&lt;/span&gt;);

&lt;span class=&quot;hljs-comment&quot;&gt;// Add a custom header&lt;/span&gt;
headers-&amp;gt;push_back((&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;)&lt;span class=&quot;hljs-string&quot;&gt;&quot;X-ApiKey: MySuperSecretAwesomeAPIKey&quot;&lt;/span&gt;);

&lt;span class=&quot;hljs-comment&quot;&gt;// Modify a header curl otherwise adds differently&lt;/span&gt;
headers-&amp;gt;push_back((&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;)&lt;span class=&quot;hljs-string&quot;&gt;&quot;Cache-Control: no-cache&quot;&lt;/span&gt;);
headers-&amp;gt;push_back((&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;)&lt;span class=&quot;hljs-string&quot;&gt;&quot;Host: example.com&quot;&lt;/span&gt;);

&lt;span class=&quot;hljs-comment&quot;&gt;// Add a header with &quot;blank&quot; contents to the right of the colon. Note that&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;//    we&#039;re then using a semicolon in the string we pass to curl!&lt;/span&gt;
headers-&amp;gt;push_back((&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;)&lt;span class=&quot;hljs-string&quot;&gt;&quot;X-silly-header;&quot;&lt;/span&gt;);

&lt;span class=&quot;hljs-comment&quot;&gt;// Make the URL call&lt;/span&gt;
bz_addURLJob(&lt;span class=&quot;hljs-string&quot;&gt;&quot;api.example.com&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, &lt;span class=&quot;hljs-literal&quot;&gt;NULL&lt;/span&gt;, headers);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I apologize that this chapter didn&#039;t have a complete plug-in that we built together but I couldn&#039;t think of a quick and easy to plug-in to write that used public APIs. Depending on the demand, I may revisit this chapter with a more in-depth tutorial about using APIs and parsing JSON. However, I believe I&#039;ve covered enough for you to be able to make use of URL jobs and use the returned data in conjunction with what you&#039;ve learned in the previous chapters.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>July 2017 Radio Silence</title>
            <link href="https://allejo.io/blog/july-2017-radio-silence/" rel="alternate" type="text/html" title="July&#x20;2017&#x20;Radio&#x20;Silence" />
            <updated>2017-07-24T04:00:00+00:00</updated>
            <id>f504dd0a20c1f890952e4075aba54b45873a2e9b</id>
            <content type="html" xml:base="https://allejo.io/blog/july-2017-radio-silence/">&lt;p&gt;If you&#039;ve been following my &lt;a href=&quot;/anthologies/&quot;&gt;&lt;em&gt;BZFlag Plug-ins for Dummies&lt;/em&gt; series&lt;/a&gt;, you may have noticed the radio silence for the past two weeks. Don&#039;t worry, the project is still alive and scheduled to continue. The past two weeks have just been incredibly busy for me with work, summer school, and projects so I haven&#039;t had time to write any new chapters; heck even my GitHub contributions graph has been quiet. I&#039;ve got a new chapter for that series planned for July 29, so don&#039;t worry you&#039;ll be able to continue writing plug-ins.&lt;/p&gt;
&lt;p&gt;If you&#039;re curious as to why there&#039;s been radio silence, it&#039;s because I&#039;ve been busy researching and learning more about web accessibility; BZiON is the guinea pig for everything I&#039;ve been learning so far. You can expect a huge update to the &lt;a href=&quot;http://leaguesunited.org&quot;&gt;Leagues United&lt;/a&gt; website by the end of the August or mid-September. One of the most anticipated features is the introduction of individual ELO ratings for players and as I promised, that&#039;s coming by the end of summer. There have been some remarks made concerning the lack of progress with this feature and my response to that is: BZiON is an open source project maintained by two people during their spare time.&lt;/p&gt;
&lt;p&gt;Anyhow, BZiON 0.9.3 is scheduled for some time before the end of September and that&#039;s as accurate as I can be. Chapter 6 of &lt;em&gt;BZFlag Plug-ins for Dummies&lt;/em&gt; is scheduled for next Saturday.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 5</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-5/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;5" />
            <updated>2017-07-08T08:00:00+00:00</updated>
            <id>e263ca469e27d9fdafc9f1d4afa1957ec762234a</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-5/">&lt;p&gt;After last week&#039;s pretty long chapter, this&#039;ll be shorter in comparison. So far we&#039;ve written simple plug-ins where the behavior we define is all the behavior the plug-in gets. But what if we want to allow the server owners or map makers to control the behavior of the plug-ins. This can be done in several ways but in this chapter, we&#039;ll be covering using configuration files.&lt;/p&gt;
&lt;p&gt;So what do configuration files look like?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot;&gt;&lt;span class=&quot;hljs-section&quot;&gt;[pluginname]&lt;/span&gt;
    setting1 = my value 1
    setting2 = another value&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The API provides a &lt;code&gt;PluginConfig&lt;/code&gt; object, which is available through the &lt;code&gt;plugin_config.h&lt;/code&gt; header and this is what we use to parse configuration files for us.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;When plug-ins are loaded, you can pass one parameter to it when you load it either via &lt;code&gt;-loadplugin&lt;/code&gt; or &lt;code&gt;/loadplugin&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-loadplugin messengerPlugin,/path/to/configuration.cfg&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value passed at load time is accessible to us in the &lt;code&gt;Init()&lt;/code&gt; method as the &lt;code&gt;config&lt;/code&gt; parameter. We can advantage of this functionality to expect the path to a configuration file when the plug-in is loaded. Personally, I like to keep all of the logic for parsing configuration files and setting things up in its own dedicated method so I create &lt;code&gt;loadConfiguration()&lt;/code&gt;. I declare it in the plug-in&#039;s class definition and call it in the &lt;code&gt;Init()&lt;/code&gt; method.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you&#039;d like to reload the configuration while the plug-in is loaded, be sure to save the &lt;code&gt;config&lt;/code&gt; value as an instance variable in the class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
};

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// config = &quot;/path/to/configuration.cfg&quot;&lt;/span&gt;
    loadConfiguration(config);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;plug-in-specification&quot;&gt;Plug-in Specification&lt;/h2&gt;
&lt;p&gt;Now that we have declared the function and are using it, let&#039;s define what we need it to do. Now &lt;code&gt;PluginConfig&lt;/code&gt; only works with strings but our configuration file will need to support booleans and integers too.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up!&lt;/strong&gt; Do not use quotes in your configuration file unless you would like a literal quote in the value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot;&gt;&lt;span class=&quot;hljs-section&quot;&gt;[messenger]&lt;/span&gt;
    DEBUG_LEVEL = 0
    SEND_MESSAGES = true
    WELCOME_MESSAGE = Welcome to this server
    CAPTURE_MESSAGE = Yay someone captured...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this plug-in that we&#039;re building, here&#039;s is what each configuration option is going to do:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DEBUG_LEVEL&lt;/td&gt;
&lt;td&gt;The default debug level that will be used in &lt;code&gt;bz_debugMessage()&lt;/code&gt; function calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SEND_MESSAGES&lt;/td&gt;
&lt;td&gt;Set to false to disable the plug-in entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WELCOME_MESSAGE&lt;/td&gt;
&lt;td&gt;A message sent to players when they join&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAPTURE_MESSAGE&lt;/td&gt;
&lt;td&gt;A message sent to everyone when someone captures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As I mentioned, all of our logic will be defined in &lt;code&gt;loadConfiguration()&lt;/code&gt; because it&#039;s a good idea to keep things together. Additionally, we can implement functionality to reload the configuration file while the plug-in is loaded.&lt;/p&gt;
&lt;p&gt;Let&#039;s start our &lt;code&gt;loadConfiguration()&lt;/code&gt; method by creating a &lt;code&gt;PluginConfig&lt;/code&gt; object, defining the section we&#039;ll be reading, and exit out if there are any errors reading the configuration file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* configPath)&lt;/span&gt;
&lt;/span&gt;{
    PluginConfig config = PluginConfig(configPath);
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; section = &lt;span class=&quot;hljs-string&quot;&gt;&quot;messenger&quot;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (config.errors)
    {
        bz_debugMessage(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Your configuration file has errors&quot;&lt;/span&gt;);
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After we&#039;ve created the parser and checked for errors, we can start working with the data from the configuration file but first, we need a place to store all of that data. Now, remember the previous chapter where we discussed stateful data? We&#039;ll be storing configuration data the same way. Let&#039;s create some instance variables in our plug-in&#039;s class to store the information we get from our configuration file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Plug-in configuration&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; DEBUG_LEVEL;
    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; SEND_MESSAGES;
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; WELCOME_MESSAGE, CAPTURE_MESSAGE;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a place to store the information, let&#039;s start pulling information from our configuration file. Our &lt;code&gt;PluginConfig&lt;/code&gt; has an &lt;code&gt;item()&lt;/code&gt; method that we use to pull values. You may be wondering why our variables are uppercase, well that&#039;s just a convention I follow so I can easily tell what variables pertain to a configuration option and which don&#039;t. Pulling strings from the configuration file is easy, as seen below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* configPath)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;

    WELCOME_MESSAGE = config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;WELCOME_MESSAGE&quot;&lt;/span&gt;);
    CAPTURE_MESSAGE = config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;CAPTURE_MESSAGE&quot;&lt;/span&gt;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, pulling booleans or integers... Well that will require some conversion/logic on our part.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* configPath)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Use std::stoi for string to int conversion&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt;
    {
        DEBUG_LEVEL = &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::stoi(config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;DEBUG_LEVEL&quot;&lt;/span&gt;));
    }
    &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::exception &amp;amp;e) {}

    &lt;span class=&quot;hljs-comment&quot;&gt;// Use a comparison for booleans&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; _sendMessages = bz_tolower(config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;SEND_MESSAGES&quot;&lt;/span&gt;).c_str());
    SEND_MESSAGES = (_sendMessages == &lt;span class=&quot;hljs-string&quot;&gt;&quot;true&quot;&lt;/span&gt;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, parsing a configuration file and saving the values takes a lot of work and it should belong in its own method so it can be used easily. For example, if you&#039;d like to build in a &lt;code&gt;/reload&lt;/code&gt; option for reparsing the configuration file, you can simply call this function instead of doing all the work several times.&lt;/p&gt;
&lt;h2 id=&quot;our-plug-in&quot;&gt;Our Plug-in&lt;/h2&gt;
&lt;p&gt;Here&#039;s the majority of the plug-in we&#039;ve built in this chapter. Read on to the conclusion to see which part is missing and why.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;plugin_config.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Plug-in configuration&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; DEBUG_LEVEL;
    &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; SEND_MESSAGES;
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; WELCOME_MESSAGE, CAPTURE_MESSAGE;
};

BZ_PLUGIN(Messenger)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Messenger::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Messenger&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    loadConfiguration(config);

    Register(bz_eCaptureEvent);
    Register(bz_ePlayerJoinEvent);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// Read the &quot;On Your Own&quot; section&lt;/span&gt;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Messenger::loadConfiguration&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* configPath)&lt;/span&gt;
&lt;/span&gt;{
    PluginConfig config = PluginConfig(configPath);
    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; section = &lt;span class=&quot;hljs-string&quot;&gt;&quot;messenger&quot;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (config.errors)
    {
        bz_debugMessage(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Your configuration file has errors&quot;&lt;/span&gt;);
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
    }

    WELCOME_MESSAGE = config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;WELCOME_MESSAGE&quot;&lt;/span&gt;);
    CAPTURE_MESSAGE = config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;CAPTURE_MESSAGE&quot;&lt;/span&gt;);

    &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt;
    {
        DEBUG_LEVEL = &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::stoi(config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;DEBUG_LEVEL&quot;&lt;/span&gt;));
    }
    &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::exception &amp;amp;e) {}

    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; _sendMessages = bz_tolower(config.item(section, &lt;span class=&quot;hljs-string&quot;&gt;&quot;SEND_MESSAGES&quot;&lt;/span&gt;).c_str());
    SEND_MESSAGES = (_sendMessages == &lt;span class=&quot;hljs-string&quot;&gt;&quot;true&quot;&lt;/span&gt;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It&#039;s a good idea to allow map makers or server owners to customize the plug-in&#039;s functionality easily without having to recompile the entire plug-in each time you&#039;d like to change the welcome message. It&#039;s a good idea to introduce a configuration file for plug-ins that have a lot of features or can be used in different settings.&lt;/p&gt;
&lt;h3 id=&quot;on-your-own&quot;&gt;On Your Own&lt;/h3&gt;
&lt;p&gt;I&#039;ve been doing my best to write these chapters in an order where you build on top last chapter&#039;s lesson. So this chapter&#039;s plug-in will be left for you to complete. The necessary events have already been registered in the &lt;code&gt;Init()&lt;/code&gt; function. Your task for this week is to implement this plug-in&#039;s &lt;code&gt;Event()&lt;/code&gt; method that will send the appropriate messages when a capture happens or a player joins.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Updates to docs.allejo.io</title>
            <link href="https://allejo.io/blog/updates-to-docs.allejo.io/" rel="alternate" type="text/html" title="Updates&#x20;to&#x20;docs.allejo.io" />
            <updated>2017-07-04T08:00:00+00:00</updated>
            <id>fb288a8bfcd36846fb10db1b0426907be78504b5</id>
            <content type="html" xml:base="https://allejo.io/blog/updates-to-docs.allejo.io/">&lt;p&gt;It&#039;s the 4th of July and you know what that means for me, right? It means I do my best to avoid spending time with friends and extended family. Today, I spent time improving my docs.allejo.io domain, which is used for hosting the documentation for my projects/libraries.&lt;/p&gt;
&lt;p&gt;Here are the updates I pushed out to the domain today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is now served via HTTPS only&lt;/li&gt;
&lt;li&gt;All URLs are served as lowercase and without the &lt;code&gt;.html&lt;/code&gt; extension
&lt;ul&gt;
&lt;li&gt;Case sensitive URLs will automatically be rewritten as lower case&lt;/li&gt;
&lt;li&gt;Links with &lt;code&gt;.html&lt;/code&gt; extensions will automatically be rewritten without them&lt;/li&gt;
&lt;li&gt;There should be enough redirects in place to prevent any broken links but let me know if you find any&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Documentation is now updated within minutes of commits to the project (if things don&#039;t break)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The domain is hosted on &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; and the repository for the configuration of this site is available under &lt;a href=&quot;https://github.com/allejo/docs.allejo.io&quot;&gt;&lt;code&gt;allejo/docs.allejo.io&lt;/code&gt; on GitHub&lt;/a&gt;. I&#039;m using &lt;a href=&quot;https://www.netlify.com/docs/webhooks/#incoming-webhooks&quot;&gt;Netlify&#039;s incoming webhooks&lt;/a&gt; with &lt;a href=&quot;https://help.github.com/articles/about-webhooks/&quot;&gt;GitHub&#039;s outgoing webhooks&lt;/a&gt; in both the PhpSoda and PhpPulse repositories so it&#039;ll trigger a rebuild of the documentation each commit I make to these respective repositories.&lt;/p&gt;
&lt;p&gt;There&#039;s room for improvement in this project; e.g. don&#039;t rebuild the documentation for both projects when only one project has been updated. However, it works for now!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 4</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-4/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;4" />
            <updated>2017-07-01T08:00:00+00:00</updated>
            <id>152220bc492e082f3a23e872b75a2469ee381c49</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-4/">&lt;p&gt;For those of you who think I write these chapters ahead of time, you&#039;re wrong.&lt;/p&gt;
&lt;p&gt;In the last chapter, we went over how slash commands worked at a very basic level and today we&#039;ll be going in-depth with slash commands and &lt;a href=&quot;https://en.wikipedia.org/wiki/State_(computer_science)&quot;&gt;stateful data&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;plug-in-specification&quot;&gt;Plug-in Specification&lt;/h2&gt;
&lt;p&gt;In this chapter, we&#039;ll be building a plug-in that allows players to respawn at the same location as a player of their choice.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a player uses the &lt;code&gt;/sneak&lt;/code&gt; command, they will be specifying which player&#039;s location they would like to spawn at.&lt;/li&gt;
&lt;li&gt;If a player has used the &lt;code&gt;/sneak&lt;/code&gt; command, their next spawn location will be dictated by the plug-in.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;stateful-data-in-plug-ins&quot;&gt;Stateful Data in Plug-ins&lt;/h2&gt;
&lt;p&gt;I won&#039;t go into detail what &lt;a href=&quot;https://en.wikipedia.org/wiki/State_(computer_science)&quot;&gt;stateful data&lt;/a&gt; is because there&#039;s a Wikipedia page for it but I will give an example. Let&#039;s consider the following situations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On a server, a player has been &lt;code&gt;/mute&lt;/code&gt;&#039;d by an admin. How does the server remember that the player cannot talk each time the player tries to send a message? This is stateful data, the server needs to remember the current &lt;em&gt;state&lt;/em&gt; of the player and their inability to talk.&lt;/li&gt;
&lt;li&gt;During a CTF game, we need to check if the teams are fair. We&#039;ll keep a boolean of whether or not CTF is currently enabled. This boolean will be used when checking whether or not to allow a player to grab a team flag. This is the current &lt;em&gt;state&lt;/em&gt; of the CTF match.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a common requirement when building plug-ins especially to communicate data between different events, slash commands, functions, etc. This information could be thought of as &amp;quot;global variables,&amp;quot; however it&#039;s still within the scope of the plug-in (they&#039;re actually instance variables). Any stateful data stored this way will remain in the server&#039;s memory. If a server were to restart or the plug-in is unloaded, then all this data would be lost.&lt;/p&gt;
&lt;p&gt;What stateful data do we need to keep track of? We need to keep track of whom a player has specified as their target. A good number to remember is: 256; this is approximately the maximum number of player slots a BZFlag server can handle (some numbers are reserved, so it&#039;s actually less).&lt;/p&gt;
&lt;p&gt;When we would like to keep stateful data, we&#039;ll be declaring the necessary variables in the class definition of the plug-in and initializing those variables to default values in our &lt;code&gt;Init()&lt;/code&gt; function. We&#039;ll be storing most of our stateful data in simple arrays since they&#039;re faster than vectors or maps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up!&lt;/strong&gt; Depending on how complicated the information you&#039;re handling is, a vector or a map may be better suited.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&#039;s create an array that will be able to handle information for all of our players on the server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; sneakAttack[&lt;span class=&quot;hljs-number&quot;&gt;256&lt;/span&gt;];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like that, we&#039;ve got 256 records; one for each player slot even the reserved player slots. Here&#039;s how the structure will behave:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sneakAttack[ &amp;lt;player ID of whom this record belongs to&amp;gt; ] = &amp;lt;the ID of the victim of this sneak attack&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that doesn&#039;t make sense, let&#039;s consider the following situation: Player 0 would like to perform a sneak attack on Player 5. In the above int array, we&#039;ll have the following values:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sneakAttack[0] = 5;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#039;ll be initializing the values for the entire array to &lt;code&gt;-1&lt;/code&gt;, which will mean that there is no sneak attack planned yet. Why &lt;code&gt;-1&lt;/code&gt;? Because player slots start from 0, so a negative value will be apt. This way, we won&#039;t bombard player slot 0 with a lot of sneak attacks.&lt;/p&gt;
&lt;p&gt;Here&#039;s what our class will look like and our initialization of the array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; sneakAttack[&lt;span class=&quot;hljs-number&quot;&gt;256&lt;/span&gt;];
};

&lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Range-based for loop syntax; a C++11 feature&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; &amp;amp;i : sneakAttack)
    {
        i = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;slash-command-parameters-stateful-data&quot;&gt;Slash Command Parameters + Stateful Data&lt;/h2&gt;
&lt;p&gt;Following the plug-in specification, we&#039;ll be implementing the &lt;code&gt;/sneak&lt;/code&gt; command and make use of the stateful data. Here&#039;s the syntax of the slash command:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/sneak &amp;lt;player ID | callsign&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#039;s get the skeleton of the slash command implementation from our handy &lt;a href=&quot;https://bz-plugin-starter.projects.allejo.io/&quot;&gt;plug-in skeleton generator&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remembering from last chapter, &lt;code&gt;playerID&lt;/code&gt; is the ID of the player who executed the command and &lt;code&gt;params&lt;/code&gt; is a list of parameters given to the command (separated by space and respecting quotes).&lt;/p&gt;
&lt;p&gt;Let&#039;s start our implementation by saving the parameter data into an easy to remember variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* victimID = params-&amp;gt;get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str();

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have information stored, let&#039;s get started with our implementation! So &lt;code&gt;victimID&lt;/code&gt; stores the victim of the sneak attack, but we don&#039;t know how the player typed it in. It could be any of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my callsign&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The BZFS API works exclusively with player IDs, which are integers. So what are we going to do with the above strings? Luckily for us, &lt;a href=&quot;https://wiki.bzflag.org/Bz_getPlayerBySlotOrCallsign&quot;&gt;&lt;code&gt;bz_getPlayerBySlotOrCallsign()&lt;/code&gt;&lt;/a&gt; exists exactly for this purpose. It will take whatever value the player gave us and give us one of two things we know how to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bz_BasePlayerRecord*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#039;s use it to get a player record of the victim or exit out if the victim doesn&#039;t exist on the server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* victimID = params-&amp;gt;get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str();

        bz_BasePlayerRecord *pr = bz_getPlayerBySlotOrCallsign(victimID);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr)
        {
            bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;player \&quot;%s\&quot; not found&quot;&lt;/span&gt;, victimID);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
        {
            bz_freePlayerRecord(pr);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have a player record, let&#039;s set our stateful data for this player&#039;s sneak attack. This is important because this information will be accessed the next time a player spawns, which is an event, which is implemented in our &lt;code&gt;Event()&lt;/code&gt; function. Using stateful data allows us to share information across different parts of our code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* victimID = params-&amp;gt;get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str();

        bz_BasePlayerRecord *pr = bz_getPlayerBySlotOrCallsign(victimID);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr)
        {
            bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;player \&quot;%s\&quot; not found&quot;&lt;/span&gt;, victimID);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
        {
            sneakAttack[playerID] = pr-&amp;gt;playerID;
            bz_freePlayerRecord(pr);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#039;re all done with our slash command! Let&#039;s give our slash command a try with different information.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Works as intended. Yay!
&lt;pre&gt;&lt;code&gt;/sneak allejo&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Got a message: &lt;code&gt;player &quot;callsign&quot; not found&lt;/code&gt;
&lt;pre&gt;&lt;code&gt;/sneak callsign with spaces&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Got a message: &lt;code&gt;player &quot;&quot; not found&lt;/code&gt;
&lt;pre&gt;&lt;code&gt;/sneak&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, we definitely want some type of warning if I give the command too much or too little information. Let&#039;s fix that, the command expects one parameter so let&#039;s ensure we always have 1 parameter before we do any of our business logic.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (params-&amp;gt;size() != &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) 
        {
            bz_sendTextMessage(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;/sneak &amp;lt;player ID|callsign&amp;gt;&quot;&lt;/span&gt;);
            &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* victimID = params-&amp;gt;get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str();

        bz_BasePlayerRecord *pr = bz_getPlayerBySlotOrCallsign(victimID);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr)
        {
            bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;player \&quot;%s\&quot; not found&quot;&lt;/span&gt;, victimID);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
        {
            sneakAttack[playerID] = pr-&amp;gt;playerID;
            bz_freePlayerRecord(pr);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, now we&#039;re done with our slash command. Moving on...&lt;/p&gt;
&lt;h2 id=&quot;modification-events-stateful-data&quot;&gt;Modification Events + Stateful Data&lt;/h2&gt;
&lt;p&gt;Our slash command will save information about our sneak attack, but now we have to make use of it during our spawn event. In &lt;a href=&quot;/blog/bzflag-plug-ins-for-dummies-chapter-2/#modification-api-events&quot;&gt;chapter 2, we went over modification events&lt;/a&gt; and their ability to change how the server behaves. For this plug-in, we&#039;ll be modifying the spawn location of a player and will need an event relating to spawning. Here are our options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bz_eAllowSpawn&lt;/li&gt;
&lt;li&gt;bz_eGetPlayerSpawnPosEvent&lt;/li&gt;
&lt;li&gt;bz_ePlayerSpawnEvent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember, events beginning with with &lt;code&gt;bz_eAllow&lt;/code&gt; are modification events but there are some exceptions to the rule. If we take a look at the documention for of these events, we&#039;ll want to pick &lt;code&gt;bz_eGetPlayerSpawnPosEvent&lt;/code&gt; since this is the only event that gives us coordinates for a player&#039;s spawn location. Additionally, there&#039;s a &lt;code&gt;handled&lt;/code&gt; boolean, which let&#039;s us tell the server that we have modified the spawn location.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eGetPlayerSpawnPosEvent:
{
    bz_GetPlayerSpawnPosEventData_V1 *data = (bz_GetPlayerSpawnPosEventData_V1*)eventData;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Data&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// ----&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (int)          playerID  - ID of the player that is requesting the spawn position.&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (bz_eTeamType) team      - The team the player is currently in.&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (bool)         handled   - The current state representing if other plug-ins have modified the spawn position.&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (float[3])     pos       - Position where the player will be spawned. This value is initialized to the server&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (float)        rot       - The rotational direction that the player will be spawned at. This value is initialized&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (double)       eventTime - The local server time of the event.&lt;/span&gt;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&#039;s write our implementation of this event using our stateful data.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eGetPlayerSpawnPosEvent:
{
    bz_GetPlayerSpawnPosEventData_V1 *data = (bz_GetPlayerSpawnPosEventData_V1*)eventData;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Step 1&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (sneakAttack[data-&amp;gt;playerID] &amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 2&lt;/span&gt;
        bz_BasePlayerRecord *pr = bz_getPlayerByIndex(sneakAttack[data-&amp;gt;playerID]);

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 2.1&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr) 
        {
            bz_sendTextMessage(BZ_SERVER, data-&amp;gt;playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Your sneak attack failed! Victim not found.&quot;&lt;/span&gt;);
            &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
        }

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 3&lt;/span&gt;
        data-&amp;gt;handled = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
        data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;];
        data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;];
        data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;];
        data-&amp;gt;rot = pr-&amp;gt;lastKnownState-&amp;gt;rotation;

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 4&lt;/span&gt;
        bz_freePlayerRecord(pr);

        &lt;span class=&quot;hljs-comment&quot;&gt;// Step 5&lt;/span&gt;
        sneakAttack[data-&amp;gt;playerID] = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
    }
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;Remember, a &lt;code&gt;-1&lt;/code&gt; value means that there isn&#039;t a sneak attack prepared so we can move on if there isn&#039;t a sneak attack planned.&lt;/p&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Create a player record for the victim of the sneak attack. The player record contains a &lt;code&gt;bz_PlayerUpdateState&lt;/code&gt; object which contains the player&#039;s last known position.&lt;/p&gt;
&lt;h3 id=&quot;step-2-1&quot;&gt;Step 2.1&lt;/h3&gt;
&lt;p&gt;Remember, a player record could be NULL so we need to make sure that our victim actually exists.&lt;/p&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;We set the &lt;code&gt;handled&lt;/code&gt; boolean to true to notify the server that a plug-in will be changing the spawn location of a player. A player&#039;s position is denoted by a &lt;code&gt;float[3]&lt;/code&gt; where &lt;code&gt;0 -&amp;gt; x&lt;/code&gt;, &lt;code&gt;1 -&amp;gt; y&lt;/code&gt;, and &lt;code&gt;2 -&amp;gt; z&lt;/code&gt; on a 3D coordiante system.&lt;/p&gt;
&lt;h3 id=&quot;step-4&quot;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;Remember to free any memory you allocate!&lt;/p&gt;
&lt;h3 id=&quot;step-5&quot;&gt;Step 5&lt;/h3&gt;
&lt;p&gt;Set the sneak attack value to &lt;code&gt;-1&lt;/code&gt; so next time the player spawns, they won&#039;t be at the same victim&#039;s location &lt;strong&gt;unless&lt;/strong&gt; they use the &lt;code&gt;/sneak&lt;/code&gt; command again.&lt;/p&gt;
&lt;h2 id=&quot;keeping-our-state-clean&quot;&gt;Keeping Our State Clean&lt;/h2&gt;
&lt;p&gt;An important aspect about using state data is that we need to keep it clean. It&#039;s been mentioned before that player IDs are reused when players join/part, so what would happen if Player 0 plans a sneak attack on Player 5 but leaves before they respawn? The next player who joins as Player 0 will have a sneak attack on Player 5 already without even using the &lt;code&gt;/sneak&lt;/code&gt; command. To prevent this from happening, it&#039;s a good idea to reset state data whenever necessary.&lt;/p&gt;
&lt;p&gt;In this case, since our state data is directly tied to each player, we&#039;ll need to reset the data each time the player leaves. We could also reset our data when a player joins and in this case example, it wouldn&#039;t matter. However, if you start keeping more memory intensive information, why bother keeping that around when a player is already gone? So it&#039;s a good idea to reset data when a player parts.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerPartEvent:
{
    bz_PlayerJoinPartEventData_V1 *data = (bz_PlayerJoinPartEventData_V1*)eventData;

    sneakAttack[data-&amp;gt;playerID] = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;our-sneak-plug-in&quot;&gt;Our Sneak Plug-in&lt;/h2&gt;
&lt;p&gt;Here&#039;s our whole plug-in from this chapter!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomSlashCommandHandler
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;&lt;/span&gt;;

    &lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; sneakAttack[&lt;span class=&quot;hljs-number&quot;&gt;256&lt;/span&gt;];
};

BZ_PLUGIN(Sneak)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Sneak::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Sneak&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_eGetPlayerSpawnPosEvent);
    Register(bz_ePlayerPartEvent);

    bz_registerCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);

    &lt;span class=&quot;hljs-comment&quot;&gt;// Range-based for loop syntax; a C++11 feature&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; &amp;amp;i : sneakAttack)
    {
        i = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
    }
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();

    bz_removeCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eGetPlayerSpawnPosEvent:
        {
            bz_GetPlayerSpawnPosEventData_V1 *data = (bz_GetPlayerSpawnPosEventData_V1*)eventData;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (sneakAttack[data-&amp;gt;playerID] &amp;gt;= &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)
            {
                bz_BasePlayerRecord *pr = bz_getPlayerByIndex(sneakAttack[data-&amp;gt;playerID]);

                &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr) 
                {
                    bz_sendTextMessage(BZ_SERVER, data-&amp;gt;playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Your sneak attack failed! Victim not found.&quot;&lt;/span&gt;);
                    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
                }

                data-&amp;gt;handled = &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
                data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;];
                data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;];
                data-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;] = pr-&amp;gt;lastKnownState-&amp;gt;pos[&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;];
                data-&amp;gt;rot = pr-&amp;gt;lastKnownState-&amp;gt;rotation;

                bz_freePlayerRecord(pr);

                sneakAttack[data-&amp;gt;playerID] = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
            }
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerPartEvent:
        {
            bz_PlayerJoinPartEventData_V1 *data = (bz_PlayerJoinPartEventData_V1*)eventData;

            sneakAttack[data-&amp;gt;playerID] = &lt;span class=&quot;hljs-number&quot;&gt;-1&lt;/span&gt;;
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Sneak::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sneak&quot;&lt;/span&gt;)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (params-&amp;gt;size() != &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) 
        {
            bz_sendTextMessage(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;/sneak &amp;lt;player ID|callsign&amp;gt;&quot;&lt;/span&gt;);
            &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* victimID = params-&amp;gt;get(&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;).c_str();

        bz_BasePlayerRecord *pr = bz_getPlayerBySlotOrCallsign(victimID);

        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr)
        {
            bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;player \&quot;%s\&quot; not found&quot;&lt;/span&gt;, victimID);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt;
        {
            sneakAttack[playerID] = pr-&amp;gt;playerID;
            bz_freePlayerRecord(pr);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;After last week&#039;s short chapter, was this chapter long enough for you? I hope I didn&#039;t overwhelm you with too much information and that all of this made sense to you. Next up, we&#039;ll be going over configuration files.&lt;/p&gt;
&lt;p&gt;Special thanks to the_map for being this week&#039;s guest editor!&lt;/p&gt;
&lt;h3 id=&quot;on-your-own&quot;&gt;On Your Own&lt;/h3&gt;
&lt;p&gt;If you would like to continue practicing, expand on this plug-in on your own by accomplishing the following task:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep track of a new bit of stateful data for each sneak attack. The new information will be a flag the player will receive when they spawn.
&lt;ul&gt;
&lt;li&gt;Modify the slash command to be: &lt;code&gt;/sneak &amp;lt;player ID | callsign&amp;gt; &amp;lt;flag abbv&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Hints: &lt;code&gt;bz_ePlayerSpawnEvent&lt;/code&gt; &amp;amp; &lt;code&gt;bz_givePlayerFlag()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 3</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-3/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;3" />
            <updated>2017-06-24T08:00:00+00:00</updated>
            <id>5d367369093c7b28647aefc90687f9ba5791478f</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-3/">&lt;p&gt;We&#039;ve both made it to chapter 3; we should be proud. You, because you&#039;ve made it this far. Me, because I&#039;ve been able to write this on a weekly basis without missing a day thus far. Today, we&#039;ll be talking about slash commands. Don&#039;t worry, like always, we&#039;ll be going step by step.&lt;/p&gt;
&lt;h2 id=&quot;slash-commands&quot;&gt;Slash Commands&lt;/h2&gt;
&lt;p&gt;Registering custom slash commands is pretty easy once you&#039;ve got the hang of things and understand what is going on. Once again, I&#039;ll be using my &lt;a href=&quot;https://bz-plugin-starter.projects.allejo.io/&quot;&gt;plug-in generator&lt;/a&gt; to take care of all the generation and set up.&lt;/p&gt;
&lt;h3 id=&quot;extending-a-class&quot;&gt;Extending a Class&lt;/h3&gt;
&lt;p&gt;As mentioned in the first chapter, our plug-ins will always extend the &lt;code&gt;bz_Plugin&lt;/code&gt; class but in order to implement slash commands, we must extend one more class: &lt;code&gt;bz_CustomSlashCommandHandler&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up!&lt;/strong&gt; This is an opinionated approach where all the logic is in one class. You may wish to create a separate class dedicated to handling slash command logic but I prefer keeping things together so this chapter will keep things in one class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By extending &lt;code&gt;bz_CustomSlashCommandHandler&lt;/code&gt;, we&#039;re required to implement the following virtual function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#039;s an explanation of each parameter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;playerID&lt;/code&gt; - The ID of the player who executed the slash command&lt;/li&gt;
&lt;li&gt;&lt;code&gt;command&lt;/code&gt; - The command that was executed (this is useful when you register more than one command)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;message&lt;/code&gt; - The slash command and all of the parameters together as a string. This &lt;em&gt;parameter&lt;/em&gt; is commented out in order to prevent compiler warnings of unused variables since &lt;code&gt;message&lt;/code&gt; is rarely used. Notice carefully that only the variable name is commented out and not the data type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;params&lt;/code&gt; - A list of the parameters given to the command; parameters are split by spaces and respects quotes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;registering-the-slash-command&quot;&gt;Registering the Slash Command&lt;/h3&gt;
&lt;p&gt;When coming up with your custom slash commands, there are few things that you should keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#039;t accidentally override existing slash commands&lt;/li&gt;
&lt;li&gt;Slash commands names cannot have spaces&lt;/li&gt;
&lt;li&gt;Keep your slash commands easy to remember&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We must tell BZFS that we&#039;ll be creating a custom command so the server knows that when this custom command is executed, it is forwarded to our plug-in instead of having the server attempt to handle an unknown command. To register a custom slash command called &amp;quot;mycommand&amp;quot;, we call the &lt;a href=&quot;https://wiki.bzflag.org/Bz_registerCustomSlashCommand&quot;&gt;&lt;code&gt;bz_registerCustomSlashCommand()&lt;/code&gt;&lt;/a&gt; function inside of our plug-in&#039;s &lt;code&gt;Init()&lt;/code&gt; function. Remember, the &lt;code&gt;Init()&lt;/code&gt; function is the first function called when a plug-in is loaded, so you must tell BZFS your custom commands when your plug-in is first loaded.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_registerCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second argument to this function is a reference to the class handling the implementation of the slash command, so &lt;code&gt;this&lt;/code&gt; refers to the current class we&#039;re in. If you&#039;d like to separate the commands into a separate class, you would use a reference to that class instead.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up!&lt;/strong&gt; &lt;a href=&quot;https://forums.bzflag.org/viewtopic.php?f=78&amp;amp;t=19007&quot;&gt;Since 2.4.4, custom slash commands can override existing commands&lt;/a&gt; meaning that we can override built-in server commands such as &lt;code&gt;/kick&lt;/code&gt; or &lt;code&gt;/ban&lt;/code&gt;; more on this later on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;removing-the-slash-command&quot;&gt;Removing the Slash Command&lt;/h3&gt;
&lt;p&gt;And when the plug-in is unloaded, we must tell BZFS that we&#039;ll be removing the slash command. Otherwise, the server will attempt to forward the command to a plug-in that is no longer loaded and that&#039;s just not nice. We need to call &lt;a href=&quot;https://wiki.bzflag.org/Bz_removeCustomSlashCommand&quot;&gt;&lt;code&gt;bz_removeCustomSlashCommand()&lt;/code&gt;&lt;/a&gt; the &lt;code&gt;Cleanup()&lt;/code&gt; function, which is called when the plug-in is unloaded.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_removeCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;implementing-commands&quot;&gt;Implementing Commands&lt;/h3&gt;
&lt;p&gt;The finally step to creating custom slash commands is implementing their behavior and what they do. We do this in the implementation of the virtual &lt;code&gt;SlashCommand()&lt;/code&gt; function that we saw at the beginning of this chapter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-comment&quot;&gt;// step 1&lt;/span&gt;
    {
        bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;You ran the %s command.&quot;&lt;/span&gt;, command.c_str()); &lt;span class=&quot;hljs-comment&quot;&gt;// step 2&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;; &lt;span class=&quot;hljs-comment&quot;&gt;// step 3&lt;/span&gt;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;; &lt;span class=&quot;hljs-comment&quot;&gt;// step 4&lt;/span&gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-1&quot;&gt;Step 1&lt;/h4&gt;
&lt;p&gt;The first step is to check which command got called. If your plug-in has multiple custom commands, you need to check that you&#039;re defining the behavior for the correct command and in this example, we&#039;re doing that with an if statement. Notice that &lt;code&gt;command&lt;/code&gt; is a &lt;code&gt;bz_ApiString&lt;/code&gt; so that means we can use the &lt;code&gt;==&lt;/code&gt; operator for comparisons.&lt;/p&gt;
&lt;h4 id=&quot;step-2&quot;&gt;Step 2&lt;/h4&gt;
&lt;p&gt;We send a message to the player who executed the command and tell them what command they ran.&lt;/p&gt;
&lt;h4 id=&quot;step-3&quot;&gt;Step 3&lt;/h4&gt;
&lt;p&gt;Notice how &lt;code&gt;SlashCommand()&lt;/code&gt;&#039;s return type is a boolean? If a slash command&#039;s implementation returns true, then the BZFS will know that the command has been handled and doesn&#039;t need to do anything else. If this implementation returns false, then BZFS will attempt to take care of the implentation; this is especially useful for partially overriding built-in slash commands.&lt;/p&gt;
&lt;h4 id=&quot;step-4&quot;&gt;Step 4&lt;/h4&gt;
&lt;p&gt;This return statement will typically never be reached unless you have not implemented your command so it&#039;s a good idea to return false so BZFS will throw an error of an unknown command.&lt;/p&gt;
&lt;h3 id=&quot;our-sample-plug-in&quot;&gt;Our Sample Plug-in&lt;/h3&gt;
&lt;p&gt;Here&#039;s what our final plug-in looks like.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin, &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_CustomSlashCommandHandler
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt; &lt;/span&gt;{ &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;; };
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;&lt;/span&gt;;
};

BZ_PLUGIN(SAMPLE_PLUGIN)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;SAMPLE_PLUGIN&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    bz_registerCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;this&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();

    bz_removeCustomSlashCommand(&lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;SAMPLE_PLUGIN::SlashCommand&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID, bz_ApiString command, bz_ApiString &lt;span class=&quot;hljs-comment&quot;&gt;/*message*/&lt;/span&gt;, bz_APIStringList *params)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (command == &lt;span class=&quot;hljs-string&quot;&gt;&quot;mycommand&quot;&lt;/span&gt;)
    {
        bz_sendTextMessagef(BZ_SERVER, playerID, &lt;span class=&quot;hljs-string&quot;&gt;&quot;You ran the %s command.&quot;&lt;/span&gt;, command.c_str());
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Was this chapter too short for you? Sorry about that, it&#039;s not laziness I swear! I did not want this chapter to be overwhelming with a lot of information. Stay tuned for next week&#039;s chapter where we&#039;ll be learning about slash command parameters and &lt;a href=&quot;https://en.wikipedia.org/wiki/State_(computer_science)&quot;&gt;stateful data&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;on-your-own&quot;&gt;On Your Own&lt;/h3&gt;
&lt;p&gt;Exploring the &lt;a href=&quot;https://wiki.bzflag.org/Functions_(API)&quot;&gt;list of API functions&lt;/a&gt;, create commands to match the following scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A slash command that will double your current score. Hint: &lt;code&gt;bz_incrementPlayerWins()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A slash command that will return the current flag that you&#039;re carrying&lt;/li&gt;
&lt;/ul&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 2</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-2/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;2" />
            <updated>2017-06-17T08:00:00+00:00</updated>
            <id>a850cd814ce9d82807574023df3b04fb6defc6f4</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-2/">&lt;p&gt;I told you it wouldn&#039;t take another 2 years until the next post. We&#039;re back and in this chapter we&#039;ll be discussing player records and modification API events.&lt;/p&gt;
&lt;p&gt;Last week, I briefly mentioned &lt;a href=&quot;https://wiki.bzflag.org/Bz_BasePlayerRecord&quot;&gt;player records&lt;/a&gt; and the fact that they&#039;ll typically be used whenever we want information regarding a player on the server. Let&#039;s go over what information is available, how to create them, how to use them, and how to free them when we&#039;re done.&lt;/p&gt;
&lt;h2 id=&quot;player-records&quot;&gt;Player Records&lt;/h2&gt;
&lt;p&gt;All player records are &lt;code&gt;bz_BasePlayerRecord&lt;/code&gt; objects and they can either be created manually or are automatically given to you by the API. For example, the &lt;a href=&quot;https://wiki.bzflag.org/Bz_ePlayerJoinEvent&quot;&gt;&lt;code&gt;bz_ePlayerJoinEvent&lt;/code&gt;&lt;/a&gt; has a &lt;code&gt;record&lt;/code&gt; attribute that is automatically created and freed for you; however, the player record will only exist in the same scope of &lt;code&gt;*data&lt;/code&gt; as seen in the snippet below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_ePlayerJoinEvent:
{
    bz_PlayerJoinPartEventData_V1 *data = (bz_PlayerJoinPartEventData_V1*)eventData;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Data&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// ----&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (int)                  playerID  - The player ID that is joining&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (bz_BasePlayerRecord*) record    - The player record for the joining player&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (double)               eventTime - Time of event.&lt;/span&gt;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So what happens when we&#039;re in a place of our code where the API doesn&#039;t provide a record for us? We create it by ourselves. We&#039;ll be using the &lt;a href=&quot;https://wiki.bzflag.org/Bz_getPlayerByIndex&quot;&gt;&lt;code&gt;bz_getPlayerByIndex(int playerID)&lt;/code&gt;&lt;/a&gt; function to create a record for the player with the given player ID and we&#039;ll receive a pointer to our player record; since we&#039;re receiving a pointer to the data, we &lt;strong&gt;must&lt;/strong&gt; free the record once we&#039;re done with it with &lt;a href=&quot;https://wiki.bzflag.org/Bz_freePlayerRecord&quot;&gt;&lt;code&gt;bz_freePlayerRecord()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the previous chapter, it was mentioned that player IDs were unique to players during their current session on a server; these player IDs will be used throughout your plugin. Let&#039;s create a player record for the player whose ID is currently 1.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt; playerID = &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;;
bz_BasePlayerRecord *record = bz_getPlayerByIndex(playerID);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, before we access the information in the player record, we must check that &lt;code&gt;record&lt;/code&gt; is not NULL because &lt;code&gt;bz_getPlayerByIndex()&lt;/code&gt; will return NULL if a player with the given ID does not exist.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!record) {
    &lt;span class=&quot;hljs-comment&quot;&gt;// The player did not exist&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#039;ve created the player record and have checked that it actually contains information, here&#039;s &lt;em&gt;some&lt;/em&gt; of the information we now have access to. Take a look at the &lt;a href=&quot;https://wiki.bzflag.org/Bz_BasePlayerRecord&quot;&gt;bz_BasePlayerRecord documentation&lt;/a&gt; to view all of the information available in each record.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;playerID&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;callsign&lt;/td&gt;
&lt;td&gt;the player&#039;s callsign&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;team&lt;/td&gt;
&lt;td&gt;the current team the player is on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;currentFlag&lt;/td&gt;
&lt;td&gt;the current flag they&#039;re holding; an empty string if no flag is being held&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spawned&lt;/td&gt;
&lt;td&gt;a boolean set to true if they&#039;re alive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;verified&lt;/td&gt;
&lt;td&gt;set to true if the player is a registered account (i.e. has a &lt;code&gt;+&lt;/code&gt; next to their name)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bzID&lt;/td&gt;
&lt;td&gt;the registered player&#039;s globally unique ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;td&gt;whether or not the player is an admin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Since &lt;code&gt;record&lt;/code&gt; is our pointer to our player record, we can access all of this information with the &lt;code&gt;-&amp;gt;&lt;/code&gt; operator (or the &lt;code&gt;.&lt;/code&gt; operator too).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; callsign = record-&amp;gt;callsign;

&lt;span class=&quot;hljs-comment&quot;&gt;// or&lt;/span&gt;

&lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; callsign = (*record).callsign;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&#039;ve stored the callsign, we&#039;re done with player record so now we must free it or else it&#039;ll continue to take up memory, a small amount but if you continue creating more records without freeing them those small amounts of memory will add up.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_freePlayerRecord(record);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;modification-api-events&quot;&gt;Modification API Events&lt;/h2&gt;
&lt;p&gt;In the previous chapter, I mentioned &amp;quot;notification&amp;quot; API events and now I&#039;m mentioning &amp;quot;modification&amp;quot; events? These aren&#039;t vocabulary words from the API, they&#039;re just what I heard from flying_popcorn years ago and so the names have stuck. What I consider a notification is an event that is used solely for notification purposes and doesn&#039;t allow you to change the behavior of the server and a modification event is an event that will allow you to control how the server behaves. Make sense? It&#039;s ok if it doesn&#039;t, we&#039;ll be building a plug-in to show you what it means.&lt;/p&gt;
&lt;p&gt;Our plug-in will restrict which flags players can grab based on certain criteria. Let&#039;s build a ridiculous plug-in that does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All players can grab every flag except GM and WG&lt;/li&gt;
&lt;li&gt;Only registered players will be able to grab GM&lt;/li&gt;
&lt;li&gt;Only admins will be able to grab WG&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&#039;s see what event can we use to achieve this. &lt;code&gt;bz_eFlagGrabbedEvent&lt;/code&gt; sounds perfect for the job, right? &lt;code&gt;bz_eFlagGrabbedEvent&lt;/code&gt; is a notification event meaning we&#039;ll only be notified of when a player grabs the flag but we won&#039;t be able to prevent a player from actually grabbing the flag, since that&#039;d require us to change the behavior of the server. Most modification events typically begin with &lt;code&gt;bz_eAllow&lt;/code&gt; so if we look at the list of events again, we&#039;ll see that there&#039;s a &lt;code&gt;bz_eAllowFlagGrab&lt;/code&gt; event; now &lt;em&gt;that&lt;/em&gt; sounds like what we&#039;re looking for.&lt;/p&gt;
&lt;p&gt;The typical server behavior is as follows: when a player drives over a flag, the server gives the player the flag. We would like to change this behavior so only some players can grab certain flags; this is where our modification event comes in. Notice how there is an &lt;code&gt;allow&lt;/code&gt; boolean available to use in this event and how our event data is a pointer.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eAllowFlagGrab:
{
    bz_AllowFlagGrabData_V1 *data = (bz_AllowFlagGrabData_V1*)eventData;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Data&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// ----&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (int)         playerID  - The ID of the player who is grabbing the flag&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (int)         flagID    - The ID of the flag that is going to be grabbed&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (const char*) flagType  - The type of the flag about to be grabbed&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (bool)        allow     - Whether or not to allow the flag grab&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (double)      eventTime - The server time at which the event occurred (in seconds).&lt;/span&gt;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we&#039;re working with a pointer, any modifications to the event data will be seen by the server. In order to prevent the server from giving the player a flag, we need to change the &lt;code&gt;allow&lt;/code&gt; boolean to false.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eAllowFlagGrab:
{
    bz_AllowFlagGrabData_V1 *data = (bz_AllowFlagGrabData_V1*)eventData;

    data-&amp;gt;allow = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#039;s it! We&#039;re done, right? Let&#039;s take a closer look at what we&#039;re doing. Whenever a player is about to grab a flag, the &lt;code&gt;bz_eAllowFlagGrab&lt;/code&gt; event is triggered and when that&#039;s triggered, we&#039;re telling the server not to give the player the flag. Our plug-in was not meant to disable all of the flags for everyone. Let&#039;s fix that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eAllowFlagGrab:
{
    bz_AllowFlagGrabData_V1 *data = (bz_AllowFlagGrabData_V1*)eventData;
    bz_BasePlayerRecord *record = bz_getPlayerByIndex(playerID); &lt;span class=&quot;hljs-comment&quot;&gt;// step 1&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;// step 2&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!record) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
    }

    &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; flag = flagType; &lt;span class=&quot;hljs-comment&quot;&gt;// step 3&lt;/span&gt;

    &lt;span class=&quot;hljs-comment&quot;&gt;// step 4&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flag == &lt;span class=&quot;hljs-string&quot;&gt;&quot;GM&quot;&lt;/span&gt; &amp;amp;&amp;amp; !record-&amp;gt;verified) {
        data-&amp;gt;allow = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
    }
    &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flag == &lt;span class=&quot;hljs-string&quot;&gt;&quot;WG&quot;&lt;/span&gt; &amp;amp;&amp;amp; !record-&amp;gt;admin) {
        data-&amp;gt;allow = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
    }

    bz_freePlayerRecord(record); &lt;span class=&quot;hljs-comment&quot;&gt;// step 5&lt;/span&gt;
}
&lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;As we saw in the previous section, a player record will give us access to the following information that we&#039;ll be using in our plug-in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Whether or not the player is registered&lt;/li&gt;
&lt;li&gt;Whether or not the player is an admin&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;As mentioned previously, &lt;code&gt;bz_getPlayerByIndex()&lt;/code&gt; may return NULL if the given player doesn&#039;t exist. In this situation, it is highly unlikely we&#039;ll ever get an event triggered by a non-existent player but let&#039;s play it safe. If we try to access information from a NULL player record, we&#039;ll get a segfault. What happens when a plug-in segfaults? The server crashes. Let&#039;s prevent that from happening by making sure we&#039;re not working with NULL.&lt;/p&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;The flag a player is attempting to grab is given to us through the &lt;code&gt;flagType&lt;/code&gt; variable but it&#039;s given to us as a &lt;code&gt;const char*&lt;/code&gt;. Let&#039;s remember that this is C++ and to compare &lt;code&gt;const char*&lt;/code&gt; values, we need to use &lt;a href=&quot;http://www.cplusplus.com/reference/cstring/strcmp/&quot;&gt;&lt;code&gt;strcmp&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;==&lt;/code&gt;. For this reason, I&#039;m assigning the data to an &lt;code&gt;std::string&lt;/code&gt; just so I can use the convenience of &lt;code&gt;==&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-4&quot;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;Now, this is where we have to do our checks for both the flag types and the player status. We have &lt;code&gt;record-&amp;gt;verified&lt;/code&gt; and &lt;code&gt;record-&amp;gt;admin&lt;/code&gt;, which both return true when the player is using a registered account and when the player is an admin, respectively. If we negate both of these values, we&#039;ll get unregistered account status and non-admin status.&lt;/p&gt;
&lt;h3 id=&quot;step-5&quot;&gt;Step 5&lt;/h3&gt;
&lt;p&gt;Remember, we need to free our player record once we&#039;re done with it. Don&#039;t leak memory!&lt;/p&gt;
&lt;h2 id=&quot;final-plug-in&quot;&gt;Final Plug-in&lt;/h2&gt;
&lt;p&gt;That&#039;s it! Here&#039;s what our final plug-in will look like.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagRestrictions&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;
};

BZ_PLUGIN(FlagRestrictions)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;FlagRestrictions::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;FlagRestrictions&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagRestrictions::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_eAllowFlagGrab);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagRestrictions::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;FlagRestrictions::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eAllowFlagGrab:
        {
            bz_AllowFlagGrabData_V1 *data = (bz_AllowFlagGrabData_V1*)eventData;
            bz_BasePlayerRecord *record = bz_getPlayerByIndex(playerID);

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!record) {
                &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
            }

            &lt;span class=&quot;hljs-built_in&quot;&gt;std&lt;/span&gt;::&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; flag = flagType;

            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flag == &lt;span class=&quot;hljs-string&quot;&gt;&quot;GM&quot;&lt;/span&gt; &amp;amp;&amp;amp; !record-&amp;gt;verified) {
                data-&amp;gt;allow = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
            }
            &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (flag == &lt;span class=&quot;hljs-string&quot;&gt;&quot;WG&quot;&lt;/span&gt; &amp;amp;&amp;amp; !record-&amp;gt;admin) {
                data-&amp;gt;allow = &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;;
            }

            bz_freePlayerRecord(record);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations on finishing your second plug-in! As always, your feedback is welcome and appreciated! I will be more than happy to improve this series to benefit all future plug-in developers.&lt;/p&gt;
&lt;h3 id=&quot;on-your-own&quot;&gt;On Your Own&lt;/h3&gt;
&lt;p&gt;If you would like to expand on what you&#039;ve learned, here some tasks for you to try and tackle on your own:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When a player is not allowed to grab a flag, send a message to that player from the server stating why they are not allowed to grab the flag. For example, if a non-admin attempts to grab Wings, send them a message saying that only admins have the privilege of grabbing the Wings flag. Same for unregistered players and GM.&lt;/li&gt;
&lt;li&gt;Only allow your own callsign to grab the Laser flag.&lt;/li&gt;
&lt;li&gt;Add a new flag restriction that will only allow players with an even BZID to grab the Genocide flag. Hint: &lt;code&gt;record-&amp;gt;bzID&lt;/code&gt; is stored as a string.&lt;/li&gt;
&lt;/ol&gt;</content>
        </entry>
            <entry>
            <title>BZFlag Plug-ins for Dummies: Chapter 1</title>
            <link href="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-1/" rel="alternate" type="text/html" title="BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies&#x3A;&#x20;Chapter&#x20;1" />
            <updated>2017-06-10T08:00:00+00:00</updated>
            <id>4e799508b11161c4fa3fc8afa613b3a204f3c6a1</id>
            <content type="html" xml:base="https://allejo.io/blog/bzflag-plug-ins-for-dummies-chapter-1/">&lt;p&gt;&lt;a href=&quot;/blog/my-new-series-bzflag-plug-ins-for-dummies/&quot;&gt;Back in 2015&lt;/a&gt;, I said I&#039;d write this series. Well, here it is. Better late than never. Right? In this first chapter, I&#039;ll be going over the structure of a plug-in while building our first sample plug-in.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://bzflag.org&quot;&gt;BZFlag&lt;/a&gt; plug-ins are written in C++ and each plug-in is a class which extends the &lt;code&gt;bz_Plugin&lt;/code&gt; base class. Moving forward in this tutorial, when the word &amp;quot;plug-in&amp;quot; is used, it refers to the class that defines the plug-in&#039;s functionality. For example, when I say there are three methods each plug-in must define I mean there are three methods that the plug-in&#039;s class must define. This should make sense as you continue to read further in this tutorial.&lt;/p&gt;
&lt;p&gt;I have a built an &lt;a href=&quot;https://bz-plugin-starter.projects.allejo.io/&quot;&gt;(opinionated) generator&lt;/a&gt;, which will build the skeleton of your plugin and will take care of all the configuration of everything. For the rest of this tutorial, I&#039;ll be relying heavily on this generator.&lt;/p&gt;
&lt;h2 id=&quot;plug-in-core-setup&quot;&gt;Plug-in Core + Setup&lt;/h2&gt;
&lt;p&gt;The four methods that each plug-in must define are the following:&lt;/p&gt;
&lt;h3 id=&quot;virtual-const-char-name&quot;&gt;&lt;code&gt;virtual const char* Name ();&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method defines the name of the plugin and what will be returned in debug messages and when &lt;code&gt;/listplugins&lt;/code&gt; is run.&lt;/p&gt;
&lt;h3 id=&quot;virtual-void-init-const-char-config&quot;&gt;&lt;code&gt;virtual void Init (const char* config);&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method is where all of the plug-in configuration is done and is the method that is called when the plug-in is loaded. In this method, we will need to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Register which events we&#039;ll be listening to&lt;/li&gt;
&lt;li&gt;Register custom flags&lt;/li&gt;
&lt;li&gt;Register custom slash commands&lt;/li&gt;
&lt;li&gt;Register custom map objects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;virtual-void-cleanup&quot;&gt;&lt;code&gt;virtual void Cleanup ();&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method is called whenever the plug-in is unloaded and is where you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Call the &lt;code&gt;Flush();&lt;/code&gt; function (to unregister your event callbacks)&lt;/li&gt;
&lt;li&gt;Remove custom flags&lt;/li&gt;
&lt;li&gt;Remove custom slash commands&lt;/li&gt;
&lt;li&gt;Remove custom map objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This method has a default implementation that calls &lt;code&gt;Flush();&lt;/code&gt; automatically, however it&#039;s a good idea to always create this implementation so you don&#039;t forget remove custom registrations if you add any in the future of your plug-in development.&lt;/p&gt;
&lt;h3 id=&quot;virtual-void-event-bz-eventdata-eventdata&quot;&gt;&lt;code&gt;virtual void Event (bz_EventData* eventData);&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This method is where the main behavior (our callbacks) of the plug-in is written.&lt;/p&gt;
&lt;h2 id=&quot;api-events&quot;&gt;API Events&lt;/h2&gt;
&lt;p&gt;BZFlag plug-ins work (mainly) by defining callbacks for specific events depending on the purpose of the plug-in. Plugins are not limited solely to events but in this first chapter, that&#039;s what we&#039;ll be covering. If you use the plug-in skeleton generator, you&#039;ll be able to check off which events you intend to use and it&#039;ll have documentation notes for you to use with what information is available in each event.&lt;/p&gt;
&lt;p&gt;There are events available for the majority of things that happen on a server such as a player joining, leaving, dying, capturing a flag, etc. When you build your plug-in, you&#039;ll need to plan out which events you will need.&lt;/p&gt;
&lt;h2 id=&quot;building-a-plug-in-with-notification-type-events&quot;&gt;Building a Plug-in with Notification Type Events&lt;/h2&gt;
&lt;p&gt;I will be taking you through on building a plug-in that will notify unregistered players why they cannot speak. This is a hypothetical plug-in that will be used on servers where only registered players are allowed to speak.&lt;/p&gt;
&lt;h3 id=&quot;1-naming-your-plug-in&quot;&gt;1. Naming Your Plug-in&lt;/h3&gt;
&lt;p&gt;What&#039;s in a name? This name will be used in debug messages and &lt;code&gt;/listplugins&lt;/code&gt; so what you return is up to you. I&#039;ve adopted the practice of using the following pattern in my plug-ins:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Plug-in Name&amp;gt; &amp;lt;Major&amp;gt;.&amp;lt;Minor&amp;gt;.&amp;lt;Revision&amp;gt; (&amp;lt;Build&amp;gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why so explicit? My plug-ins are never perfect the first time around and I always add more features, so I&#039;d like to know which version is currently loaded on a server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Chat Notifier 1.0.0 (1)&quot;&lt;/span&gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-registering-events&quot;&gt;2. Registering Events&lt;/h3&gt;
&lt;p&gt;The first step in building the plug-in is to notify the server which events we&#039;ll be listening to when the plug-in is loaded. We do this in the &lt;code&gt;Init()&lt;/code&gt; method and will register the events we want to listen to using the &lt;code&gt;Register()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;For this plug-in, we&#039;ll be listening to &lt;code&gt;bz_eRawChatMessageEvent&lt;/code&gt;, which is called every time a player sends a message before any filtering and permission checks occur.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_eRawChatMessageEvent);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-defining-behavior&quot;&gt;3. Defining Behavior&lt;/h3&gt;
&lt;p&gt;After we&#039;ve specified to the server that we want to listen to this event, we now have to define what needs to happen when that event is triggered; we do this in the &lt;code&gt;Event()&lt;/code&gt; method. In this method, we&#039;ll be defining the behavior for &lt;strong&gt;all&lt;/strong&gt; of the events we&#039;re listening to so I&#039;ll be using a switch block to handle the behavior for each event (only one event in this plug-in&#039;s case) but I can also use an if statement.&lt;/p&gt;
&lt;p&gt;Using the plug-in generator, here&#039;s what I&#039;ve got as the definition for our &lt;code&gt;Event()&lt;/code&gt; method. For each event, we&#039;re given a &lt;code&gt;bz_EventData&lt;/code&gt; object that we can cast into more specific objects based on the event. Some events are used for notification purposes only and do not have any objects you can cast &lt;code&gt;bz_EventData&lt;/code&gt; to; for &lt;code&gt;bz_eRawChatMessageEvent&lt;/code&gt;, we&#039;re given &lt;code&gt;bz_ChatEventData_V2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eRawChatMessageEvent:
        {
            bz_ChatEventData_V2 *data = (bz_ChatEventData_V2*)eventData;

            &lt;span class=&quot;hljs-comment&quot;&gt;// Data&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// ----&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (int)             from        - The player ID sending the message.&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (int)             to          - The player ID that the message is to if the message is to an individual, or a broadcast. If the message is a broadcast the id will be BZ_ALLUSERS.&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (bz_eTeamType)    team        - The team the message is for if it not for an individual or a broadcast. If it is not a team message the team will be eNoTeam.&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (bz_ApiString)    message     - The filtered final text of the message.&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (bz_eMessageType) messageType - The type of message being sent&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// (double)          eventTime   - The time of the event.&lt;/span&gt;
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So here&#039;s the logic that we&#039;ll be following in this plug-in:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a player record for the player who sent the message&lt;/li&gt;
&lt;li&gt;Does the record show the player is registered?
&lt;ol&gt;
&lt;li&gt;If not, then send them a message&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Delete the player record to free memory&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;bz_ChatEventData_V2 *data = (bz_ChatEventData_V2*)eventData;
bz_BasePlayerRecord *pr = bz_getPlayerByIndex(data-&amp;gt;from); &lt;span class=&quot;hljs-comment&quot;&gt;// step 1&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;// step 2&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr-&amp;gt;verified) {
    &lt;span class=&quot;hljs-comment&quot;&gt;// step 2.1&lt;/span&gt;
    bz_sendTextMessage(BZ_SERVER, data-&amp;gt;from, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Register an account on forums.bzflag.org in order to talk.&quot;&lt;/span&gt;);
}

&lt;span class=&quot;hljs-comment&quot;&gt;// step 3&lt;/span&gt;
bz_freePlayerRecord(pr);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&#039;s break things down further.&lt;/p&gt;
&lt;h4 id=&quot;step-1&quot;&gt;Step 1&lt;/h4&gt;
&lt;p&gt;If we want information regarding an individual player, we&#039;ll often need to create a &lt;a href=&quot;https://wiki.bzflag.org/Bz_BasePlayerRecord&quot;&gt;player record&lt;/a&gt; with the &lt;a href=&quot;https://wiki.bzflag.org/Bz_getPlayerByIndex&quot;&gt;bz_getPlayerIndex()&lt;/a&gt; function, which returns a pointer (meaning we&#039;ll need to free this pointer in step 3). In this case, we want to create a record for the player who sent the message and as we can tell from our handy documentation, &lt;code&gt;data-&amp;gt;from&lt;/code&gt; is the player ID who sent the message.&lt;/p&gt;
&lt;p&gt;The player ID is not a universally unique ID for the player across all servers but it&#039;s unique to the player during their current session. Once a player leaves and someone else joins, the new player will likely take on this ID. Also if the player rejoins, they&#039;ll like get a new ID too. If you&#039;d like to uniquely identify &lt;strong&gt;registered&lt;/strong&gt; players, use &lt;code&gt;pr-&amp;gt;bzID&lt;/code&gt;. You may also use &lt;code&gt;pr-&amp;gt;ipAddress&lt;/code&gt; to identify players uniquely to some extent but it isn&#039;t always reliable (e.g. players on the same Wi-Fi network would share the same IP).&lt;/p&gt;
&lt;h4 id=&quot;step-2&quot;&gt;Step 2&lt;/h4&gt;
&lt;p&gt;Our player record has a boolean value &lt;code&gt;verified&lt;/code&gt;, which is set to true if a player is registered and false otherwise. We use an if statement to check if a player is registered and negate that value.&lt;/p&gt;
&lt;h4 id=&quot;step-2-1&quot;&gt;Step 2.1&lt;/h4&gt;
&lt;p&gt;We use &lt;a href=&quot;https://wiki.bzflag.org/Bz_sendTextMessage&quot;&gt;bz_sendTextMessage()&lt;/a&gt; to send a message to the player who just tried to talk. The &lt;code&gt;BZ_SERVER&lt;/code&gt; value is a constant available to plug-ins to trigger actions on behalf of the server. In this case, the server will be sending a private message to this user.&lt;/p&gt;
&lt;h4 id=&quot;step-3&quot;&gt;Step 3&lt;/h4&gt;
&lt;p&gt;Lastly, we need to free the player record we created in step 1 to free the memory we&#039;ve been using; there&#039;s no garbage collector around here.&lt;/p&gt;
&lt;h3 id=&quot;4-cleaning-up&quot;&gt;4. Cleaning Up&lt;/h3&gt;
&lt;p&gt;We need to define what needs to be cleaned up when the plug-in is unloaded. When a plug-in &lt;em&gt;only&lt;/em&gt; registers event listeners, we simply call &lt;code&gt;Flush()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-final-plug-in&quot;&gt;5. Final Plug-in&lt;/h3&gt;
&lt;p&gt;Here&#039;s what the final plug-in would look like with a special note about &lt;code&gt;BZ_PLUGIN()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-cpp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-meta-keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;hljs-meta-string&quot;&gt;&quot;bzfsAPI.h&quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;hljs-class&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier&lt;/span&gt; :&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; bz_Plugin
{
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;&lt;/span&gt;;
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;&lt;/span&gt;;
};

&lt;span class=&quot;hljs-comment&quot;&gt;// This is a macro defined by the BZFS API which registers this plug-in. It&#039;s not&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// a function, therefore the lack of semi-colon here is NOT a typo.&lt;/span&gt;
BZ_PLUGIN(ChatNotifier)

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Name&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Chat Notifier&quot;&lt;/span&gt;;
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Init&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;char&lt;/span&gt;* config)&lt;/span&gt;
&lt;/span&gt;{
    Register(bz_eRawChatMessageEvent);
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Cleanup&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;{
    Flush();
}

&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;ChatNotifier::Event&lt;/span&gt; &lt;span class=&quot;hljs-params&quot;&gt;(bz_EventData* eventData)&lt;/span&gt;
&lt;/span&gt;{
    &lt;span class=&quot;hljs-keyword&quot;&gt;switch&lt;/span&gt; (eventData-&amp;gt;eventType)
    {
        &lt;span class=&quot;hljs-keyword&quot;&gt;case&lt;/span&gt; bz_eRawChatMessageEvent:
        {
            bz_ChatEventData_V2 *data = (bz_ChatEventData_V2*)eventData;
            bz_BasePlayerRecord *pr = bz_getPlayerByIndex(data-&amp;gt;from); &lt;span class=&quot;hljs-comment&quot;&gt;// step 1&lt;/span&gt;

            &lt;span class=&quot;hljs-comment&quot;&gt;// step 2&lt;/span&gt;
            &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (!pr-&amp;gt;verified) {
                &lt;span class=&quot;hljs-comment&quot;&gt;// step 2.1&lt;/span&gt;
                bz_sendTextMessage(BZ_SERVER, data-&amp;gt;from, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Register an account on forums.bzflag.org in order to talk.&quot;&lt;/span&gt;);
            }

            &lt;span class=&quot;hljs-comment&quot;&gt;// step 3&lt;/span&gt;
            bz_freePlayerRecord(pr);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;

        &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations! You&#039;ve just completed your very first BZFlag plug-in! Don&#039;t worry, there&#039;s a lot more of the API to cover and more posts to come; this time, the next post won&#039;t take another 2 years.&lt;/p&gt;
&lt;p&gt;Your feedback is most welcome and appreciated! However, please do not request what you&#039;d like to see written next. I&#039;ll be writing these posts in the order I deem best.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>I hate Fridays</title>
            <link href="https://allejo.io/blog/i-hate-fridays/" rel="alternate" type="text/html" title="I&#x20;hate&#x20;Fridays" />
            <updated>2017-03-31T08:00:00+00:00</updated>
            <id>1eb9987c7e48f9624b485c656aeff8b3885b6744</id>
            <content type="html" xml:base="https://allejo.io/blog/i-hate-fridays/">&lt;p&gt;So Fridays are typically my days off from work and school; they&#039;re awesome and I&#039;m always looking forward to them. However, this morning I woke up to a not so awesome Friday. Everything on my computer had crapped out.&lt;/p&gt;
&lt;h2 id=&quot;everything-is-damaged-or-incomplete-on-macos&quot;&gt;Everything is &amp;quot;Damaged or Incomplete&amp;quot; on macOS&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Terminal.app&amp;quot; is damaged and can&#039;t be opened. Delete &amp;quot;Terminal.app&amp;quot; and download it again from the App Store.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well. WTF? Right? Now, this happened for literally all of my applications. I couldn&#039;t open Disk Utility, Terminal, Chrome, Safari. Anything. So, what did I do fix this? I logged in to my secondary admin account and had to delete my account but I kept the home directory. I then recreated the account using the existing home directory. After this, it magically fixed this. Yay. Moving on to the next issue.&lt;/p&gt;
&lt;h2 id=&quot;homebrew-s-broken&quot;&gt;Homebrew&#039;s Broken&lt;/h2&gt;
&lt;p&gt;Alright, so what else is broken? Well, &lt;code&gt;brew&lt;/code&gt;. I can run &lt;code&gt;brew install&lt;/code&gt; and &lt;code&gt;brew search&lt;/code&gt; however, I couldn&#039;t use &lt;code&gt;brew doctor&lt;/code&gt; or &lt;code&gt;brew uninstall&lt;/code&gt; and that&#039;s problematic. Here&#039;s a snippet of the error message.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error: 757: unexpected token at &#039;&#039;
Please report this bug:
  http://docs.brew.sh/Troubleshooting.html
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse&#039;
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse&#039;
/usr/local/Homebrew/Library/Homebrew/tab.rb:62:in `from_file_content&#039;
/usr/local/Homebrew/Library/Homebrew/tab.rb:57:in `block in from_file&#039;
/usr/local/Homebrew/Library/Homebrew/tab.rb:57:in `fetch&#039;
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So. How did I fix this? Ugh. Fuck my life. Took me a while to finally think of this and figure out what the hell was going on but I managed to fix it by doing the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I modified this file &lt;code&gt;/usr/local/Homebrew/Library/Homebrew/tab.rb:62&lt;/code&gt; and added a &lt;code&gt;puts path&lt;/code&gt; as the first line of &lt;code&gt;self.from_file_content(content, path)&lt;/code&gt;. I wanted to know what file was causing this to fail.&lt;/li&gt;
&lt;li&gt;This will list all of the files that are being processed and the last file listed will be the one that&#039;s failing (though there may be more). It&#039;s a &lt;code&gt;INSTALL_RECEIPT.json&lt;/code&gt; file that was causing me the issue with some invalid JSON somehow getting put in there.&lt;/li&gt;
&lt;li&gt;I could either fix the JSON file if it was easy to fix, however it wasn&#039;t so I deleted the folder for that package and used &lt;code&gt;brew install&lt;/code&gt; to reinstall it.&lt;/li&gt;
&lt;li&gt;Last step, remove the &lt;code&gt;puts&lt;/code&gt; after I&#039;ve fixed all the broken packages.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;intellij-ides-failing&quot;&gt;IntelliJ IDEs failing&lt;/h2&gt;
&lt;p&gt;Lastly. All my IntelliJ IDEs began failing. Ugh. FUCK. This was the error that I was getting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0xa) at pc=0x0000000114d909b3, pid=856, tid=0x000000000000f20f
#
# JRE version: OpenJDK Runtime Environment (8.0_112-b13) (build 1.8.0_112-release-736-b13)
# Java VM: OpenJDK 64-Bit Server VM (25.112-b13 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# C  [libFontParser.dylib+0x29b3]  TSFNTFont::GetFormat() const+0x5b
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try &quot;ulimit -c unlimited&quot; before starting Java again
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, I couldn&#039;t find a fix for this solution. The only way I was able to fix this was a clean install of macOS, just the base system; i.e. I didn&#039;t wipe any content or my disks.&lt;/p&gt;
&lt;h2 id=&quot;overall&quot;&gt;Overall&lt;/h2&gt;
&lt;p&gt;I was really hoping for a productive Friday with a proof-of-concept project I started working on this week... But I guess being productive today wasn&#039;t for me.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>A Jekyll TOC without Plugins or JavaScript</title>
            <link href="https://allejo.io/blog/a-jekyll-toc-without-plugins-or-javascript/" rel="alternate" type="text/html" title="A&#x20;Jekyll&#x20;TOC&#x20;without&#x20;Plugins&#x20;or&#x20;JavaScript" />
            <updated>2017-02-03T08:00:00+00:00</updated>
            <id>27fda27b1cb3ea35cf2ee5152c9ed5ac415867d4</id>
            <content type="html" xml:base="https://allejo.io/blog/a-jekyll-toc-without-plugins-or-javascript/">&lt;p&gt;So on Tuesday, I was on #jekyll like any other day and a user by the name of misty came in asking about using &lt;code&gt;{:toc}&lt;/code&gt; in a Jekyll layout instead of a markdown file. It makes sense, on large websites with a lot of markdown files, you don&#039;t want to make sure you include &lt;code&gt;{:toc}&lt;/code&gt; in each document and if you do manage that, you&#039;re restricted with where it will be rendered—alongside the content. There&#039;s another problem with &lt;code&gt;{:toc}&lt;/code&gt;, it won&#039;t work separately from the markdown file because Jekyll doesn&#039;t give Liquid the raw markdown so you can&#039;t prepend &lt;code&gt;{:toc}&lt;/code&gt; and markdownify the combination nor can you combine &lt;code&gt;{:toc}&lt;/code&gt; with HTML. I&#039;m well aware that there are several &lt;a href=&quot;https://github.com/dafi/jekyll-toc-generator&quot;&gt;Jekyll plugins&lt;/a&gt; out there and &lt;a href=&quot;https://github.com/ghiculescu/jekyll-table-of-contents&quot;&gt;JavaScript solutions&lt;/a&gt; to this problem but there are two problems with each:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Jekyll plugin will not run GitHub pages&lt;/li&gt;
&lt;li&gt;A JavaScript solution is slow and useless if the user doesn&#039;t have JavaScript enabled&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Drag and drop the code snippet that is available on GitHub into your repository: &lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;allejo/jekyll-toc&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Now I&#039;ve been working with Liquid for a while and from first hand experience, I&#039;ve come to realize that this quote accurately describes my experience with Liquid.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;...like all things liquid - where there&#039;s a will, and ~36 hours to spare, there&#039;s usually a/some way&lt;/p&gt;
&lt;p&gt;- jaybe&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, why am I speaking about Jekyll when I&#039;m working on &lt;a href=&quot;/blog/welcome-stakx/&quot;&gt;my own static site generator and even ported my website to use my tool instead of Jekyll&lt;/a&gt;? Well... Because I got bored and wanted to see what I could come up. Besides, I never said that I had stopped working with Jekyll sites entirely. Anyhow, I came up with the following Liquid snippet that can easily be used as an &lt;code&gt;{% include %}&lt;/code&gt; for any Jekyll site. Want to see it in action? It&#039;s already being used on &lt;a href=&quot;https://github.com/docker/docker.github.io/pull/1474&quot;&gt;the docs.docker.com website&lt;/a&gt; and &lt;a href=&quot;https://github.com/ministryofjustice/technical-guidance/pull/7&quot;&gt;the UK Ministry of Justice Technical Guidance site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A Liquid solution to generate a table of contents for each markdown file seems like it would be slow and it actually might be compared to a plugin solution, but compared to the overall speeds of building the current Docker docs site (~2 minutes), it&#039;s really not slow at all; there are other files that are eating up more performance than the TOC builder (at the time of writing this, they&#039;re currently working on optimizing things).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Filename                                          | Count |      Bytes |    Time
--------------------------------------------------+-------+------------+--------
_includes/tree.html                               |   817 | 289571.48K | 225.508
_layouts/docs.html                                |   817 | 114017.42K |  90.628
allpagelinks.md                                   |     1 |    112.33K |  19.170
_includes/toc_pure_liquid.html                    |   813 |    524.17K |   6.422&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I&#039;ve tailored this solution to not depend on any specific front matter or &lt;code&gt;_config.yml&lt;/code&gt; setup meaning all you have to do is drop in this snippet to the &lt;code&gt;_includes&lt;/code&gt; folder of your Jekyll site and &lt;code&gt;{% include %}&lt;/code&gt; it with whichever parameters you want, and it&#039;ll just work.&lt;/p&gt;
&lt;p&gt;What if you &lt;strong&gt;want to&lt;/strong&gt; configure this snippet from your &lt;code&gt;_config.yml&lt;/code&gt; or front matter? Well that&#039;s cool too but do it in your layout instead of this snippet. The goal is to keep this snippet as independent as possible so you can easily reuse it or update it and have things continue to work with any Jekyll site. As an example, in one of your layouts this is what you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-twig&quot;&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;assign&lt;/span&gt; my_min = page.toc_min | default: site.toc_min | default: 1 %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;assign&lt;/span&gt; my_max = page.toc_max | default: site.toc_max | default: 3 %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{% &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;include&lt;/span&gt;&lt;/span&gt; toc.html html=content sanitize=true h_min=my_min h_max=my_max %}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yup! You can daisy chain &lt;code&gt;default&lt;/code&gt; filters to ensure you get &lt;em&gt;some&lt;/em&gt; value. In this case, we&#039;re looking for a front matter value of &lt;code&gt;toc_min&lt;/code&gt; first and if that&#039;s not set, use the &lt;code&gt;toc_min&lt;/code&gt; defined in your &lt;code&gt;_config.yml&lt;/code&gt; and if &lt;em&gt;that&#039;s&lt;/em&gt; not defined, just use 1.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h2&gt;
&lt;section class=&quot;c-alert c-alert--warning&quot;&gt;
  &lt;header class=&quot;c-alert__header&quot;&gt;
    &lt;p&gt;
      &lt;i class=&quot;fas fa-fw fa-exclamation-triangle&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;
      Technical Information
    &lt;/p&gt;
  &lt;/header&gt;

  &lt;p&gt;
    The rest of this article explains how the &lt;strong&gt;jekyll-toc&lt;/strong&gt; project works at a fundamental level. It is not designed as a step-by-step tutorial for how to use the above snippet; see the &lt;a href=&quot;https://github.com/allejo/jekyll-toc#usage&quot;&gt;GitHub README&lt;/a&gt; for that.
  &lt;/p&gt;
&lt;/section&gt;
&lt;p&gt;This snippet is designed to work with markdownify-ed HTML given to us by Jekyll. Due to the way Jekyll was designed, you can&#039;t access the unrendered markdown of a document and are stuck with HTML.&lt;/p&gt;
&lt;h3 id=&quot;step-1&quot;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;The very first step is to find all of the headings in the given HTML. To do so, I start off by spliting the HTML at every &lt;code&gt;&amp;lt;h&lt;/code&gt;. This is my way of cheating and finding all of the headings in the given HTML and now we have an array that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    &#039;1 id=&quot;heading-1&quot;&amp;gt;Heading 1&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;....&amp;lt;/p&amp;gt;&#039;,
    &#039;2 id=&quot;hello&quot;&amp;gt;Hello&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;...&amp;lt;/p&amp;gt;&#039;
]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-2&quot;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;At this point, I loop through each array item (I call it a &#039;node&#039; in the code). I slice the string to get the first character, which is the heading level. I then do some Liquid magic to cast the sliced string into an integer by multiplying the string by 1.&lt;/p&gt;
&lt;h3 id=&quot;step-3&quot;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Now that we have the heading level as an integer, respect the &lt;code&gt;h_min&lt;/code&gt; and &lt;code&gt;h_max&lt;/code&gt; parameters and toss any headings we don&#039;t want by moving on to the next node if necessary.&lt;/p&gt;
&lt;h3 id=&quot;step-4&quot;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;Now, I split each node at the &lt;code&gt;&amp;lt;/h&lt;/code&gt; and only keep what&#039;s on the left side of the split. So now I have an array of nodes that look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    &#039; id=&quot;heading-1&quot;&amp;gt;Heading 1&#039;,
    &#039; id=&quot;hello&quot;&amp;gt;Hello&#039;
]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-5&quot;&gt;Step 5&lt;/h3&gt;
&lt;p&gt;I now split the nodes by &lt;code&gt;&quot;&lt;/code&gt; and I access index 1 of that split, which will give me access the generated ID for that heading (thank you kramdown for this); i.e. &lt;code&gt;heading-1&lt;/code&gt; and &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-6&quot;&gt;Step 6&lt;/h3&gt;
&lt;p&gt;Now that I have the heading level and the ID, it&#039;s time to extract the actual heading content. So I build what the node looked like in step 4 up until the &lt;code&gt;&amp;gt;&lt;/code&gt;, and replace it with nothing. Voilá. I&#039;ve extracted the heading content.&lt;/p&gt;
&lt;h3 id=&quot;step-7&quot;&gt;Step 7&lt;/h3&gt;
&lt;section class=&quot;c-alert c-alert--warning&quot;&gt;
  &lt;header class=&quot;c-alert__header&quot;&gt;
    &lt;p&gt;
      &lt;i class=&quot;fas fa-fw fa-exclamation-triangle&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;
      Outdated Information
    &lt;/p&gt;
  &lt;/header&gt;

  &lt;p&gt;
    This project has since been rewritten to use a new algorithm (the same one stakx uses internally), making it more robust. Steps 7 and 8 remain here for historical purposes but the current version of the project no longer uses the approach explained in these steps.
  &lt;/p&gt;
&lt;/section&gt;
&lt;p&gt;Now that we have all the necessary information, all we have to do is actually build the TOC. Here&#039;s the trick, we build our TOC using markdown (e.g. &lt;code&gt;- [text](#id)&lt;/code&gt;). I take the heading level for the current node, and subtract one. I then take that value (X) and repeat 2 spaces X times; this serves as our indentation.&lt;/p&gt;
&lt;p&gt;For example, for our &lt;code&gt;h2&lt;/code&gt; becomes &lt;code&gt;2 - 1 = 1&lt;/code&gt;. Then I repeat 2 spaces 1 time. So our generated markdown would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-md&quot;&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;- &lt;/span&gt;[&lt;span class=&quot;hljs-string&quot;&gt;Heading 1&lt;/span&gt;](&lt;span class=&quot;hljs-link&quot;&gt;#heading-1&lt;/span&gt;)
&lt;span class=&quot;hljs-bullet&quot;&gt;  - &lt;/span&gt;[&lt;span class=&quot;hljs-string&quot;&gt;Hello&lt;/span&gt;](&lt;span class=&quot;hljs-link&quot;&gt;#hello&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-8&quot;&gt;Step 8&lt;/h3&gt;
&lt;p&gt;Lastly, now that we have a markdown version of the TOC with the correct indentation, we &lt;code&gt;markdownify&lt;/code&gt; it and output that. And, that&#039;s it! Pretty cool, right?&lt;/p&gt;
&lt;h2 id=&quot;project-repository&quot;&gt;Project Repository&lt;/h2&gt;
&lt;p&gt;I originally shared this code as a GitHub Gist, however I made a promise months ago regarding this code. If I got bored enough, I&#039;d move the code to its own repository; and so I have: &lt;a href=&quot;https://github.com/allejo/jekyll-toc&quot;&gt;allejo/jekyll-toc&lt;/a&gt;. Heck, I even added unit tests to show sample usage and make sure that it works as intended. Don&#039;t worry, the snippet&#039;s still under BSD-3.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;small&gt;2021-12-30 - Updated post with notices about outdated information and it not being a &amp;quot;how to&amp;quot; tutorial&lt;/small&gt;&lt;/li&gt;
&lt;li&gt;&lt;small&gt;2017-09-07 - Updated post with an overview of how the code works and updated links to the new GitHub repository&lt;/small&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
        </entry>
            <entry>
            <title>It's 2017</title>
            <link href="https://allejo.io/blog/its-2017/" rel="alternate" type="text/html" title="It&#x27;s&#x20;2017" />
            <updated>2017-01-15T08:00:00+00:00</updated>
            <id>8ef31aeb35c2a7889a16e3295a64e50d69f8940e</id>
            <content type="html" xml:base="https://allejo.io/blog/its-2017/">&lt;p&gt;Welcome to a new year. Only 15 days late but, whatever. Looking back at 2016, it was a pretty awesome year for me with regards to all of the projects I participated in. Here are some of my projects and accomplishments for 2016:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BZiON had &lt;a href=&quot;https://github.com/allejo/bzion/releases/tag/v0.9.0&quot;&gt;its first major release&lt;/a&gt; after 2.5 years of development&lt;/li&gt;
&lt;li&gt;stakx development began and reached its &lt;a href=&quot;https://github.com/stakx-io/stakx/releases/tag/v0.1.0-beta3&quot;&gt;third beta&lt;/a&gt; with an incredible amount of features and bug fixes&lt;/li&gt;
&lt;li&gt;Published &lt;a href=&quot;http://analytics.smgov.net/&quot;&gt;web analytics for the City of Santa Monica&lt;/a&gt; and &lt;a href=&quot;https://github.com/CityofSantaMonica/analytics.smgov.net/tree/master/App_Data/jobs/triggered&quot;&gt;automated the whole process&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Wrote &lt;a href=&quot;https://github.com/CityofSantaMonica/jekyll-frontmatter-jsonify&quot;&gt;my first Jekyll plug-in&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Participated in my first Hacktoberfest&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting on Dec 31st, 2016 and continuing into the first week of 2017, I spent my time rewriting my BZFlag plug-in starter to be &lt;a href=&quot;https://github.com/allejo/bzflagPluginStarter2&quot;&gt;entirely JavaScript based&lt;/a&gt; (where the first version was a &lt;a href=&quot;https://github.com/allejo/bzflagPluginStarter&quot;&gt;PHP script&lt;/a&gt;). I decided to tackle code generation in a object oriented approach by writing the library of that website in &lt;a href=&quot;http://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; and wrote the UI using &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue.js&lt;/a&gt;—two things I had been wanting to learn/experiment with. TypeScript definitely makes writing object-oriented JS easier but one thing I really struggled with was the differences between ES5, ES6, ES2017, Babel, synonyms; the amount of jargon that exists in the JS world... It was a nightmare for me to get a grasp of things.&lt;/p&gt;
&lt;p&gt;So what do I have planned for 2017? I definitely want to get more adept with JavaScript and TypeScript. I also really want to do more work with C#, C++, and Python. Lastly, I want to attempt to build my own lexer simply for the experience.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Thank You, AS</title>
            <link href="https://allejo.io/blog/thank-you-as/" rel="alternate" type="text/html" title="Thank&#x20;You,&#x20;AS" />
            <updated>2016-11-19T08:00:00+00:00</updated>
            <id>3d874e7e39ffb2e27a3e73298949a4e6453d1c46</id>
            <content type="html" xml:base="https://allejo.io/blog/thank-you-as/">&lt;p&gt;I&#039;m often curious to see other people&#039;s personal websites and am disappointed when I notice that their websites are dated or unmaintained. The pot calling the kettle black, huh? My personal website has become the most neglected project I maintain but I plan on changing that. I intend on writing more often, hence this post.&lt;/p&gt;
&lt;p&gt;I&#039;m known for having a pessimistic view on education and while that hasn&#039;t changed, I&#039;ve started to appreciate the college experience a bit more. I&#039;m starting my 5th year of college and I&#039;m about to hit my third year working at CSUN&#039;s Associated Students. Soon after I started college, I took some time off and then was a part time student for a while because I simply wanted a break, something I hadn&#039;t had since what feels like middle school.&lt;/p&gt;
&lt;p&gt;Because of my time off from college, I was still a freshman when I saw a job opening for a student web developer position posted in the computer science Facebook group I was a part of. I filled out the application and submitted it on a whim but I didn&#039;t expect anything to come of it. I had already submitted an application to a previous department on campus but I never even got a confirmation so I figured this application would be the same way.&lt;/p&gt;
&lt;p&gt;Shortly after, I got an email to schedule an interview with my future boss, Kevin. The email stated to have a portfolio so I worked on my website really late into the morning the day of my interview. Now, what really pisses me off was that my portfolio was never even mentioned nor looked at during the interview. God damn it. I could have been sleeping instead! But. Whatever.&lt;/p&gt;
&lt;p&gt;Maybe about a week later, I was hiking with a friend when received the following email from Kevin,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dear &lt;/p&gt;
&lt;p&gt;Thank you for your interest in the Web Design Student Assistant. I am delighted to offer you the position if you are still interested...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notice how there&#039;s no name after &amp;quot;Dear...&amp;quot; I didn&#039;t know whether to get my hopes up or not so I sent a reply asking for clarification. I got nervous when I didn&#039;t hear back from Kevin soon; I thought maybe I came off as if I were mocking him. Turns out my reply wasn&#039;t sent until I came down from the trail. Apparently, trees and bushes don&#039;t make reliable Wifi hotspots. Who knew?&lt;/p&gt;
&lt;p&gt;Shortly after, I got the following response.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes, it was!!! I misspelled your name and accidentally erased it rather than correct it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sigh... After knowing and working with Kevin for 3 years, I can look back at this and know for a fact, that this is totally something he would do.&lt;/p&gt;
&lt;p&gt;In my three years with AS, I have made a lot of friends and have learned so much from my coworkers. Working in an office with graphic designers has taught me so much about what makes a good design and I&#039;ve been able to apply that knowledge to designs I&#039;ve worked on myself. I&#039;ve also been able to experiment a lot with different projects and learn what&#039;s best without being too worried about breaking everything. What sucks the most is that I&#039;ve been in the office for so long that I&#039;ve seen many people come and go; frankly, I miss a lot of them (some more than others) and all the fond memories I have with them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Watching friendships get brutally destroyed over a game of Munchkin&lt;/li&gt;
&lt;li&gt;Browsing Imgur late at night laughing at the stupidest of posts&lt;/li&gt;
&lt;li&gt;Playing Borderlands 2, FIFA, Minecraft, Rock Band, UFC, and Yu-Gi-Oh in the office after-hours&lt;/li&gt;
&lt;li&gt;Pulling off pranks and getting IT involved because they think I broke something&lt;/li&gt;
&lt;li&gt;Playing Cards Against Humanity after-hours&lt;/li&gt;
&lt;li&gt;Changing the browser&#039;s default font to Comic Sans on one of the graphic designer&#039;s computer (she found it funny)&lt;/li&gt;
&lt;li&gt;Going to Disneyland for an office retreat&lt;/li&gt;
&lt;li&gt;Printing out a flight ticket on our large poster printer (on accident)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I still have some time before I graduate and leave AS but as I was reflecting on my college experience, AS has definitely made me hate college a little less. I&#039;m thankful to my boss and all of my coworkers who have made this experience more fun.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Welcome Stakx</title>
            <link href="https://allejo.io/blog/welcome-stakx/" rel="alternate" type="text/html" title="Welcome&#x20;Stakx" />
            <updated>2016-11-18T08:00:00+00:00</updated>
            <id>306d69d84ba5756308be9c067f82a0f7863311f3</id>
            <content type="html" xml:base="https://allejo.io/blog/welcome-stakx/">&lt;p&gt;A &lt;a href=&quot;/blog/evidently-i-have-a-life/&quot;&gt;while back&lt;/a&gt;, I mentioned that I was working on my own &lt;a href=&quot;https://github.com/stakx-io/stakx&quot;&gt;static website generator&lt;/a&gt; and that I was going to post about it later. For the past 6 months, I&#039;ve been working on Stakx on and off and I&#039;ve finally reached a point where I can easily and efficiently build a website with Stakx.&lt;/p&gt;
&lt;p&gt;My personal website is actually the third website I&#039;ve built with Stakx; the first website was the &lt;a href=&quot;http://csunas.org/sustainabilitycenter/&quot;&gt;Associated Students&#039; Sustainability Center website&lt;/a&gt;. I&#039;ve been wanting to redesign my website and continue building upon it so it could serve as my portfolio for my work for quite some time now, so in honor of Stakx becoming usable, I&#039;d like to introduce my completely redesigned website! In addition to the new design, I&#039;ve gone back through my previous posts and have updated them to work with Stakx or removed some of the more sillier posts; not that many people read my posts or would care.&lt;/p&gt;
&lt;p&gt;So why exactly did I build yet another static website generator? Well of all the more popular generators out there, each one that I&#039;ve tried has had shortcomings whether it&#039;s the template engine or the organization the generator expects. I didn&#039;t write Stakx as a tool that will replace Jekyll in my life, but more so as an alternative to the available tools already out there. I wanted a tool that used a powerful template engine, doesn&#039;t require a package manager to build a website, and can be distributed as just a single executable.&lt;/p&gt;
&lt;h2 id=&quot;stakx-vs-jekyll&quot;&gt;Stakx vs Jekyll&lt;/h2&gt;
&lt;p&gt;I&#039;ll be writing a more in-depth document regarding the differences between Stakx and Jekyll on the official Stakx website but here are some things I immediately ran into as I was porting over my website to Stakx.&lt;/p&gt;
&lt;h3 id=&quot;no-post-url-tag&quot;&gt;No &lt;code&gt;{% post_url %}&lt;/code&gt; tag&lt;/h3&gt;
&lt;p&gt;Jekyll has this Liquid tag available to reference other posts, but Stakx does not have this and it&#039;s not an oversight on my part when I was designing it. A link to another collection item can be achieved with Twig alone. In addition, you&#039;re not limited to just accessing other posts and their links, you can access any Content Item from any Collection and access all of their attributes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-twig&quot;&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ url(collections.posts[&#039;file-name-without-extension&#039;].permalink) }}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I admit, not the most elegant method of accessing the permalink of another Content Item, but it is functional. In addition, Twig allows you to define macros, which is similar to Jekyll&#039;s include functionality, to create a shortcut of sorts to access permalinks with less markup.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-twig&quot;&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{%- &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;macro&lt;/span&gt;&lt;/span&gt; item_url(collection, file) -%}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ url(collections[collection][file].permalink) }}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;hljs-template-tag&quot;&gt;{%- &lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;endmacro&lt;/span&gt;&lt;/span&gt; -%}&lt;/span&gt;&lt;span class=&quot;xml&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;hljs-template-variable&quot;&gt;{{ _self.item_url(&#039;collection-name&#039;, &#039;file-name-without-extension&#039;) }}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;no-gist-tag&quot;&gt;No &lt;code&gt;{% gist %}&lt;/code&gt; tag&lt;/h3&gt;
&lt;p&gt;There is no custom Twig tag to fetch a GitHub Gist&#039;s content and embed it on the page at compile time without the need of having JavaScript enabled for your website readers. GitHub, however, does let you embed Gists on your page by simply pasting a script tag with information such as your GitHub username and Gist ID.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://gist.github.com/:username/:gist-id.js&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;twig-plug-ins&quot;&gt;Twig plug-ins?&lt;/h3&gt;
&lt;p&gt;I do have plans for supporting custom Twig filters and tags by adding support for extending Stakx but that will have to wait for a future release.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Will both of these features be written and supported as plug-ins?&lt;/strong&gt;&lt;br /&gt;
Probably.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Will these become part of Stakx core?&lt;/strong&gt;&lt;br /&gt;
Possibly, being able to reference other Content Items is important and it should be an easy task for users&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;moving-forward&quot;&gt;Moving Forward&lt;/h2&gt;
&lt;p&gt;I have learned a lot by building Stakx and there is a lot I can still improve on, such as using dependency injection instead of explicitly passing objects. I have every intention of continuing to develop and support Stakx for the forseeable future. However, Stakx is still very young so I don&#039;t think porting a large existing Jekyll website, for example, to a Stakx website would be worth the effort. At the moment, I&#039;d recommend Stakx to be used for new small scale websites or for experimentation.&lt;/p&gt;
&lt;p&gt;If anyone discovers Stakx and builds a website with it, I would love to know! Leave a comment on here or give me feedback by creating &lt;a href=&quot;https://github.com/stakx-io/stakx/issues&quot;&gt;an issue on GitHub&lt;/a&gt;.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>My Experience with the Youth Tech Program</title>
            <link href="https://allejo.io/blog/my-experience-with-the-youth-tech-program/" rel="alternate" type="text/html" title="My&#x20;Experience&#x20;with&#x20;the&#x20;Youth&#x20;Tech&#x20;Program" />
            <updated>2016-08-15T00:00:00+00:00</updated>
            <id>e0f8cba32123ca239dd80f6c94857acb0be59a1b</id>
            <content type="html" xml:base="https://allejo.io/blog/my-experience-with-the-youth-tech-program/">&lt;p&gt;I&#039;ve been wanting to write about the &lt;a href=&quot;http://www.santamonicayouthtech.com/&quot;&gt;Santa Monica Youth Tech Program&lt;/a&gt; (YTP) for quite some time and what my experience was like as a participant in 2012 and now as a staff member. After searching for posts regarding YTP, all I could find were articles written by program partners and guest presenters.&lt;/p&gt;
&lt;p&gt;I hope that an uncensored, unedited, and unofficial post will be beneficial to future participants. For that reason, I decided to post this on my personal blog because I would like to speak about my experience and thoughts without being interrupted or having my words rephrased by someone else.&lt;/p&gt;
&lt;p&gt;Before I discuss my experience, here are some things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For those of you who know me, I&#039;m brutally honest when I can be; therefore, this post will be nothing but honest.&lt;/li&gt;
&lt;li&gt;Even though I&#039;m a staff member at the time of writing this post, everything I say are my own opinions and experiences that should &lt;strong&gt;not&lt;/strong&gt; be treated as statements on behalf of YTP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The very first thing I want to address is an extremely common misconception that I had as a participant and have heard from several participants during my time as a staff member. Even though the program has the word &amp;quot;technology&amp;quot; in the name, you should not go into the program thinking that all you will be focusing on and learning about is technology. YTP is a summer program where teams of participants will create start-ups, this means that all participants will learn the basics of business, graphic design, and web development. It&#039;s similar to college where you will have enough theoretical knowledge to continue your start-up but you can only learn so much without first-hand experience. If you are like me, who was expecting a programming bootcamp, then this program is definitely not for you.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the ladies, &lt;a href=&quot;https://girlswhocode.com/&quot;&gt;Girls Who Code&lt;/a&gt; is an amazing program you should look at and definitely be a part of&lt;/li&gt;
&lt;li&gt;For everyone, &lt;a href=&quot;https://www.idtech.com/tech-camps/&quot;&gt;iD Tech Camps&lt;/a&gt; is another awesome program that I would have liked to join if I could have&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Truthfully, if you&#039;re interested in the web development portion of the program, chances are you will just be learning the basics of HTML, CSS, and some JavaScript to build a website that looks like it works. Here are some things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Time is extremely limited throughout the program, so it&#039;s shared between two other topics as well. This means you will be doing a lot of research and self-teaching.&lt;/li&gt;
&lt;li&gt;Web dev presentations are taught at a pace where someone with no prior coding experience will be able to follow along. If you have prior experience, this will be too slow for you.&lt;/li&gt;
&lt;li&gt;Building a fully functional back-end to the website will require a lot of self-teaching or prior knowledge because there is not enough time during the program to do both front-end and back-end development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though I&#039;m advertising and recommending other programs, I encourage you to read on before you decide to cross out YTP from your possible summer programs.&lt;/p&gt;
&lt;p&gt;All throughout high school I dedicated time to teaching myself how to code and it was well worth it. In order to learn to code, I had to spend a lot of time in front of a screen and my parents weren&#039;t fond that I spent so much time on the computer &amp;quot;talking with strangers.&amp;quot; I was called into my school&#039;s dean office one day and I was wondering what I had done &lt;em&gt;this&lt;/em&gt; time to get in trouble but to my surprise, he was recommending this program to me. I had the misunderstanding that it was going to be an internship at City Hall regarding technology and so I applied and got accepted.&lt;/p&gt;
&lt;p&gt;To my disappointment, it wasn&#039;t the coding or technology bootcamp I had imagined but it was definitely a great experience for me regardless. I was able to make some really great friends during the program and I actually learned some business concepts, which have now become common sense to me. I never thought I would have actually enjoyed a basic understanding about how businesses work, but I did. I can&#039;t do finance or write business plans, but I can definitely think of the direction a business should take and why that&#039;d be the appropriate decision; I can now apply this knowledge to projects that I lead. I can also give feedback to my college classmates who really want to create the next Instagram but with triangular pictures instead of squares!&lt;/p&gt;
&lt;p&gt;To this day, I am still the only participant who was able to write a fully functional website from scratch. In 2012, there weren&#039;t any web development lectures or teachers; all I had was a book I had checked out from the Santa Monica Public Library (which I still owe a late fee for...). I was only able to achieve this because I had prior experience with writing code and I made this program into my own bootcamp by dismissing the majority of business lectures and a lot of late nights. Even though my code for this website was horrendous, I was still able to impress a few web developers from the web development team at City Hall. One of these developers is now my mentor who has taught me an incredible amount and the other is now my manager. I would have never been able to work with them, let alone meet them, if it were not for this program. I simply cannot express my gratitude as words to accurately describe how amazing an opportunity I was able to get because of this program.&lt;/p&gt;
&lt;p&gt;Going back to why I mentioned my parents not being fond of me being in front of a computer so much, it wasn&#039;t until after this program that other people praised me for my potential and knowledge that my parents actually believed that I was doing more than just wasting time on a computer. Shortly after, my parents researched average yearly income for developers and suffice to say, they supported me fully after that.&lt;/p&gt;
&lt;p&gt;The program is still very young and will require some time to mature; after all, it just barely completed its 5th year on July 21st, 2016. At this point, the program is run solely by alumni with a few staff members being the original founders of the program; this is both really impressive and harmful. The reason I believe it&#039;s become harmful is because I feel the program has reached a plateau of innovation where change is now frowned upon because &amp;quot;it&#039;s always been done like this&amp;quot; and the alumni writing the curriculum for the program are heavily influenced by prior years. I believe a fresh pair of eyes would greatly benefit the program making it better for next year&#039;s participants. For example, it&#039;s now my 3rd year writing the web development curriculum from scratch because I have not been satisfied with it previously. While this year&#039;s curriculum was much better than prior years, I was not satisfied with how it was planned out because subconsciously, I still mistakenly treat YTP as a coding bootcamp. I have requested that someone else write the curriculum for next year but to my dismay, my curriculum for this year has been saved in the staff archive so it can be &amp;quot;used as a reference, if necessary.&amp;quot;&lt;/p&gt;
&lt;p&gt;As I mentioned, the program has become very set on its ways and change won&#039;t come about easily. If you plan on joining this program, be careful with what category you place yourself under: business, development, or graphic design. There have been several participants who have chosen a category on a whim and have come to regret their decision because they chose a category they are not interested in. The teams are chosen based on the interests you specified and what presentations you participate in during the first week; once you&#039;ve classified yourself there&#039;s no going back. There have been several participants who became really interested in web development after they experienced the first few lectures but they had to continue working on the business tasks assigned to them. By not allowing participants to switch their roles, we are ensuring that a team is not dominated by all business people, for example. While I agree with this policy, it does affect morale and lessen productivity, which isn&#039;t something I would want for a summer program intended to be a fun learning experience.&lt;/p&gt;
&lt;p&gt;If you&#039;re going to take away anything from this post, I hope it&#039;s that if you&#039;re going to go into this program, you should expect that you will be learning a little bit of business, graphic design, and web dev regardless of your preference or how boring a specific topic may be. In other words, even though you are in love with business you will still need to sit through coding and business lessons; hey, you never know what you&#039;ll find interesting.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Evidently I have a life</title>
            <link href="https://allejo.io/blog/evidently-i-have-a-life/" rel="alternate" type="text/html" title="Evidently&#x20;I&#x20;have&#x20;a&#x20;life" />
            <updated>2016-03-08T00:00:00+00:00</updated>
            <id>f1e2da55ce1862369e2e845d83a4d69bfd1f634a</id>
            <content type="html" xml:base="https://allejo.io/blog/evidently-i-have-a-life/">&lt;p&gt;I have spring break coming up in about 2 weeks; while that would typically have me excited, I&#039;m too distracted and drained for any unnecessary emotions. I&#039;ve been in a rather odd slump the past few months with where I&#039;m at.&lt;/p&gt;
&lt;p&gt;In just the past two months or so, I&#039;ve been working on projects that I&#039;m actually quite proud of and will continue to maintain and work on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kongr45gpen&quot;&gt;alezakos&lt;/a&gt; and I had been working to finally get &lt;a href=&quot;https://github.com/allejo/bzion/releases/tag/v0.9.0&quot;&gt;BZiON 0.9.0&lt;/a&gt; released on February 21st along with &lt;a href=&quot;http://leaguesunited.org/news/316&quot;&gt;Leagues United&lt;/a&gt;—the new unified BZFlag league. This project finally sees the initial release after about 2 years and 6 months of development.&lt;/li&gt;
&lt;li&gt;bzElectron has reached &lt;a href=&quot;https://github.com/allejo/bzelectron/releases/tag/v0.1.1&quot;&gt;version 0.1.1&lt;/a&gt; with some minor new features and an awesome pull request from alezakos to support regular group files.&lt;/li&gt;
&lt;li&gt;At one of my current jobs, I started PhpPulse—a wrapper for the DaPulse API. The wrapper has finally reached it&#039;s &lt;a href=&quot;https://github.com/allejo/PhpPulse/releases/tag/0.1.0&quot;&gt;initial release&lt;/a&gt; after a few alpha releases and has received a description of &lt;a href=&quot;https://twitter.com/dapulseLabs/status/679654705841176576&quot;&gt;&amp;quot;awesomeness&amp;quot; by DaPulse&#039; Twitter account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I have also started &lt;a href=&quot;https://github.com/allejo/stakx&quot;&gt;stakx (my own static website generator)&lt;/a&gt;, just like Jekyll, but mine&#039;s written in PHP and uses Twig—a far more powerful alternative to Liquid. I intend on using this at my current job to start building more powerful static websites, if that makes sense. There&#039;s a lot more to come regarding this project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am really proud about my achievements above, but that hasn&#039;t been enough for me to feel satisfied or content.&lt;/p&gt;
&lt;p&gt;As much as I care about these projects, they simply aren&#039;t very significant to anyone else except myself and a handful of people. While I&#039;m ecstatic about the fact that a handful of people use it, I&#039;m saddened that I haven&#039;t been able to join or contribute to a larger community or project.&lt;/p&gt;
&lt;p&gt;I have always taken objection to school and its practices, but that&#039;s a rant for another day. I&#039;m taking 6 classes this semester and I&#039;m only enjoying 2 of those classes—a Linux class and a GIS class; I&#039;ve learned an incredible amount in those classes and am very happy about taking those professors. The downside? Very little of the knowledge that I&#039;ve picked up is applicable to either of my current jobs or have been things that I&#039;m already familiar with.&lt;/p&gt;
&lt;p&gt;The worst feeling that I&#039;ve been having lately, and has probably contributed to my current mood, is having a lot go over my head and not being able to learn it quickly as I have with pretty much everything else. The fact that I am having to learn a lot of these new things in an environment with deadlines is far from ideal. In addition, there are a lot of people I admire and respect who have been able to learn these concepts or tools very easily. Of course, everyone always tells me not to compare myself to others but it&#039;s hard not to, sometimes.&lt;/p&gt;
&lt;p&gt;Anyhow, I didn&#039;t mean for this become a rant like other blog posts I&#039;ve written so I&#039;m very excited to get an early version of Stakx built and distributed to a few people to have them give it a shot to compare it to Jekyll or Sculpin.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Clickbait — You won't believe what I've seen!</title>
            <link href="https://allejo.io/blog/clickbait--you-wont-believe-what-ive-seen/" rel="alternate" type="text/html" title="Clickbait&#x20;&#x2014;&#x20;You&#x20;won&#x27;t&#x20;believe&#x20;what&#x20;I&#x27;ve&#x20;seen&#x21;" />
            <updated>2016-01-20T00:00:00+00:00</updated>
            <id>2f49c45443493764a566f49922c5c7ffacb85d87</id>
            <content type="html" xml:base="https://allejo.io/blog/clickbait--you-wont-believe-what-ive-seen/">&lt;p&gt;For those of you who don&#039;t know me, my sense of humor is quite cynical, sarcastic, and satirical at times. The number one thing I absolutely &lt;strong&gt;loathe&lt;/strong&gt; when scrolling through my newsfeed—whether it be Facebook, LinkedIn, or Twitter—is seeing clickbait. Ok, on Facebook or Twitter it&#039;s to be expected but on LinkedIn also? These are three titles of articles I saw on LinkedIn before I quit my browser:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;quot;This is the worst part about the new Apple TV&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;How a small design tweak got millions more people to use one of Facebook&#039;s products&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Apple is slightly more diverse than it was last year&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Seriously... What do any these titles tell me about the respective articles?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Article #1&lt;/strong&gt; - This article tells me the author is bitchy and took objection to a single feature of the new Apple TV and now anyone who is searching for the pros and cons behind the new Apple TV will find this author&#039;s whiny opinion. This article is showing up on my LinkedIn because I follow Apple. Seriously LinkedIn? I expected serious articles regarding a company I choose to follow, not some whiny clickbait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Article #2&lt;/strong&gt; - This sounds like those male enhancement pill emails that you receive in your spam folder. Must I say more? If your article is that boring that you must resort to sounding like a Viagra ad, reconsider your website&#039;s mission because I&#039;m sure porn websites already have that nailed down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Article #3&lt;/strong&gt; - Alright, I&#039;ll admit that the title of the article is actually more appropriate than the previous two but really? There is an entire article regarding how a single company is &amp;quot;slightly&amp;quot; more diverse than the previous year? Instead of talking about a single company&#039;s leadership, maybe you should be analyzing this trend in a specific country or around the world; something that, you know, requires actual research or journalism? Oh you&#039;re probably not capable of that, sorry.&lt;/p&gt;
&lt;p&gt;I find it extremely disappointing that websites like Business Insider and Washington Post (the websites where these articles are housed) are becoming no better then Buzzfeed, Clickhole, or The Onion where clickbait is their thing because it&#039;s either their style or it&#039;s satire.&lt;/p&gt;
&lt;p&gt;In conclusion, I love satire. For those of you who find my humor hard to understand, I purposely formatted this post to be like every other clickbait article I see. If your website is resorting to using clickbait to get people to read your articles simply to get ad revenue, you seriously need to reconsider your writers&#039; abilities and your business strategy.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>"How did you learn all of this?"</title>
            <link href="https://allejo.io/blog/how-did-you-learn-all-of-this/" rel="alternate" type="text/html" title="&quot;How&#x20;did&#x20;you&#x20;learn&#x20;all&#x20;of&#x20;this&#x3F;&quot;" />
            <updated>2015-11-21T00:00:00+00:00</updated>
            <id>2236ed62b3ceb9c47a7a35e04577a92737826cc2</id>
            <content type="html" xml:base="https://allejo.io/blog/how-did-you-learn-all-of-this/">&lt;p&gt;Since I&#039;ve started taking more computer related classes and have met some pretty cool people, the most common question I keep getting is: &amp;quot;how did you learn all of this?&amp;quot; As a friend pointed out a few days ago, I&#039;ve been fooling around with code and technology for almost a decade now; I&#039;m not entirely satisfied with the progress I&#039;ve made, since I wish I had time to learn so much more.&lt;/p&gt;
&lt;p&gt;Quite frankly, I can never really figure out how to answer that question. I always just say, &amp;quot;I got bored so I looked it up.&amp;quot; This is exactly what I did but it doesn&#039;t really give you any detail as to my process. Looking back at my past, I don&#039;t really know what I did. I started by playing &lt;a href=&quot;http://bzflag.org&quot;&gt;BZFlag&lt;/a&gt; and was curious how a specific developer did all that he did. I wanted to be just like him, so I asked him to teach me some basics. He pointed me to learning Perl—dear god, I know—and sent me links to tutorials via AIM. At this point, websites like Codecademcy may have existed but coding did not have the hype it has now so I didn&#039;t know of any such resources. I had a lot of trouble getting started; it took me 3 separate attempts to actually get the hang of coding. I bothered this developer a lot; and I mean &lt;strong&gt;a lot&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After I got the hang of some basics in Perl, I gave C++ a shot and picked it up fairly quick, or so I felt. I didn&#039;t know what loops, conditionals, classes, or any of that was. I simply would look up things like, &amp;quot;if this is true, do this if not do this in C++&amp;quot; or &amp;quot;how do i do something several times in C++.&amp;quot; It wasn&#039;t until months later that I actually began learning vocabulary and my Google searches improved.&lt;/p&gt;
&lt;p&gt;I then got into server adminstration when I started running a BZFlag server; I started off maintaining BZFMaps. My process for learning how to manage a Linux server wasn&#039;t much different at all, I kinda just started looking up what SSH was and what commands meant. Then I would search how to do things in Linux or I would copy paste error messages.&lt;/p&gt;
&lt;p&gt;I guess you could say I kinda dove into the deep end and then searched how to swim. You have no idea how much I have fucked up on a production server. I have...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;firewalled everyone (including myself)&lt;/li&gt;
&lt;li&gt;executed &lt;code&gt;sudo pkill sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;made servers non-responsive&lt;/li&gt;
&lt;li&gt;run a fork bomb&lt;/li&gt;
&lt;li&gt;shutdown a remote server (not reboot...) without a UI to boot the server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oh man, those were fun times. On the bright side, I now know how to get out of those sticky situations and no longer make the same mistakes!&lt;/p&gt;
&lt;p&gt;To be honest, I don&#039;t know how I learned all of this. I just recently started picking up Python and have written some scripts. My friend asked me how I learned Python and quite frankly, I don&#039;t actually know/remember even though it was just about a month or two ago. I just looked up the syntax for loops, functions, classes, variables, and started typing and fixing syntax errors as I went along.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Twig Date Parser Filter</title>
            <link href="https://allejo.io/blog/twig-date-parser-filter/" rel="alternate" type="text/html" title="Twig&#x20;Date&#x20;Parser&#x20;Filter" />
            <updated>2015-11-12T00:00:00+00:00</updated>
            <id>923d5e6be1062b4f9c7366b116a3854238c9e760</id>
            <content type="html" xml:base="https://allejo.io/blog/twig-date-parser-filter/">&lt;p&gt;I&#039;ve recently been working with &lt;a href=&quot;/blog/wufoo-and-dapulse/&quot;&gt;joining Wufoo and DaPulse together happily in marriage&lt;/a&gt; and have made solid progress. However, one problem I encountered was the lack of consistency between Wufoo&#039;s POST data and API responses for their date fields. For example, I would get dates formatted as &lt;code&gt;YYYYMMDD&lt;/code&gt; from the POST data received from their webhooks but I&#039;d get &lt;code&gt;YYYY-MM-DD&lt;/code&gt; whenever I sent an API request.&lt;/p&gt;
&lt;p&gt;The problem I ran into with this was that Twig (and most likely PHP) could not create proper DateTime objects from the inconsistent data. Twig had to guess the date and I ended up getting August 20, 1970. Because a quick Google search didn&#039;t give me anything and I had to resolve this issue, I wrote a &lt;code&gt;parse_date&lt;/code&gt; Twig filter to handle these types of situations.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;Date: &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;{{ PostData | parse_date([&quot;Ymd&quot;, &quot;Y-m-d&quot;]) | date(&quot;M d, Y&quot;) }}&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may &lt;em&gt;responsibly&lt;/em&gt; give it as many possible date formats you&#039;d like to try and it&#039;ll return a DateTime object after it finds the first valid date; this DateTime object can now be used properly with the &lt;code&gt;date&lt;/code&gt; filter. This filter uses &lt;a href=&quot;http://php.net/manual/en/datetime.createfromformat.php&quot;&gt;&lt;code&gt;DateTime::createFromFormat()&lt;/code&gt;&lt;/a&gt; to check which date format will work. This filter will also accept a single date format if you do not need to guess from an array of possible formats.&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/allejo/896b10f5630bbbf8fb632de5ff4c83fe.js&quot;&gt;&lt;/script&gt;</content>
        </entry>
            <entry>
            <title>Wufoo and DaPulse</title>
            <link href="https://allejo.io/blog/wufoo-and-dapulse/" rel="alternate" type="text/html" title="Wufoo&#x20;and&#x20;DaPulse" />
            <updated>2015-11-07T00:00:00+00:00</updated>
            <id>2bc78b60ecdc3cf46c6b26bff293111d91a5243b</id>
            <content type="html" xml:base="https://allejo.io/blog/wufoo-and-dapulse/">&lt;p&gt;At one of my jobs, we have recently moved from &lt;a href=&quot;https://asana.com/&quot;&gt;Asana&lt;/a&gt; to &lt;a href=&quot;https://dapulse.com/&quot;&gt;DaPulse&lt;/a&gt; for task management and collaboration. I&#039;m not part of either&#039;s marketing team nor am I sponsored by either of them so I won&#039;t advocate for either or say one&#039;s better than the other. We also use &lt;a href=&quot;http://www.wufoo.com/&quot;&gt;Wufoo&lt;/a&gt; for forms and we used to use &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt; to automatically create entries in Asana and assign them to my coworkers and myself based on the entries. At the time of writing this, Zapier falls unbelievably short with their support of DaPulse to the point where it&#039;s actually quite embarrassing and they shouldn&#039;t even be offering it in the first place.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Somedays, you just just need Wufoo and DaPulse to work together, but the native integration either a) doesn&#039;t exist or b) doesn&#039;t do what you want.&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;https://zapier.com/zapbook/dapulse/wufoo/&quot;&gt;Zapier&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hah... Zapier doesn&#039;t do what I want.&lt;/p&gt;
&lt;p&gt;I took it upon myself to write my own webhook that would actually be usable for my team. I started writing &lt;a href=&quot;https://github.com/allejo/PhpPulse&quot;&gt;PhpPulse&lt;/a&gt;—a PHP wrapper for working with the DaPulse API—so I would be able to make a lot more than just a link between Wufoo and DaPulse. Now, the wrapper still needs a lot of work but creating boards, pulses, notes, and updating column values all work; that is exactly what I used to build a &lt;a href=&quot;https://github.com/allejo/AS-Webhooks&quot;&gt;quick Silex project&lt;/a&gt; that will accept POST data from Wufoo and then create pulses with appropriate notes containing the Wufoo data. This project is far from scalable and could use a lot of thought in making it better, but for anyone else in the same situation that my team and I were in and uses PHP, this is a good starting point for you.&lt;/p&gt;
&lt;p&gt;There will be more to come regarding PhpPulse as I finish writing support for the rest of the API and there&#039;ll also be more to come for PhpSoda. I have a smaller project planned for all of the wrappers I&#039;ve started so I&#039;ll write about that once I&#039;ve launched it.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Welcome Into My Life, Jekyll</title>
            <link href="https://allejo.io/blog/welcome-into-my-life-jekyll/" rel="alternate" type="text/html" title="Welcome&#x20;Into&#x20;My&#x20;Life,&#x20;Jekyll" />
            <updated>2015-10-25T00:00:00+00:00</updated>
            <id>65b8047e905b0297cde6c577a49dfebd44743327</id>
            <content type="html" xml:base="https://allejo.io/blog/welcome-into-my-life-jekyll/">&lt;p&gt;It&#039;s been an eternity since I launched my Jekyll based website (this one) but up until now, I hadn&#039;t done much with it nor had I done much with Jekyll. It wasn&#039;t until recently where at both of my jobs, I started using Jekyll a lot more and was able to experiment and discover some better practices. I&#039;m no Jekyll expert but I finally feel comfortable enough with Jekyll and Liquid to work with it efficiently to get a product out. Better yet, I am comfortable enough to be able to assist on &lt;a href=&quot;irc://irc.freenode.net/jekyll&quot;&gt;#jekyll&lt;/a&gt;; that&#039;s an IRC channel for you youngins who only know Slack.&lt;/p&gt;
&lt;p&gt;I was finally able to dedicate a weekend to work on my website and well, here we are; this is it. There is still a lot more that I have planned for my personal website, but that will probably be built in the coming days. I&#039;m not using any CSS frameworks other than Thoughtbot&#039;s Bourbon and my very own &lt;a href=&quot;https://github.com/allejo/OnTheRocks&quot;&gt;OnTheRocks&lt;/a&gt;—a flexbox based grid system built before they were cool and common. There is still a lot of Sass I need to rewrite, fix, or optimize and probably quirks on the website but I really hope none of them are embarrassingly bad.&lt;/p&gt;
&lt;p&gt;Welcome into my life, Jekyll.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>My New Series: BZFlag Plug-ins for Dummies</title>
            <link href="https://allejo.io/blog/my-new-series-bzflag-plug-ins-for-dummies/" rel="alternate" type="text/html" title="My&#x20;New&#x20;Series&#x3A;&#x20;BZFlag&#x20;Plug-ins&#x20;for&#x20;Dummies" />
            <updated>2015-10-25T00:00:00+00:00</updated>
            <id>2571cd374296871cbab7b6ef4d1e1f0e65e86777</id>
            <content type="html" xml:base="https://allejo.io/blog/my-new-series-bzflag-plug-ins-for-dummies/">&lt;p&gt;Throughout my time with BZFlag, I have written a large amount of plug-ins. Nowadays, I have adopted the API as my child. By this, I mean that I have started to contribute a lot to the API and documenting it for others to use. I have also shaped certain parts of it to my liking mainly so it benefits my plug-ins.&lt;/p&gt;
&lt;p&gt;The plug-in API was originally created by JeffM2501 with the release of BZFlag 2.0.0; such a long time ago, right? There have been many amazing plug-in authors but all of them have moved on to other projects; e.g. flying_popcorn, LouMan, JeffM2501, and sigonasr2. I&#039;ve respected all of them very highly, especially flying_popcorn since he was the one who (patiently) taught me how to write plug-ins.&lt;/p&gt;
&lt;p&gt;With BZFlag activity being how it is, active plug-in developers are dwindling in the community. So, I would like to change that by sharing my knowledge of the API and write about how to write your own plug-in. To do that, I&#039;m going to start a &amp;quot;BZFlag Plug-ins for Dummies&amp;quot; series. I will be explaining things in a manner so even a nontechnical person would be able to understand it; if you&#039;re a more advanced programmer who doesn&#039;t appreciate things being written in layman&#039;s terms, either bare with me or learn on your own because you&#039;re not my target audience.&lt;/p&gt;
&lt;p&gt;For now, I will &lt;strong&gt;not&lt;/strong&gt; be posting this information to the BZFlag forums or the wiki. Don&#039;t worry, there isn&#039;t any juicy drama between me and the other developers and that&#039;s why I&#039;m not posting it elsewhere; it&#039;s easier to format and design things on my blog than on the forums or the wiki.&lt;/p&gt;
&lt;p&gt;In this series, I will be using Ubuntu but besides the initial configuration, the code will be identical and it won&#039;t matter what I use. Also, I&#039;m going to make a few assumptions about you, the reader:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You know some basic &lt;a href=&quot;http://simple.wikipedia.org/wiki/C%2B%2B&quot;&gt;C++&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;I will not be teaching you how to write C++; there are several people on the Internet better suited to do that&lt;/li&gt;
&lt;li&gt;If you have experience with another C-style or OOP language, that may be enough&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;You can install things on your computer
&lt;ul&gt;
&lt;li&gt;This is required so you can configure it to be a development environment&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;You have some basic knowledge regarding Git
&lt;ul&gt;
&lt;li&gt;This isn&#039;t mandatory nor does it play a huge role, but I will not teach it for the same reason I&#039;m not teaching C++&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will be writing this series on my spare time, so be patient if the intervals between my &amp;quot;chapters&amp;quot; aren&#039;t to your liking. Remember, BZFlag is an open source project meaning we&#039;re all volunteers that donate our time, effort, and resources so everyone can enjoy a great game; there have been certain individuals who fail to understand this concept, so I felt it necessary to put that friendly reminder.&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>Introducing PhpSoda</title>
            <link href="https://allejo.io/blog/introducing-phpsoda/" rel="alternate" type="text/html" title="Introducing&#x20;PhpSoda" />
            <updated>2015-06-10T00:00:00+00:00</updated>
            <id>249894e01343be2714826ccfe7c91329fb51d848</id>
            <content type="html" xml:base="https://allejo.io/blog/introducing-phpsoda/">&lt;p&gt;I&#039;m very proud to announce the birth of a new project and its &lt;a href=&quot;https://github.com/allejo/PhpSoda/releases/tag/v0.1.0&quot;&gt;first release&lt;/a&gt;! It&#039;s called &lt;a href=&quot;https://github.com/allejo/PhpSoda&quot;&gt;PhpSoda&lt;/a&gt; and it&#039;s not for the type of soda that you drink (even though I &lt;em&gt;love&lt;/em&gt; soda), it&#039;s actually an acronym for the &lt;a href=&quot;http://dev.socrata.com/&quot;&gt;Socrata Open Data API&lt;/a&gt;. What&#039;s &lt;a href=&quot;http://socrata.com/&quot;&gt;Socrata&lt;/a&gt;? Well they&#039;re a pretty neat company that hosts a lot of open data for governments. They host open data for the &lt;a href=&quot;http://www.socrata.com/industries/open-data-federal-governments/&quot;&gt;US Federal Government&lt;/a&gt; and for local governments like the &lt;a href=&quot;https://data.smgov.net/&quot;&gt;City of Santa Monica&lt;/a&gt;, where I work at the time of writing this.&lt;/p&gt;
&lt;p&gt;At my job, we use C# for everything development and my coworker contributed the &lt;a href=&quot;https://github.com/CityofSantaMonica/SODA.NET&quot;&gt;.NET library&lt;/a&gt; to the Socrata community because the library that existed before had room for some changes and refactoring. Similarly, Socrata has an official PHP library for working with the API but... It had plenty of shortcomings and it wasn&#039;t written in a very object oriented fashion. While I&#039;m definitely not one to talk, I felt that it only contributed to the reputation of bad PHP code. I decided to write my own PHP library and &lt;a href=&quot;https://github.com/socrata/dev.socrata.com/issues/234&quot;&gt;send a pull request&lt;/a&gt; to have my library listed under the &amp;quot;&lt;a href=&quot;http://dev.socrata.com/libraries/&quot;&gt;Community Libraries&lt;/a&gt;&amp;quot; section of the Socrata developer website.&lt;/p&gt;
&lt;p&gt;Socrata is mainly aimed at the public sector and I know PHP isn&#039;t too common in the public sector but for the PHP enthusiasts out there, I hope this library helps! I&#039;m always open to suggestions and feedback, just &lt;a href=&quot;https://github.com/allejo/PhpSoda/issues&quot;&gt;submit an issue&lt;/a&gt; and I&#039;ll be glad to discuss things.&lt;/p&gt;
&lt;p&gt;I have spent hours documenting my library so please check out the Wiki and install the package via Composer!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://packagist.org/packages/allejo/php-soda&quot;&gt;Packagist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/allejo/PhpSoda/wiki&quot;&gt;Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#039;m ecstatic to see my library listed on their website! This has been the first library I&#039;ve written and one of my first standalone open source projects not related to BZFlag or WordPress. I plan on extending my horizons a bit further with some more projects I have in mind that I will open source, so stay tuned... This is exciting!&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>From Underachiever to Uhm...</title>
            <link href="https://allejo.io/blog/from-underachiever-to-uhm.../" rel="alternate" type="text/html" title="From&#x20;Underachiever&#x20;to&#x20;Uhm..." />
            <updated>2015-03-13T02:10:00+00:00</updated>
            <id>056cd956e67caa64775811967ca3a137caee5494</id>
            <content type="html" xml:base="https://allejo.io/blog/from-underachiever-to-uhm.../">&lt;p&gt;A really long time ago I posted that I was an underachiever and sorry to disappoint but I still am. Well... Sorta. Maybe. Mostly.&lt;/p&gt;
&lt;p&gt;Alright, so it&#039;s my third year of college and I&#039;m still not entirely fond of it nor the concept of it but I&#039;ve grown to slightly tolerate it now that I&#039;ve taken some classes that I&#039;ve actually liked. I am still pissed off with the administration and the politics that happen within my university but that&#039;s anywhere, I guess... Not much I can do about that. Regardless of me liking the class, I&#039;m still an underachiever where I have not had one class where I go out of my way to actually complete a project or homework assignment or study for it. Not even my computer science classes. Really? Yes, really. I really do not mean to sound arrogant but my computer science classes are beyond boring, I haven&#039;t learned much from my professors with the exception of ARM. Heck, I have even had friends in higher level classes come to me for help and I&#039;ve been able to help them. So computer science classes get the least amount of effort and the most amount of experimentation. By experimentation, I mean I try a lot of ideas that I come up with no matter how bad or good of a practice they are, so it has its perks.&lt;/p&gt;
&lt;p&gt;I always open source my homework assignments and it seems that my classmates have &amp;quot;forked&amp;quot; my homework assignments to turn it in as their own... While that was not my intention, I won&#039;t cease to open source my homework simply because other people are turning it as their own. I know a few classmates do appreciate the examples and working examples.&lt;/p&gt;
&lt;p&gt;Now, I&#039;m not going to say I&#039;m a complete underachiever, I do put a lot extra effort into projects that I have started and care deeply about such as BZiON or LeagueOverseer. I also do put a lot effort into the projects I have at work simply because I enjoy it.&lt;/p&gt;
&lt;p&gt;I guess you could say I only put effort when it&#039;s something I enjoy. I guess I don&#039;t enjoy school. Oops...&lt;/p&gt;</content>
        </entry>
            <entry>
            <title>From WordPress to Jekyll</title>
            <link href="https://allejo.io/blog/from-wordpress-to-jekyll/" rel="alternate" type="text/html" title="From&#x20;WordPress&#x20;to&#x20;Jekyll" />
            <updated>2015-02-06T22:45:50+00:00</updated>
            <id>39e100235eb9f118edeae315d6d8b96646cdb7f7</id>
            <content type="html" xml:base="https://allejo.io/blog/from-wordpress-to-jekyll/">&lt;p&gt;Well I never thought the day would come where I&#039;d move away from WordPress for my personal website. I&#039;ve been using WordPress since 2009 and it has changed a lot since I started using it. Don&#039;t get me wrong, I still love WordPress and I do plan on contributing to it, but I want to give Jekyll a try. The main thing that drew me to Jekyll is the use of markdown and YAML for everything and I definitely love both of those things; using this combination definitely beats writing a WordPress plugin to handle custom data. I will admit, making a theme for Jekyll is far simpler especially since I&#039;ve started working with Symfony which uses Twig; Liquid is very similar to Twig so it&#039;s incredibily simple for me to get used to. Adding real time information to Jekyll is a pain since there is no server side processing; everything is static (no shit, sherlock). By using JavaScript and APIs, I can get by with having some dynamic content; although for the 1% of people who don&#039;t have JavaScript enabled, you just can&#039;t fully experience my awesome site.&lt;/p&gt;
&lt;p&gt;I am going to really miss reading all of the password combinations, I just might create a dummy page just to continue reading them. I mean who wouldn&#039;t want to see &lt;code&gt;admin:pussy69&lt;/code&gt; in their failed login logs?&lt;/p&gt;
&lt;p&gt;By moving to Jekyll, it has given me a great opportunity to clean up my website and finally organize things properly so this was the perfect moment. Not only will I be cleaning up all of my pages, I will also be deleting a lot of blog posts that I&#039;ve written in the past that I&#039;m just embarrassed about.&lt;/p&gt;
&lt;p&gt;Since I just started using Jekyll here are some immediate pros and cons:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Much faster load times&lt;/li&gt;
&lt;li&gt;Quicker and simpler theme development&lt;/li&gt;
&lt;li&gt;No need for a database with a lot of different entries&lt;/li&gt;
&lt;li&gt;Nothing to hack...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lack of server side processing
&lt;ul&gt;
&lt;li&gt;I am unable to have any dynamically generated content; I will be forced to create my own API and use other APIs to generate some dynamic content. On the bright side, I will be able to practice my JavaScript more&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far, I&#039;m satisfied with Jekyll and I&#039;m hoping I&#039;ll be able to continue using it and hacking on it by writing my own Jekyll plug-ins.&lt;/p&gt;
&lt;p&gt;Also, there are a lot of shit designs out there for Jekyll websites. I do have to say, mine is pretty awesome in comparison.&lt;/p&gt;</content>
        </entry>
    </feed>
