Lightweight CSV reader for Unity

Managing game data in Unity scene objects can get really painful especially when more than one people needs to edit the same thing. It’s usually better to have some data in CSV file where it can be controlled centrally.

When facing this problem I couldn’t find CSV reader for Unity that would have been exactly what I need. i.e. not be very buggy, huge or require new assembly dependencies.
So here is very simple CSV reader for Unity. It is bare bones but still robust enough parse quoted text and comma’s inside text. Reader assumes that csv files are in Resources folder of your Unity project.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class CSVReader
{
	static string SPLIT_RE = @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
	static string LINE_SPLIT_RE = @"\r\n|\n\r|\n|\r";
	static char[] TRIM_CHARS = { '\"' };

	public static List<Dictionary<string, object>> Read(string file)
	{
		var list = new List<Dictionary<string, object>>();
		TextAsset data = Resources.Load (file) as TextAsset;

		var lines = Regex.Split (data.text, LINE_SPLIT_RE);

		if(lines.Length <= 1) return list;

		var header = Regex.Split(lines[0], SPLIT_RE);
		for(var i=1; i < lines.Length; i++) {

			var values = Regex.Split(lines[i], SPLIT_RE);
			if(values.Length == 0 ||values[0] == "") continue;

			var entry = new Dictionary<string, object>();
			for(var j=0; j < header.Length && j < values.Length; j++ ) {
				string value = values[j];
				value = value.TrimStart(TRIM_CHARS).TrimEnd(TRIM_CHARS).Replace("\\", "");
				object finalvalue = value;
				int n;
				float f;
				if(int.TryParse(value, out n)) {
					finalvalue = n;
				} else if (float.TryParse(value, out f)) {
					finalvalue = f;
				}
				entry[header[j]] = finalvalue;
			}
			list.Add (entry);
		}
		return list;
	}
}

Drop this in your Scripts folder as CSVReader.cs and make folder Resources under your Assets folder. Put there any CSV files you want to use.

Example CSV file example.csv in Resources folder.

name,age,speed,description
cat,2,4.5,"cat stalks, jumps and meows"
dog,2,5.5,dog barks
fish,1,1.1,fish swims

How to use

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Main : MonoBehaviour {

	void Awake() {

		List<Dictionary<string,object>> data = CSVReader.Read ("example");

		for(var i=0; i < data.Count; i++) {
			print ("name " + data[i]["name"] + " " +
			       "age " + data[i]["age"] + " " +
			       "speed " + data[i]["speed"] + " " +
			       "desc " + data[i]["description"]);
		}

	}

	// Use this for initialization
	void Start () {
	}

	// Update is called once per frame
	void Update () {

	}
}

See full example code in Github: https://github.com/tikonen/blog/tree/master/csvreader

Now Hiring in Mobile Gaming!

nonstop-4-2

I’m pleased to announce that my start-up Nonstop Games recently joined the King family and we are now a King studio in Singapore. We’re expanding the studio and it’s a great time to join us, as you will contribute and shape the games in the pipeline and be part of an awesome and passionate team!

We are looking for talented game and server developers and if you are looking for an exciting challenge where you can really make a difference, then check out our positions and apply!

http://www.nonstop-games.com/jobs

Please also follow us on LinkedIn for regular updates

https://www.linkedin.com/company/nonstop-games

Finding Patterns in Data Sequences

Some time ago I was presented question if there would be easy and quick way to recognize certain usage patterns in huge amount of event data. The events in question were common user activity like clicking buttons, waiting certain time between and so on. This data could then used to improve user experience or catch cheaters that write bots to play their game for them and so on.

There are multiple ways to approach this problem but I came up with relatively simple method that seems to have flexibility. We can think the events of interest as sequence of symbols and in case also time between events is of interest, quantize the time as symbols for short, long and medium time. Then, sequence of events can be presented as list of symbols

ABC123ABC123ABC123ABC123XABC123ABC123ABC123ABC123XABC123ABC123ABC123ABC123X

Here A could be button click to open window, B a tab click in window, C close button, 1 a second pause before moving mouse, 2 a few second pause before moving mouse and X is collect resources etc..

Now we can approach problem with pattern finding. First let’s define function that is supposed to find recurring patterns and output them and input as sequence of those patterns. (Following is written in Python)

def findpattern(s, trivial):
	patterns = {}
	symbolmap = {}

	p = patterns
	acc = []
	output = []
	for c in s:
		if not c in p:
			if not c in patterns or (not trivial and acc[-1] == c):
				p[c] = {}
				p = p[c]
				acc.append(c)
			else:
				acc = ''.join(map(lambda x: str(x),acc))
				if not acc in symbolmap:
					symbolmap[acc] = true
				output.append(acc)

				acc = [c]
				p = patterns[c]
		else:
			p = p[c]
			acc.append(c)

	if acc:
		acc = ''.join(map(lambda x: str(x),acc))
		if not acc in symbolmap:
			symbolmap[acc] = true
		output.append(acc)

	return (symbolmap.keys(), output)

Function accepts two arguments, the list of symbols and flag if trivial pattern should be accepted. This is needed to determine if you consider a input list “AAAAA” to be one instance of pattern “AAAAA” or 5 instances of pattern “A”.

When this function is applied on the example string above, we get list of core patterns and the input as pattern list.

> s = "ABC123ABC123ABC123ABC123XABC123ABC123ABC123ABC123XABC123ABC123ABC123ABC123X"
> findpattern(s, False)
['ABC123', 'ABC123X'] ['ABC123', 'ABC123', 'ABC123', 'ABC123X', 'ABC123', 'ABC123', 'ABC123', 'ABC123X', 'ABC123', 'ABC123', 'ABC123', 'ABC123X']

Here it’s easy to see that the string is composed of two main patterns, ABC123 and ABC123X. The second output list is the input string split as instances of pattern.
Real data is noisier than the example here, so proper data cleaning and event definition / filtering are vital for any kind of useful pattern recognition.

In this case one could guess that the player might be bot, as the same sequence occur regularly with too much precision and too little variability for human player. (Remember that the symbols 1, 2 and 3 presented also time in this example.)

Going forward we can also define function that recursively uses the output of the findpattern to find patterns of patterns up to the “prime” pattern of the input, i.e. the pattern that when repeated N times produces the original input string.

def findprimepattern(s, trivial=False):
	o = s
	while True:
		s, o = findpattern(o, trivial)
		print s,o
		if len(s) == 1:
			return s[0]

Example with more complicated pattern string

> s = (((("ABC"*2)+"DE")*3+"X")*2+"O")*4
"ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXOABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXOABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXOABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO"
> findprimepattern(s)
['ABCDEXO', 'ABC', 'ABCDE', 'ABCDEX'] ['ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEX', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEXO', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEX', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEXO', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEX', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEXO', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEX', 'ABC', 'ABCDE', 'ABC', 'ABCDE', 'ABC', 'ABCDEXO']
['ABCABCDE', 'ABCABCDEX', 'ABCABCDEXO'] ['ABCABCDE', 'ABCABCDE', 'ABCABCDEX', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEXO', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEX', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEXO', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEX', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEXO', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEX', 'ABCABCDE', 'ABCABCDE', 'ABCABCDEXO']
['ABCABCDEABCABCDEABCABCDEX', 'ABCABCDEABCABCDEABCABCDEXO'] ['ABCABCDEABCABCDEABCABCDEX', 'ABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEX', 'ABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEX', 'ABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEX', 'ABCABCDEABCABCDEABCABCDEXO']
['ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO'] ['ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO', 'ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO']
ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO

And indeed the input string is the prime pattern ABCABCDEABCABCDEABCABCDEXABCABCDEABCABCDEABCABCDEXO concatenated 4 times.

Note effect of parameter trival on the function.

> findprimepattern('AAAA', False)
['AAAAA'] ['AAAAA']
AAAAA
> findprimepattern('AAAA', True)
['A'] ['A', 'A', 'A', 'A', 'A']
A

Former is single instance of pattern “AAAA” and latter is 4 instances of pattern “A”.

Permutation of a list

Let’s consider following requirement. We have list of objects, say questions, that we want to show our users. Let’s also assume that the list is very long, 500 messages.

Each message is unique and we want to

  1. show each question only once for each user.
  2. show the questions in different order to each user.

Each user responds to only few questions each day so it will take several months before they exhaust the list. We need to remember over each session where user left off.

Basic way to implement this is to take the list of questions, make a copy of it, shuffle it’s content and save it as part of users profile data. This has obvious drawbacks of the data duplication and maintenance. Also if the list changes, every single copy of the list has to be updated.

Cheaper way to implement this is to use prime walk. In this method we can keep only single copy of the list and store only number of answered questions in the users profile.

  1. First define the list, say we have 500 items.
  2. Find prime number that is greater than 500. For example 503.
  3. Select seed for each user. This seed is any number that is constant for an user, like user id or time of account creation.

Then we define function that uses these values. Note that this implementation is tail-recursive for code simplicity, efficient implementation should not use recursion.

var prime = 503;
var list = ["question1", "question2", ... "question500"];

function pick_nth(seed, n, p, l) {
    if(!n) return l[seed % p];
    return pick_nth((seed + l.length) % p, n - 1, l);
}

Now picking up n:th question from list becomes easy. For example:

pick_nth(userId, 0, prime, list);  // first question
pick_nth(userId, 1, prime, list);  // second question
...
pick_nth(userId, 499, prime, list);  // 500th question

Function returns items from list in different order for every unique user id. Only thing needed to keep track of progress is to store the number of questions user has answered.

Changing list requires some care. To delete question, just mark the entry in list to null to skip it. To add questions, add another list that is used after user has exhausted the original one.
If you change the list length, the order of selection changes and it’s not anymore guaranteed that user sees every question and sees every question only once.

Selecting biased random objects from a set

Normal random pick from set of objects will return every object equally likely.
For example, following function returns any character with equal probability from the argument string.

function randomselect(s) {
    return s[~~(Math.random() * s.length)];
}

This is usually the desired functionality, but games often require to pick one thing more likely than another. Typical use case are random drops from enemies, the common loot should drop most often and the good stuff should be much more rare.

Biased random pick is selecting object from a set randomly, so that some of them selected more often than others.

Calling randomselect("abc") will return each “a”, “b” and “c” with 33% probability.
Selection can be biased by just adding more of characters in the string. Calling randomselect("aaabbc") will return “a” with 50%,”b” with 33% and “c” with 16% propability.

Just adding entries in a list is bit clumsy, and more generic method is to use integral of probability density.

// define elements and probability for each
var elems = [
  ['a', 0.5],
  ['b', 0.333],
  ['c', 0.166]
]

// Biased random entry selection
function select(elems) {
    var r = Math.random();
    var ref = 0;

    for(var i=0; i < elems.length; i++) {
        var elem= elems[i];
        ref += elem[1];
        if (r < ref) return elem[0];
    }
    // This happens only if probabilities do not add up to 1
    return null;
}

Calling select(elems) returns a with 50%, b with 33.3% and c with 16.6% probability.
If defined probabilities do not sum to 1 you can get null, i.e not selection. In this case 0.5 + 0.333 + 0.166 is 0.999 so there is 0.1% probability (one out 1000) to get nothing.

For example to pick 100 times randomly from the set.

var elems = [
  ['a', 0.5],
  ['b', 0.333],
  ['c', 0.166]
];

var s = '';
for(var i=0; i < 100; i++) {
	s += select(elems);
}
console.log(s);

Run this with node to get string of randomly picked characters. Here you can see that selection bias works, half of the letters are ‘a’, one third ‘b’ and rest ‘c’.

$ node biased.js
babbaaaabaabbaabaccbaacabbbabbacbaabacabccacacacbacaaaaacbcaaaababaccbcbbbcaabcaabaccabbbcbcbacaabaa

Easy HTML5 canvas and CSS Sprite texture atlas

Texture atlas is a collection of images, all composed in single large image. The game or HTML5 app can load this single image instead of wasting time for requesting every small image separately.

I wrote tool that can be used to package collection of images to single atlas and let web app use that conveniently both as CSS sprite or in HTML5 canvas drawing.

Tool itself is Python script that uses ImageMagick commands to read and write images. The output is the atlas image, CSS, raw JSON and Javascript import file. CSS file defines CSS sprite for each image. JSON is raw data of each images position in the atlas, Javascript import file can be included on HTML page and it loads the atlas position info into global variable.

Setup

Get script from here: https://raw.github.com/tikonen/blog/master/packer/packer.py

Make it executable

$ chmod +x packer.py

Verify that all is ok

~/work/blog/packer $ ./packer.py -h
usage: packer.py [-h] [-o OUTFILE] [-jo JSONOUTFILE] [-jso JSOUTFILE]
                 [-co CSSOUTFILE] [-p PAD] [-mw WIDTH] [-mh HEIGHT]
                 FILE [FILE ...]

Packs images to atlas. Uses ImageMagick to parse and compose the images

positional arguments:
  FILE             Image file

optional arguments:
  -h, --help       show this help message and exit
  -o OUTFILE       Output atlas file
  -jo JSONOUTFILE  Output atlas json file
  -jso JSOUTFILE   Output atlas import js file
  -co CSSOUTFILE   Output atlas css file
  -p PAD           Padding
  -mw WIDTH        Maximum width
  -mh HEIGHT       Maximum height

Install ImageMagick easily on OS/X with Macports or Homebrew.

$ sudo port install ImageMagick

Verify that ImageMagick installation works

$ identify --version
Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC
Delegates: bzlib djvu fftw fontconfig freetype gslib jng jpeg lcms ltdl lzma png ps png tiff webp x xml zlib

Usage

You use script by giving image file as arguments and defining the desired output files and maximum image dimensions

For example:

$ ./packer.py img/*.png -o sprites.png

This would produce sprites.png, sprites.json, sprites.css and sprites.json.js

Options
Following options are supported

  • -p PAD
    Defines padding. The amount of empty pixels around each image. This prevents scaling artifacts on HTML canvas use when drawing from decimal source coordinates.
  • -mw WIDTH,-mh HEIGHT
    Maximum width and height of output image.
  • -o FILE
    Output image file.
  • -jo FILE, -jso FILE, -co FILE
    Output JS import, JSON and CSS file.

Example

We have following small images that are needed in our web app. Loading them all independently would take 5 HTTP requests.

  • button_minus.png
  • button_plus.png
  • cannon_marker.png
  • pause.png
  • status_bar.png

montage

Running tool converts them to single atlas

~/work/blog/packer $ ./packer.py example/pics/* -o example/html/sprites.png -mw 512
Checking ImageMagick
Found: Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org
===========================
Resolving file dimensions
button_minus.png -> 53x43
button_plus.png -> 53x42
cannon_marker.png -> 43x28
pause.png -> 53x42
status_bar.png -> 496x74
===========================
fitting 5 images, padding 1
successfully fitted 5 images to 496x118 padding 1
Wrote: atlas to example/html/sprites.png
Wrote json to example/html/sprites.json
Wrote js to example/html/sprites.json.js
Wrote css to example/html/sprites.css

In this example we defined maximum width of 512 pixels. The resulting image is cropped to minimum size 496×118.

sprites
This atlas can be used now in two different ways on the web page.

CSS Sprite

Web page includes the generated CSS file and defines class by filename (e.g. “bg-sprites status_bar” for each element that uses sprite.

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="sprites.css">
    <style type="text/css">
        .bg-sprites {
            color: white;
            text-align: center;
        }
    </style>
</head>
<body>
    <div>
        <h1>Example</h1>
        <div>
            <h2>Cannon - simple sprite with image</h2>
            <div class="bg-sprites cannon_marker">
            </div>
            <h2>Status bar - sprite with text inside</h2>
            <div class="bg-sprites status_bar">
                <span style="font-size:xx-large;position:relative;top:15px;">Here is some text</span>
            </div>
            <h2>Buttons - list</h2>
            <div>
                <span style="float:left;margin:5px;" class="bg-sprites button_minus"></span>
                <span style="float:left;margin:5px;" class="bg-sprites button_plus"></span>
                <span style="float:left;margin:5px;" class="bg-sprites pause"></span>
            </div>
        </div>
    </div>
</body>

This renders following page:
example css sprite

See example page here: http://ikonen.me/examples/packer/example_css.html

HTML5 Canvas

Another option is to use it with HTML canvas. Page links the generated JSON import script and loads the image.

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <style type="text/css">
        canvas {
            width: 600px;
            height: 400px;
        }
    </style>
</head>
<body>
    <div>
        <h1>Canvas Example</h1>
        <div>
            <canvas id="thecanvas" width="600" height="400"></canvas>
        </div>
    </div>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
  <script type="text/javascript" src="sprites.json.js"></script>
  <script type="text/javascript">
      $(function() {
          var canvas = document.getElementById('thecanvas');
          var ctx = canvas.getContext('2d');
          ctx.strokeStyle = 'hotpink';
          ctx.strokeRect(0, 0, 600, 400);
          ctx.font = '20px Arial';

          // Load image and the json that defines locations
          var sprites = new Image();
          sprites.src = 'sprites.png';
          sprites.addEventListener("load", function() {
                  var assets = bg_sprites; // imported by sprites.json.js

                  // draw them all
                  var xoffset = 5,
                      yoffset = 25;

                  for (var pic in assets) {
                      var asset = assets[pic];
                      ctx.fillText(pic, xoffset, yoffset-3);

                      // draw image from sprite
                      ctx.drawImage(sprites, asset.x, asset.y, asset.w, asset.h, xoffset, yoffset, asset.w, asset.h);

                      yoffset += asset.h + 20;
                  }
          }, false);
      });
  </script>
</body>
</html>

This renders following page:

example canvas

See example page here: http://ikonen.me/examples/packer/example_canvas.html

Get complete example from Github.

Reduce mp3 file size for app and web use

Typical 2 minutes piece of music is roughly 2-4MB in common mp3 format. This is size for usual quality that is stereo audio 256 kbps and 44.1 kHz sample rate. Tough the file size is not that large, it will slow down loading HTML5 game or mobile app, especially on wireless networks.

Web HTML5 or mobile game applications do not need nearly as high quality and the size can be reduced to ~400-700kB without too much audible quality penalty. Difference can be noticed if you listen for it, but it’s unlikely that users do notice anything, as they have nothing to compare it against. This also depends lot of the music, sharp high frequency sounds tend to distort first.

MP3/WAV to reduced size MP3

Easy way to convert mp3s is lame. A flexible command line mp3 decoder and encoder.

Here we use 1:45 minutes 1.7MB music piece Lake Hylia from Zelda Reorchestrated. I don’t own any rights to this music and it’s used purely for demonstration purposes. I selected this piece because it’s melodic and instrumental like most game music is.

Key to reducing file size is lower sample and bitrate, and most importantly converting file to mono. Example below is how to do this with lame 3.99.5 on OS/X. I installed lame using Macports.

$ lame -hv -mm --resample 22.05 "Zelda Reorchestrated - Lake Hylia.original.mp3" -B 32 "Zelda Reorchestrated - Lake Hylia.small.mp3"
LAME 3.99.5 64bits (http://lame.sf.net)
Autoconverting from stereo to mono. Setting encoding to mono mode.
Resampling:  input 44.1 kHz  output 22.05 kHz
polyphase lowpass filter disabled
Encoding Zelda Reorchestrated - Lake Hylia.mp3
      to Zelda Reorchestrated - Lake Hylia.small.mp3
Encoding as 22.05 kHz single-ch MPEG-2 Layer III VBR(q=4)
    Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA
  3996/3996  (100%)|    0:01/    0:01|    0:01/    0:01|   82.375x|    0:00
  8 [  36] **
 16 [  24] *
 24 [   4] *
 32 [3932] **************************************************************************************************************************************
-------------------------------------------------------------------------------------------------------------------------------------------------
   kbps       mono %     long switch short %
   31.7      100.0        99.9   0.1   0.1
Writing LAME Tag...done
ReplayGain: +3.7dB

This converts the input mp3 (lame also accepts .wav files) to 22.05kHz 64kbs monoaural mp3. The output file size is 412kB, resulting to nearly 75% savings. You can reduce file further by using even lower bitrate, (experiment -B 24 or -B 16), but you may start hearing distortions.

$ du -hs Zelda\ Reorchestrated\ -\ Lake\ Hylia.*
1.6M	Zelda Reorchestrated - Lake Hylia.original.mp3
404K	Zelda Reorchestrated - Lake Hylia.small.mp3

Test play the files here and listen for any differences.

Original: Zelda Reorchestrated – Lake Hylia.original.mp3
Reduced: Zelda Reorchestrated – Lake Hylia.small.mp3

MP3/WAV to reduced OGG Vorbis

This is main for Firefox as it still supports only Ogg Vorbis. You can convert mp3/wav file to reduced mono ogg vorbis with ffmpeg tool.

This example uses ffmpeg 2.1, and like lame I installed is on OS/X using Macports.

$ ffmpeg -i "Zelda Reorchestrated - Lake Hylia.original.mp3" -strict experimental -acodec libvorbis -ab 32k -ar 22050 -ac 1 "Zelda Reorchestrated - Lake Hylia.small.ogg"
ffmpeg version 2.1 Copyright (c) 2000-2013 the FFmpeg developers
  built on Oct 30 2013 23:24:10 with Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
  configuration: --prefix=/opt/local --enable-swscale --enable-avfilter --enable-avresample --enable-libmp3lame --enable-libvorbis --enable-libopus --enable-libtheora --enable-libschroedinger --enable-libopenjpeg --enable-libmodplug --enable-libvpx --enable-libspeex --enable-libass --enable-libbluray --enable-gnutls --enable-libfreetype --disable-outdev=xv --mandir=/opt/local/share/man --enable-shared --enable-pthreads --cc=/usr/bin/clang --arch=x86_64 --enable-yasm --enable-gpl --enable-postproc --enable-libx264 --enable-libxvid --enable-nonfree --enable-libfaac
  libavutil      52. 48.100 / 52. 48.100
  libavcodec     55. 39.100 / 55. 39.100
  libavformat    55. 19.104 / 55. 19.104
  libavdevice    55.  5.100 / 55.  5.100
  libavfilter     3. 90.100 /  3. 90.100
  libavresample   1.  1.  0 /  1.  1.  0
  libswscale      2.  5.101 /  2.  5.101
  libswresample   0. 17.104 /  0. 17.104
  libpostproc    52.  3.100 / 52.  3.100
Input #0, mp3, from 'Zelda Reorchestrated - Lake Hylia.original.mp3':
  Metadata:
    title           : Lake Hylia
    artist          : Zelda Reorchestrated
    album           : Twilight Princess
    track           : 11
  Duration: 00:01:44.39, start: 0.000000, bitrate: 128 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 128 kb/s
Output #0, ogg, to 'Zelda Reorchestrated - Lake Hylia.small.ogg':
  Metadata:
    title           : Lake Hylia
    artist          : Zelda Reorchestrated
    album           : Twilight Princess
    track           : 11
    encoder         : Lavf55.19.104
    Stream #0:0: Audio: vorbis (libvorbis), 22050 Hz, mono, fltp, 32 kb/s
    Metadata:
      title           : Lake Hylia
      artist          : Zelda Reorchestrated
      album           : Twilight Princess
      TRACKNUMBER     : 11
      encoder         : Lavf55.19.104
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 -> libvorbis)
Press [q] to stop, [?] for help
size=     398kB time=00:01:44.38 bitrate=  31.2kbits/s
video:0kB audio:387kB subtitle:0 global headers:3kB muxing overhead 1.866299%

Resulting OGG file size and quality is comparable to reduced mp3.

$ du -hs Zelda\ Reorchestrated\ -\ Lake\ Hylia.*p3
400K	Zelda Reorchestrated - Lake Hylia.small.ogg
1.6M	Zelda Reorchestrated - Lake Hylia.original.mp3

Compare here:

Original: Zelda Reorchestrated – Lake Hylia.original.mp3
Reduced: Zelda Reorchestrated – Lake Hylia.small.ogg

Simple Cooldown and Rate Control Algorithm

Software that accepts requests from external, potentially unfriendly sources often need to implement some kind of rate restrictions or cooldowns to limit the amount of resources single user or app client can use.

Typical uses for cooldowns are

  • Limit number of login attempts per user
  • Limit rate of API calls
  • Batch incoming messages or jobs
  • Throttle access to shared resource

There are several approaches how limitations are calculated, but it usually boils down to simple TPS limit, i.e. transactions per second.

Well behaving rate limiter must allow bursts, as client may not have control over the network buffering and even when it sends on steady rate server can receive requests as chunked together. Therefore it does not make sense to enforce strict TPS limit that only understands about seconds but it’s better to use average over time. Optimally rate limiter should also not require lot of data management nor storage.

The algorithm

This is proposal for implementing described rate limiter with minimal overhead as variant of Leaky Bucket algorithm.

  • Let T be time span in seconds and n number of operations we want to allow per T. For example n = 3 and T = 60 would mean 3 operations per minute.
  • Let S = 0 be initial score for rate limited resource.

Every time resource is accessed,

  1. Compute trial score Sn where now is seconds since some globally fixed reference time.
    • Sn = now + T/n if S=0
    • Sn = Sn + T/n otherwise.
  2. Compare trial score Sn and current time.
    • Update S = now + T/n and allow request if Sn < now.
    • Reject request if Sn > now + T
    • Otherwise update score S = Sn and allow request.

This algorithm has some performance benefits as it only needs to store a single integer for score for each throttle and it does not need to be updated if request is rejected so its system load characteristics are well bounded. Algorithm also accepts max n requests in T as burst and averages to (n/T) TPS for constant load.
Implementation also fits well in distributed environments where redis or memcached can be used to atomically increment the score value.

Example

Here is javascript example for reference, save it in file and run with node.js.



var n = 3; // # of requests
var T = 60; // timestamp

var S = 0;
function throttle() {

	var Sn = S;
	var now = ~~(Date.now() / 1000);
	if (!Sn) {
		Sn = now + T/n;
	} else {
		Sn += T/n;
	}
	if ( Sn < now ) {
		Sn = now + T/n; // reset back to initial value
	} else if ( Sn > now + T ) {
		console.log('REJECT', S - now);
		return false; // throttled
	}
	S = Sn;

	console.log('ALLOW', S - now);
	return true; // ok
}


console.log('Allow '+n+' requests per '+T+'s');
var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
rl.setPrompt('hit>');
rl.prompt();
rl.on('line', function() {
	// each enter is request
	throttle();
	rl.prompt();
});

When you run the example, hit enter to simulate request. It tells if you if it was accepted or rejected with the current score delta.

~/work $ node throttle.js
Allow 3 requests per 60s
hit>
ALLOW 20
hit>
ALLOW 40
hit>
ALLOW 60
hit>
REJECT 59
hit>
REJECT 55
hit>
REJECT 50
hit>
REJECT 45
hit>
ALLOW 59
hit>
REJECT 58
hit>

Adjust n and T to your liking. This implementation is also easy to change to return number of seconds client has to wait until trying again.

See also Simple popularity algorithm with decay how to apply this method for popularity scoring.

Simple Slot machine game using HTML5 Part 4: Offline mode

This is the fourth part of the Slot machine game in HTML5 (previous parts 1, 2, and 3) and this time we modify the game to support HTML5 offline mode, also known as HTML5 Application Cache.

Slots Offline

Try out offline supported version here.

Word of warning. HTML5 offline mode is powerful but very fragile feature. It’s tricky to get right, but once you get it to work the mobile user experience can be very native app like.

Some problems you will encounter

  • Browser refresh logic is confusing. Especially the fact that browser does not use updated manifest and resources when they change but only after next reload. Fortunately Javascript workarounds exists.
  • Application cache file maintenance needs diligence. For example, browser will not reload any assets if this file is not modified.
  • Externally linked resources do not generally work offline. This makes CDN use difficult.
  • Web server has to use right MIME type and cache settings to reliably use application cache files. Most web servers don’t do this in default configuration.
  • No reliable way to detect if page was loaded in online or offline mode.
  • Chrome bypasses some restrictions (e.g. cross-domain issues) in the specification and what works in Chrome may not work anywhere else.
  • Each browser has slightly different meaning and heuristic for using offline mode. For example if browser can load some unrelated pages but can’t currently load your app page it may not show your page in offline mode and simply shows “Can not reach the server error”. This may happen especially if it knows from last load that manifest has been updated. Then at other times, it may load page few times in offline mode even when connectivity has returned.

Manifest file

Web page must use application cache manifest file to support offline mode. This manifest file is specified in html tag of the page.

<!DOCTYPE html>
<html manifest="slots.appcache">
<head>
 ...

The file has listing of all content that page needs. For detailed explanation of each section, refer to Beginners guide to HTML5 application cache

CACHE MANIFEST
# version 10

NETWORK:
# here goes resources that must be never cached
js/online.json

CACHE:
audio/nowin.mp3
audio/nowin.ogg
audio/roll.mp3
audio/roll.ogg
audio/slot.mp3
audio/slot.ogg
audio/win.mp3
audio/win.ogg
css/webfonts.css
css/reset.css
css/slot.css
css/Slackey.ttf
img/build-64.png
img/cash-64.png
img/energy-64.png
img/gold-64.png
img/goods-64.png
img/loading.gif
img/staff-64.png
js/slot.js
js/jquery.min.js
index.html

Web Fonts

Web fonts must be hosted locally if you want to use them offline. Note that some web fonts may have licensing restrictions for local hosting.

<link type='text/css' rel='stylesheet' href="css/webfonts.css"/>

The webfont.css defines the font face and loads true type file.

@font-face {
  font-family: 'Slackey';
  font-style: normal;
  font-weight: 400;
  src: local('Slackey'), url(Slackey.ttf) format('truetype');
}

The file Slackey.ttf is hosted locally in css directory.

Web Server Support

Web server must use correct MIME type for text/cache-manifest application cache manifest. For example, in NGINX web server edit the mime.types and add following file type to MIME type mapping.

text/cache-manifest		appcache;

Browsers should check manifest every time page is loaded online, but it may not do this often enough if cache control is too long. Therefore, set short cache lifetime for the manifest files by adding this inside server section of NGINX configuration file. This forces cache lifetime of 1 minute to all *.appcache files.

# set 1 minute cache life for HTML5 offline manifests
location ~* \.(appcache)$ {
   expires 1m;
}

Verify with cURL that server response Content-Type has right MIME type and that the Expires and/or Cache-Control have correct 1 minute cache life time. If you get 404 error, make sure that site root configuration is set in http section.

$ curl -I http://localhost:8081/slots.appcache
HTTP/1.1 200 OK
Server: nginx/1.4.0
Date: Sun, 05 May 2013 04:43:32 GMT
Content-Type: text/cache-manifest
Content-Length: 38
Last-Modified: Sun, 05 May 2013 04:25:28 GMT
Connection: keep-alive
ETag: "5185df38-26"
Expires: Sun, 05 May 2013 04:44:32 GMT
Cache-Control: max-age=60
Accept-Ranges: bytes

Detecting online status

Currently only “reliable” method to do is to make Ajax request and check the response. There are some caveats

  • Request may fail for other reasons, and this does not mean browser is in offline mode
  • Offline status may change while user is in page, you may want to do repeat polling check.
  • User may be in public WiFi that redirects requests to login server. This can confuse your app that gets response but is not what was expected.

Slots game checks online status in parallel while game loads and only on startup. Slots game does not really need to know if it’s online or offline, but just writes the status on screen for debugging purposes.

<script type="text/javascript">$(function () {

    var game = SlotGame();

    // Attempt loading static json file from server to detect online or offline mode.
    // The url has unique random parameter to avoid browser or proxy caches
    $.ajax({
        url: 'js/online.json?ts=' + (~~new Date()),
        dataType: 'json',
        success: function(data) { 
            if ( data.online ) { 
                game.setOnlineStatus(true);
            } else {
                // might be online, but we didn't get expected response. Could be
                // e.g. Wifi login page.
                game.setOnlineStatus(false);
            }
        },
        error: function() {
            game.setOnlineStatus(false);
        }
    });

});</script>

Otherwise loading images, audio and other content should be fully transparent to your app. Think twice before doing separate logic for online and offline as things will get difficult. Best advice I can give is to that you write code for online use with proper handling for Ajax errors. In this way when app loads in online mode but loses network later in session (e.g. when user goes in subway tunnel), the experience does not break completely.

Testing

This is the part where things get interesting, offline is tricky to test because of caching and browser reload logic. See detailed lamentation about subject here in Dive into HTML5.

These are the best practices I’ve come up with. First, set browser manually to offline mode to try things out. e.g. in Firefox this is enabled from File->Work Offline.

Firefox offline

Second, if you develop the game from local server, do not use http://localhost as host, but use real domain name that resolves to localhost. In this example I’ve used http://hexxie.com that supports wildcard subdomain. Any subdomain resolves to address 127.0.0.1.

$ nslookup anything-goes-here.hexxie.com
Server:		192.168.1.254
Address:	192.168.1.254#53

Non-authoritative answer:
Name:	anything-goes-here.hexxie.com
Address: 127.0.0.1

In this way you can always start from scratch the offline debugging simply by changing subdomain name. For example I just used slotsoff1.hexxie.com, slotsoff2.hexxie.com, … etc.:

Trick url

Note that at least Firefox asks each time if you allow offline content.
Accept offline

Before each deploy, remember to increment the version comment in manifest file, so web server notices that the file has changed and browser will refresh it on next load. Server does not look inside the manifest file, so it does not matter how you change the file, as long as it’s changed.

Good Luck!

Code is available in Github.

Simple Slot machine game using HTML5 Part 3: Loading

UPDATE: See also Simple Slot machine game using HTML5 Part 4: Offline mode

In previous part 1 and part 2 I implemented simple slots machine purely in HTML5 with audio support.
After adding audio, the initial loading time increased a lot up to several seconds. This slowdown is easy to miss during development, as developer has always primed browser cache and content is loaded from development server that is hosted locally or even in developers machine.
It’s very important to occasionally clear the browser cache and put the game to remote server and reload the game to get realistic estimation of new users loading time (i.e. the empty cache experience).

This part improves the landing experience by adding loading progress bar so users know that the game is loading and will start soon.

Loading screen

Try out version with loading bar here.

How it works

Loading bar is simple nested div, where parent draws the outline and the child is the filling progress bar.

<div id="progressbar">
   <div id="progress"></div>
</div>

The CSS rules set the colors and position.

#progressbar
{
    margin-top: 10px;
    background: black;
    border: 1px solid mediumaquamarine;
    width: 80%;
    height: 15px;
    margin-left: auto;
    margin-right: auto;
}
#progressbar #progress
{
    height: 100%;
    position: relative;
    width: 0%;
    left: 0;
    background: #77e0fb;
}

Indicating progress is now simply matter of updating the width of #progress div.

Any HTML5 game loading bar implementation should be done purely with HTML and CSS and not use any images, as this just makes total loading experience slower and loading bar does not appear immediately when page renders. If you must use images, consider using base64 embedded images.

#progressbar {
  background-image:url(data:image/png;base64,iAE28w0KGgoABBANSAxhEegBB54DIA...);
  ...
}

If possible, void building the loading view with javascript as your page must load javascript before being able to show anything.
For best experience you should always have the CSS file in the <head> section of the HTML file and the javascript at the end of file, just before closing </body>. This way page can render with proper layout before any javascript has been loaded.

<!DOCTYPE HTML>
<html>
<head>
    ...
    <link href='http://fonts.googleapis.com/css?family=Slackey' rel='stylesheet' type='text/css'/>
    <link type="text/css" rel="stylesheet" href="css/reset.css"/>
    <link type="text/css" rel="stylesheet" href="css/slot.css"/>

</head>
<body>
<div id="viewport">
    ...
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="js/slot.js"></script>
<script type="text/javascript">$(function () { SlotGame(); });</script>
</body>
</html>

Both image and audio assets are loaded with same preloader, that keeps track of the progress and updates the progressbar. When loading is completed it hides the loading view. See the code for details how the preloader is used to load images and web/html5 audio files.

var progressCount = 0; // current progress count
var progressTotalCount = 0; // total count

function updateProgress( inc ) {
    progressCount += (inc || 1);
    if ( progressCount >= progressTotalCount ) {
        // done, complete progress bar and hide loading screen
        $('#progress').css('width', '100%');
        $('#loading').slideUp(600);
    } else {
        // Update progress bar
        $('#progress').css('width', parseInt( 100 * progressCount / progressTotalCount)  + '%');
    }
}

// Generic preloader handler, it calls preloadFunction for each item and
// passes function to it that it must call when done.
function preloader( items, preloadFunction, callback ) {

    var itemc = items.length;
    var loadc = 0;

    // called by preloadFunction to notify result
    function _check( err, id ) {
        updateProgress(1);
        if ( err ) {
            alert('Failed to load ' + id + ': ' + err);
        }
        loadc++;
        if ( itemc == loadc ) callback();
    }

    progressTotalCount += items.length;

    // queue each item for fetching
    items.forEach(function(item) {
        preloadFunction( item, _check);
    });
}

Improving loading time

You can further improve initial loading time by conventional web tricks

  • Enable HTTP gzip compression in the web server
  • Include jquery and library scripts from google api url, user may have those already in the cache
  • Merge and minify the javascript e.g. with uglify-js
  • Merge and minify the CSS with CSS compressor
  • Minify PNG image files with pngout. Use JPG where possible.
  • Compose all images in single montage and use that with CSS sprites and canvas ctx.drawImage
  • Convert audio files to mono to save half of the size. Use e.g. Audacity or ffmpeg
  • Host asset files in CDN, such as Akamai or Amazon S3. Note that if assets are loaded from different domain than the HTML document, you may encounter security problems if Cross-origin policy is not properly set for the content.

Use Chrome browsers audit tool get idea what takes most time.

Code is available in Github.

Continue to Simple Slot machine game using HTML5 Part 4: Offline mode