Encountered Problems

Technical problems

Observable was not serializable

Java has two standard classes to implement the Observer pattern: Observer and Observable. Observers can register themself to an Observable, which notifies all registered observers when it has changed some internal data. By inheriting from this Observable class, a user can create observable software components.

The registration of the observers is internally done by Observable in a private Vector. The problem is that Sun for some reason didn't make Observable serializable. This means you can't create a subclass of Observable that can be serialized, since the private Vector that contains all the observers is lost in the serialization process.

This posed a big problem, since the Observer pattern was used extensivily in the package {@link quest.global.game}, which implements the logic of an Amazing Quest game. The root object that defines a complete game ({@link quest.global.game.GameLogic}) is constructed at the game server, and then sent to all the clients. Because all the registered observers inside that object are lost during the serialization and deserialization of the observable parts, clients received a crippled version of the object.

We made a workaround, {@link quest.global.util.SerializableObservable}, that extends Observable by making it serializable. This is done by providing the same functionality as the base class, but saving the data (the registered observers) also in the derived class. This redundant data is correctly serialized. During the deserialization process, the SerializableObservable restores the data in the base class with its redundant information. This way the registred observers were not lost, and the code could still use the Java Observer class.

ObjectInputStream issues

We had to make a design decission of how to internally support multiple concurrent connections with the server. The decission we made afterwards posed that it was not possible to detect client timeouts.

The default Java (Sun) paradigm is to use a single thread per listening client. At that time we found that paradigm outrageous because the server would have to support possibly hundreds to thousands of clients, and that would produce much threading overhead. This threading overhead could of course be minimalized if the host operating system's scheduler is sophisticated enough. Because we didn't want to depend on the operating system that much in order to let the server function reasonably fast, we chose to use a single thread for receiving the messages, which spawns seperate threads for handling the responses.

This way we had to rely on the available method of ObjectInputStream to return the amount of bytes that could be read from the input stream. The problem was that this method didn't return non-zero when the other side had (wanted to) sent an object over this connection. Possible because ObjectInputstream and ObjectOutputstream classes use a rendez-vous scheme that has to be hidden from the user. We circumvented this problem by using the SocketInputstream the ObjectInputStream was based on. Here the available method worked reliably.

Also it occurred to us that it wasn't possible to determine whether a connection was terminated, using the available method. This was a serious problem because the termination could only be determined by sending or trying to receive a message or object. We found it strange that in the available method the IOException exception was declared in the API, but never thrown. The documentation wasn't clear about when this exception would be raised. A glance at Sun's Java forum at java.sun.com made clear out of numerous developer discussions that Sun didn't (want to) actively support the single-thread solution. Because the only solution to the single-thread problem would be a hack, Sun will probably succeed in enforcing their thread-per-client solution.

(This way they would probably force heavy-load servers to use an OS/system that would support this solution (read: sun products).)

We generated the following solutions to this problem:

  1. Sending a heartbeat between the connections.
  2. Only block for a small amount of time(time spent on this operation is unreliable).
  3. Using some sort of leases: sending a keepalive packet after a period of inactivity.
  4. Using the thread-per-client method anyway (takes lot of time).
  5. Ignore it until later.
We chose to lower the priority of this problem. The perceived hinder would not crash the clients, hence it wasn't crucial.

Blending textures in Java3D

The 3D maze that makes up the games' board contains a number of treasures that are identified by their color and symbol. This is implemented by using a black-and-white texture for each symbol, and mapping this onto a 3D box (which represents a treasure) with a certain color. By blending the texture with the color of the box, we needed only one texture for each symbol, instead of needing a seperate texture for each combination of a color and a symbol.

The problem is that the same blending behaviors differently on various execution platforms. In Windows98, the blending results in black boxes with a colored symbol on it. In Windows NT, the same code results in colored boxed with a black symbol on it. So much for platform-independency.

Since this doesn't hinder the game from being played (you can still recognize the treasures) we just accept this behavior of Java3D.

Starting the applet in Netscape

Loading of the applet in netscape under SunOS resulted in a ModifyThreadGroup exception when the classpath included the path where the classes resided. Typically when including '.' in the classpath and starting netscape in the root of the quest directory, Java produced a couple of SecurityExceptions. The simple solution was to just start netscape in another directory, the real reason why netscape did value CLASSPATH over the Java plugin settings couldn't be determined.

Development environment

Java3D was only runnable on one machine for a long time

To run Java3D programs it has to be installed, of course. The Windows NT machines didn't have a decent Java installation at all untill the start of the 2001/2002 college year, so we had to test everything on UNIX machines. Getting the JavaPlugin to work under UNIX Netscape was an exercise on it's own, by the way.

The problem was that Java3D program only ran on hardware OpenGL accelerated X-servers, since the installed OpenGL software libraries where too old to be used by Java3D. There was only one machine available for students with a special 3D graphics card (the one the student assistant room). Since the game was meant to run with multiple client, testing the behavior of the program with multiple clients became almost impossible.

We discovered that by using Exceed on the Windows NT machines (which are all installed with a 3D graphics card) we could use the Java installation of UNIX and the OpenGL capabilities of these computers. This method was still not very satisfactory, since the Exceed-layer in between made the system very slow, and a lot of fonts and keybindings weren't available this way. This crippled the program and the testing of it a lot.

At the start of the new college year, all the Windows NT machines were installed with the latest JDK and Java3D version, so finally we got a decent testing environment. Much of the testing of multiple clients is therefore done in the last week before the deadline, so we weren't able remove all the bugs involving multiple clients.

JavaPlugin+Java3D is not really stable

We experienced a lot of crashes during the testing. Sometimes the JavaPlugin just freezes, and the runtime system of Java3D also crashes sometimes. It even made Windows NT crash a number of times, which isn't done very easily. Since it is still being developed, the relativily unstable nature of Java3D is understandable, but irritating sometimes. Especially the testing of multiple clients was hindered by it, since once a while a client just crashed.

The database/webserver was pretty slow

The only available webserver we could get shell permission on to run a server for our game was the raderboot. We needed a shell account to run the quest server on because of a java applet sandbox restriction: the applet can only open connections with the server where the codebase resides. The machine on which the webserver runs is a SPARCstation 5 with a 70Mhz processor and 64Mb of physical RAM. Furthermore a practical course 'Inleiding Gegevensverwerking'(IGV) was being held in spring 2001 on the same machine, on which participants used the Mysql server also operating there. On busy days it took for ages to retrieve the applet over the webserver and to start the server. Using an external server (out of reach of the NFS we used) would introduce file synchronization problems and an upload delay. Because of this we chose to use the raderboot nevertheless as webserver.

Learning curve

Java3D

The interface of the game, which uses Java3D, was made by Mathijs den Burger:

"Understanding how to work with Java3D took some time. The first challenge was to show some 3D object at all. You have to create a standard scene graph part which defines how the user views the scene before Java3D will display your 3D world. The demo's that come with Java3D mostly use the default SimpleUniverseManager to handle that part automatically. This class hides a lot of things I wanted to control myself (such as placing the viewer at a certain position in the 3D world), so it took a while before I understood how to do this part myself and was able to show simple 3D objects in a window.

The theory of the scene graph was pretty clear, but using it was a different story. When I understood this part, building the static scene was possible. The next difficult part were the animations: most examples that come with Java3D perform a continues animation (like spinning a cube), but the program would have to perform a different animation each time (e.g. to let the user walk through the maze). This was solved by generating the desired behavior at runtime, and adding it to the scene graph.

The last problem was to select objects in the 3D scene. The basic selection was possible by using the classes about picking in Java3D. This I used in a little framework for selecting 3D objects, by defining PickableObjects about which a PickBehavior could dispatch PickEvents to interested PickListeners. This provided a very usefull way of handling the selection of 3D objects.

A lot of Java3D's functionality isn't used in this game. Especially the methods to load complex 3D models and display all kinds of fancy 3D objects aren't used. But this practical work wasn't about 3D modeling of course."

Swing & building GUIs

The interface of the login procedure and chat environment was made by Sander Brandenburg:

"Because I've worked with Swing in the practical course "Software Engineering" there wasn't much new to me. Though this time I used much of the nifty 'Document' features that visualize panels on which you can draw colored or styled text etc. The biggest problem was how to structure the GUI in such a way that it was easy to communicate with eachother. Though the search, help and ranking overviews are quite distinct features, the chat, globalchat and channeloverview had to communicate with eachother in order to decide whether to enable or disable certain actions (buttons). The Singleton design pattern aided me in solving this, because all windows had to be instantiated only once.

Most of the GUI work was done using GridBagLayout as the layoutmanager, and emacs as editing tool. Because GridBagLayout offers lots of flexibility over bunches of variables, it was hard to design the GUI in text mode and evaluate them in Java, which takes a while to load. Still, sometimes a frame was layouted differently (read: strangely) in CDE X, KDE2 X, Exceed under Windows NT and plain Windows NT, which gave us a hard time."

Handling multiple threads

The central server was made by Merijn Evertse:

"Jaah eeuh...zat ik net lekker te poepen komt er een bronstige berggnoe voorbijgevlogen die m'n Windows 2000 bak onderschijt."

Tot zover Merijn's commentaar :)