What is turning into one of my “big” projects is Perverted Lands, an erotic game set in the Nine Sisters. The game was inspired by Kingdom of Loathing, Fallen London, and Corruption of Champions. I had gotten a small proof of concept in November.

In December, I decided to do a “simple” task of upgrading the entire library to .NET Core 5. What I didn't realize was one of the base libraries I had used was not updated to .NET Core 5 and, even if it was, they made so many changes that I would have to rewrite about 60% of the front end. So, I said “screw that” and rewrote 100% of the front end but in a manner that wouldn't depend on a library and should be stable going forward.

In many ways, this was a mistake but it also forced me to settle down some ideas and hopefully start to work toward treating this as a full-blown project instead of taking random shots and constantly rewriting everything.

At the end of the month, I was talking about my effort and a good friend requested I write up a little bit about the project infrastructure.


Almost everything is hosted on Gitlab. I've been using Gitlab for years, mainly for my novels and website. Along the way, I've gotten to appreciate appreciate the features; giving everyone unlimited private repositories wasn't a bad start.

With this project, I decided to open up the issues page with the intent of using it for reported issues, requested features, and found typos. The code is kept private though along with the CI/CD pipeline. There is also the discussion group for more… non-technical conversations.


So the November release was built on an instance where I installed Sqlite (then MySQL) and the appropriate components. My main reasoning for this was because someone I was following talked about how Docker and containers were horrible in the big picture of technology.

After a few weeks of that, I decided that the convenience of Docker and being able to simplify management was critical if I didn't want to spend all my time patching and maintaining. I don't enjoy that, I have erotica to write, so I switched over to a Docker Compose setup that creates the various containers I need and stands them up.

It took me almost two weeks to figure all of that out, but in the end, I had a set of scripts that let me easily do everything I needed to create the docker images and run utilities inside them.

I also ended up being a “base” image that included all the tools I use. Fortunately, Gitlab provides container registries so I can keep all the porn local to me not to upload it to Dockerhub. This also means the tedious process of installing happens once in the build image and I can just use it on the deployments (below).

While the build image just as the “latest” right now, the game itself is tagged with the versions (also) below so I can pull out a version if I have to.

Semantic Releases

I like versions. They let everyone know what program is running and help identify if a bug has been fixed or not.

I decided that Perverted Lands was going to use a semantic releases. Basically, this means that changes are automatically versioned instead of coming up with a formal “version” at the end of the month. So, if I throw together a new fix, for say version 1.6.0, it will automatically become 1.6.1 and you'll be able to tell at the bottom of the page.

I already do this for my writing projects but I don't really tell anyone. You can just see the version on the top of the page (assuming it was working). I would tell my Patreon and SubscribeStar subscribers, but I felt like it was a lot of noise since I do a lot of commits and changes when I write.

Continual Integration (CI) and Continual Deployment (CD)

One of the key points of this is that I needed to reduce the manual effort of deploying the website. I'm only one person, and a heavily overloaded one that that.

To do this, I use Gitlab's CI/CD which is an amazing piece of work. Whenever I push up code to the server, Gitlab will run scripts for me. This is controlled by a file in the Git repository and it runs various “jobs” that perform actions.

In the above example, we have three jobs:

  • test-build runs all the unit tests against a Sqlite database, make sure the functionality mostly works, and there are no obvious flaws.
  • test-commit makes sure I formatted all of my Git commit messages correctly and reject the build if I haven't. (Yes, I could combine these, but I was playing).
  • If both of those run without errors, then tag-version runs which performs the semantic release checking based on the commit messages verified by test-commit. If there is a release (a commit starting with feat: or fix:, it triggers an automatic deployment.)

The biggest reason for this is I want to be able to test my code and make sure everything is good while I'm writing it, then merge it into master to deploy it. If the test fails because of a last-minute change (happens a lot), then it gives up. Otherwise, it then kicked off the deployment to unstable.

As a side note, while this application is mainly C#, I have a hunk of Node in there to handle packing the assets (Webpack) and management (Husky and commitlint on the client side so it is tested before the push).

Automatic Deployments

Deployments are just another set of Gitlab CI jobs, but with a different layout.

The first one, docker-web-image creates a Docker image of the game itself. Everything is tagged, so the version is cooked into the image and uploaded to the registry I showed above.

The deploy-unstable is the actual deployment. Through a bit of trickery, SSH, and encryption, the jobs hops over to the server and performs an update. When it is done, the new version of the game is deployed along with any needed database changes and assets that have to be updated. The most anyone has to do is hit F5 to get the latest and greatest.

You may notice that deploy-stable has a play button after that. While I don't have a stable environment yet, the entire idea is that unstable may break because of something I missed. When I'm absolutely and utterly sure it is ready for production release, I can hit that button and it will then deploy the exact same version on the stable environment.

All That Work

This process took me a few weeks to puzzle out, but the end result is:

  1. I make a change to the game.
  2. I push up the change.
  3. A half hour later, it is sitting on unstable.

Overall, I think that's a pretty good process that should hopefully let me fix on the fun parts of writing this game, like figuring out inventory and writing various scenes.


Just to keep me on track and avoid getting overwhelmed by the massive amount of tasks I want to do (I know Kingdom of Loathing was effectively written in a weekend, but I need more time), I decided to also use milestones on Gitlab. That way, I can move things in and out of them as I have bandwidth and everyone can get a sense of progression.

Because I'm going with semantic releases, milestones are just going to be the month I'm working on the game and a silly title related to the big changes. For example, writing this post is actually issue #45 which is the last item in the first milestone which means I'll close it as soon as the website updates.

Near the beginning, almost everything is going to be “code”, but as those stablize, there will be a lot more plot related items and the content will start to build up.

What's Next?

Well, I need to keep poking at this. This is going to be a marathon project where I add a scene or two over time until it gets to be a massive game like my inspirations. Giving me an idea of what you are looking for, such as up voting on Gitlab or talking about it on Discourse is always good for inspiration. Otherwise, just the likes on the posts make me feel good about creating things.

In the end, have fun and I hope you enjoy this journey with me.

Love, t'Sade