Four tips for porting your game to the web with Native Client

SHARE THIS ARTICLE

When people hear ‘web gaming,’ they usually get the image of a 2D game with bad artwork and simplistic gameplay. With the power of Chrome and Native Client, however, you can bring console-quality games to the browser.

A screenshot of Ubisoft’s From Dust running in Chrome on Native Client. This game, originally an XBOX 360 title, looks beautiful on the web.

Since the official launch of Native Client in December of 2011, developers have shipped over two dozen games using this technology, and several popular games middleware products now support Native Client.

My role as a Developer Advocate at Google has allowed me to work directly with these early developers to assist with their porting efforts, and to collect feedback to help improve Native Client technology as well as Google’s larger gaming platform efforts.

This article details what I have learned through the course of this work, so that you can get the benefit of my knowledge, in bite-sized form, to fast-track your porting effort.

What is Native Client?

Simply put, Native Client is a technology that allows you to run C++ code in a web page, as safely as JavaScript, without a plugin.

This is a sweet spot for game developers, who pride themselves on optimizing for current trends in mobile, console, and desktop development, but who also like to make their games available to the most users possible.

Running C++ in a web page opens the door to reaching a slew of new users on the Internet; and game developers and publishers are aggressively embracing this strategy.

With this motivation in mind, here are my tips for how to port your game to the web.

1. Learn the basics: Understand how the web, Chrome, Pepper, and Native Client work

This article assumes that you’re a traditional game developer, coming from the desktop/mobile/console space, and that web development is new and scary to you.

Chrome, being a modern web browser, works in a different way than most browsers, and it’s important to have a background understanding of how Chrome works – this is useful both at a ‘how does the magic happen’ level, as well as a ‘this is where Native Client is processed’ level.

The Pepper Plug-in API (PPAPI), called Pepper for convenience, is an open-source, cross-platform API for browser plugins.

From the point of view of Native Client, Pepper is a set of APIs that allow a C or C++ Native Client module to communicate with the hosting browser and get access to system-level functions in a safe and portable way.

One of the security constraints in Native Client is that modules cannot make any OS-level calls. Pepper provides analogous APIs that modules can target instead. For a short introduction to Pepper, watch this section of my recent talk at Google I/O.

Finally, it’s helpful to understand why Google created Native Client, from the perspective of both performance and security.

2. Understand how porting will affect the structure of your game

As I describe in Tip # 3 below, I recommend a two-step porting process: Port your game to Pepper, and then to Native Client.

Before diving into the mechanics of these steps, it’s important to understand some basic considerations that will affect how your game is structured.

Porting to Pepper

You should consider porting to Pepper much like a port to any other platform. You’ll need to change your platform-specific APIs, maybe modify some assets, configure builds, and tune performance for the target platform. Here are some big hurdles to look out for.

There are three primary things you need to understand about the Pepper APIs :

Pepper APIs must be called from the main thread

Pepper APIs are all asynchronous

Your main thread cannot spin-loop

In combination, these three issues can create difficult problems for many developers.

For instance, developers who expect fread() to be blocking, and have written their code base expecting such, will be disturbed to find that the Pepper API for file I/O is non-blocking.

This can be a difficult problem to deal with by itself, but when you combine it with the fact that all Pepper calls must be on the main thread (meaning all your file I/O must now come from the main thread), things get pretty tricky.

One other important consideration is that any call on the main thread of your Native Client application has to be non-blocking; this means that you can’t place your primary logic loop on that thread or it will halt the browser.

To deal with this problem, some developers immediately start a worker thread when their Native Client module starts, and run their primary logic loop on that worker thread, adding proper inter-thread communication to allow them to issue commands to the main thread (sometimes called the ‘Pepper thread’).

This setup enables you to get around some of the nuances of logic control, and as a side benefit also allows you to overload fread() to be a blocking command again.

Be warned however, that the OpenGLES2 APIs must still come from the main thread, so if you do spin up a worker thread, sooner or later you’ll need to relinquish control to the Pepper thread for GL do to rendering.

Before you begin porting to Pepper, it’s crucial to take stock of the APIs that are available in Pepper, so that you can chart what APIs you’re using and how they can be ported.

For example, Pepper does not support raw TCP/IP socket access; instead it supports WebSockets, which provides the same functionality though with a different API structure.

Porting to Native Client

Native Client comes with its own set of restrictions, the primary one being that you need to use a custom GCC compiler to generate the final static libs, dynamic libs, and executable binaries.

This means that if your application uses 3rd-party libraries (middleware, for instance), you will need to compile the libraries with the same GCC compiler in the Native Client SDK.

Fret not, however: You can search through a list of closed source and open source projects that have already been ported to Native Client to see if your software is already there. If not, feel free to contact us, or the middleware provider, and demand Native Client support!

You should also be aware that Native Client has restrictions on some APIs that are considered harmful / insecure (for instance, some inline assembly functions).

Running on the web

Another important consideration that will impact your application is the modifications you will need to make to distribute its assets on the web. Ask yourself, “Where are all my assets going to be served from?”

Native Client applications must currently be distributed through the Chrome Web Store (CWS), and must be one of two CWS application types: hosted or packaged.

Hosted applications are effectively bookmarks: Your game’s web pages and Native Client modules are stored on a web server that you manage, and the user visits your web site to access that content.

Packaged applications work more like a digital distribution service: Your game’s assets, web pages and Native Client modules are packaged, compressed, and stored in the Chrome Web Store, and installed on the user’s machine when the user clicks the ‘install’ button.

There are pros and cons to both hosted and packaged applications. For example, with hosted applications, you are distributing large volumes of gaming content to users who may or may not be paying you; as such, it may be beneficial for you to invest in bandwidth capacity and asset segmentation. (If you plan to distribute your game as a hosted application, you should take a good, long look at Google App Engine.)

Packaged applications have a 100MB asset limit; if your game is over that size, you’ll need to stream in data from a web server while the user is running the game.

Whether you distribute your game as a hosted application or a packaged application, you should have a clear understanding about how your game is going to load asset files from the web.

For instance, if you are going to distribute your game as a hosted application, you can reduce the amount of data transferred over the web by fetching game assets using the Pepper URLLoader API, and caching the assets locally using the Pepper FileIO API.

This setup is crucial for performance when the user replays your game, so plan for some level of caching and streaming.

There are a number of additional considerations that you should take into account when your game runs on the web, including user authentication, localisation, and monetisation. For a rundown of some of these issues, see my GDC 2012 talk.

3. Port to Pepper first, Native Client second

Pepper Plugin First

A technical look at how Chrome works reveals that Chrome allows you to build external .DLLs (plugins) that can access the Pepper APIs directly and render in the web page with ease. This is great, because the largest part of your porting effort will be porting to the Pepper APIs.

Use your existing IDE to help you do that work (see a demo at Google I/O). While the Native Client toolchains are powerful, at this time they are not as polished and complete as a solid development platform, so put the power of your IDE to work for you.

As announced at the Google I/O 2012 talk, a Visual Studio 2010 add-in will be available soon to aid development on Windows.

This add-in will allow you to create platform configurations for both Pepper and Native Client, so that you can use your natural workflow as you port your game.

Developing your game as a Pepper plugin grants you some nice abilities in addition to being able to debug and use breakpoints properly; for instance, you can mix Pepper API calls and platform-specific API calls in the same codebase.

Native Client Second

Once you’ve ported your game to Pepper, the final step is to modify platform calls in the code for Native Client.

This may involve a number of tasks depending on how the code is set up. For instance, if the code uses threading, you’ll need to replace platform-specific threading primitives with POSIX threading primitives. (Depending on your platform, POSIX headers may not be available outside of the Native Client SDK, which means you wouldn’t have been able to port to POSIX primitives earlier).

You’ll also need to remove any last traces of platform-specific code in your game.

4. The web is what you make of it

Don’t be disillusioned about the economic nature of the web: It’s a booming world of monetisation, driving some of the most profitable companies around, but also burying many creative voices in the noise.

Success on the web doesn’t happen overnight, and it doesn’t happen for free, especially with games. Successful web games (regardless of technology) really take advantage of the Internet as a platform.

They aggressively work to break out of the ‘leaderboard’ model for content distribution that currently hinders most mobile and console distribution silos, striving to reach everyone, everywhere. They prosper by connecting people and by finding monetization models that work within a fickle and often skeptical ecosystem.

This is the real challenge you have in moving your C++ game to the web – once you figure out success on this front, porting to Native Client is the easy part.