Another week, another iOS release, another multi-gigabyte SDK download. You have been sitting all day in front of nondescript backdrops under the rain, coffee mug attached to your paw. Your doctor left a message saying your liver looks like last year’s filter from the hospital’s coffee vending machine. You lost your Barista of the Day 2010 Tracker Calendar and now every time you show up there is a dude with an apron behind the counter. iPod keeps shuffling once and again over the sad parts of the Start Wars soundtrack. You have to accept it. You are stuck again.

It all started with the interface. You were there positioning buttons, entering coordinates by hand: the score label, the Menu-pause™ widget, the alertness/drunkenness meter… it really started bugging you after having to re-compile for the tenth time just to find your designer (that is, you on Fridays) had decided to change the size of all the graphics. Then there was the turtle. I mean, how difficult do you think it is to calculate the mass of a freaking turtle? But somehow anytime you tried to change it the physics engine either made it go through the floor or bounce like crazy. Gosh man, didn’t we go through this already? Except your fancy level editor was of no help with any of this. You see, I may run the risk of repeating myself, but maybe is time to revisit the advice I gave you. First you need better tools.

Implementing a Socket Server for your iPhone Game

If there is anything to learn from all this frustration is that waiting to recompile sucks. Seeing changes live, that’s what you need. Being able to move your interface around until you get it perfectly lick-able, or change the hit points of your Pretzel Dragon till you find the perfect balance between Masochist level and N00b. At runtime. That’s what we are talking about! Sure you can already do all that with the debugger, but really… do you enjoy pausing and restarting, fishing for the right breakpoint and variable? That’s like having a swimming pool when what you need is an umbrella. Not fun.

So you come up with a plan: you are going to run a server on your game, connect to it from your computers command line, and send commands for the things you want to modify. You look on the interwebs for information on how to do this and then start wondering if maybe Networking 101 in school had nothing to do with hanging in the bar with the girl of the anchor tattoo. I could explain you how Berkeley sockets work, and the difference between transfer protocols, and we could talk about streams and datagrams, and the beautiful C API… or we could just use AsyncSocket. You will like AsyncSocket cause:

  • you only need to add one class to your project! There are two available, one for TCP connections and another one for UDP. We will stay with TCP cause we are going to use Telnet to connect to your server.
  • you don’t need to worry about low level details. Whenever the server accepts connections, clients disconnect or timeout, or a number of other things happen, AsyncSocket will just call your delegate methods. And for starters, you only need to really worry about implementing 4 methods.
  • it plays nice with the current application NSRunLoop by default, and is non-blocking.

So you open Xcode, and decide to create a new barebones Window-based Application to test all this. Let’s just call it the Mystery Coconut HQ Simulator. You include AsyncSocket, then the CFNetwork.framework, then finally create a normal NSObject descendant for the Debug Server itself.

DebugServer.h ends up just with a few instance variables: the AsyncSocket object that works as server, an array of connected clients, and a flag to tell if is already running or not. One method for starting the server on a specific port, another for stopping, and we are done.

@class AsyncSocket;

@interface DebugServer : NSObject {
    AsyncSocket *debugServer;
    NSMutableArray *connectedClients;
    bool running;
}

@property (readonly,getter=isRunning) bool running;

- (void) startOnPort:(int)port;
- (void) stop;

@end

Then you implement a few delegate methods. I see your server can send messages to connected clients with writeData:withTimeout:tag:, and that it requires NSData versions of your strings. And since you want to listen to connected clients until they disconnect, you call readDataWithTimeout:tag: when they first connect, and then every time you get some input, cause if you didn’t the server would only ever receive the first message!

@implementation DebugServer

@synthesize running;

- (id) init;
{
    self = [super init];
    if (self != nil)
    {
        debugServer = [[AsyncSocket alloc] initWithDelegate:self];
        connectedClients = [[NSMutableArray alloc] initWithCapacity:1];
        running = false;
    }
    return self;
}

- (void) dealloc;
{
    [self stop];
    [connectedClients release];
    [debugServer release];
    [super dealloc];
}

- (void) startOnPort:(int)port;
{
    if (running) return;
   
    if (port < 0 || port > 65535)
        port = 0;
   
    NSError *error = nil;
    if (![debugServer acceptOnPort:port error:&error])
        return;
   
    NSLog(@"My Awesome Debug Server has started on port %hu", [debugServer localPort]);
   
    running = true;
}


- (void) stop;
{
    if (!running) return;
   
    [debugServer disconnect];
    for (AsyncSocket* socket in connectedClients)
        [socket disconnect];
   
    running = false;
}

- (void)onSocket:(AsyncSocket *)socket didAcceptNewSocket:(AsyncSocket *)newSocket;
{
    [connectedClients addObject:newSocket];
}


- (void)onSocketDidDisconnect:(AsyncSocket *)socket;
{
    [connectedClients removeObject:socket];
}

- (void)onSocket:(AsyncSocket *)socket didConnectToHost:(NSString *)host port:(UInt16)port;
{
    NSLog(@"Accepted client %@:%hu", host, port);

    NSData *welcomeData = [@"Welcome to my Awesome Debug Server\r\n"
        dataUsingEncoding:NSUTF8StringEncoding];
    [socket writeData:welcomeData withTimeout:-1 tag:WelcomeMsgTag];

    [socket readDataWithTimeout:-1 tag:GenericMsgTag];
}


- (void)onSocket:(AsyncSocket *)socket didReadData:(NSData *)data withTag:(long)tag;
{
    NSString *tmp = [NSString stringWithUTF8String:[data bytes]];
    NSString *input = [tmp stringByTrimmingCharactersInSet:
                        [NSCharacterSet whitespaceAndNewlineCharacterSet]];
   
    if ([input isEqualToString:@"exit"])
    {
        NSData *byeData = [@"Bye!\r\n" dataUsingEncoding:NSUTF8StringEncoding];
        [socket writeData:byeData withTimeout:-1 tag:GenericMsgTag];
        [socket disconnectAfterWriting];
        return;
    }
   
    [socket readDataWithTimeout:-1 tag:GenericMsgTag];
}

@end

Nice! That was fast. You have a socket server.

Fast app switching in iOS 4

What? You don’t think is going to work? You think the whole AsyncSocket thing is a prank, like that time the girl of the anchor tattoo convinced you the NSCoders meeting was at the Blue Oyster? Well, let’s see; just add a DebugServer to the AppDelegate and… Holy crap! Who the hell put all this garbage in your AppDelegate?! Oh, wait… does this have anything to do with you using iOS 4? You skim through the docs as your eyes glaze over. OK, don’t get stressed. The important part is: where do you put the code to start and stop the server?

You could put the startOnPort: call on applicationWillEnterForeground:, but then iOS 3 devices will never run that code… no thanks. You end up putting it in applicationDidBecomeActive:.

As per the stop call… applicationWillTerminate: for sure, but also in applicationWillResignActive:, so that when the app goes to sleep you don’t keep the server running and draining battery and stuff. You know the system will kill it if you did that!

You wonder, how would you do all this if it was a game that needs to save state, and handle a run loop? If it was me, I think I would do something like this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // Load your game state here
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // We are back! Start the run loop!
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Save the game state (probably stop the run loop first)
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Stop the run loop or we will be killed!
    // Don't save yet, maybe he doesn't answer that phone call from his mom...
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Save and release any memory you can load fast,
    // maybe that way we are last when they line us up against the wall
}

So let’s see what you got. You added a DebugServer instance variable to your AppDelegate. Then started it in say port 9990 in the applicationDidBecomeActive: method. Stopping happens in applicationWillTerminate: and applicationWillResignActive:. With a self-congratulatory smile you check the IP of your iPhone in the LAN before compiling and running.

There it is. A boring gray background. But unaffected by the laughs of imaginary peers looking over your shoulder, you fire up the Terminal and type telnet 0.0.0.0 9990 (or whatever your device’s IP was, I cannot remember now). And… you see nothing. Gosh! Open the port in your Firewall man! Do I have to explain you everything?

Finally. There it is. Those two days you spent messing with color settings in the Terminal were so worthy of this screenshot…

Now… what happens if you run this in the simulator? It works too?! Good job!

Commands?!

There you are, glancing at the screen full of pride. You can hear the Force Theme finally coming up on iTunes. Sun shines outside. Is that the calendar you were looking for under that pile of printed PDFs?

But wait… what can you do now with this? Are you telling me that you can type “exit”??? Really?! How the heck are you going to use this to modify your game at runtime?! Come on, look at those clouds outside!

Well, you obviously have to put more work on this! You grab a notebook and start scribbling a list of commands you need to have, like “get” and “set” and “list” and “make coffee with sock and fish tank”, and… I think you are going to need a parser for all that. But after that what? Well you will need to hook your variables to this system, so when you write “set turtle-mass 1.1” it converts “turtle-mass” into a reference to a specific instance of your StupidTurtle.mass… You start thinking about registers, and command patterns, and lexers… and start crying while the Imperial March booms in the stereo.

OK, OK. Look, I will help you again. Let me present you my friend…

Lua

Would not it be nice if you could just type code in that Terminal? That it magically knew how to do operations, and loops, and assignments… that it knew about your variables… that it was as easy as writing in Xcode? Well, you cannot do that. At least not in Objective-C cause, you know, your friend the compiler waits at the end of that track. So what you need is a scripting language. You have read before about games using Python and similar languages to help with their fancy data-orientation. So you look around and find Lua’s ugly website. A scripting language that was made especially to be integrated with C? And with other languages too?? Wait, is that Objective-C in that list?

You check around diverse Objective-C/Lua projects and end up grabbing the Lua Objective-C Bridge. Seems kind of old and unmaintained, but you think is worth a try. You download the latest Lua 5.1.4 too, and add all the files to your project except for the lua.c, luac.c, and print.c, that you read somewhere are not needed. You go back to your DebugServer header and add a lua_State instance variable, then you change your init method implementation…

// Prepare Lua interpreter
luaState = lua_objc_init();
lua_settable(luaState, LUA_GLOBALSINDEX);

Then you add this to your onSocket:didReadData:, right after checking the client didn’t type “exit”.

// Run Lua
luaL_loadbuffer(luaState, [input UTF8String], [input length], nil);
lua_pcall(luaState, 0, 0, 0);

Then you run the app again, connect through the Terminal, and start typing.

But you get nothing. Then you remember you have not fed the client the results… but what is that in Xcode’s console?

Yes! You start coding like crazy. In a few minutes you have a game loop, some sprites, and a GameState object with some properties and methods. You add the following to your Lua setup:

// Prepare Lua interpreter
luaState = lua_objc_init();

AppDelegate* app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
GameState* game = app.gameState;

lua_pushstring(luaState, "game");
lua_objc_pushid(luaState, game);
lua_settable(luaState, LUA_GLOBALSINDEX);

Just grabbing the GameState object and adding it as a Lua variable “game”? How do you call the methods of this object then? Seems “game:test()” calls [gameState test], and “game:setX_Y_(0, 2)” calls GameState’s setX:Y: selector! And since properties create accessors, you can also call those??? Can it really be that easy?!

The sun is bright, birds sing… as the Rebel Fanfare quicks in, you look at your phone and see somebody is calling… from your favorite coffee place.

Liked this article? Check out the follow–up and learn how to include your Lua Telnet Server only in the Debug build of your app.

LuaSockets Source Code!

Updated 7/11/2010

This Xcode project features one of the inhabitants of Mystery Coconut HQ, Pancho in his usual begging mode. Take a look at the GameState class and its methods and properties. You can access all of these from a telnet session (on port 9990) by typing, for example…

game:setRequestsPerSecond(10) // that's closer to the real Pancho.

game:setCloud_toSpeed(0, 3.5) // notice how multiple arguments are handled here.

print(game:numBalloons()) // to get properties, just print them through Lua. Remember you don't have access to member variables, just to methods.

Have fun experimenting!

Download LuaSockets project

22 Responses

  1. Dude! That’s well worth the wait.

    The more I read, the bigger my smile got :)

    This is going into my current prototype as soon as I finish typing this comment! I think I’ll leave the Lua side of things out for now, and stick with a simple lookup table.

    But the TCP connection stuff is brilliant. Nice work!

    George Sealy; July 7, 2010, 8:59 PM

  2. Great post! Always wanted to do stuff with Lua but never got around to it, I think might check it out now though.

    Just a note of caution, you should save your state in the “applicationDidEnterBackground” as well as in “applicationWillTerminate”.

    Why? Well it’s a little known fact is that applicationWillTerminate *never gets called* if a user closes your app using the multitasking ui while it’s suspended. Seems crazy, but it’s true: https://devforums.apple.com/message/239897#239897

    Matt Rix; July 7, 2010, 9:37 PM

  3. Interesting! I do save in “applicationDidEnterBackground”, but are you saying I should do it too in “applicationWillResignActive” so if they go into the multitasking UI with my app open, then kill the app, the state is saved? Crazy!

    Miguel A. Friginal; July 7, 2010, 9:45 PM

  4. Just something that’s worth adding – I wasn’t sure if this would work in the simulator, but you can just connect to 127.0.0.1 and it gets routed through to the simulator, nice!

    George Sealy; July 7, 2010, 10:26 PM

  5. Indeed. I think I have half a sentence somewhere up there about the simulator :)

    Miguel A. Friginal; July 7, 2010, 10:46 PM

  6. Fantastic post and one I think lots of us will be referring to in the future. Expect an influx of networked games in the future inspired to look deeper :)

    Steve Parkes; July 8, 2010, 1:29 AM

  7. Great post! I’ve been wanting to add a telnet debugger to my engine for a long time, but wasn’t really sure how to start approaching it. Thanks for the step-by-step!

    Owen Goss; July 8, 2010, 4:42 AM

  8. Thanks for the post Miguel, some great ideas in there. I will be adding something like this soon to my game (as soon as I can squeeze out some more time!)

    It seems like this method will also help with testing actual game behavior on the device with some automated and repeatable scripts.

    Doug Sjoquist; July 8, 2010, 6:47 AM

  9. Thanks! Yes, you can pass whole Lua scripts using luaL_loadfile() instead of luaL_loadbuffer(). They will have access to any ObjC objects registered with lua_objc_pushid(), so in theory you could write full automated testing in Lua that way. You can then fire the tests through a Telnet command, maybe even start it with a cron job on your server… many possibilities!

    Miguel A. Friginal; July 8, 2010, 7:19 AM

  10. Great post, Miguel! I’m also a fan of remote debugging. It’s awesome in non-PC platforms like iPhone and consoles.

    A perfect complement to a tweaking system like this, is a GUI app that parses the different tweaks and their types and creates a GUI on the fly (unfortunately mine is still written in .Net so I have to run it in VMWare :-()

    One thing that I found is that pure text telnet channel was too slow for high-frequency updates. So for certain things that I need to record and send back to the PC every frame, I ended up opening a separate binary channel.

    Noel; July 8, 2010, 10:23 AM

  11. Holy Cow! This is amazing. I’ve always had my doubts of how to implement Lua to run stuff. I know there is a bunch of tutorials over there, but this one iPhone specific is THE big thing.

    Just a thought: this can’t be implemented to release, right? I mean, since Apple is blocking interpreted stuff, I can’t make a game engine and then script the game and actions with Lua?

    Alfred; July 11, 2010, 8:19 AM

  12. That’s a good question. My idea was to use Lua just for tweaking stuff with the telnet debugger. Since you don’t want that available in your game you need to wrap all that in #ifdefs to not compile it into the release version. But I have seen some reports of scripted games live in the store, and Apple’s developer agreement changed recently in a way that may make it OK to script games, as long as the user doesn’t have the option to upload their own scripts into the app. Is worth checking.

    Miguel A. Friginal; July 11, 2010, 8:53 AM

  13. Sweet, I’ll check that. That was my only concern about iPhone development (not having a scripting language). But if now I can use Lua, everything is perfect.

    Another question: everytime I run your example and put a command in telnet the simulator just freezes.

    I can’t even get a print message, like:
    print(game:numBalloons())

    Note that it works for “pure” Lua commands, like: print(‘a’)

    But doesn’t work for the GameState object. Any tips? (and it doesn’t print any error message at all).

    Alfred; July 11, 2010, 5:38 PM

  14. Hi Alfred,

    Just tried it again, compiled from zero, ran it on the simulator, telnet into it, typed print(game:numBalloons()) and got 6 without any crashes. Are you using the latest SDK and the iPhone Simulator 4.0? I didn’t test in anything older. Does it work at all for things like game:shutup()?

    Miguel A. Friginal; July 11, 2010, 5:58 PM

  15. Doh! I found the problem. I forgot to set the pipe that captures output and redirects to the Telnet session as non-blocking. Download the code again (I just updated it) and it should work.

    By the way, I also checked and it compiles without problems with the iPhone OS 3.1.3 version of the SDK. Cheers!

    Miguel A. Friginal; July 11, 2010, 7:02 PM

  16. Amazing! Everything is just working now.
    I’ll tell you when I have something working using it – I’ll use for both testing and tweaking and creating the game content. :)

    Alfred; July 11, 2010, 8:36 PM

  17. Just a quick note – I’ve just integrated OpenFeint, and it seems they use the same AsyncSocket code, or at least the same names. The end result is a failed compile due to name clashes. It’s a simple fix, just rename AsyncSocketException and AsyncSocketErrorDomain to something else.

    George Sealy; July 14, 2010, 2:39 PM

  18. Pingback: How To Implement A Socket Server For Easy App Tweaking | iPhone iOS 4 iPad SDK Development Tutorials, Programming Tips, News

  19. Crap that was epic. Going to be sipping some tea and trying this out immediately. Thanks for the goodness!

    David McGraw; July 21, 2010, 11:21 AM

  20. Pingback: App Tweaking at Under The Bridge

  21. Pingback: oF関連リンク〜気になった〜 | oF / iOS Thinking.

  22. Pingback: Remote Game Editing | Games from Within

Leave a Reply

Your email address will not be published. Required fields are marked *

five × four =