Goal: set up a minecraft server using free EC2 account from OSX
[update: FAIL — game play doesn’t work on a micro instance, if we want to use EC2 it looks like we need to use at least a small one. so be careful to adjust the steps below to pick a small instance if you want to play.]

I learned that there are a lot of minecraft server implementations. I decided to use Craftbukkit server largely because I saw it referenced from a RubyConf talk by Tom Enebo — might be fun to mod it someday :)

I pieced together information from some helpful tutorials from Ubuntu and Robert Sosinski.

  1. Sign-up for an AWS account (1 year free for new AWS accounts, but you need a credit card)
  2. Go to AWS Management Console
  3. Create a Private Key
    • Select EC2 tab, click on “0 Key Pairs” on the right side
    • name it ec2.pem (or anything you want, but I’ll use ec2 in the rest of this tutorial)
    • save it to ~/.ec2/ec2.pem
  4. Create a Certificate
    • Under your name in the top right corner, select “Security Credentials”
    • Download the private key and certificate and save them in ~/.ec2
  5. Make your credential files private.  In your local terminal, type:
    • cd ~/.ec2
    • chmod go-rwx ~/.ec2/*.pem
  6. Download EC2 API Tools
    • Unzip the Amazon EC2 Command-Line Tools
    • Move both the bin and lib directory into your ~/.ec2 directory
  7. Your ~/.ec2 directory should have:
    • The cert-xxxxxxx.pem file
    • The pk-xxxxxxx.pem file
    • The bin directory
    • The lib directory
    • ec2.pem
  8. Set up EC2 Command-line Tools
    • Put the following into your ~/.bash_profile:
      # Setup Amazon EC2 Command-Line Tools
      export EC2_HOME=~/.ec2
      export PATH=$PATH:$EC2_HOME/bin
      export EC2_PRIVATE_KEY=`ls $EC2_HOME/pk-*.pem`
      export EC2_CERT=`ls $EC2_HOME/cert-*.pem`
      export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home/
    • On the command-line, type:
      source ~/.bash_profile
  9. Startup a server
    • [Update] We want to be careful to choose an AMI that works with the micro instance, which is what we get for free.  Ubuntu takes up too much disk space by default to fit into a micro instance, so you need to use one of the micro Ubuntu AMIs they created for this purpose.  I’m going to use Maverick 10-10 — it’s the most recent version that has a micro version.
    • [Update] I picked one in us-east, since that’s where Amazon started my account by default and it seems that it needs to be in the same region matching the one set for my key:
      ec2-run-instances ami-cf33fea6 –instance-type t1.micro –region us-east-1 –key ec2
    • check to see if it is running, by typing:
      ec2-describe-instances
    • make a note of your hostname!  It should look something like:
    ec2-###-##-##-##.compute-1.amazonaws.com
  10. Open relevant ports (22 for ssh, 80 for http, 25565 for minecraft):
    ec2-authorize default -p 22
    ec2-authorize default -p 80
    ec2-authorize default -p 25565
  11. ssh into your new instance
    ssh -i ec2.pem ubuntu@ec2-###-##-##-##.compute-1.amazonaws.com
  12. Install Java (nice Ubuntu instructions)
    Note: to accept the license, use tab to get to the OK “button” then hit return, then arrow to get to “Yes” and hit return again.
    To verify installation:
    $ java -version
    java version “1.6.0_26”
    Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
    Java HotSpot(TM) Client VM (build 20.1-b02, mixed mode, sharing)
  13. See this tutorial for setting up minecraft, but if you are playing with the Minecraft 1.0 client (at least of today) you’ll need to install a dev build which you can find on the ci server. I found that I needed to run the server, stop it and run it again to get it to start without errors.
    • get the latest dev build (I’m running 1502):
      wget http://ci.bukkit.org/job/dev-CraftBukkit/lastSuccessfulBuild/artifact/target/craftbukkit-1.0.0-SNAPSHOT.jar
      
    • create a file called “start.sh” with the following contents:
      #!/bin/sh
      java -Xmx613M -Xincgc -jar craftbukkit-1.0.0-SNAPSHOT.jar
      
    • run the server in screen:
      screen
      ./start.sh
      

[Update] By tweaking the memory allocation, we can get it to work (most of the time) with a single player. I found that I can raise the memory allocation for java and use virtual memory, but that it sometimes maxes out the CPU.

Here’s the setting where the game couldn’t be failed (couldn’t fight monsters or build things):

#!/bin/sh
java -Xmx613M -Xincgc -jar craftbukkit-1.0.0-SNAPSHOT.jar

$top
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                 
 1642 ubuntu    20   0  978m 300m  10m S 12.3 50.7 100:44.96 java                                                                     

Note:
VIRT – 978M of virtual memory
RES – 300m resident (physical) memory


With this it sometimes works:

#!/bin/sh
java -Xmx1024M -Xincgc -jar craftbukkit-1.0.0-SNAPSHOT.jar

 2829 ubuntu    20   0 1336m 271m  10m S 14.0 45.8   0:18.76 java                                                                     

With this works almost all the time (but we’ve only tested one player):

#!/bin/sh
java -Xmx2048M -Xincgc -jar craftbukkit-1.0.0-SNAPSHOT.jar

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                 
 2893 ubuntu    20   0 2499m 341m  10m S 99.8 57.6   0:27.68 java                                                                     

 2893 ubuntu    20   0 2499m 341m  10m S 18.2 57.7   1:27.30 java                     

when the CPU maxes out, I see this in the game console:
22:39:19 [WARNING] Can't keep up! Did the system time change, or is the server overloaded?

I’ve been creating and using cross-platform tools for application development since the late 80s, from VideoWorks, HyperCard, and Authorware to Director, SuperCard, ScriptX and mTropolis to Shockwave, Flash, and HTML/Javascript. We’ve moved past the experimental days when it was called multimedia and techniques for interactivity, graphics, animation and information design are routinely applied for mainstream consumer and business applications. The dominant paradigm is now HTML.

When people say HTML, they really mean HTML, CSS, Javascript and a collection of proprietary browser APIs which are similar enough across desktop and mobile browsers that we can build applications that look and feel the same even if the code under-the-hood needs to be a little different.

Flash successfully enabled one set of code for one application, independent of browser and OS. Macormedia documented the file format back in the 90s. The Flash Player itself remained proprietary, but the open file format enabled competitive authoring tools like OpenLaszlo in addition to the original Flash authoring tool and later Flex. Of course, OpenLaszlo moved to support HTML many years ago, as browsers grew faster and more capable making HTML a practical choice for apps with desktop-like interactivity.

It’s Not Just About Technical Merits

I believe that the technical reason that Flash lost was that Flash never completely made the leap to app development from its origins in games and entertainment (some of the following may be out of date, since I stopped using Flash for anything but video 2 years ago). However, the big reason that Flash lost was an emotional one that Steve Jobs did a good job of propagating. Macromedia and Adobe never successfully embraced and fostered an open source approach to the platform, and sadly I think the marketing of this is more important today than the reality. The reality is that any effective, so-called HTML5 implementation that rivals Flash uses proprietary browser APIs that differ across browsers. Today a lot of that is hidden by popular frameworks, but any Javascript developer that has produced something meaningful that is widely used knows what I’m talking about.

The HTML, CSS, and JavaScript you need to write for the iOS implementation of the webkit browser has a lot in common with what you would need to write for the Windows Phone 7 Internet Explorer which both share a lot with the documented standards and their desktop counterparts, but share a lot less with BlackBerry 4 browsers or IE6, let alone earlier browser versions. Standards are only as good as their widespread and shared implementations. In the real-world, we do what works, which means we write to proprietary APIs.

For a while it looked like Flash might just offer a proprietary API that worked effectively across desktop, web and mobile, but they didn’t effectively make the leap to mobile, nor did they keep up with some of the capabilities of the desktop browsers; however, neither have web browsers yet to fully catch up to Flash. The standards committee approach doesn’t seem to be effectively driving new capabilities and the platforms are splintering even more than they were in the late 90s.

So why did Flash lose (technically)?

  • Text was never a first class citizen (undo, spellcheck, etc.) — the truth is that text is hard and really important. HTML forced web browsers to do this very well. With the addition of JavaScript, with its dynamic manipulation of the DOM, web applications started to be able to have the kinds of interactions people were familiar with from the desktop, but Flash never matched with regard to text.
  • Settings… (This should have been called Flash Player Settings: with the addition of context menu items, the “Settings…” menu may appear to control application setting; however, if someone picks that menu item when an iframe is partially obscuring the app, it will not appear to the end-user and the app will seem to hang. I have to admit personally responsibility for screwing this up circa 2000 when I was at Macromedia leading the Flash video team. In my own defense, it is very hard to balance usability and security. I wish we had done more of a trusted app model with authorization of features (like Android and iOS do fairly well for mobile apps) and used OS dialogs to prevent spoofing.
  • Drag & drop not integrated with the desktop (Browsers are doing better on this, but there is still a ways to go)
  • Cut/Copy/Paste really needs to work well for text and all media types, which is still not fully there on browsers.
  • http auth: seriously silly that this was not easily supported
  • Sometime network operations fail — real apps need reliability

Why HTML still sucks, just like Flash

  1. “This script has caused the Flash Player to run slowly” — web browsers haven’t figured this out either, I like how Apple handles this with desktop apps better
  2. From a performance perspective, HTML/Javascript has the same challenges as Flash — has anyone else noticed the return of “skip intro” and progress bar loading screens, but they are so-called HTML5 sites instead of Flash?

How HTML sucks more than Flash

Desktop HTML video suffers from same of the same issues as mobile web video:

  1. The only use case that is well-vetted and works effectively cross-platform is playing a movie with VCR controls. There are a lot of other video interaction use cases that Flash did a very good job of supporting and HTML isn’t there yet on even modern browsers, let alone on the range of browsers and platforms supported by Flash.
  2. There is no accepted standard here — we’re back to the codec/format wars of the late ’90s with Apple sticking to QuickTime and Google arguing that zero cost = open. This is a hard problem, and pretending it is solved isn’t helping anyone.

and of course, same goes for audio.

I’m coding again in native Objective-C coding for iOS with the lovely XCode.  It has been a while so I’ve been reading up on debugging tricks.  Sadly, it still feels a bit like debugging in Macsbug in 1990…

The graphical display of object values is fairly useless, but in gdb, we can use the po command to get a pretty printing of the object if it comes with a built in description (for details see nice writeup from cocoa with love).

For our own objects, we can define a debugDescription method. For example, if I have a Thing class which is a subclass of NSObject and just has a title and subtitle:

@implementation Thing

@synthesize title;
@synthesize subtitle;

- (NSString *)debugDescription { return [NSString stringWithFormat:@"title: %@n subtitle: %@", title, subtitle]; }

@end

Then if I find myself at a breakpoint with an instance of Thing in scope, called “thing”:

(gdb) po thing
title: Another
subtitle: Another example

Or if I have an array of Thing instances, called “things”:

(gdb) po things
<__NSArrayM 0x6a157a0>(
title: Basic
subtitle: My Thing,
title: Something
subtitle: Example of something,
title: Another
subtitle: Another example
)

iphonedevelopertips illustrates this nicely in more detail with the description method which will do the same thing for any subclass of NSObject.

Note: print-object actually calls the debugDescription method of the specified object. NSObject implements this method by calling through to the description method. Thus, by default, an object’s debug description is the same as its description. However, you can override debugDescription if you want to decouple these; many Cocoa objects do this. (via Apple Tech Note 2124)

Update: LLVM Debugger sucks less

If you choose the LLVM Debugger, instead of gdb, you can see properties on an object without defining a description or debugDescription method:

(lldb) po thing
(Thing *) $8 = 0x06a487d0 
(lldb) po thing.title
(NSString *) $9 = 0x00005984 Another
(lldb) po thing.subtitle
(NSString *) $10 = 0x00005994 Another example

…but still no love for Arrays:

(lldb) po things
(NSMutableArray *) $11 = 0x06a0b6a0 (
,
,

)

…just for fun, let’s see how to look at a property of the first object in an array:

(lldb) po [things objectAtIndex:0]
(id) $12 = 0x06a44d20 
(lldb) po [things objectAtIndex:0].title
error: warning: instance method '-objectAtIndex:' not found (return type defaults to 'id')
error: property 'title' not found on object of type 'id'
error: 1 errors parsing expression
(lldb) po (Thing*)[things objectAtIndex:0].title
error: warning: instance method '-objectAtIndex:' not found (return type defaults to 'id')
error: property 'title' not found on object of type 'id'
error: 1 errors parsing expression
(lldb) po ((Thing*)[things objectAtIndex:0]).title
(NSString *) $14 = 0x00005944 Basic
(lldb) po [[things objectAtIndex:0] valueForKey:@"title"]
(id) $15 = 0x00005944 Basic

Whew!