Subscribe to Personal Log via RSS and find other blogs to read in my blogroll.

Graveyard Shift

Finally, Brick Rick: Graveyard Shift has been released!

The initial import to the git repo was on October 1st 2020, and I tend to do that as soon as possible because my backup is git! If the latest pure development commit was on February 2nd 2021, that was around 4 months.

Physical edition, by Tim Drew

Look at this!

As I mentioned here, the physical edition delayed things, and last Saturday I finally made the game public.

So far the reception has been excellent, I’m happy!

Some reactions:

This is the first long-play that I know of –warning: spoilers–. A quote from the review:

It is tough to come up with something new when it comes to platform games, but this one seems to be rather special for the Spectrum scene. It is a huge and challenging game where you will need to play tactical to defeat the different type of monsters.

The final score is 90%, which is pretty good.

Releasing software is always stressful, there’s no much difference to what I do in my day job, but it wasn’t too bad. Two early bugs that were easy to fix, and it looks like that will be it.

May be software is rarely bug-free, but I call it success if there are no known bugs!

I’ve been thinking how I can unit-test a big part of the code, and my next project hopefully will be verified in a better way, instead of relaying on manual testing only –oh, my games are always very well tested and yet!–. In fact on all my recent projects I’ve settled in a structure that splits logic and presentation (update vs draw), so it sounds possible; and at very least writing the tests will force me to have a deeper look at the code, meaning: less mistakes.

Anyway, go an grab the game. It is fun! And if you have the real hardware and fancy collecting, you can treat yourself with a TFW8b 499 copy.

Poor man's fzf

Scala made me change the way I use vim, not only because relying on LSP for code navigation (previously gtags via gen_tags), but because the projects’ directory structure is way more complex than what I used to have in Python.

I used to rely on plain :ed filenameto open files, and then change buffers by just using :b name<TAB> (listing occasionally with :ls and selecting by number with :b number). But enter Scala and, for example, in my Gemini server the code lives in server/src/net/usebox/gemini/server/.

So I ended installing NERDTree and, although I have it always closed and only display the file browser when I need to open a file that I can’t reach immediately by using LSP navigation, it changed the way I work.

For a while I had a partial solution via the :find command. By setting this in my .vimrc:

set path=$PWD/**

In this way :find will search in all sub-directories, for example I can do:

:find filename<TAB><TAB>

But this is far from ideal. It kind of works, but very slowly and is not smart at all, so you get a lot of guff when it tries to find files by scanning all subdirectories (and when you want to open filename.scala you may find that filename.class gets in the way).

Why I didn’t use –or know– the awesome fzf? I can’t tell, but I installed it last night and I’m in love!

Not that I’m going to get rid of NERDTree, but I suspect I’ll use it less and less, because as Tom MacWright puts it in the return of fancy tools:

[…] This is obviously not the future of coding: shouldn’t I be navigating the source tree in 3D like in Jurassic Park? Sure, but the names of things, their functionality, and how it all fits together should be things that exist in one’s mind, not just in a computer.

That resonated with me, because that’s exactly what I was doing in Python, so I tried that fzf tool that he mentions in the post. I guess late is better than never.

Smart editors or IDEs?

I’ve been vim (or neovim) user for around 20 years –can’t say how long exactly, but that sounds about right–, and I’ve tweaked my way of using the editor every now and then, adapting to whatever I’ve been working on at the time.

For example, 5 years ago I was happy writing Python with syntastic and just calling Pydoc from my editor; but now I think I prefer using Python language server, plus some auto-formatting “on save” to conform to PEP8. It’s just a nicer experience.

On that context, I recently gave another go to emacs with LSP Mode, because it looks like the most complete LSP experience around (LSP comes from Language Server Procol), besides I guess using VS Code.

Not that I’m unhappy with vim-lsc, but LSP Mode has support for code lenses (for example), that add some context based operations to your code, such as “run this test”. The LSP server I use for Scala is metals –I don’t think there’s any other option–. It keeps improving on each release, but I’m aware vim-lsc doesn’t support all that metals offers. I just wanted to know what I’m missing.

So basically I gave emacs vanilla a go, after installing from a PPA repo version 26 (my Ubuntu at work only ships up to version 25), and got a very basic emacs configuration, enough to run LSP Mode. And it is nice, although I didn’t see anything that I would consider essential, it was enough to make wonder if I could move full-time to emacs (again; is not the first time I try!).

To make this attempt different, I decided to give Doom emacs a go, because if I need to get my own emacs configuration to the point where I can do the equivalent work I do in vim, is not going to happen. I just don’t have the motivation.

I compiled emacs 27, with only console support –I find the X11 version adds to much latency, and I’m very happy with tmux–, and Doom emacs took around 8 minutes to install with Python and Scala support (both with LSP). That already put me off considerably. To be fair: if I did’t want that level of complexity, I could be using vanilla emacs (but we would go back to not having the time or will power to get all configured).

Long story short: Doom emacs didn’t work for me. Not only because the overwhelming amount of options, but also because after all the steps, I couldn’t get LSP Mode to work as well as I managed to make it work on my hand crafted emacs configuration.

Although I didn’t get anywhere useful, it was a good experience. I think know now that emacs is not the answer to any vim’s shortcomings, because emacs comes with its own issues. So I guess I will give the JetBrains IDE a go, and see if a full IDE is worth it. I suspect is not going to be it, for the comments I hear around me: slow, bloated and complex. oh, isn’t that an IDE?

I was commenting my experience with my team mates, one of them long time emacs user (Clojure guy!) and the other one VS Code user, and the first conclusion was that part of the problem is Scala, or may be its ecosystem, because without the assistance of a smart editor or an IDE, it is very difficult to use third part libraries because the usual complexity of the type system.

The other was that IDEs have got to current levels of complexity (and power, to be fair) because of languages like Java, that really can use the help of such tools.

I know I’m not an IDE person, but I still want to know what is the state of the art, in case there’s anything I can add to my work with vim so I’m perhaps a bit more productive.

This week in gamedev #4

It is Thursday and, if you were paying attention, last week there was no gamedev update.

Life is sometimes busy, and you know I have been a bit intense with my Gemini server. Considering that I can do only one thing at a time (mostly), I haven’t progressed any of my new projects, and I was waiting on some things for Graveyard Shift.

And good news: the cover is ready, the inlay design is ready, the loading screen is almost ready, so what is left?

Cover art

The cover art is fantastic!

I need to write a short press release to be sent to a handful of specialised websites that cover retro-gaming, and that’s pretty much it.

When I have the loading screen, I will assemble a release candidate. There will be some last minute smoke tests, and finally release; likely next weekend.

I always prefer waiting a few days before sending a master to the publisher, so expect the physical release to be out from one to two weeks after the release. Remember tfw8b, 499 range.

Now, is not 100% accurate that I haven’t done anything else related to gamedev, but let’s keep it a secret for a bit longer, shall we?

Python's pattern matching

So Python has pattern matching in 3.10:

# the syntax highlighter doesn't know what to do
# with this new construct ;)
match point:
    case Point(x, y) if x == y:
        print(f"Y=X at {x}")
    case Point(x, y):
        print(f"Not on the diagonal")

And I think it looks fantastic!

I always found the chains of elif a bit awkward, but I guess that was part of what made Python simple yet powerful.

And this feature goes beyond that –take a look at the tutorial, search for value extractors-, and being this one of the features I liked most in my first contact with Scala, this is probably one of the few recent changes in Python that I find truly exciting (I must admit I didn’t like type hints initially, but now I think they are a very good idea).

It is still an alpha release, and it will need some time until it lands my Linux distribution (this Debian box is still on 3.7.3), but if I go back to do Python professionally, looks like it could be a lot of fun!

Hello Gemini!

So finally I decided to use one of my underused servers and deploy my Gemini server, and host some content.

If you don’t have a Gemini browser, I guess this link is not very useful, but there you go:

gemini://capsule.usebox.net/

I found it quite funny because, after configuring everything and checking that all was working, obviously nobody is going to visit my capsule –like a website in the Gemini jargon–, because nobody knows it is there.

But it was exciting nevertheless. I used the opportunity to write a short “how to”: deploying SpaceBeans on Debian.

It is using systemd to manage the service. Nothing special, but it works nicely. Not 100% happy with the logs, so I may revisit that part explaining a few options to deal with them.

One thing I’ve noticed is that the library I use to parse the command line depends on a library that is a bit heavy. Well, not that much in the grand scheme of things, but let’s say that adding 10MB to a project like this, sounds excessive.

So on the next version it will use scopt, that is excellent and it will make the distributed bundle smaller. It is a learning process. Worrying about 10MB in my day job would be laughable, so I’m not used to it.

I have a few more ideas to add to the sever, so I may implement something from that list before making a new release.

First SpaceBeans release!

Last night I finished the tests, or at least enough tests, so I could make a first release of my Gemini server: SpaceBeans.

With the name I tried to link the Gemini space theme with the idea of the server running on the Java Virtual Machine. So those beans are coffee beans, of course.

In case you haven’t read about it on this blog, the current list of features is:

  • Static files, including optional directory listings
  • IPv4 and IPv6
  • Configurable MIME types, or a built-in resolver
  • Virtual hosting, with SNI support
  • User provided certificates or auto-generated in memory (for development)
  • Configurable SSL engine (e.g. TLSv1.2 and/or TLSv1.3), with configurable ciphers

Which I believe it the minimum to make the server useful.

This is also my first release of anything Scala –out of what I do in my day job, that is–, so it has been an interesting experience.

Other than the source code (MIT licensed), you can download a .jar file that bundles everything you need to run the service on the JVM. My experience releasing services so far has been via Docker images, but in this case I think the lightweight nature of Gemini makes it a bit overkill.

There are a good number of Gemini servers out there, and some of them are very popular already, so I don’t expect anybody to use this; but you never know!

Tilde Club

I knew about SDF for years, a community coming from the early 90s, from the early BBS era –it started on a Apple IIe microcomputer– where you can have a free “UNIX Shell Account” (it is running NetBSD, so the “UNIX” part is true).

I even have vague memories of my own SDF account at some point, but the thing is that since the late 90s I’ve been using Linux, so having a shell account kind of lost the mystery at some point for me.

As part of my short trips around Gemini, I’ve ended in a couple of git hosting services I didn’t know about, like Sourcehut and tildegit. “tildegit” makes reference to the tildeverse.

“Tildeverse?” you may ask. Well, there’s a website for that:

we’re a loose association of like-minded tilde communities. if you’re interested in learning about *nix (linux, unix, bsd, etc) come check out our member tildes and sign up!

tildes are pubnixes in the spirit of tilde.club, which was created in 2014 by paul ford.

And that’s why I started mentioning SDF, because it looks pretty similar.

It is worth reading this post by Paul Ford where he explains how his tilde.club came about. Although there are a few references here and there about it being “not a social network”, for me, it’s all down to this quote form the post:

Tilde.club is one cheap, unmodified Unix computer on the Internet.

It reminds me very much of my own experiences early 2000s when I was involved in Free Software advocacy, as part of all the Linux User Group movement. A few of those groups, spawning sometimes from public newsgroups, formed large communities of self-hosted services with shared accounts.

My local group had an interesting twist on this, because we also formed a non-profit metropolitan area wireless network.

I guess everybody can have a Linux shell on a Raspberry Pi, but there’s something exciting about having an account on a public UNIX system, isn’t it? It is the community.

So tilde.club may be a social network after all.

PS: my sysadmin senses are constantly tingling when learning about these services. The old Linux User Groups systems relied heavily on trust, and I don’t know how these new communities deal with security, but it must be interesting for sure!

Gemini diagnostics: PASS!

I think I have ready everything I wanted to support in my Gemini server, at least on a first version that is “useful”. I may add more things later, perhaps experimenting with some ideas like server-side support for gemlogs.

This is the feature list:

  • Static files, including optional directory listings.
  • IPv4 and IPv6.
  • Configurable MIME types, or a built-in resolver.
  • Virtual hosting, with SNI support.
  • User provided certificates or auto-generated in memory (for development).
  • Configurable SSL engine (e.g. TLS 1.2 and/or TLS 1.3), with configurable ciphers.

Other than the virtual hosting with SNI support, there’s nothing too exciting.

Although I have already tests for the tricky parts, I still need to write some more to validate a few branches, and to have some regression protection moving forward.

But then I found this Gemini diagnostics repo created by the author of Jetforce Gemini server, as a torture test for Gemini servers.

And turns out, my server was passing all tests but two!

The first issue was related to returning a “not found” response when the request contained an invalid UTF-8 sequence, instead of “bad request”.

This was easy to fix: I was trusting Akka’s decode method, and it replaces invalid sequences with the infamous . Then I was checking for a resource on disk that obviously didn’t exist (hence the “not found” response).

The solution was to write my own decoder:

  val charsetDecoder = Charset.forName("utf-8").newDecoder()
  def decodeUTF8(value: ByteString): Either[Throwable, String] =
    Try(charsetDecoder.decode(value.toByteBuffer).toString()).toEither

Very simple. I included it in the input validation, and that test now passes.

The other issue is related to the TLS implementation provided by Akka, that doesn’t seem to provide a close_notify TLS signal before closing the connection.

I’m not a TLS expert, so I will investigate to see how important is this, what are the consequences, and if I can fix it or perhaps file a bug report to Akka.

So I’ll see to write some tests and I’m almost ready for a first release –that nobody will use, of course; look a Jetforce, it is very nice!–. I may deploy the service as well, and put some content on the Gemini space!

Not winning with Akka II: Won!

I got some help on Akka’s Gitter channel, and finally got to the bottom of the problem, and it wasn’t me!

It was all down to the TLSClosing parameter when creating the TLS listener. It uses IgnoreComplete, and that was breaking my code.

The explanation:

All streams in Akka are unidirectional: while in a complex flow graph data may flow in multiple directions these individual flows are independent from each other. The difference between two half-duplex connections in opposite directions and a full-duplex connection is that the underlying transport is shared in the latter and tearing it down will end the data transfer in both directions.

When integrating a full-duplex transport medium that does not support half-closing (which means ending one direction of data transfer without ending the other) into a stream topology, there can be unexpected effects. Feeding a finite Source into this medium will close the connection after all elements have been sent, which means that possible replies may not be received in full. To support this type of usage, the sending and receiving of data on the same side (e.g. on the Client) need to be coordinated such that it is known when all replies have been received. Only then should the transport be shut down.

So that’s why they had to change the behaviour on the TLS wrapper.

The thing is that you don’t need to use queues –most of the time, at least–, because Akka is smart enough to do the right thing if you define your flow correctly.

For example:

    Tcp()
      .bind(conf.address, conf.port) // not using TLS!
      .runForeach { connection =>
        logger.debug(s"new connection ${connection.remoteAddress}")

        val handler = Flow[ByteString]
          .via(
            Framing
              .delimiter(
                ByteString("\r\n"),
                maximumFrameLength = maxReqLen,
                allowTruncation = true
              )
          )
          .map(b => Request(b.utf8String))
          .map { req =>
              handleReq(connection.remoteAddress.getHostName(), req)
          }
          .take(1)
          .flatMapConcat(_.toSource)

        connection.handleWith(handler)
    }

I’m using framing to define what is a request, I map that into a Request, then my handler does its thing and maps that request into a Response.

At that point the element that is being handled by the flow is a response, so I say that I want to send back only one (using take), and I convert that response into a sequence of sources that are processed and converted into a stream of bytes that end in the client.

Akka is smart enough to keep the connection open until the response is sent, and then close automatically (I said I wanted to send back only one response).

And that’s managed with an internal status in the flow that is complete, and it was ignored by the TLS wrapper. So my connection was stuck after I had finished sending the response, no matter how I did it.

I found that using IgnoreCancel is what preserves the behaviour that I need:

IgnoreCancel means to not react to cancellation of the receiving side unless the sending side has already completed.

So basically, if the client has finished sending data, we don’t close until we have sent all our data (the completed signal), but the fact that the completed signal closes the connection is OK because Gemini protocol doesn’t support responses by the client.

This is a bit more complicated than what I’ve shown in the example, because it is possible that the client sends more data than the request. That happens, for example, if we try to access the Gemini server using curl –that speaks HTML and expects sending multi-line requests–. In those cases we have to drain that input (I’m using prefixAndTail, that splits one element from the input and provides a source that I can drain to essentially consume the data ignoring it).

I can’t tell how many different solutions I tried to this problem, and the issue was in the TLS layer. Anyway, looks like the server may work at the end!