Savegames - how to?

Hey, I have a small game with an isometric map and I would like to save it in a file. But how can i do this?
I tried it with “toString()”, but then I can’t resolved it back to an int array. Any ideas how to solve the problem without using xml. Would be a very large xml file, if I do so.

I compress my save data using the Deflater/Inflater classes:

      protected String compressed(String string) {
            byte[] data = deflaterCompressed(string);
            
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < data.length; i++) {
                  String toAdd = Integer.toString(data[i] - Byte.MIN_VALUE, Character.MAX_RADIX);
                  if (toAdd.length() == 1)
                        sb.append('0');
                  sb.append(toAdd);
            }
            return sb.toString();
      }
      
      protected String decompressed(String string) {
            try {
                  byte[] newData = new byte[string.length()/2];
                  for (int i = 0; i < string.length() - 1; i += 2) {
                        newData[i/2] = (byte)(Integer.parseInt(string.substring(i, i+2), Character.MAX_RADIX) + Byte.MIN_VALUE);
                  }
                  
                  return inflaterDecompressed(newData);
            }
            catch (Exception e) {
                  if (DEBUG)
                        System.out.println("decompression failed, returning origional");
                  return string;
            }
                  
      }
      
      private byte[] deflaterCompressed(String string) {
            byte[] input = string.getBytes();
          
          // Create the compressor with highest level of compression
          Deflater compressor = new Deflater();
          compressor.setLevel(Deflater.BEST_COMPRESSION);
          
          // Give the compressor the data to compress
          compressor.setInput(input);
          compressor.finish();
                    
          // Create an expandable byte array to hold the compressed data.
          // You cannot use an array that's the same size as the orginal because
          // there is no guarantee that the compressed data will be smaller than
          // the uncompressed data.
          ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
          
          // Compress the data
          byte[] buf = new byte[1024];
          while (!compressor.finished()) {
              int count = compressor.deflate(buf);
              bos.write(buf, 0, count);
          }
          try {
                bos.close();
          } catch (IOException e) {
          }
    
          // Return the compressed data
          return bos.toByteArray();
      }
      
      private String inflaterDecompressed(byte[] data) throws DataFormatException {
            // Create the decompressor and give it the data to compress
          Inflater decompressor = new Inflater();
          decompressor.setInput(data);
          
          // Create an expandable byte array to hold the decompressed data
          ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
          
          // Decompress the data
          byte[] buf = new byte[1024];
          int count = -1;
          while (!decompressor.finished() && count != 0) {
                count = decompressor.inflate(buf);
              bos.write(buf, 0, count);
          }
          try {
              bos.close();
          } catch (IOException e) {
          }
          
          // Get the decompressed data
          return new String(bos.toByteArray());
      }

The shifting you’ll see there with the adding ‘0’ to the String sometimes is so that I can save the byte data in a String without losing data. If you plan on saving to a file, you can write it straight as bytes and save a little bit of extra space. Try that out and see how it helps!

By the way, much of that code was thanks to www.javaalmanac.com

So essentially you can keep your XML structure, and just in one block have blah blah blah with all the compressed data inbetween. It should help a lot!

However, there might be other ways to manage it. For example, how are you creating the map? If you create a new Random() object and just crunch out numbers and you want to save the whole map set up you just randomly generated, instead you should keep track of the seed:


Random rando = new Random();
long genKey = rando.nextLong();
rando = new Random(genKey);

generateMap(rando);
loadGame();

When you go to save your game, just save genKey, and you can call rando = new Random(genKey) later and get the same resulting map.

GameDev.net has some articles on datafile format ;D

The maps might be created with a map editor or just random. But i have to save the informationen. No one can play the whole game in one day, when it is finished :wink:

If they’re made at random, save the generation seed. If they’re made with a map editor, you could try saving the info as a gif. I’ve heard of that idea once. You have a .gif with the dimensions of your map, each pixel representing a tile, have a different color for each type. That’d be easy to save compressed because you’d let gif do the work there. Or you could use .png and tack on some of the PNGCrush functionality to compress them as much as you can.

At any rate, however you save, you can use that zipping code to compress what you have.

Sounds nice. BUt how to save the other relevant data? like items that are placed on my tiles?

well the layout can go in the png, whereas the rest of the stuff could be in xml format, then compressed.

Ok, i will give it a try. For the moment I did it with DataInputStream and DataOutputStream. The user don’t know what the values are for and i have the data saved :wink:

Maybe I will try a combine of DOS/DIS and the gif/png/xml stuff.

I think the absolutly simplest way to save game data is to use the XMLEncoder and XMLDecoder classes. It is no work at all. All you need to do is to make your data classes Java Bean compatible, which as you may know, in this case means your data classes only have to provide an empty costructor and get/set methods for all your variables.

My experience is that it’s fast and creates small data files.

A quick example

 public void saveBeanToDisc(String url,Object o){
  try{
   XMLEncoder e = new XMLEncoder(
       new BufferedOutputStream(
       new FileOutputStream(url)));
 
   e.writeObject(o);
   e.close();
  }catch(FileNotFoundException ex){
 
  }
 }
 
public Object loadBeanFromDisc(String url){
 
  try{
   XMLDecoder e = new XMLDecoder(
    new BufferedInputStream(
    new FileInputStream(url)));
 
   Object o = e.readObject();
      e.close();
   return o;
  }catch(FileNotFoundException ex){
 
  }
 
  return null;
 
 }

That won’t be save against cheaters.

Or you could combine XML with the built in zip library. Then when you create your zip file, just don’t use a zip extension. That will deter most people from cheating.

right, you could use my Zip code I just gave you, and write it to anything you want. In your save file, if they open it, they’ll find crap. Since the zip code above is nothing standard because of the shifts to make it work in String form, it’s highly unlikely they’d ever make a “guess” to decode it. If they do, they’ll see, moreZippedCrap and they’ll have to deal with that too :slight_smile:

Can you explain me the usage of the MAX_RADIX constant?
I did a search on google and find out that it defines the base for the given system. Like decimal -> Radix: 10

But I don’t understand the contex with it in your deflater/inflater example :frowning:

You’re right, radix is the base, and can also be thought of as a descriptor for the method of converting Bytes to numbers and letters. The higher the radix, the more letters will be included in the conversion, so that the max radix will enable you to store a byte in 1 or 2 characters. Converting with a radix of 10 instead of MAX_RADIX will result in a MUCH longer String of data. So say you set the radix to be 16, that would mean the byte would be converted into hexadecimal format. I set it to be the maximum possible radix so that the compression of space would be the highest. If the language ever somehow supports a higher radix, that code will compress even further

Ah, thx :slight_smile:

Is this code ok, or does there is a better way to compare the pixel color with a specific value? My image load code:


// loads a map from a given map file
public void load(String map) {
    File mapFile = new File(map);
    BufferedImage mapImage = null;
        
    // try to read the map file
    try {
        mapImage = ImageIO.read(mapFile);
    } catch (IOException ioe) {
        System.err.println("error while loading map\n\n"+ioe);
        // create a random map :P
        init(true);            
    }
        
    // if we loaded our image, get the data and move on :)
    if (mapImage != null) {
        int bandNum = mapImage.getRaster().getNumBands();
        int pixelSample[] = new int[bandNum];
        int baseTile = 0;
        Color color;            
        // reassign xTiles and yTiles
        xTiles = mapImage.getWidth();
        yTiles = mapImage.getHeight();
        // init the new map
        init(false);
        // populate our map with the image pixel data
        for (int i=0; i<xTiles; i++) {
            for (int j=0; j<yTiles; j++) {
                // get the pixel sample from the current position
                mapImage.getRaster().getPixel(i,j,pixelSample);
                color = new Color(pixelSample[0],pixelSample[1],pixelSample[2]);
                if (color.equals(Color.GREEN))
                    baseTile = 0;
                else if (color.equals(Color.ORANGE))
                    baseTile = 1;
                else if (color.equals(Color.GRAY))
                    baseTile = 2;
                else if (color.equals(Color.BLUE))
                    baseTile = 3;
                // assign the basetile
                    fields[i][j][1] = baseTile;                                       
            }
        }
    }
}

the code tag is mad, it destroys my syntax : /

This seems kinda strange to me but no one metioned Serialization? Is it that bad? ??? I use it.

If you make a code change, old save files become unusable. Since I change code daily, that’d be real bad :wink:

My question hasn’t been answered yet :frowning:

Is there any better way to obtain the color and comparing?

EvilD -

It appears your code should work. Have you tried it?

I might recommend doing something similiar, but using less memory. Just write out a binary file that maps to your tiles. Make byte 0 be the width, byte 1, be the height and the rest of the file the tiles to use. It would take potentially much less file space, unless the image is using indexed color, where it would then be a wash.

For a 3x4 map, you would have 2 + 3 * 4 bytes = 14 bytes for the map. You would have 256 differrent types of tiles using that format.

You could also use ints for the format, then you could mask the bytes to determine different things. Bytes one and two could hold state info such as: treasure here, trap, hidden/secret area, etc. The third byte could give you an index into a graphic to use for the floor or whatever. The fourth byte would be your base tile info.

HTH,
Dr. A>

@Malohkan

If you want to keep versions of a serialized class compatible then override

private static final long serialVersionUID = 1234567L

With your own version number and take care of the new vars, or any other changes in your class yourself.

I was just suggesting it beacuse it sounded like he wanted to save more then a 2D map. But I do like the idea of saving it in a gif ;D