BYOND Docker Development Environment

The itch to get back into Space Station 13 development has struck again and, naturally, I am spending much more time writing development tooling than actually making shiny projects. Enter the dockerised BYOND development environment.

I’ve been playing with Docker lately as part of my day job, and as such I was eager to flex my new found skills to the issue of having a consistent UNIX-like development environment for Space Station 13 development. I refuse to run SS13 servers on a Windows environment, partly out of principle, partly out of being a cheapskate. This does however mean that there is often a great disparity between the local development environment and the “production” environment - a container fixes that.

There is, however, a minor problem - BYOND only provides 32-bit binaries for Windows and *nix. Thankfully, there are i386 images of Debian and others available, and grabbing the latest version of BYOND off of the website and installing it is trivial. One stumbling block remains - Windows Subsystem for Linux does not officially support 32-bit binaries, and attempting to build my project on it results in a strange error:

# /byond/bin/DreamMaker ExampleProject.dme
DM compiler version 513.1528
ExampleProject.dme does not exist.
ExampleProject.dmb - 0 errors, 0 warnings (7/28/20 8:16 pm)
Total time: 0:00

But, clearly, it does exist! Something else was at work here, and I started down a rabbit hole of trying to mount a python FUSE filesystem to sanitise the oddities of the filesystem, and scrubbing through strace logs to try and figure out how the process was interacting with the filesystem.

stat64("ExampleProject.dmb", {st_dev=makedev(0, 0x68), st_ino=281474976933708, st_mode=S_IFREG|0777, st_nlink=1, st_uid=1000, st_gid=1000, st_blksize=512, st_blocks=72, st_size=35227, st_atime=1595453181 /* 2020-07-22T21:26:21.406437300+0000 */, st_atime_nsec=406437300, st_mtime=1573148122 /* 2019-11-07T17:35:22.261953800+0000 */, st_mtime_nsec=261953800, st_ctime=1595447565 /* 2020-07-22T19:52:45.140988700+0000 */, st_ctime_nsec=140988700}) = 0
stat64("ExampleProject.dmb", {st_dev=makedev(0, 0x68), st_ino=281474976933708, st_mode=S_IFREG|0777, st_nlink=1, st_uid=1000, st_gid=1000, st_blksize=512, st_blocks=72, st_size=35227, st_atime=1595453181 /* 2020-07-22T21:26:21.406437300+0000 */, st_atime_nsec=406437300, st_mtime=1573148122 /* 2019-11-07T17:35:22.261953800+0000 */, st_mtime_nsec=261953800, st_ctime=1595447565 /* 2020-07-22T19:52:45.140988700+0000 */, st_ctime_nsec=140988700}) = 0

However, I really didn’t know what I was doing and in the end I had to seek help. Enter @_Ninji, who is far too good at reverse engineering things for their own good. After showing them some strace logs of the processes in question, they quickly wrote a utility to interrogate the filesystem on a low level, and the results spoke for themselves:

# ./test.so ExampleProject.dmb
claimed error: Value too large for defined data type
stat() returned -1
  st_dev=68
  st_ino=223052
  st_mode=-1517468
  st_nlink=-134668288
  st_uid=0
  st_gid=0
  st_rdev=17860885494741147648
  st_size=-134668288
  st_blksize=-134668288
  st_blocks=0
  st_atime=-136405509
  st_mtime=1
  st_ctime=1448756243

Remember how BYOND is 32 bit? st_rdev was returning a value greater than 32 bits in size, and this was the source of the problem. The erroneous error of ExampleProject.dme not found was a red herring! Even better, strace was returning erroneously valid values for the system calls! Thankfully, there was a simple solution to all this, which was to pre-load a library with a patched stat system call, which has already been done here. I hooked in the compiled library and hey presto, it worked!

# /byond/bin/DreamMaker ExampleProject.dme
DM compiler version 513.1528
loading ExampleProject.dme
loading ExampleProject/map.dmm
saving ExampleProject.dmb (DEBUG mode)
ExampleProject.dmb - 0 errors, 0 warnings (7/28/20 8:28 pm)
Total time: 0:02

Thanks again to @_Ninji for all of their help, and if you’re interested, the repository for this docker image is available on my GitHub page.