Those of you who were in the ECR at all during the NFQ/Incident ### game may have noticed a black laptop on the west facing wall (that nearest to the directors office) which had some nice green text on a black backdrop that updated itself pretty infuriating slowly :) That was one of mine, and something I really had some fun putting together, although it was all a bit bodgey internally. So this is going to be a walk through a download of that anyone thinking of putting together something similar can see some of the weird tricks I used, I’ll also be talking about lessons learnt from the event and things I’d change next time.
Okay so first off you’ll want to go snaffle a copy of the .zip file of everything for the NFQ ECR – nfqecr_2015-02-15_final.zip (116k) Got that? Good, now unpack that somewhere, its basically an entire home directory from the Ubuntu laptop in question. So first off lets start by talking about how the user account was set up. (p.s. if you want to play with this you’ll need at least the following: openbox, unclutter, libpoe-perl libio-pty-perl, xscreensaver-data-extra, xserver-xorg-input-synaptics). The requirement was that this work on the day, not that it necessarily be the best it could be, it only had to be robust enough for users to play with so speed of implementation was in some ways valued over doing the right thing.
Imagine the setup like an onion, a series of things form layers, one starting another, or perhaps a chain reaction, tripping each other. So I’ll try and go in order, looking at how all the things interact and sort of push each other on. I’m going to start with all the stuff that makes the account go, and gets things ready to start, then move onto the menu system itself.
- .xsessionrc: This is where things start. When you log into the graphical user interface Linux machines look for a file called something like this, which basically says what the window manager is going to be used, note that this starts openbox-session, not just openbox, which is needed to make the autostarts load
- .config/openbox/rc.xml: Notable for removing all the keyboard mappings it could try try and stop things like switching to other destops, or bringing up the alt-tab program switching menu. If you look down the bottom for the <application> section you can see its configured to look for any window called “phosphor” and make it have no window decorations (max/min buttons), full screen, maximised and always on top. So this is the window of the terminal we’re using to do the fancy green/black display.
- .config/openbox/autostarts: This file says what openbox should automatically start, notably it tries to kill off the machines touchpad (touchpad-kill.sh) hides the mouse pointer (unclutter) and then kicks off a script that starts the ECR interface.
- touchpad-kill.sh: Small script that uses xinput to disable both the mouse pad and the small thumb mouse
- start-nfq-ecr.sh: This uses the Phosphor terminal emulator from the xscreensaver-data-extra package to run the interface, it runs on a loop so if phosphor should die for some reason (including the menu system dying) then it will pause a second then restart it.
- nfq-ecr-runner.sh: This little script changes into the right directory (so this one actually needs to know the right directory) then executes the main shell interface inside a wrapper to create distortion in its output.
So that’s sort of all the background stuff that gets things going, now I’m going to talk about the stuff inside the nfq-ecr/ subdirectory that contains more of the direct menu system itself, which is probably more of the interesting stuff. Mostly the players are interacting with .shell which launches each little command, however that runs inside of the .output-wrapper.pl which distorts its output randomly to give the appearance of corruption. This means that it could be run without this to avoid that effect.
- nfq-ecr/.output-wrapper.pl: This is a modified version of an example from POE Perl. This output wrapper literally sits between whatever program its running and its terminal and rewrites its output, while passing its input in … mostly okay. It has issues doing things like backspacing over a character to delete it, but for this purposes (of a broken old computer interface) it’s pretty good. If you want to see how it works go look at the handle_child_stdout() function, which randomly swaps cases of letters and also swaps out random characters for junk. Now really this could be improved to be handling more of the menu itself, but for this quick implementation it was good enough.
- nfq-ecr/.baton: This is a small program that displays a simple spinning loop of characters to make it appear that “processing” is happening. Technically its a compiled C binary, so there’s a chance it won’t run on your Linux machine. I’ve included the source at the top level in baton.c and a small portable Perl version called “baton-spinner2.pl”
- nfq-ecr/.shell: This is the core of the program, although it in itself is actually quite basic. If you scroll down and look for the bit commented “# Main body” you’ll find the loop it normally sits in. Essentially it prints the menu, reads the users choice, spins the “I’m thinking” animation then tries to match the choice to a command, then goes round again.
- each command executes one of the smaller programs in nfq-ecr/.bin/ so each can be written, tested and debugged separately making it easy to extend the menu as I could knock up quick little commands in bash, or write more involved ones in Perl, but anything could be used.
- Fun functions include additional_output() and command_failure(). The first sometimes slowly prints out, then deletes a quote designed to unsettle the players, the second just randomly makes their commands sometimes fail to infuriate them (which was especially good to watch when Dany was trying to make it give him information against the clock)
The .shell command basically deals with four directories:
- .bin: This contains all the small programs that make the shell simple. So one for each command that runs, one for printing system error messages, one for slowly printing then deleting spooky messages from the computer to the players, and one for printing the “press a key to return to menu” thing.
- .data: This directory contains data for the programs that live in .bin that aren’t user facing, its got a dot on the front to make it hidden as if players somehow accidentally end up at a shell then they wouldn’t see it. Mostly it has the spooky lines to output, and the components for building random excuses.
- documentation: This directory contains all the files that players can read of general in character documentation, everything read by the “2_read_documentation” command
- foundlings: This directory contains all the corrupted foundling data files, which are displayed by the “3_read_foundling_data_files” command.
The .bin directory contains one small program per command that the players can run from the shell’s menu. They’re a mix of small bits of bash and perl as each one is a quick job. The particularly interesting bits which people may want to steal for other things are:
- 1_containment_check: This produces a short random list of sensors it can’t talk too right now, just to give the impression that things are changing randomly and badly throughout the scenario. In order to keep the players aware of the threat it always tags F5225 (The Suckerman) on the end of the list to make sure they really know its really bad.
- 2_read_documentation: Useful because it reads input from players, then tries to approximate it against a list of files in .nfq-ecr/documentation looking for partial word matches to help the players out so thats an interesting feature.
- 3_read_foundling_data_files: This reads the files in .nfq-ecr/foundlings and attempts to deal with typos like not padding the number with leading 0s or not putting an F on the start, but pretty dull.
- 4_abort_current_emergency_status ; 5_fire_suppression_system and 7_power_system_status: All pretty much clones of each other, they just print out a scary error message and exit, very simple.
- 6_heating_system_status: This prints out values for temperature at points around the base and tries to randomly generate the values to give a hint that things aren’t just always the same.
- 8_reboot_failsafe_ecr_system: This runs through a yes/no process then generates a bunch of error messages and false waits before exiting. This one is interesting because originally it was self contained (you can see how it originally worked in the commented out stuff) but for robustness of not stacking up processes it cooperates with the .shell to create its effect. This is interesting as a time when doing something as a special case was better than trying to make the generalized case more flexible. To understand it look in .shell for the reboot command, essentially instead of calling press-for-menu like it does for the others it calls the START_SEQUENCE() again.
- press-for-menu: This prints out a dividing line so players have a consistant way of seeing that a command has finished and waits for them to press enter.
- print-additional-output.pl: This prints out quotes from a file slowly one character at a time, then pauses, then deletes them one character at a time. It’s quite similar to how the baton spinner works (the adding/deleting characters) but with randomised quote picking.
- system_error: This is an example of code reuse. I originally wrote this as a generator for BOFH style excuses as per the BOFH Excuse Board. Here it’s being used for about the same purpose, to output technobabble style “fuck you the computer says no” style messages.
So yeah, congrats on making it through that epic slog if you did. Good luck, may the farce be with you. I’ll tidy things up and license things under the GNU GPL at some point soon probably.
If I was doing things over again I’d make it clear that this was a slow old system, slooooow and oooooolllldd despite the fact that its green screen and laggy :) The other thing I’d probably do is make it ignore all the input from a user after taking their choice in the .shell, I saw a number of people type a command, hit enter and when the baton-spinner started they’d keep typing things and hope to get some reaction from it, which just meant their input would go into the command they’d started – and chances are it wouldn’t understand the shell choice, so would error, then they’d be back at the menu – and if they’d kept typing input would stack up and they’d have to wait and waaaaait and waaaaait through it looping a number of failures. Simply finding a way of telling the .shell to throw away all the input would have solved this issue :)