Adventures!? With Text!?

They’re more likely than you think…

Throughout the pandemic (which is still going on, I guess) I’ve repeatedly switched between working a number of personal creative projects. Some get closer to being done while others remain in a perpetual state of “I’m working on it“. But recently, one project has shifted from the latter to the former… kind of-ish. Maybe?

The homebrew text adventure game engine I’ve been writing.

I talked about the engine briefly in a previous post, and it’s still by no means done. I even dropped the version down from 2x to 0.2x after I re-wrote pretty much all of the engine following that post (although it’s on v0.4x now). In any case, there will always be more bugs to fix and more improvements to make, but I did kind-of finally kind-of accomplish one of my goals mentioned in that post. That being:

creating a custom scripting language to build games in rather than requiring they be written in python3

The engine ( itself was, barring a few bug fixes, working as expected in v0.3x. The biggest change by far with v0.4x is the completion of a program ( that can read in custom game “scripts”.

These “scripts” are actually just flat text files with the game objects described in sections delimited by the ==== string. The “custom game loader” program-thing parses the file and creates those objects in the specific order they need to be created in using magic loops and regex.

It’s not as if the flat files are the only way to make a game with this engine though. You can also treat the engine like any other python3 library and call classes and functions and whatnot. Writing the game this way can also give you more control and ability to customize, but the flat file method is going to be a lot simpler (and a lot more readable).

I’ve included examples of both the flat text file method and the pure python method in the engine’s repo, as well as documented writing a flat text file in the repo’s README. I’ll eventually get the python way documented as well, but anyone who wants to go that route will need to be familiar enough with python that they should be able to make sense of the example game file easily (I left comments!). Or maybe not. I have been told before that my code is “wonky”, but in the words of the greatest salesman this (or any) world has ever known:

The stupider it looks, the more important it probably is.

J. R. “Bob” Dobbs

So my code is unquestionably very important.

Some time in the last couple of years, I was playing around with ncat and figured out how to automate the setup of a “disposable chat server” secured with SSL. Why? No real reason. Maybe it was some nostalgia from the CTF we did in the last week of my Systems Administration class at UMW where we had to portscan a server and grab (using nc/ncat) some information that was being served (using nc/ncat) on a random port.

As I worked more and more on this engine I realized that I could probably use ncat to run a game as a server. I mean, any interaction with a game written using this engine is going to be via the command line, and ncat facilitates network stuff via a command line. It’s perfect.

This is the script I am using to run the game as a server:

while true
	/usr/bin/ncat -l -k -p 64132 -v -c '/usr/bin/python3 /opt/vuurengine/ /opt/vuurengine/examplegame_plaintext.vegame' && wait

The ncat command is wrapped in a while loop to deal with some weirdness that occurs after a connection ends; sometimes the instance of ncat ended with it, but this ensures that when/if it closes, it will be started again.

  • -l means that ncat is going to take incoming connections.
  • -k means that multiple connections are allowed. I thought this would negate the need for the while loop I mentioned, but maybe I’m just misunderstanding how this feature of ncat works. Either way, with both things it’s working as expected.
  • -p specifies the port that the server will run on. I chose a high numbered port that was not used by any other standard service running on a Linux server.
  • -v just runs everything in verbose mode, so if there is some more weirdness I’ll have an easier time finding out why.
  • -c specifies what command to run and serve via ncat. This is where I pass along the path to the game; I’m using the program to load a custom game script, but it works if you specify a pure python game as well.

The engine I wrote has a debug command that basically gives you a python interpreter. It’s useful to test changes to the engine when I need to get or set info of a particular object, but it’s also dangerous when running as a server like this because it basically gives you a python interpreter. An attacker, with that access, could run import os and then have full access to the underlying server. To prevent this, the debug option is disabled by default, and you have to explicitly enable it. But I’m a SysAdmin not a Software Engineer, so there could easily be some vulnerability in my code that I’m not even aware of which an attacker could use to get this level of access.

This is where Docker comes in.

Last year at Reclaim we did a group Docker training thing, and I learned quite a bit. When it came time to test my knowledge and write out my own Dockerfiles, the “game server” was a natural choice. Docker was the missing piece for it. Keeping things containerized meant that even if there was an attack and that level of access was achieved, it remains isolated to that specific container. Separate from the real server underneath. And it’s a lot easier to destroy a container than to nuke a full server.

Believe me.

This is the Dockerfile I’m using to run the game server:

# 	docker build -t vuurengine_example_game .
#	docker run -d -p 64132:64132 vuurengine_example_game
# 	nc $THIS_SERVER 64132

# Use AlpineLinux base image
FROM alpine

# Create working dir
WORKDIR /opt/vuurengine

# Add dependencies
RUN apk add bash
RUN apk add screen
RUN apk add python3
RUN apk add nmap-ncat
RUN apk add git

# Clone git Repo of Game
RUN git clone /opt/vuurengine
RUN git -C /opt/vuurengine pull

# Expose port
EXPOSE 64132

# Run the ncat server(s)
COPY ./ /opt/vuurengine/
CMD /bin/bash /opt/vuurengine/ being the name of the script I mentioned above that actually runs the game as a server with ncat. I just had to include it in the same dir as the Dockerfile.

As of writing this post, this container is running on a Docker Engine server on Reclaim Cloud. Since this whole thing is just for the included example game, I will probably take this server down some time in the future when/if we need to free up IPs or when/if I set up a more permanent space for an actual game I write. Or maybe it will stay up if I use the environment as a general test space for docker containers. Who knows.

But for now, you can access the game server with the following command (which should work on any *nix):

nc 64132

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.