One. More. Block.

Note: be sure to read to the end before you start implementing any of the scripts. They're currently out of date and no longer work.

Our little family has undergone something of a revolution of late: it’s called Minecraft. Matthew started playing it, and watching YouTube videos about it, a few months ago. I’ve never been a gamer, despite rumors to the contrary, but Minecraft captured my attention. It’s a sandbox game, where you gather materials and use them to craft new materials and make tools and build structures and create an entire world. It’s addictive and amazing, and the story behind it is a classic indie-game David vs. entertainment industry Goliath tale.

Minecraft has a multi-player mode where people can share a world hosted on a server. Matthew watches a lot of Minecraft tutorials by paulsoaresjr in which he and his family play together. It looked like a lot of fun. The server software is free and I have quite a lot of excess capacity, so we decided to set up a server. We’ve created a lot in our little shared universe. Well, mostly Matthew and Joshua have, as I have little time for gaming. Still, it’s an amazing experience and — unlike a lot of gaming — is one in which we can share and interact instead of disappearing into our own, individual worlds.

Of course, Matthew soon wanted to invite his friends to join him. We have to limit access to the server to keep the interactions appropriate to youngsters and to minimize griefing. That meant putting up a web site for friends and their parents to visit to get access. Then I found that I needed a way to easily post information about changes to the server configuration and status. I decided to use a Twitter account, and put a Twitter feed on the web site so people visiting the site could get up-to-the-moment information even if they didn’t use Twitter.

I watch the logs whenever I can, mostly to make sure that all the activity stays appropriate. I’ve often noticed a friend of Matthew’s logging on, and when I mention it to him he’ll quickly log in so that they can give one another tours of their latest creations. It occurred to me that the Twitter feed, since it appears on the web site, would be an interesting place to announce when people join or leave the game. The result was a pair of bash scripts which monitor the logs and post tweets whenever anyone joins or leaves our world. The user names are partially obscured to enhance privacy and help prevent name spoofing.

The scripts should work with any server installation on a Linux or Mac OS X machine. I don’t know if Windows supports any kind of bash shell equivalent, and I suspect that, at least, they would require some modification.

The first script analyzes the log and sends (to stdout) a list of all the people who have joined or left the game since the last time the script was run. User names are obscured by showing only the first three letters, an elipsis (…), and the last letter. That should be more than enough for friends to recognize one another without giving away full user names.

The second script is a minor modification to a script by Luka Pusic that posts messages to a user’s Twitter account. (The original script only accepted input from the command line; I made a slight modification to allow it to accept ‘-‘ as a command line parameter to accept input from stdin.)


#!/bin/bash

# Analyzes a Minecraft log file and returns a list of all logins and logout since
# the script was last run.
# Ron Risley  21December2011

MCPATH=/home/minecraft # path to the Minecraft server directory
MCNAMEPAT="([a-zA-Z0-9_]{3})([a-zA-Z0-9_]+)([a-zA-Z0-9_])"
LOGINPAT="^< [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] $MCNAMEPAT \[\/[0-9.:]{9,21}\] logged in.*$"
LOGOUTPAT="^< [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] $MCNAMEPAT lost connection.*$"
TIME=`date +%H:%M`

diff $MCPATH/server.log $MCPATH/server.log.lastlog  | egrep "($LOGINPAT)|($LOGOUTPAT)"  | sed --regexp-extended "s/$LOGINPAT/\1...\3 joined@$TIME/"  | sed --regexp-extended "s/$LOGOUTPAT/\1...\3 left@$TIME/"  
cp $MCPATH/server.log $MCPATH/server.log.lastlog

The Luka Pusic script:

#!/bin/bash
#Twitter status update bot by http://360percents.com
#Author: Luka Pusic 
#modified to use standard input ron@risley.net

#REQUIRED PARAMS
username="twoprops@twoprops.net"
password="------------------------------------"
tweet="$*" #must be less than 140 chars

#EXTRA OPTIONS
uagent="Mozilla/5.0" #user agent (fake a browser)
sleeptime=0 #add pause between requests

if [ "$tweet" == "-" ]; then
   tweet=`cat /dev/stdin`
fi

if [ $(echo "${tweet}" | wc -c) -gt 140 ]; then
   echo "[FAIL] Tweet must not be longer than 140 chars!" && exit 1
fi

if [ "$tweet" == "" ]; then
   echo "[FAIL] Nothing to tweet. Enter your text as argument." && exit 1
fi

TMPFILE="/var/tmp/cookie.$$"
touch $TMPFILE #create a temp. cookie file

#INITIAL PAGE
echo "[+] Fetching twitter.com..." && sleep $sleeptime
initpage=`curl -s -b "$TMPFILE" -c "$TMPFILE" -L --sslv3 -A "$uagent" "https://mobile.twitter.com/session/new"`
token=`echo "$initpage" | grep "authenticity_token" | sed -e 's/.*value="//' | sed -e 's/" \/>.*//'`

#LOGIN
echo "[+] Submitting the login form..." && sleep $sleeptime
loginpage=`curl -s -b "$TMPFILE" -c "$TMPFILE" -L --sslv3 -A "$uagent" -d "authenticity_token=$token&username=$username&password=$password" "https://mobile.twitter.com/session"`

#HOME PAGE
echo "[+] Getting your twitter home page..." && sleep $sleeptime
homepage=`curl -s -b "$TMPFILE" -c "$TMPFILE" -L -A "$uagent" "http://mobile.twitter.com/"`

#TWEET
echo "[+] Posting a new tweet..." && sleep $sleeptime
tweettoken=`echo "$homepage" | grep "authenticity_token" | sed -e 's/.*value="//' | sed -e 's/" \/>.*//' | tail -n 1`
update=`curl -s -b "$TMPFILE" -c "$TMPFILE" -L -A "$uagent" -d "authenticity_token=$tweettoken&tweet[text]=$tweet&tweet[display_coordinates]=false" "http://mobile.twitter.com/"`

#LOGOUT
echo "[+] Logging out..."
logout=`curl -s -b "$TMPFILE" -c "$TMPFILE" -L -A "$uagent" "http://mobile.twitter.com/session/destroy"`

rm $TMPFILE

To use the scripts, use cron (or your favorite task scheduler) to execute “/path/to/loglog | /path/to/twitpost – >/dev/null” at frequent intervals. Unless your log file is huge, this script consumes very little resource. I run it every minute.

Notes:

If a server is really busy, or the run interval is too long, the script will fail because the resulting log data won’t fit in the 140-character Twitter limit.

The timestamp on the tweets appears to be mandatory. Twitter doesn’t seem to want to send the exact same tweet more than once. It’s hard to know what kind of time frame there is on this behavior, but presumably sending an identical tweet 24 hours later wouldn’t cause it to be suppressed. Otherwise things like “out to lunch” tweets would get lost.

Ron Risley – 2011-12-21

*Update*

I found the joined/left tweets too confusing. I couldn’t glance at a tweet and know who was on and who wasn’t. Sure, it only took a few seconds of analysis, but I’m lazy. I decided that what I really need is to see who is playing at any given time. My bash skills weren’t up to the task of creating that script, though, so I resorted to perl. Here’s the result:

#!/usr/bin/perl

# analyzes a minecraft log from standard input and returns a list of
# currently logged in players
# www.looseassociations.com Ron Risley

use strict;

my $namelist;

my $mcnamepat="([a-zA-Z0-9_]{3})([a-zA-Z0-9_]+)([a-zA-Z0-9_])";
my $loginpat= "^[0-9]{4}-[0-9]{2}-[0-9]{2} ([0-9]{2}:[0-9]{2}):[0-9]{2} \\[INFO\\] $mcnamepat \[\/[0-9.:]{9,21}\] logged in.*\$";
my $logoutpat="^[0-9]{4}-[0-9]{2}-[0-9]{2} ([0-9]{2}:[0-9]{2}):[0-9]{2} \\[INFO\\] $mcnamepat lost connection.*\$";


foreach () {
   if ( /$loginpat/ ) {
      $namelist .= "$2..$4\n";
   }
   if ( /$logoutpat/ ) {
      $namelist =~ s/$2..$4\n//g;
   }
}

print $namelist;

The cron job command is:

echo Now playing: $(/home/ron/players.pl </home/minecraft/server.log) | /home/ron/twitpost -  >/dev/null  

Have fun!

Ron Risley – 2012-02-22

25September2012

The Twitter posting script hasn’t worked for some months now. I don’t know whether Twitter has been deliberately trying to prevent scripted posting (there are very good reasons why they might want to) or if they just changed the design of their web site enough to break this code. I haven’t had the time and inclination to fix it.

Ron Risley – 2012-09-25