New Server

Got a new server! It's way cooler than the last one.

I even have Terraria running as a systemd unit, cleanly saving the world state when shutting down, automatically starting up on system boot, logging to the system log, and with a fifo for sending it commands.

Terraria dedicated server

Originally, I had the Terraria server start with a FIFO being piped in as input, but quickly found out that you could pretty much only ever enter a single server command. Once the process that you use to enter the command is done, the FIFO spits out an end-of-file signal, causing the server (or process that was piping to the server) to stop accepting any new input.

There were various unreliable ways of making 'tail -f' or 'cat' do kinda sorta what I wanted, but I decided to just knock out some very simple C++ code to do the job for me.

Behold, the EOF filter...

pipewrapper.cpp

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, char *argv[])
{
    fstream pipefile;
    bool quit = false;

    if(argc < 2) {
        cerr << "Usage: " << argv[0] << " <input fifo>" << endl;
        return 1;
    }

    while(!quit) {
        pipefile.open(argv[1]);

        if(!pipefile.good()) {
            cerr << "Could not (re)open input file." << endl;
            break;
        }

        while(pipefile.good()) {
            std::string line;
            getline(pipefile, line);

            if(line == "reallyexit") {
                quit = true;
                break;
            } else {
                cout << line << endl;
            }
        }
        pipefile.close();
    }

    return 0;
}

Yup. That's it. It just keeps its own output going, and re-opens the input file (supplied as the first command line argument) every time it finds it closed. Once it receives a special command, "reallyexit", it shuts itself down (and doesn't pass this message along to the output).

Here are the other scripts involved in keeping this thing running...

server_run.bsh

(This one is expected to run until the server is shutdown.)

#!/bin/bash

echo "----------------------------------------" >> log.txt
echo " Server starting at $(date)" >> log.txt
echo "----------------------------------------" >> log.txt

cd terraria_install
../pipewrapper_bin ../server_stdin | \
    ./TerrariaServer \
        -world ../worlds/PUT_YOUR_WORLDFILE_HERE.wld \
        -pass PUT_YOUR_PASSWORD_HERE | tee ../log.txt

Note: It is definitely better to use the Terraria server config files instead of sticking your password in the command line. It doesn't matter here because everyone who has access to the server has access to the Terraria server too.

Note: 'server_stdin' is the FIFO. 'terraria_install' is the directory under this one that a copy of the Terraria install right out of a Steam directory lives. 'worlds' is a directory containing all the .wld files. 'pipewrapper_bin' is just the compiled binary for 'pipewrapper.cpp'.

server_shutdown.bsh

#!/bin/bash

SERVER_STILL_RUNNING_COMMAND="pgrep -u $USER TerrariaServer"

if $SERVER_STILL_RUNNING_COMMAND > /dev/null; then
    echo "Server is up.";
else
    echo "Server is not running. Are you running it as a different user?";
    exit 0;
fi

echo "Sending exit command..."
echo "exit" > server_stdin

echo "Waiting for TerrariaServer process to quit..."
while $SERVER_STILL_RUNNING_COMMAND > /dev/null; do
    sleep 1;
done

echo "Shutting down pipe..."
echo "reallyexit" > server_stdin

echo "Server shutdown complete!"

This one issues the 'exit' command to the server. It then waits for it to shut down (blocking the server shutdown process for a bit while the Terraria server saves the world). Finally, it issues that 'reallyexit' command to kill the pipe.

Note: This has a serious flaw in it. You can't run multiple Terraria servers on the same system, or this command will never finish until ALL the servers have been shut down. It's just a quick-and-dirty implementation. A correct version would involve some PID wrangling that I didn't want to mess with at the time!

terraria.service

[Unit]
Description=Terraria server
After=network.target

[Service]
User=terraria
Type=simple
ExecStart=/home/terraria/Terraria/server_run.bsh
ExecStop=/home/terraria/Terraria/server_shutdown.bsh
WorkingDirectory=/home/terraria/Terraria
TimeoutStopSec=20
KillMode=mixed

[Install]
WantedBy=multi-user.target

This one lives in '/lib/systemd/system'. This is the systemd unit file. It just runs the Terraria server setup out of the 'terraria' user's home directory, in simple mode.

With this setup, some useful commands are...

Depending on if there's any interest in this or not, I might be able to cobble together a more convenient package, with proper PID wrangling and all that stuff. Hope this is useful to anyone else trying it.

I'm also interested in any feedback that might help make this a little more solid. It works great for this specific server in a one-instance thing I have going on here, but it could be a little nicer.

Posted: 2015-08-13

Tags: life, terraria