Christmas mission 2023

Apparently this is just going to be a thing now.

This past Christmas marked the third annual Christmas Mission for my son.  It started simple enough, with a semi-educational crypto puzzle two years ago.  Then there was last year's mission, which involved multiple puzzles and a theme.  And he loved both of them, which is great.  But now it looks like I'm going to be coming up with one every year for the foreseeable future.

This year's mission was very involved.  Maybe a little too involved.  The Word document I used to take notes and print out the clues was eleven pages this time, including charts and graphics.  It had five "quests", plus the introduction and conclusion notes.  It took a while to complete, but apparently this one was a hit too, so I guess I'm doing something right.

This year, Zane wanted to be included in designing his mission.  Obviously the puzzles were up to me, but he had some requests for the theme.  Last year was Peanuts/World War I.  This year, he wanted it to be based on Minecraft.  And not just "regular Minecraft".  He came up with an elaborate plot outline involving an actualization of the Internet drama war between Minecraft and Fortnite, because apparently that's a thing.  It involved a spy mission, crafting things, resurrecting the Ender Dragon, and getting messages from various Mojang staffers.  I jotted all this down in OneNote and Zane double-checked my notes several times leading up to Christmas.  So at least he was engaged, which is good.

My first task was to try to hammer these notes into a semi-coherent story.  Since it was supposed to be a spy mission that involved an "actual war" between Minecraft and Fortnite and the resurrection of the Ender Dragon, I somehow got the rather dark idea of a mission to use the dragon as a biological weapon to destroy the world of Fortnite.  Granted, that probably constitutes genocide and is certainly a war crime, so not exactly a happy Christmas story.  But it made sense as a motivation and used the desired plot elements, so I ran with it.

The second task was to break this up into a series of missions.  One of Zane's notes involved creating an End Portal using the Eye of Ender.  Apparently you can craft this from some glass, a Ghast tear, and an Eye of Ender.  So I decided to base the missions around that.  We would gather the ingredients for an Eye of Ender so that he could craft the End Portal and send the Ender Dragon to the world of Fortnite.

But how do you resurrect an Ender Dragon?  I don't play Minecraft, so I've got no freakin' clue.  However, my sister-in-law was giving Zane a Dungeons and Dragons themed gift, which gave me an idea - magic!  Why not?  I mean, how would you resurrect a dragon?  When in doubt, just call it magic and get on with your life.

So with that, I came up with five quests.  For each one, I wrote a note from one of the aforementioned Minecraft staffers giving a puzzle to solve with a hint on the location of the next letter.  It started with an introduction letter laying out the quest to wipe out the world of Fortnite and directing him to the living room to find the first quest letter.  For the first three quests, I also included a printout of the ingredient itself, lifted from the Minecraft wiki.  The quests were as follows:

  1. First quest: Get the glass.  This involved solving a simple logic puzzle.  The glass was in one of the kitchen cabinets, but he couldn't just look in all of them because it's very delicate and too many vibrations would shatter it.  So the puzzle laid out a few rules and Zane had to deduce which cabinet it was in.  
  2. Second quest: Get the Ghast tear.  This one was a simple Morse code message.  The letter included a hint directing Zane to the bathroom to find a key to decode the Morse hidden with the spare toilet paper.  The decoded message directed him back to the living room to look under the coffee table.
  3. Third quest: Get the Eye of Ender.  This one was another relatively simple decoding, this time of an ASCII-encoded message.  This one was a two-step problem, as the letter contained a series of 8-bit binary numbers and a hint pointing to the kitchen silverware drawer, which contained an ASCII chart.  So Zane had to convert the binary to decimal and then do the character lookup.  The decoded message pointed to the bathroom sink, where the next message was hidden in the cabinet underneath.
  4. Fourth quest: Build the End portal.  This quest was a bit more involved.  The puzzle was to decode some text, but there was a different key for each line.  (There was only one word per line, so I was nice about that.)  A hint pointed Zane to a "decoder disk" that he got at a gift shop on our trip to Gettysburg last summer.  It came with directions on how to use a two-character key to decode a message.  So the letter said that each line had a different key at the start, followed by a space and then the message.  It actually took a bit to get Zane to figure out how that was supposed to work, but he did get it eventually.  The message led him to the coat closet, which had a Minecraft storage box that was supposed to serve as the crafting table to make the portal.
  5. Fifth quest: Resurrect the Ender Dragon.  This one was difficult.  To resurrect the Ender Dragon, the letter directed Zane to see his aunt, who was holding a "magic scroll" for him which would serve as the key.  The "scroll" was actually a printout of a page from an old Dungeons and Dragons manual, particularly the page for the "Wish" spell, which seemed an appropriate way to resurrect a dragon.  The puzzle was actually a set of 4--tuples, one per line, values separated by dashes.  At the bottom was a hint that these were coordinates, hinting that the page was divided into columns, which had lines, which had words, which had letters.  The idea was that, e.g. 2-5-3-4 would map to the fourth letter of the third word of the fifth line of the second column.  Clearly this was not as clear to Zane as it was in my head, because he had a hard time trying to figure out what he was supposed to do.  I eventually had to guide him through it, but once he got the idea he was able to decode the message successfully.  

The last message pointed Zane to his laptop.  His success letter was tucked inside, telling him that I'd installed a copies of Minecraft Java Edition and Minecraft Education Edition, as well as setting up a Minecraft server for him to play with.  He's been into Minecraft for a while, but hadn't had it on his computer, so that seemed like a suitable way to finish out a Minecraft-themed quest.

At the end of the day, Zane seemed pretty happy.  This was definitely a more involved mission than last year, and the puzzles were more challenging for him, but he said he enjoyed it.  So I count that as a success.  And at this rate, I suspect he'll want another mission this year, so I'll have to start planning earlier.

SSH Agent with Powershell

Another "note to my future self", recoded here for posterity.

Today I finally got around to making SSH agent work in Powershell with Git.  For the last year or so, I haven't had to deal with because my work mostly involved writing PHP code inside of WSL.  In that scenario, you're essentially using Linux, so ssh-agent works just fine.

But on native Windows with Powershell...not so much.

I mean, sure, I could just use Git BASH.  But why would I want to do that?  Powershell is awesome, and even if I probably know BASH better, I prefer Powershell.

But it turns out it wasn't all that difficult to get things working in Powershell.  There were two pieces to it:

  1. Use the right SSH agent (i.e. the one that comes with Git).
  2. Write a Powershell adapter for it.

It turns out that there's a Windows service for "OpenSSH Authentication Agent".  I'm not entirely sure what installs that, but apparently it's different from the ssh-agent that's installed with the Windows Git package and the ssh-add command from that doesn't seem to talk to it properly.

My solution was to just disable that service and use the plain-old ssh-agent that comes with Git.  The only problem with that is that the traditional invocation of eval `ssh-agent` doesn't work in Powershell because it outputs a BASH-formatted script.  But that's easily fixed with a couple of regular expressions.  So I added this to my Powershell $profile:

Set-Alias ssh-agent "$env:ProgramFiles\git\usr\bin\ssh-agent.exe"
Set-Alias ssh-add "$env:ProgramFiles\git\usr\bin\ssh-add.exe"

# Need to turn off Open SSH Authentication Agent system service,
# then run the ssh-agent from Git.
function Start-SshAgent {
    $output = (ssh-agent)
    $sock_match = ($output | Select-String -Pattern 'SSH_AUTH_SOCK=(\S+);')
    $sock_path = $sock_match[0].Matches.Groups[1].Value
    $pid_match = ($output | Select-String -Pattern 'SSH_AGENT_PID=(\d+);')
    $agent_pid = $pid_match[0].Matches.Groups[1].Value
    $env:SSH_AUTH_SOCK = $sock_path
    $env:SSH_AGENT_PID = $agent_pid
    Write-Output "Agent pid $agent_pid"


And there we go!  Now I can just run ssh-add to add my key and Git picks it up as expected.

Minor WSL GUI annoyance

Minor annoyance: For some reason, WSL GUI windows like to hide themselves.  I'm not sure why.

I see this all the time on my Windows 10 work laptop, where I use WSL extensively.  It usually happens after my laptop has gone to sleep.  I come back in the morning, log in, and all of my open WSL gVim windows are just gone.  The processes are still running, they're just not displayed and they're MIA from the Windows taskbar.

Fortunately, this is easily fixed by just starting a new WSL GUI app.  So I generally just pop up my Windows Terminal and start a new gVim instance, and then the running instances pop back into existence and they're fine until the system sleeps again.

I have no idea why that happens, nor if it's specific to Windows 10 or anything else.  It's just one of those things that's annoying but not a big deal.  Rant over.

Sometimes WSL needs an update, apparently

Note to self: Sometimes, it seems that WSL needs to be updated.  I mean, yes, obviously it does.  But apparently sometimes Windows updates will break it and you need to manually intervene.

I saw this the other day.  At some point, something got updated and when I tried to open a new WSL terminal, it would always fail with the message Error code: Wsl/Service/0x80040326.  Oddly enough, the WSL shells and processes I already had running still worked, I just couldn't start any new ones.

Luckily, the fix is pretty simple.  You just have to open up a Powershell instance run wsl --update to update WSL and then wsl --shutdown to reboot it.  That's it.  It's just annoying because that's a manual process - you still need to manually run the update, even if you reboot your computer.

The joy of WSL and desktop Linux

I had a workflow revelation on my work machine the other week: Corporate IT pushed a Windows 10 update that brought me up to a version that actually supports running Linux GUI apps under WSL!

I've WSL GUI apps on my home Win11 system for a while, but having that on my work machine is huge.  I use WSL extensively there.  See, we've been standardizing our dev environment setup using Lando, which is basically a convenience layer on top of docker-compose.  It's a handy tool, but the relevant thing to note here is that it works by directly mounting your project code directory inside the Docker container.  This is convenient for Mac and Linux users, but is kind of a problem for Windows users.

As you may (or may not) know, WSL2 is great and out-performs WSL1 in every way except one: cross-filesystem performance.  As long as you keep everything inside the WSL filesystem, you're golden and everything is fast.  But as soon as you try to cross from the Windows filesystem to the Linux one, or vice versa, performance just falls off a cliff.  Sure, it's not a big deal if you just want to edit a file or something like that, but anything that does any remotely significant amount of filesystem access (e.g. running an npm install) is just painful.  In my experience, it's not unheard of for the performance penalty to be on the order of 10x. 

Clearly that's not something you want to endure as part of your normal workflow.  The simple fix is to do all your work inside WSL, which for me means installing Lando in WSL and hosting my code inside WSL.  The only problem is managing the code.  If you want to do that in Windows, you need to do it over a network share, which works, but isn't exactly a great experience.  It also causes various permissions and file ownership issues for apps like Git that actually care about that.

That's where the WSL GUI apps come in.  Rather than dealing with the network share hassle, you can just install your favorite GUI tools inside WSL and run them just like you're on native Linux.  Problem solved!

Well, mostly solved.  Sadly, not everything runs on Linux.  In particular, there's no Linux port of SourceTree, which is currently my graphical Git client of choice.  But it's not that I particularly like SourceTree - it's just that I hate it less than all the other Git clients I've tried.  So I was forced to try some other options.

This part did not go well.  I tried a few different options, including GitFiend (which was nice, but would randomly crash under WSL) and Git-Cola, which was also decent, but which I had to drop because if left alone it would occasionally lock up and then somehow take down the entire system if I tried to close it.  I have no idea how it managed to do that (presumably some bug in WSL's GUI layer), but that's a different problem.  I also attempted to try Gittyup, but I couldn't, because it only offered Flatpak packages.  And, of course, the Flatpak daemon (or whatever it's called) won't install on WSL because it's missing some of the system-level stuff that it uses.  But that's a different post.

Eventually, I declared GUI bankruptcy and decided to actually learn how to use Fugitive, the Git plugin for Vim.  Turns out that Fugitive is actually pretty good and figuring it out was easier than finding a good graphical Git client.  But that's also a story for another post.

In any event, having WSL GUI apps is pretty nice.  Now I can do all my work in WSL, and still have both GVim and PHPStorm, if I need it, without having to pay the cross-OS performance price.  So I can have nice things and good performance.  Yay!