Show Source


The Carnegie Mellon University KGB plays a delightful game called Capture The Flag With Stuff. The game relies on wall-clock time and previously we just used a bunch of stop-watches manually set by hand. That stunk.

What have you done?

We have brought technology to bear. In particular, we use MQTT to dispatch messages about the current game state to subscribers and rely on NTP to keep devices in temporal synchrony. We have hardware devices, based around the ESP8266 and nodemcu, at the teams’ jails within the game, and additionally offer open, anonymous subscription to the data feed.

The devices’ firmware (in Lua) is available here. The device itself, at least for v1, looks like this:


Because machine-readable messages over MQTT is perhaps not the friendliest thing in the world, we have written several wrappers around the core, provided in addition to the devices themselves.

For player use, there is

  • an Android application (on the market) that I wrote (with some help from Cameron Wong), which can display various stats about the game.
  • a webpage (deployed here) which recreates much of the above application’s interface and has its own stylistic tweaks. Full credit to Michael Murphy for this.

Beside the webpage source, there are also some utility scripts for speaking the protocol, suitable for use by the head judge.

How Can I Help?

Would you like to write another frontend for us? Please feel free to observe the existing clients’ behaviors, but the below should be a more or less complete description of the wire protocol.

The Protocol

All numbers herein are base-10 encoded and devoid of leading zeros for ease of parsing.

MQTT messages should be set persistent so that devices that reboot or lose their connection will display the right thing upon reconnection. Most messages are designed to be idempotent, in the sense that they carry timestamps of their veracity, so out-of-order delivery is partially mitigated.

Topic Tree

Public, Centrally-set topics

The public aspects of the game are set under ctfws/game (e.g. ctfws/game/config). We grant read-only views of these topics to guest logins as well as our jail timers’ users.

  • config the string none or a whitespace-separated text field:

    • starttime – POSIX seconds indicating start state
    • setupduration – setup duration, in seconds
    • rounds – number of rounds (intervals between jail breaks)
    • roundduration – seconds per round
    • nflags – number of flags per team
    • gamecounter – (integer) which game in a bunch is this? Since often several are played in a night, it is likely useful to indicate to clients which game this is. The value 0 may be interpreted as suppressing indication in the client; we 1-index games to be friendly to people. ;)
    • territory – (integer) specifies territory configuration; index into ctfws/game/territories array; see below.
    • any additional fields are to be ignored.
  • flags – the string ? or a whitespace-separated text field:

    • red – red team flag capture count (int)
    • yel – yellow team flag capture count (int)
    • any additional fields are to be ignored.
  • endtime – a single number, denoting POSIX seconds of a forced game end. If this is larger than the last starttime gotten in a config message, then the game is considered over.

  • message – Message to be displayed everywhere. This and all other message/# topics have a POSIX-seconds timestamp followed by whitespace before the message body. These permit messages from previous games to be suppressed, should they end up resident on the MQTT broker.

  • message/player – Message to be displayed specifically to players, if they ever come to have their own devices (e.g. apps)

  • message/jail – Message to be displayed specifically at jail glyph units. For the moment, that’s all of them, but maybe we want to allow other things in the future.

  • messagereset – A single number, denoting POSIX seconds before which messages should not be displayed. This is useful in the event that the judges send out an incorrect message.

  • territories – a JSON array of descriptive strings for territory layouts. At CMU, this takes on the value

    [ "Red defending Doherty; Yellow defending Wean",
      "Yellow defending Doherty; Red defending Wean" ]


    While hardly ideal to store this kind of information in the message broker, it seems like a better place than needing some other configuration system or hard-coding it into the (not-necessarily-CMU-specific) clients. There may be periods in a client’s lifetime when it does not know the value of ctfws/game/territories, even if it knows ctfws/game/config. During that (hopefully brief) interval, it should suppress its display of the territory layout information.

    Dedicated devices at game sites (e.g., CMU) may, of course, bake the knowldege of territories into their firmwares, if they wish to use it, since they have more constrained displays and large strings may be problematic. There may, for example, be better iconography than text.

Private, Centrally-set topics

Some information is used internally by the judges; it is not intended for player view. These are set under the prefix ctfws/judge.

  • flags a whitespace-separated text string with two integer fields:
    • red – An integer, encoding the set of red flags that have been captured. The 1 bit corresponds to flag A, 2 to flag B, etc. That is, if yellow has captured red flags C and H, this field has the value 132.
    • yellow – As above, but for the set of yellow flags that have been captured.
Device-set topics

Devices get to send messages to some topics, too, to provide centralized view of the world.

  • ctfws/dev/$DEVICENAME/beat

    • one of alive, beat, or dead (LWT; no further fields)
    • time (UNIX time, from local clock)
    • ap (MAC addr)
    • any additional fields are to be ignored.

    The device should publish alive at gain of MQTT connectivity and having registered a last will and testament to set the message dead. Thereafter, it should publish beat messages every minute.

ACL Configuration

For example:

# global read permissions
pattern read ctfws/game/#
pattern read ctfws/dev/+/beat

# allow devices to publish their heartbeats
pattern readwrite ctfws/dev/%u/#

# master write to all ctfws parameters
user ctfwsmaster
pattern readwrite ctfws/game/#
pattern readwrite ctfws/judge/#

Example Command Line Usage

For the sake of simplicity in the below examples, set:

M=(-h $MQTT_SERVER -u ctfwsmaster -P $CTFWSMASTER_PASSWD -q 1)

To watch what’s going on in the world:

mosquitto_sub "$M[@]" -t ctfws/\# -v

To send MQTT messages, try variants of these. Note that in all cases, we set messages persistent so that devices that (re)connect mid-way into a game get the latest messages automatically.

  • To start the 2nd game now, with 15 minutes of setup, 4 x 15 minute rounds, 10 flags, with red team defending Wean hall and the yellow team defending Doherty hall:

    mosquitto_pub “$M[@]” -t ctfws/game/flags -r -m ‘0 0’ mosquitto_pub “$M[@]” -t ctfws/game/config -r -m date +%s’ 900 4 900 10 2’

  • To post information (The messages must have date stamps on the front!):

    mosquitto_pub "$M[@]" -t ctfws/game/flags -r -m '1 2'
    mosquitto_pub "$M[@]" -t ctfws/game/message -r -m `date +%s`' Red team captured a flag!'
  • Note that you can deliberately hide the flag scores, if you like, by publishing ? to the /flags topic:

    mosquitto_pub "$M[@]" -t ctfws/game/flags -r -m '?'
  • To end a game:

    mosquitto_pub "$M[@]" -t ctfws/game/endtime -r -m `date +%s`


Due to a bug in nodemcu (See, do not send messages with QoS 2; stick to QoS 1 and it appears to work. Ideally these should be QoS 2, but that will have to wait.