Android development with rock 0.9.5

👋 This page was last updated ~12 years ago. Just so you know.

rock 0.9.5 is out! It's the meanest, slimmest, baddest rock release yet.

To update, run git pull && make rescue as usual. To install from scratch, clone the repo, cd into it, and run make rescue from there - it'll download the latest bootstrap, compile itself from C, then recompile itself from ooc.

Running rock -V should print this happy little version line:

rock 0.9.5 codename panda, built on Wed Feb 13 00:39:52 2013

That's right, we have codenames now! Panda is an inside joke at nevargames, and I'm loving it.

A photo of my touch-based game design, to distract you from the wall of text.

In this release, legacy code has been slashed, many bugs have been squashed, and yet through all of this, many new features have shown their head - although all in all the codebase hasn't grown much!

Most importantly, many constructs in ooc are now more robust: enums, properties, varargs, generics, as long as many basic operations: launching processes, querying the number of processors and the hostname of a machine, etc.

The compilation process itself has been improved, and is now more reliable on all platforms - recompilations are faster than before, thanks to improved libcaching and launching several C compiler jobs in parallel.

.use files have seen a lot of love, allowing for features like CustomPkg, to accomodate for non-pkg-config software packages, Linker for pesky C++ software like llvm, Additionals for when you just have to bundle your C code with you, and Frameworks for when you can't get enough of Apple's OS.

But the single most exciting feature to me is definitely the android driver - ie. making rock generate Android.mk files to be compiled with the Android NDK, instead of compiling them itself. Read on to learn more about it.

You can read the full changelog for 0.9.5 on GitHub, or go on reading the meat of the article already.

Making an Android game

For my February #1GAM, I wanted to do a mobile app. But I'm not too fond of Java or Objective-C altogether, so I wanted to keep those to a minimum.

My first target of choice is Android, because I own an Android phone, so it's just easier for testing. My first step was to get an SDL + OpenGL ES 2 app running there, and that went well:

The code was basically adapted from online tutorials for OpenGL ES 2 (I've dealt with shaders before but my memory of them was a bit.. shady).

The interesting part about running SDL on Android is that they provide a base Android project to work from. You see, most Android apps have what they call an 'Activity'. An Activity can have windows, deal with events (such as touch input, text input, but also the accelerometer etc.) - and it can handle an OpenGL ES context.

But activities have to be written in Java in order to integrate with the ecosystem. Also you need a manifest and a few resources for the app's name, icon, and so on. The good news is that SDL provides the Java source for an Activity, along with a template project folder that you can just copy and make your own.

Which leaves us with the question of running ooc (or C, for that matter) code in there. The way it's done is having your .c sources in the jni/ folder (might depend on your project setup), and have an Android.mk file, which is the bastard child of a Makefile and a CMakeLists.txt.

Then, instead of running make, you run ndk-build, which builds your code into a dynamic library, and then from your Java Android activity, you can load native code like so:

Java code
package com.nevargames.swoon;

import org.libsdl.app.SDLActivity;

public class SwoonGame extends SDLActivity {

    // Load the .so
    static {
        System.loadLibrary("SDL2");
        System.loadLibrary("sdk");
        System.loadLibrary("deadlogger");
        System.loadLibrary("sdl2");
        System.loadLibrary("stbi");
        System.loadLibrary("dye");
        System.loadLibrary("yaml");
        System.loadLibrary("chipmunk");
        System.loadLibrary("gnaar");
        System.loadLibrary("swoon");
        System.loadLibrary("main");
    }

}

Beware to load the native libraries in the right order, though, or you'll get unresolved symbol errors at runtime.

SDL then has all the glue code to create the window, the OpenGL context, handle native events, and finally launch your SDL_main.

Using rock to build for Android

So, that's all good and fancy, but how do we go about compiling .ooc code into those .so dynamic libraries that we can then load from our Java code?

Well, one way would be to do straight cross-compilation with rock's default driver, ie. SequenceDriver. However, we would have to adjust a lot of settings to make sure the Android toolchain (for example, the GCC 4.7 toolchain) is used correctly, with the right sysroot, include paths, etc.

I did have to cross-compile a few things by hand, and I've found/adapted scripts to do so for autotools-based projects and cmake-based projects - with that I've been able to cross-compile libyaml, the chipmunk physics engine, and the boehm gc.

For pure ooc libraries, it's much simpler. Here what my current Makefile:

OOC_FLAGS := -v -g
ROCK := rock

desktop:
        $(ROCK) $(OOC_FLAGS)

osx:
        OOC_LIBS=${OOC_LIBS}/ooc-sdl2/uses/osx:${OOC_LIBS} $(ROCK) $(OOC_FLAGS)

android:
        rm -rfv android/assets/*
        cp -rfv assets/ android/assets/
        OOC_LIBS=${OOC_LIBS}/ooc-sdl2/uses/mobile:${OOC_LIBS}/deadlogger/uses/mobile:${OOC_LIBS}/ooc-yaml/uses/mobile:${OOC_LIBS}/ooc-chipmunk/uses/mobile:${OOC_LIBS}  $(ROCK) $(OOC_FLAGS) -driver=android --outpath=android/jni

clean:
        $(ROCK) -x

.PHONY: android desktop clean

My main dev environment is Debian Linux, so the desktop target is there for that. On OSX, OpenGL is linked differently so I have a slightly different OOC_LIBS path (something new for rock 0.9.5) to pick up the OSX-specific usefile.

So the neat thing is that I get to write one single codebase for Linux, OSX, Windows, and Android (hopefully iOS soon) - and I can launch both at the same time if I want to!

And for android, not only do I copy the assets, but I also use different usefiles, because of specific flags like so (this is deadlogger/uses/mobile/deadlogger/deadlogger.use):

Name: deadlogger
Description: A dead (-simple) logging package for ooc
SourcePath: ../../../source

# That's Android stuff, get off my turf
Libs: -llog

This will all get much easier with versioned .use files, which is planned for 0.9.6!

Then, my build process looks something like this, from swoon/android:

(cd .. && make android) && ndk-build -j7 && ant debug install

The make android part launches rock, which generates .c files along with Android.mk files. ndk-build is Android's make equivalent, which produces dynamic native libraries. And the ant debug install part packages the assets, compiles the Java code, and generates the .apk for the app. Then, it just installs it to whatever Android I happen to have connected at that moment.

Logging

An interesting challenge was to see the logging output of my game under Android. On all other platforms (that's Linux, OSX, Windows, and iOS), you can see the standard output, so you can just printf away in bliss, like we always do.

But no, Android has to do things differently: there's a log library that you have to use if you hope to see any output (in adb logcat - which allows you to see the log of an actual device. That's actually pretty neat).

Thankfully, hooking into liblog from deadlogger was really easy:

version (android) {
    import deadlogger/[Formatter, Filter, Logger, Level, Handler, Internals]

    AndroidHandler: class extends ExtendedHandler {
        init: func {
        }

        send: func (logger: Logger, level: Int, emitter: Logger, msg, formatted: String) {
            __android_log_print(mapLevel(level), emitter path, msg toCString())
        }

        /**
         * Map deadlogger levels to Android levels
         */
        mapLevel: static func (level: Int) -> Int {
            match level {
                case Level debug =>
                    ANDROID_LOG_DEBUG
                case Level info =>
                    ANDROID_LOG_INFO
                case Level warn =>
                    ANDROID_LOG_WARN
                case Level error =>
                    ANDROID_LOG_ERROR
                case Level critical =>
                    ANDROID_LOG_ERROR
            }
        }
    }
}

Then, running adb logcat | python ~/bin/coloredlogcat.py gives us pretty output like that:

You can get coloredlogcat as a Gist. I found it on Jeff Sharkey's blog.

Conclusion

There's a lot more to be said about ooc on Android, but that will have to wait till the next rock release, I'm afraid.

Until then, go field-test ooc! Full docs for both the language and the SDK are coming, courtesy of @duckinator, so you'll be all set real soon.

Finally, I'm happy to welcome fellow #1GAM developers to the NevarGames IRC server: irc.nevargames.com/6667. The team & friends hang out in #nevargames!

Comment on /r/fasterthanlime

(JavaScript is required to see this. Or maybe my stuff broke)

Here's another article just for you:

So you want to live-reload Rust

Good morning! It is still 2020, and the world is literally on fire, so I guess we could all use a distraction.

This article continues the tradition of me getting shamelessly nerd-sniped - once by Pascal about small strings, then again by a twitch viewer about Rust enum sizes.

This time, Ana was handing out free nerdsnipes, so I got in line, and mine was: