In this new series, we'll talk about using Java for client/server game networking, ways of enacting smooth high-performance animation using Java, and even analyze a killer game.
When PlayLink, Inc. began cranking out games way back in 1997, it took a gamble and focused on 100% pure Java. The reasons were the usual ones rapid development, cross-platform functionality, excellent threading and network control, and so on. The main rationale, though, was that Java was cool. The programmers dug it.
Three years later, it's interesting to look back and see where Java fulfilled hopes and where it disappointed.
PlayLink was ambitious, interested in many types of games: single player arcade classics, multiplayer board and card games, huge persistent world universes, first-person shooters with kick-butt graphics, and real-time strategies.
Three years later, it's interesting to look back and see where Java fulfilled hopes and where it disappointed. Not to keep you in too much suspense, it might as well be said now that the only style of game PlayLink was unable to adequately pull off were first-person shooters. However, it can be done. Check out Frag Island by Niclas Thisell and Andreas Suurkuusk.
Rapid Development Shangri-La
Did Java make game development a gentle breeze, a springtime walk through the park?
There are some really robust and easy-to-use rapid development packages out there. But PlayLink is full of picky people. We wanted our games to look exactly the way our designers envisioned them. We didn't want to plug in solutions such as Java Beans, because we really wanted control over every aspect, from client down to server.
Graphically, we wanted custom scrollbars and floating, transparent chat areas. Today, we could use Swing to achieve these effects. But back in Java 1.0 days, we had to write our own lightweight components. Many of these we based on the original Java AWT code.
We'd dug our own perfectionist hole and had to fill it. We basically wrote our own class loader, media tracker, GIF decoder, containers, and components. It was slow, frustrating work.
The big reward was that our classes were lean as could be, with only the exact capabilities we wanted. Swing and other prefabricated libraries are often too bulky, and definitely slower than our home-brewed concoctions.
All one of our programmers needs to do to create a new game is provide the artwork, an optional artificial-intelligence evaluation algorithm, and the game's general rules.
Given all that, the rate in which we were able to crank out a game lobby, communications server, game framework, and dozens of games themselves was right on schedule. All our developers really enjoyed Java itself. It was great to have garbage collection take care of no-longer-important objects. It was a breeze to piece classes together. It was a pure pleasure to no longer worry about allocating memory or double-asterisking pointers to pointers.
PlayLink grew into a complete gaming system an interface through which players were able to update the latest code changes, download the latest games, browse through rooms, chat with and meet other players, create game sessions, and, of course, play to their hearts' content.
Pretty much all our games extend from a master class we call BoardGame. This abstract class has things like the chat window, the list of players, a way to update and receive the game's state, as well as a way to keep track of who's currently playing, observing, and on the waitlist.
Specific types of games are based on subclasses that extend from BoardGame. For instance, a special CardTable class keeps tracks of card hands, card animations, sorting, dealing, trick counting, and betting.
At this point, all one of our programmers needs to do to create a new game is provide the artwork, an optional artificial-intelligence evaluation algorithm, and the game's general rules. The BoardGame class takes care of the rest.
This completely object-oriented architecture also works great for updating. Let's say we decide we want a new button in all our games to allow players to invite their friends. We simply program that button into the BoardGame class user interface, and it automatically appears in all the board games. The next time a player logs on, our updater tool will search for any new files and simply download the latest 60K BoardGame class. Voila, all players now have the new functionality, quick and painless.
The Joy of Debugging
PlayLink began development in the days of Java 1.0. This version was notoriously rife with annoying bugs, on many levels. Some methods didn't work as documented. Some of the most annoying bugs we came across involved graphics and sound the forte of games.
We ended up writing our own focus controller thread and calling it explicitly anytime the player moved to a new window or clicked a new component.
With sound, for example, we found that anytime the system produced a tone at the same time as one of our games, the entire Virtual Machine would crash. If you got an e-mail beep while wasting time with Backgammon, your system went up in smoke. We fixed this by basically hijacking the Java sound system and playing a continuous, empty sound loop. The sound of silence, if you will. This made it impossible for other applications to get their audio foot in our door. A regrettable thing to do, but desperate times... .
Java also did highly annoying things with images. A simple GIF image, for example, should only take up 8-bits of color information per pixel. But Java sapped a full 32-bits, keeping a copy of the image cached and keeping enough room for colors that couldn't exist. Also, images bigger than 16 Megs couldn't be created if you were using a large color depth. Most business applications would never care about this boundary, but our real-time strategy game's background map certainly did. And so we had to break large images into multiple files and piece them together programmatically. We also had a stock of problems with animated GIFs. GIF89a images would often starve other threads, display garbled, or crash the system entirely. It took a few generations of Java before these suckers worked adequately.
Other big bugs involved focus. The default focus behavior changed from Java version to Java version. We ended up writing our own focus controller thread and calling it explicitly anytime the player moved to a new window or clicked a new component.
Java classes, all in all, are difficult to debug. Better and better profiling, optimization, and debugging tools are constantly being introduced. But back in the old days, it was pretty much impossible to tell how much memory our games were taking up, whether there was a memory leak, or where leaks originated. Only by using brute force and careful observation were we able to track some obstinate offenders.
At times, exceptions would appear somewhere deep in the Java libraries. These were totally out of our control. We would then be forced to trace the origin of the problem and code a way around the chasm. This called for lots of strange kluges and non-obvious solutions. But it was good fun, in any case.
We developed our Java games on Windows NT. Our earliest arcade applets worked pretty well when tested in the latest browsers on Macintosh, Windows 95, and Solaris.
We faced the law of bug interdependency: Fix a bug on one system, create a bug on another.
But as the games became more complex, with more-hungry threads, teetering data structures, and raging sockets, big problems cropped up. As it turns out, threads are handled differently from machine to machine. Getting focus means different things to different operating systems. Microsoft's Java would constantly wage small battles of code semantics against Sun's Java. Not to mention a Macintosh JVM versus Linux.
We faced the law of bug interdependency: Fix a bug on one system, create a bug on another. To bypass this law, we would isolate problems to a specific patch of code and then sniff the platform or JVM vendor using
. We'd then run a simple switch-case series and treat components, threads, or images in different ways depending on where they were running.
Since PlayLink was originally developed in Java 1.0, there was no way to sign applets. This meant that a user would have to download the entire game every time she returned to our site. Since our main lobby classes and graphics were about 3 Megs, this was an unpleasant proposition. We were also trapped in by applet security. We needed to access system properties and read from and write to the user's hard drive.
So we chose to distribute PlayLink as an application. This meant users had to download our files only once. But they also had to download a Java Runtime Environment. Unfortunately, Sun's JRE 1.1.7 distribution installer is 2.6 Megs. Microsoft's version of Java they call it JView is an even bulkier package. It was a bit of a heavy toll to pay for a simple game of checkers. Then we realized that most of our users were on Windows 98 or had Internet Explorer 5.0 installed. This meant they already had JView on their systems. So we had our installer detect for JView and utilize it if a high enough version was found.
The other big advantage to making PlayLink an application was much tighter control. As difficult as it was to code for different platforms, it was a piece of apple strudel compared to testing our games on every version of browser and JVM configuration. This helped us guarantee that the user's experience would be as fun as our own.
All in all, we are glad we used Java for our games. We believe all the up-front hassles and work were worth it and that we now have a stable, easily expandable system. In future articles in this series, we'll talk about using Java for client/server game networking, ways of enacting smooth high-performance animation using Java, and we'll analyze PlayLink's Citizen 01, which I believe is one of the most advanced games ever programmed using Java technologies. Until then....
Part II of this series:
Advanced Java Game Development: State Data
About the Author David Fox is vice president of Games at PlayLink, Inc. He's also the author of numerous books and articles about cyberculture and technology.