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
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 (
vuurengine.py) 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 (
vegame.py) 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
ncat) some information that was being served (using
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 do /usr/bin/ncat -l -k -p 64132 -v -c '/usr/bin/python3 /opt/vuurengine/vegame.py /opt/vuurengine/examplegame_plaintext.vegame' && wait done
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.
ncatis going to take incoming connections.
-kmeans that multiple connections are allowed. I thought this would negate the need for the
whileloop I mentioned, but maybe I’m just misunderstanding how this feature of
ncatworks. Either way, with both things it’s working as expected.
-pspecifies 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.
-vjust runs everything in verbose mode, so if there is some more weirdness I’ll have an easier time finding out why.
-cspecifies what command to run and serve via
ncat. This is where I pass along the path to the game; I’m using the
vegame.pyprogram 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.
This is the Dockerfile I’m using to run the game server:
# TO BUILD: # docker build -t vuurengine_example_game . # # TO RUN: # docker run -d -p 64132:64132 vuurengine_example_game # # TO CONNECT: # 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 https://gitlab.com/cblanke2/vuurengine.git /opt/vuurengine RUN git -C /opt/vuurengine pull # Expose port EXPOSE 64132 # Run the ncat server(s) COPY ./server.sh /opt/vuurengine/server.sh CMD /bin/bash /opt/vuurengine/server.sh
server.sh 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 env-6407990.us.reclaim.cloud 64132
2 thoughts on “Adventures!? With Text!?”
This is super cool. Being served by netcat, and playable via just a terminal and nc is *chef’s kiss* so simple and great.
Thanks! Yeah I wanted to look for a way that it could be played _remotely_ and ncat/netcat seemed like a better/easier option than trying to write an actual “server”.