Posts Tagged ‘data’

Anatomy of a Web App

Tuesday, October 4th, 2011

Hello! Time to dissect the application I’ve mentioned several times already: Cruz.

Long before starting Cruz, I had built many, many software applications. Some used sophisticated requirements, design, and configuration management techniques. Others, not so much. This one falls into the “not so much” camp.

But Cruz, like many other applications I have built, has very sophisticated programming. With me, that’s just the way I do things. I take pride in my programming skills, and I will go the extra mile to develop to a high level the engine of any game I create.

Cruz is a Flash game. Many Flash games are rich in graphical and sound content, but they lack programming sophistication. Inability to program forces many designers to act at the mercy of whatever tools or engines others have built, which further limits the types of games they can create. Could a graphical designer who knows little about programming create something like Cruz? Well, read on, and see for yourself.

System Components

There are six main components of the Flash application:

  1. Flash file (Cruz.swf)
  2. Graphical content (embedded in Flash file in this case)
  3. Audio content (MP3s loaded at run time)
  4. Level design data (XML files in “Cruz” format)
  5. Server-side scripts and storage (PHP and text files)
  6. Client-side savegame, progress, and config info (Shared objects)

The Flash file is loaded first, usually as an embedded object in a web browser, with graphical content loaded automatically. The audio content is loaded in two contexts: the sound effects at the beginning (a preloader precedes the title screen), and the musical tracks on an as-needed basis.

The level design data is loaded on an as-needed basis per episode, which is only when the user starts a game for a particular episode. The server-side scripts are used when handling high score update and retrieval operations. The savegame, progress, and config info are periodically updated and saved by the Flash environment on the client side.

If you’re building a Flash application, there’s a good chance you won’t need to touch the XML API, or write server-side scripts, or use shared objects. You might not even need a preloader, choosing to embed everything in the SWF. This keeps everything simple, but it limits what you can do, programming-wise.

If you’re building a game designed to give the user a full experience of rich content, savegames, and high scores with competition from everyone in the world, the “web application” way to do it is substantially different from the “home PC” way to do it or the “game system” way to do it. Different APIs, different components, different implementations of components.

But the underlying programming effectively does the same stuff, no matter what the platform is. You have load operations, save operations, ins, outs, etc. Business logic is business logic in any language.

Classes and Programming

What about the programming for Cruz? Well, as managed applications go, there is little to the imagination. Let’s look at what Cruz implements for unique classes:

  • utils: Low-level utility functions, such as sign, absolute value, random chance, etc.
  • IPoint: Coordinate pair of integers (x and y).
  • Dir8: Eight-directional calculation functions and lookups; useful for 2-D gridded games.
  • FxCont: Field effect container class (a rectangular region with specific game properties).
  • objid: Enumerated constant and name lookup container for game object types.
  • fxid: Field effect and level feature lookups.
  • SF: Special flags-per-grid-cell storage and lookup.
  • SaveGame: Represents savegame data.
  • CruzOptions: Represents configurable options for the game.
  • Game_Characters: Storage and functionality for ASCII-noir characters used in background text.
  • RandInfoArray: Grid randomization functions.
  • CruzMedal: Storage and functionality for medals (unlockable achievements found in various parts of the game).
  • Sounds: Storage and functionality for sound effects and music.
  • PathCalc: Mouse click-to-move path calculation functions.
  • MsgParser: Special object message parsing class; used with tablets and special items.
  • GPlayer: The main character’s object (the player).
  • iobj: Individual game objects, capable of interaction. A single GPlayer individual is always present as the first individual.
  • rtobj: Real-time object, representing a short-lived, animated object in the game that does not interact with anything.
  • cruzparse: XML parsing functions for translating level design content into game data.
  • game: The main gameplay class. This covers most general gameplay business logic and functions.
  • cruz: The main interfacial class, controlling Flash-specific API operations and API callbacks, such as user input, timer, load complete, etc.

If I were writing in C# for a PC game, the class layout would look the same. If I were writing in C++ for a game console, the class layout would look the same.

With three exceptions, that is: enumerated constants, polymorphism, and server-side handling.

Enumerated constant definitions will look a bit different across languages. The differences across C++ and Java alone are pretty substantial. Enumerated constants are not directly supported in ActionScript, not even in AS3. If you want to make a constant, you must declare it as a named static constant in a class. I ended up doing this for fxid, objid, and SF classes, to name a few.

ActionScript does not support virtual functions. Even if you derive from a base class, you must cascade functions manually at every level of function implementation. This is annoying. For this reason, some objects, like iobj, were not broken into individual subclasses.

Server-side scripts are necessary for Flash applications that want to maintain a central data repository; there is no dedicated client-side API for handling server-side data. The PHP scripts are very simple. All that the scripts do is take HTTP requests from the Flash application and fire back resources at the application. Just a little text file cooking, that’s it.

Special considerations

As far as the class implementation is concerned…well, it’s here where software engineers earn their pay. I have been programming for well over a decade. It takes skill to know how to properly use arrays, vectors, integers, floating point numbers, and strings. A surgeon can show off the anatomy of a rat, but he can’t tell the lay observer how to sew the rat up so that he instantly knows how to make it live and breathe again.

So I won’t go into the detailed design and cover every function in Cruz. Not today, at least. But I will discuss a very important aspect of programming for web applications: respecting the fact that what you are creating is a web application.

Decades ago, we never would have dreamed that so many of these web applications would conduct advanced calculations in the background. That’s what a client-side web application usually is: a background application. It can be an advertisement, or a game, or a slideshow, or a fundamental user interface needed to enter data into a form. Regardless of what the application is used for, it should be expected that the user’s computer will be doing other things than paying every bit of attention to running your web application.

This means that slack needs to be built into timing routines that might have demanded precision otherwise. Synchronization of animation frames and timer ticks is not guaranteed. CPU performance could be drained by a computer function not even remotely related to the web browser or its applications. The user might destroy your application’s operating environment at any time by closing the window, forcing you to update shared objects or call server-side scripts at key moments to avoid data loss.

Oh, and one final hiccup: the gargantuan task of making Cruz, a cutting-edge netted AS3 application, resemble the gameplay and timing mechanisms of Kroz, a non-netted Pascal application, written over 20 years earlier without any event-driven programming.

Now you know what went into Cruz. Web applications aren’t necessarily easier or harder to program than general-purpose applications–they’re just different.

But in some ways, they’re the same as ever.

From Data to Code and Back Again

Tuesday, September 8th, 2009

I have had the file INITDATA.BAR for a long time for internal use. Only today did I decide to release it to the public.

How many times has a software developer wanted to get data incorporated into storage in a local executable…only to discover that you have to use the native data-initialization syntax, very much unlike the format in which the data is stored?

You can be very patient and hand-enter the data (you’d never do it by hand unless there is only a small amount of it).

Take this example. I want to enter the following bytes as static constant data, to be used in the body of a program later: 40H 7FH 23H A8H. In C-based languages, you’d initialize an array like this:

static const char mydata[] = { 0x40, 0x7F, 0x23, 0xA8 };

But chances are, the data isn’t going to be written out as “0×40, 0x7F, 0×23, 0xA8″ or anything even remotely like it. Furthermore, you might want the data written out as short integers, signed or unsigned, in decimal instead of hexadecimal, etc.

Four bytes? Just hand-translate it. Over a hundred? Maybe not. Different base? Ugh. Wrong endian order? Super-ugh.

A simple solution, if not straightforward, is to load the data as a separate file from the disk, like this:


FILE *afile = fopen("mydata.dat", "rb");
if (afile) {
fread(mydata, sizeof(mydata), 1, afile);
fclose(afile);
}

Ah, but what about…

  1. Is pathname always relative? Should it be absolute? How do I reconcile with installation folder, etc?
  2. What if file can’t be loaded?
  3. Is file same size as declared variable? How do I resize it if source data changes? Should I dynamically allocate memory for it?
  4. What if I forget to go through all the proper value checks and closes because as a programmer I’m kind of lazy?
  5. Does this separate load step REALLY need to be done at all?

That last question is the clincher. The answer is NO.

The C++ initialized data converter is the answer. It reads a binary data file and spits out C++ initialized data. Or, alternatively, it can read initialized data and spit out binary data. It’s a two-way converter with many F8-mode selection functions.

If you’ve ever looked at how many files are in the BARfly installation folder, you’d know there are surprisingly few. BARfly uses BAR to run its architecture…but where are all the BAR-based architectural components? They’re not BAR files, and they’re not in the master registry.

The answer: INITDATA.BAR. I’ve long since made initialized data out of such components. The BARfly.exe executable has literally swallowed them.

Engineers of all stripes love the idea of making a machine work with fewer moving parts. Would you rather carry around a large package or several smaller ones? Same load. One’s a lot easier to carry.