Using the data created earlier, we'll restore a GamingWorld which contains the necessary locations and exits our game demo requires. As mentioned earlier, we'll also be using formatting all text displayed by the game to so that it automatically splits words over to the next line for things like room descriptions. For this reason, we need to create a WidthLimitedOutputStream.
GameDemo.java
WidthLimitedOutputStream.java
Unlike the applet from the last tutorial, this game doesn't contain hard-wired locations and exits. Instead, it loads a GameWorld from a persistent data store, using an ObjectInputStream. The application opens a local file for reading, using a FileInputStream, and then reads in an instance of a gaming world.
// Create a file input stream FileInputStream fin = new FileInputStream(filename); // Create an object input stream ObjectInputStream objectIn = new ObjectInputStream(fin); // Read an object in from object store, and // cast it to a GameWorld game = (GameWorld) objectIn.readObject(); // Set the object stream to standard output game.setOutputStream ( System.out, 40 );
You'll notice that when we read an object in from the object store, we cast it to an instance of GameWorld. The readObject method of ObjectInputStream returns an Object, which is the superclass of all objects in Java. Since we know that only GameWorld objects will be stored in our data file, its safe to cast it straight to an instance of GameWorld.
The remainder of the code is pretty straightforward, and most of the command parsing code is taken from the earlier applet tutorial. Users can move by entering the long name of their direction (e.g. "north"), or the shortened version (e.g. "n").
Output streams, like DataOutputStream (for individual data-types), and PrintStream (for printed text) should be nothing new to you. If this is not the case, I suggest you brush up on output streams in general, and check out my tutorial on input/output streams.
The WidthLimitedOutputStream has really only one useful method - print(String). Using a StringTokenizer, each line is broken into individual words. As each word is written, the currentWidth counter is incremented by the word's length. Then, if a word would exceed the maximum allowable line width, a linefeed is inserted.
// Start at zero int currentWidth = 0; // Create a string tokenizer object StringTokenizer tokenizer = new StringTokenizer ( str ); // While words remain while ( tokenizer.hasMoreTokens() ) { // Get the next token String token = tokenizer.nextToken(); // If word would exceed width limit if (currentWidth + token.length() >= width) { // Print a newline m_out.println (); currentWidth = 0; } // Print token m_out.print (token + " "); currentWidth += token.length(); }
All of the source code, and data files, for the persistent game demo is provided throughout the course of the tutorial. For those who would like the source code as a single archive, a ZIP file is available for this tutorial's source code.
Feel free to play around with the code - and the game! Persistence is a useful technique for saving objects to disk, and as we modify and enhance the base class (GameWorld), the code to save and restore GameWorld will remain unchanged. If you are writing any applications that use persistence, however, remember that they require a JDK1.1 interpreter!