Owner & Technical Director of Fishing Cactus. Love making games, hacking some electronics and playing the guitar
Posts by Julien Hamaide
  1. Cross Platform Thread Local Storage (cont'd) ( Counting comments... )
  2. Cross Platform Thread Local Storage ( Counting comments... )
  3. Team management : Use your authorities ( Counting comments... )
  4. My browser draws better than I do ( Counting comments... )
  5. On sprint reviews ( Counting comments... )
  6. Defining a Coding Standard ( Counting comments... )
  7. Particle system memory patterns ( Counting comments... )
  8. Collective Ownership ( Counting comments... )
  9. RValue reference and move semantic for the poor ( Counting comments... )
  10. Aliasing, the silent killer ( Counting comments... )

Charts offer a view of variable evolution over time. For example, frame rate can be displayed and easily analyzed using a chart. The solution I propose uses a little bit of javascript and an embedded web server : mongoose ( thanks Mike! ).

The server

Mongoose is lightweight webserver. It supports lot of platforms, mobile phone being included. Its setup is really easy as described in the following code :

#include <mongoose.h>
 
void * mongoose_callback(
    enum mg_event event,
    const void *user_data,
    struct mg_connection *conn,
    const struct mg_request_info *request_info
    );
 
...
 
mg_context * Context = mg_start( mongoose_callback, 0, 0 );

That's it, the game is now running the webserver on the port 8080. Everything is customizable, but default values should be OK.

The callback allows the game to handle some request itself. The request info contains the path pointed by the browser and parameters if any. Let's start with an empty callback :

void * mongoose_callback(
    enum mg_event event,
    const void *user_data,
    struct mg_connection *conn,
    const struct mg_request_info *request_info
    )
{
    return 0; // Nothing done here, proceed with default behavior
}

While the game is running, browse to http://your_game_machine_ip:8080 : it lists the startup directory of your application or index.html if available. Create a simple index.html file and reload the page. Your server has served its first html page!!

The chart

For chart rendering, I use flot ( a jQuery plugin ). It only needs a support div and some data. Its interface is really easy to use. The following webpage shows a example of a flot plot.

<html>
    <head>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="jquery.flot.js"></script>
        <script type="text/javascript">
            $(document).ready(
            function()
            {
                $.plot(
                    $('#plot_support'),
                    [ { label: "Example", data: [ [ 0, 1 ], [ 1, 3 ], [ 2, 2 ] ] },
                    { label: "Example 2", data: [ [ 0, 3 ], [ 1, 2 ], [ 2, 3 ] ] } ]
                    );
            }
            );
        </script>
    </head>
    <body>
        <div id="plot_support" style="width:600px;height:500px"></div>
    </body
</html>

$ represents the jQuery interface. $('#plot_support') returns the corresponding div. Finally, two curves are passed to the function. Each curve is composed of a label and a data set. The result can be seen here.

To create dynamic chart, the system must be updated once in a while. Javascript provides timer functions. setInterval executes the code given as first argument when the millisecond count given as second argument has elapsed. The following code creates a timer that calls update() every second. Easy, no?

var curve = [ { label: "Example", data: [ [ 0, 1 ], [ 1, 3 ], [ 2, 2 ] ] } ];
var x_index = 3;
 
function update()
{
    curve[ 0 ].data.push( [ x_index, 4 * Math.random() ] ); // append a new point to the table
    x_index += 1;
 
    $.plot( $('#plot_support'), curve );
}
 
$(document).ready(
    function()
    {
        setInterval( 'update()', 1000 ); // call update every second
    }
);

The update function adds the new point to the data set and redraw the chart. Result can be seen here.

Querying the frame rate

Now that the graph is updating, it's time to query data from the game. The mongoose callback is adapted to answer a specific uri :

void * mongoose_callback(
    enum mg_event event,
    const void *user_data,
    struct mg_connection *conn,
    const struct mg_request_info *request_info
    )
{
    if( event == MG_NEW_REQUEST && !strcmp( request_info->uri, "/frame_rate" ) //Only handle request of type http://127.0.0.1:8080/frame_rate
    {
        mg_printf( conn,
            "HTTP/1.0 200 OK\r\n"
            "Content-Type: text/json \r\n\r\n" // Two lines to end the header
            "{ \"frame_rate\" : \"%f\" }",
            GetFrameRate()
            );
 
        return 1; // We're done, tell mongoose
    }
 
    return 0; // Let mongoose do its default handling
}

On the other, the data is recovered using jQuery get method. It creates a asynchronous request, and calls the given function as soon as the data is available.

function update()
{
    $.get( "/frame_rate", function( data )
    {
        var json_object = eval('(' + data + ')');
        curve[ 0 ].data.push( [ x_index, json_object.frame_rate ] ); // append a new point to the table
        x_index += 1;
 
        $.plot( $('#plot_support'), curve );
    }
}

$.get query the /frame_rate page and call the function with the result. The json text is converted to a javascript table. eval is not the safest way the convert a JSON text to an object, but this isn't a website.

To keep the chart readable, the data set must "forget" old values. The easier way is to keep the last items of the table. In this case, we keep 30 seconds of data, thus 30 samples.

    var data_to_trim = curve[ 0 ].data
 
    if( data_to_trim.length > 30 ) // only keep 30 seconds
    {
        curve[ 0 ].data = data_to_trim.slice( data_to_trim.length - 30, data_to_trim.length );
    }

With few C/C++ code and some javascript, your browser become your best friend for viewing information about your game. With this technique, there's no need to develop any tools that connect to your game. At Fishing Cactus, we use the webserver to display tons of information, but not only. We edit configuration parameters, output textures used by the current frame. We even have a quake like console. There is no limit to this technology, and it's so simple to use. So why bother writing C/C++ visualization and interaction tools anymore?