marcus welz

The state of web-based Games

Posted on August 14, 2010
This entry is part 1 of 7 in the series HTML5 and Erlang Game Development

Thus far, gaming on the web has been a mixed experience. There are flash games, such as you would find on kongregate.com, and there are simple games written in HTML and whatever is generating said HTML — often PHP, Python, or Ruby. You know, those "click here to do some action", and then refresh the page. Or wait until the timer counts down to 0, which might take a few minutes or hours, at which point you can issue another command. I'm talking about slower paced text web games that don't really have any real-time action elements. Appainter, for example, let's you build such a game. They have videos demonstrating how it works (and it's really neat). But I'm talking about traditional action games. Things you'd see on a Super Nintendo.

Envision, something along the lines of Zelda, or Super Mario World. Now add MMO. Alright, so that's what I'm talking about. Responsive real-time action games. Until fairly recently, this is something that really hasn't been all that easy to implement in HTML, Javascript, and CSS. This has finally changed with HTML 5.

There's been talk about HTML 5 for a while. Some of the technology features contained within, along with CSS 3, are able to replace proprietary solutions such as Flash. The combination of HTML5 and CSS3 has even been referred to as "Web 3.5". Perhaps a bit too cute, but it works. And there's already been some folks out there busy with implementing some interesting prototypes that resemble more traditional gaming. In fact, right now, with HTML 5 and CSS 3, it's possible to implement games that have a similar feature set as nearly anything seen on a Super Nintendo. Canvas is one of the features making this possible. The next step after that is likely to be WebGL, which will provide us with N64-like capabilities on the web.

There are multiple instances of individuals working on their engines, and showing them off on YouTube:

Demo of Akihabara framework by Owen Rubel:

This seems to be an MMORPG engine written in PHP and HTML5:

There are a few other individuals dabbling away at these engines. But from what I can tell, their technology choice bothers me.

In some cases, these are purely client-side libraries, which means they're most likely single player game engines. And while it's still impressive that these can run in a browser, in just HTML and JavaScript, it doesn't strike me as quite the right approach.

In other cases, the server component is written in PHP, which in my opinion is one of the worst choices for a server-side language that's supposed to maintain and synchronize state for clients. Also, this is not a knock against PHP for the sake of it being PHP. I'm quite fond of the language, and have used it for years almost exclusively. I'm a huge fan of developing web applications in PHP and Zend Framework. The language just doesn't have the right architecture in order to support an MMO server.

  • For one, you'll want a language that's performant. PHP isn't exactly fast. It shines because it's got a rich function vocabulary and interfaces with a million libraries instead.
  • PHP isn't stateful. There's a teardown after every request. Maintaining state would require some sort of outside persistence mechanism. It could be memcache, it could be files, etc. It still requires reloading data and writing it all the time. And although one could implement a daemon IN PHP itself, accepting clients over sockets and what not…well, no, just no. Don't. Neither fast nor stable. Yes it works, but barely, and it's just not meant to do that sort of thing.
  • There's no support for concurrency. There's no multi-threading. Although PHP can fork, and can use IPC, it's neither pretty, fast, nor stable. In fact, a huge feature promoted by Rasmus Lerdorf himself is the share-nothing architecture PHP uses. And that's okay (and works great, architecturally), just not when we need something long-running and stateful. Something about the right tool for the right job and all that.

And I think this is where there's room for a new type of web-based client/server MMO engine.

Print This Post Print This Post

Caching files statically with Zend Framework

Posted on August 12, 2010

I've been using ZF (almost exclusively) since version 0.10 or so in 2006. It's come a long way since then, and the folks involved with it are very skilled and methodical. It's quite fun to see new versions roll out and see the various proposed components on the wiki come to life over time.

I do find the documentation to be a bit lacking at times, however. For instance, I was messing around with the Zend_Cache_Manager last night, and discovered templates for the "page" and "pagetag" caches, which led me to the Cache action helper. It seems that this component is  completely undocumented. I found the proposal on the wiki, though, and after I read through the code I played around with it for a new project. And I have to say, it's rather neat.

So, it provides a caching mechanism using Zend_Cache_Backend_Static, which is a cache that will write out static files that can be served by the web server directly, without invoking PHP at all. And the cache action helper lets you invalidate the generated pages easily as well. Let's say you have a forum, and you're caching each thread statically, then when someone adds a reply, you'd bust the cache.

First, you'll want to tell the cache manager about where to store the cached pages. It defaults to "public/" but that's where I put hand-coded pages, and I don't want to just throw automatically generated pages in there as well. So I added the following to my application.ini:

resources.cacheManager.page.backend.options.public_dir = APPLICATION_PATH "/../public/_cached"

And then I created that directory. And made it world-writable.

Next, the logic added to the ThreadController, where I want to control what's getting cached:

<?php

/**
 * Forum thread controller
 *
 * Handles viewing threads
 */
class ThreadController extends Zend_Controller_Action
{
    public function init()
    {
    	// the view action is cachable.
        $this->_helper->cache(array('view'));
    }

    /**
     * View a forum thread
     *
     * URL: forums.example.com/thread/<id>
     */
    public function viewAction()
    {
    	// get thread id from URL
        $threadId = $this->_getParam('id', 0);

        // Pull forum posts for this thread from the service tier
        $service = new App_Service_Forums();
        $posts = $service->findPostsByThreadId($threadId);

        // Feed the view
        $this->view->posts = $polls;
    }

    /**
     * Reply to a forum thread
     *
     * URL: forums.example.com/thread/reply/<id>
     */
    public function replyAction()
    {
    	// get thread id from URL
    	$threadId = $this->_getParam('id', 0);

    	// we want the request
        $request = $this->getRequest();

        // The form that users will compose the forum reply in
        $form = new App_Form_Forum_Reply();

        if ($request->isPost() and $form->isValid($request->getPost())) {

        	// Form was submitted, so process it

        	// Post a reply to the thread via the service tier
	        $service = new App_Service_Forums();
	        $reply = $service->replyToThread($threadId, $form->getValues());

	        // also clear the cache for the URL
	        $this->_helper->cache->removePage('/thread/' . $threadId, true);
	        $this->_redirect('/thread/' . $threadId);
        }

        $this->view->form = $form;
    }

}

Then, there are also a few pitfalls. For one, you have to turn off the front controller's output buffering, otherwise you end up with empty cache files. If you're using an .ini file to drive application configuration, you'll want to add

resources.frontController.params.disableOutputBuffering = true

And second, you need to tweak your web server to try to serve those cached files first. So my .htaccess file looks like this

RewriteEngine On

# Serve cached pages if they exist
RewriteRule ^/(.*)/$ /$1 [QSA]
RewriteRule ^$ _cached/index.html [QSA]
RewriteRule ^([^.]+)/$ _cached/$1.html [QSA]
RewriteRule ^([^.]+)$ _cached/$1.html [QSA]

# Hit files, symlinks and directories directly, if they exist.
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]

# Everything else hits the application.php
RewriteRule ^.*$ /application.php [NC,L]

And I think I just discovered a bug in the Zend_Cache_Backend_Static::removeRecursively() method, which doesn't remove the directory properly.

Still trying to find a way to scale this beyond a single server, since there's no distributed cache backend that supports tagging. Would have to use file based caching with an NFS share or something, and that doesn't seem all that optimal.

Print This Post Print This Post