FlyingGuns is a multiplayer WW1 action flight simulation. Besides being a game, it is also a demo for a technology created for distributed simulations. This technology can be found in applications of various fields where data has to be distributed at high rates. FlyingGuns is open source, and we think that anyone who might feel bored working on just another travel ticket, department-manager-employee, billing, accounting, or other system would really enjoy participating. This article provides the background for the architectural and technology choices made for FlyingGuns. Before reading this article, check out the Flying Guns siteto get an impression of what we are creating.
Back in 1998, I was a project lead at BlueByte for a multiplayer action flight simulator set in a parallel universe in the 1930s, the golden era of aviation, which featured these glorious racing planes. That game's background story was that America had been conquered by an evil European force. A small group of racing pilots in California equipped their planes with weapons and started to strike back, fighting against vast amounts of enemies on giant Zeppelin ships. Unfortunately, this project died before I could make it a reality. I really loved that idea. Later, Microsoft published "my" game: Crimson Skies.
In 2002, while working in the Java field for a (no longer existing) bioinformatics company, I came across Red Ace Squadron, a quite simple and cheap flight-sim game. Together with a colleague, we decided that it should be possible to create at least a game like this ourselves, decorated with true multiplayer features. Being more technicalthan artistic or gaming people, there was, from the very beginning, a strong focus on technology rather on artwork or gameplay. In a sense, the game FlyingGuns should be an example and demo of a technology that became the DRTS (Distributed RealTimeSimulation) project early in 2004.
Meanwhile, the project evolved into a multi-component system with topics from all areas of informatics: realtime, graphics, networking, databases, groupware, distributed systems, and so on.
Choosing Java for a 3D Realtime Multiplayer Game
We decided to use technologies far away from the mainstream of C++/DirectX to develop this game. The Java programming language seemed to better meet our list of constraints: as Flying Guns could only be a spare-time project, productivity had to be high and off-the-shelf technology had to be used as much as possible. We wanted to create a framework suitable not only for games, but also for "serious" applications, with the game as a test case. From the very beginning, we wanted to have a multiplayer environment, high accessibility, and easy installation and maintenance. We wanted the content to live and grow so that we could release new features as soon as we had them ready. Finally, we wanted someone who plays solitaire during his or her lunch break at work to be able to launch FlyingGuns instead and play it as their personal character against their colleagues. Or if they don't have colleagues, to be able to find someone else on the net. All without the need to install hundreds of megabytes at work. The game we had in mind could be played anywhere, at any time.
Of course, we wanted to have these features for free. That's why we considered Java. But can true 3D, realtime multiplayer games be made in Java? Everybody knows that Java is so slow and that garbage collector causes trouble for realtime games.
Here's a checklist for the basic ingredients we needed:
|3D||There's Java3D, at least.|
|Network||This is what Java was made for.|
|Deployment||Heard about WebStart?|
|Tools||NetBeans, Eclipse, profilers, archivers: programmer heaven.|
As mentioned above, we wanted to create more than a small game. The goal was an extendable, reusable framework for distributed, interactive, realtime simulation. Thus, we needed an architecture, forming the DRTS project. Here it is:
The client consists of a number of modules, each dealing with certain task. They are tied together in the game package. A game plays within a scene, which is the other big unit in the client and represents the (static) 3D world. Thus, the scene is the module that interfaces with the 3D engine. The distributed nature is represented by the component called HeadQuarter, which is the networking backbone of the whole thing and maybe the most complex thing in play. Currently, the server is basically just forwarding network messages and maintaining the same HeadQuarter mechanics as that of the client.
The overall scenario and gameplay was chosen with the goal of creating a network game. Form follows function. A WW1 flight simulator seems to be ideal in many respects.
- The motion of each individual plane's motion is motivated by the underlying physics. This means the motion is quite steady and can effectively be anticipated by the network code. The dead-reckoning engine (PDF) comes along with between one and three position-update messages per second. This is significantly different from, for instance, the kind of motion in a game like Quake, which is unsteady and not predictable.
- Each plane's relative positions are constantly changing in 3D space, so network artifacts are very hard to recognize and to distinguish from normal motion. Just make the motion smooth, and it looks good! Distances are relatively high, so even a deviation of, say, ten meters doesn't matter.
- There are not many points where artifacts lead to discreet, unresolvable states. You won't find things like separating walls that split a path into two.
- The weapon impact of machine guns is easy to handle because they have zero flight time. Additionally, in an airplane being hit while moving in 3D space, it is impossible to tell whether the enemy is in a valid shooting position. This makes it possible to handle all shooting logic on the shooter's side of the network. What he or she experiences is what really happens.
- Most objects are far away, so the needed level of detail is decent, which helps to keep download size small. The terrain does not have to be highly detailed either, and can efficiently be constructed from a compact heightmap delivered as a JPEG. With all of these factors, the whole game--code and resources--requires about 1MB.
For offline play, many AI opponents can be added. They serve as reasonable targets to practice maneuvering, aiming, and shooting.
Java and 3D: Java3D?
Before starting to create a 3D game, some basic decisions have to be made. The most fundamental choosing the right technologies; the right 3D technology in this case. When we started with FlyingGuns in 2002, there weren't too many options. There were justJava3Dand GL4Java. GL4Java allows low-level OpenGL access, and thus the lowest overhead and maximum performance, at least at first glance. This holds true when talking about a spinning cube, but FlyingGuns needs a terrain, a sky, airplanes en masse, shadows, effects, sound, and so on and this requires:
- A scene structure
- Object culling
- File loading
- Transparency sorting
- Texture management
- Picking and collision detection
Implementing each of these requires a lot of knowledge, time to work it out, and code. For a spare-time project, these features alone can eat up all of the time available. Java3D provides all of these things out of the box.
The 3D world for a flight simulator can be hierarchically arranged, and therefore matches the scenegraph approach of Java3D:
Java3D turned out to be quite fool-proofed and robust. The documentation and examples are sufficient and--since it's kind of a standard technology--there are many people around who can help. But Java3D also has its downsides; for instance, the sound system just does not work as expected (they took it out completely for the latest versions of Java3D), which took us a lot of time to work around. Second, it is extremely hard to make a Java3D application smooth! This is a not a matter of performance, but of frame-time coherence. Java3D has always been quick enough, but frame times vary unpredictably. Since smooth animation systems always rely on frame times being similar from one to the next, this turns out to be a serious problem.
Today, there are more choices, including the following:
These are all possible candidates for a rendering engine. FlyingGuns currently tries to abstract the rendering layer, making it possible to plug in any of these in the future.
HeadQuarter: The Distributed Infrastructure
HeadQuarter is the infrastructure, containing all relevant gaming information. This includes positions, types, and states. It is also responsible for sharing this information transparently over the net. The game itself isn't even aware of whether or not it plays on the network or is run as a completely standalone application. HeadQuarter provides a set of subsystems, each takes care of a certain facet of a game object. There is a subsystem that deals with spatial data alone; one that implements a registry-like system used for relationships and typing; and a properties subsystem that takes care of names, hitpoints, health, and so on. Applications write or read a subsystem's data by the means of its API. HeadQuarter then takes care that these changes are properly propagated so that other clients are notified.
This kind of subdivision allows us to implement different protocols and distribution schemes for different kinds of data. Additionally, it allows us to write special clients that do not need all of the data. This could include, for example, a small client showing a radar screen. This client would only use the spatial subsystem. In this way, the division into subsystems supports the distribution framework.
In order to keep the subsystems tied together,HeadQuarter has a very strong notion of identity. Everything in HeadQuarter has an identity. In contrast to other systems, which provide a "network session identity" (with
createPlayer() returning an
int),HeadQuarter identities are context-agnostic and can therefore be used for any purpose. This might include persistence or resource binding. So an identity can be just about anything appropriate to identify an object across JVM boundaries: a name, an ID, or even a SQL statement.
Internally, HeadQuarter relies on a messaging kernel named ObjectBus, which in turn is currently a thin layer on the Java NIO API introduced with 1.4. ObjectBus isn't completely covered by the subsystems but is open for application use as well. Event-like data ("player throws a grenade") that does not have to be maintained as a state ("player starts shooting with machine gun") is thought to be transmitted as bare ObjectBus messages.
Current State and Next Steps
The FlyingGuns project is now in a working state, meaning there is a playable prototype. The source is freely available from SourceForge and volunteers are deeply appreciated.
On top of the current to-do list is a better renderer abstraction, which will allow us to better isolate 3D code from the game and try out 3D engines other than Java3D. (Xith3D or jME would be the next candidates.) Have you seen CounterStrike and CounterStrike: Source? They're the same game, just with the graphics changed. That's the goal.
True gameplay needs to be added in order to turn the prototype into a game: missions, goals, rankings, different vehicles and weapons, etc. It doesn't sound that difficult, but the distributed nature of FlyingGuns sometimes makes it a challenge.
At no point was performance a real issue. I claim that today it is nearly impossible to perceive there is a garbage collector. It's just that Java3D turned out to be a bit unpredictable about the consequences of using its features; touch the wrong parameter, and the performance can break down. Networking has turned out to be the most challenging part.
It is very possible to write a true 3D action game in Java. The unique goals like high accessibility, living content, and network play make Java a first-class citizen among game-development systems. Plus, compared to the C++ games development we did, the time for debugging and hunting ugly bugs has been negligible.