Day's progress so far, and some thinking out loud:

Continuing work on Umpire client-server architecture. I realized the naive port I was attempting was going to be unnecessarily ugly---while it would have let me replace every method call with an RPC call, it did so at the cost of turning everything async, and likely introducing latency into things that really shouldn't have latency (steps in a unit movement animation, say).

So the new approach: each client maintains a cached copy of the player's (or players') view of the game state. (This can be incomplete and even stale due to the fog of war mechanic.) The protocol then focuses on high-level player commands, with the server replying with low-level updates to the player's view, for example, unit A observed tile x,y as having state S in turn T. That's a lot of placeholder variables, but whatever.

Some of this is already how the game-UI interface works, but not enough---lots of the code as written assumes that the game state will always be instantly accessible at essentially zero cost, something we can no longer guarantee. Thus the addition of a simple caching layer.

This may allow simplification of the game engine as well, which has had the task of tracking each player's observations separately. I expect this can be delegated entirely to the clients.

tarpc vs tonic

Along the way I thought I might need a bidirectional RPC mechanism, allowing the server to send messages to the client as well as the reverse. Since I couldn't at first find an example of how to do this using tarpc, I spent a while exploring tonic as an alternative. tonic distinguishes itself by relying on Protocol Buffers to generate the RPC code (while tarpc defines this all in Rust) and by focusing on HTTP/2 as a transport, rather than the more agnostic system tarpc uses. I hadn't worked much with Protocol Buffers since a prototype serialization layer for Analytics data I did at Adobe, but things haven't changed much, if at all.

In the end, though, I did find an example of bidirectional RPC using tarpc. Since it really would be far cleaner at this point to use my existing types defined in Rust rather than externalizing those to the protobuf compiler, I gave up on tonic for this use case and am moving forward with tarpc.


The conceptual shift, summed up for my own benefit: the clients will be "thicker" than they were, taking charge of tracking player observations, and the protocol will be "thinner" than it was, focusing on mutations to each player's cached observations.

That's it for now. Good night and good luck.

Current listening: Revolution-Etude, Chopin; followed by HALOHEAD, by Kunzite.

Pictured: roadmap excerpt

Doing some work on Umpire lately. Umpire is a text-based clone of that wonderful game of yore, Empire: Wargame of the Century. Since 2016 I've used it as a playground for learning Rust and also more recently for experimenting with reinforcement learning.

The objective in this current wave of development is to port the code to a client-server architecture so the game can be played as online-multiplayer. That has me diving into tarpc and tokio for the first time. So far, so good. There's long been a separation between the abstract game logic and the user interface in Umpire, and this maps nicely to the server and client roles. I don't have far to go for a very dumb implementation---one where players have to sit around and wait for each other to take their turns. That fits easily with the current architecture which was designed for hotseat multiplayer, just like the original, but probably isn't the long-term destination.

As always, will keep you posted as things develop.

Pictured: the prior release, Umpire 0.4

Status report on the so-called Git Kit project: it's now switched from plain GTK4 to libadwaita, giving us dark mode support out of the box. I also spruced up the vertical alignment a bit, and learned more about the framework.

Next step: a file-history view....

The last few days I've been focused on putting a Linux distribution on a dog-slow Windows 10 laptop I'm responsible for. (8GB RAM was not enough, it seemed to be constantly swapping to its agonizingly slow magnetic disk drive...)

First, I put Fedora SilverBlue 37 on it, in hopes the immutability would pay off in providing a hard-to-destroy environment for some less tech savvy users. But getting the Broadcom drivers to work required some finagling, which made me think upgrades (every 6 months in Fedora-land) would get messy.

I want to set this thing up and forget about it with the expectation that non-expert users can keep it updated indefinitely. SilverBlue probably isn't it, but it was fun to try, and it did demonstrate that a GNOME 3 desktop runs much better on the HP than Windows 10 did.

So I've found myself back in the world of Debian, Ubuntu, and friends. Linux Mint is the current favorite: Windows-esque, with an LTS support horizon that extends out to 2027, two years after Windows 10's. By 2027, the machine will be someone else's problem. It's not booting as quickly as I expected so... might need to work on that. But otherwise, it seems promising.

Feels like I'm getting some momentum here 😺

DONE file content displayed in gtk::TextView on click

DONE file list is now backed by gio::ListStore

Content Warning: the screenshot gets a little bit meta; could destabilize your perception of reality?

Have a good one, everybody

Next step: show the contents of the selected file.

And a TODO: eventually move the file list view to something dynamic based on SignalListItemFactory. Otherwise our memory usage will be O(n) where n is the number of paths in the repo.

Jumping back to raw gtk4-rs, got tired of the magic in Relm4.

We're now showing the files in the repo in a static gtk::ListView.

At the mo I'm aiming to recreate something like the git gui UI as a starting point.

Today: deepening my knowledge of relm4. My immediate objective is to add a files list to the sidebar. Once files are selectable, I'll show a file view in the main pane.

I spent a while reading up on Relm's factories, but kept wondering how they relate to GTK's own list views. The Relm docs recommend using GTK directly for lists with many items---I think all the files in a git commit / working directory probably qualify.

Question I've been pondering: if Git Kit (or whatever this thing is called) is going to be a sort of combination of git gui and gitk with a dash of GitLens thrown in, then we'll have to represent the relationship of commits to each other, and of each commit to its file tree, and of each file to the commits that created it.

All at once?

Which is, like, a tall order.

P.P.S. And god, that feeling of writing, like, programs that run and do things on your computer.... I guess I'm old-fashioned. Somehow the web stack doesn't give the same satisfaction.

P.S. This is reminding me what I dislike but mostly what I love about Rust. The Relm4 docs have some useful guidance on managing lifetimes / ownership in the GUI context---still plenty to learn. But man, when it clicks, it clicks. The "all clear" from the compiler is a powerful drug....

Progress report:

The project is called Kit for now because I like cats and also Git. The SEO will be terrible but whatever.

Using relm4 (GTK4 under the hood) and git2 (the Rust interface to libgit2) I got as far as reading a ref name from the project's repo and displaying it in a very basic UI. Feels like a win 😺

After 2 years working with Vue 3, the ergonomics of gtk4-rs itself (powerful as it is) do not look fun. Checking out relm4....

Working to implement my "Git Blame Through Time" concept.

In the vein of lightweight tools like git gui and gitk, and the GitLens blame tool in VSCode, but done in Rust with Gtk4—and more focused on tracking the lines back through their whole history, rather than just seeing the state in a particular commit.

We'll see what happens. Yoda's words are currently ringing in my ears:

"Do or do not; there is no 'try'."

Just fixed a problem on my instance, follows should work now... so, like, have at it 😹

One day the machines will replace us. But for now, it's human beings behind the keyboard, on the Zoom screen, reading your code review.

Hunch/belief: refunds are deliberately painful to weed out the fakers and the uncommitted.

Is 43 minutes and 21 seconds of waiting music and the onerous process of repeating your account number over a muffled phone connection really worth the $71.63 or whatever?

Probably not.

Pro tip: click the tiny "Save" button in Shutterfly before clicking "Next" and adding the thing to your cart. Call it user error, call it the holiday rush and a procrastinator's order on December 23rd, all of that is true! But they charged me to mail blank cards to my friends and family, so I did sit through the rigmarole, for justice!

Yet... I probably wouldn't do it again.

But let's be honest: the most likely cause of this server's demise is not OOM errors, or load it can't handle (ha) but rather that bane of all indie blogs: unexpected non-renewal of Let's Encrypt certs 🙀

Aaand the blog goes down with an OOM failure. Don't forget Restart=always! Also don't forget: 512 is not a lot of megabytes.

Has anyone yet crawled the ActivityPub follow graph and tried to visualize it?

I get more and more tired of web apps. They're just not as nice. Switching from Gmail's ui to Thunderbird (even as neglected as it can feel) has been glorious. So snappy. Not dumbed down. And now I have 10 billion filter rules 😹