Package net.lightstone.io

Source Code of net.lightstone.io.McRegionChunkIoService

package net.lightstone.io;

import java.io.File;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

import net.lightstone.io.region.RegionFile;
import net.lightstone.io.region.RegionFileCache;
import net.lightstone.model.Chunk;
import net.lightstone.util.nbt.ByteTag;
import net.lightstone.util.nbt.ByteArrayTag;
import net.lightstone.util.nbt.CompoundTag;
import net.lightstone.util.nbt.IntTag;
import net.lightstone.util.nbt.ListTag;
import net.lightstone.util.nbt.LongTag;
import net.lightstone.util.nbt.NBTInputStream;
import net.lightstone.util.nbt.NBTOutputStream;
import net.lightstone.util.nbt.Tag;

/**
* An implementation of the {@link ChunkIoService} which reads and writes
* McRegion maps.
* <p />
* Information on the McRegion file format can be found on the
* <a href="http://mojang.com/2011/02/16/minecraft-save-file-format-in-beta-1-3">Mojang</a>
* blog.
* @author Zhuowei Zhang
* @author Graham Edgecombe
*/
public final class McRegionChunkIoService implements ChunkIoService {

  /**
   * The size of a region - a 32x32 group of chunks.
   */
  private static final int REGION_SIZE = 32;

  /**
   * The root directory of the map.
   */
  private File dir;

  /**
   * The region file cache.
   */
  private RegionFileCache cache = new RegionFileCache();

  // TODO: consider the session.lock file

  public McRegionChunkIoService() {
    this(new File("world"));
  }

  public McRegionChunkIoService(File dir) {
    this.dir = dir;
  }

  @Override
  public Chunk read(int x, int z) throws IOException {
    RegionFile region = cache.getRegionFile(dir, x, z);
    int regionX = x & (REGION_SIZE - 1);
    int regionZ = z & (REGION_SIZE - 1);
    if (!region.hasChunk(regionX, regionZ)) {
      return null;
    }

    DataInputStream in = region.getChunkDataInputStream(regionX, regionZ);
    Chunk chunk = new Chunk(x, z);

    NBTInputStream nbt = new NBTInputStream(in, false);
    CompoundTag tag = (CompoundTag) nbt.readTag();
    Map<String, Tag> levelTags = ((CompoundTag) tag.getValue().get("Level")).getValue();

    byte[] tileData = ((ByteArrayTag) levelTags.get("Blocks")).getValue();
    chunk.setTypes(tileData);

    byte[] skyLightData = ((ByteArrayTag) levelTags.get("SkyLight")).getValue();
    byte[] blockLightData = ((ByteArrayTag) levelTags.get("BlockLight")).getValue();
    byte[] metaData = ((ByteArrayTag) levelTags.get("Data")).getValue();

    for (int cx = 0; cx < Chunk.WIDTH; cx++) {
      for (int cz = 0; cz < Chunk.HEIGHT; cz++) {
        for (int cy = 0; cy < Chunk.DEPTH; cy++) {
          boolean mostSignificantNibble = ((cx * Chunk.HEIGHT + cz) * Chunk.DEPTH + cy) % 2 == 1;
          int offset = ((cx * Chunk.HEIGHT + cz) * Chunk.DEPTH + cy) / 2;

          int skyLight, blockLight, meta;
          if (mostSignificantNibble) {
            skyLight = (skyLightData[offset] & 0xF0) >> 4;
            blockLight = (blockLightData[offset] & 0xF0) >> 4;
            meta = (metaData[offset] & 0xF0) >> 4;
          } else {
            skyLight = skyLightData[offset] & 0x0F;
            blockLight = blockLightData[offset] & 0x0F;
            meta = metaData[offset] & 0x0F;
          }

          chunk.setSkyLight(cx, cz, cy, skyLight);
          chunk.setBlockLight(cx, cz, cy, blockLight);
          chunk.setMetaData(cx, cz, cy, meta);
        }
      }
    }
    nbt.close();
    return chunk;
  }


  /**
   * Writes a chunk to a McRegion file.
   * WARNING! The files written by this method probably won't load in the Notchian server. Make backups.
   */
  @Override
  public void write(int x, int z, Chunk chunk) throws IOException {
    CompoundTag levelTag = chunkToTag(chunk);
    RegionFile region = cache.getRegionFile(dir, x, z);
    int regionX = x & (REGION_SIZE - 1);
    int regionZ = z & (REGION_SIZE - 1);

    DataOutputStream out = region.getChunkDataOutputStream(regionX, regionZ);
    try {
      NBTOutputStream nbtOut = new NBTOutputStream(out, false);

      Map<String, Tag> tagMap = new HashMap<String, Tag>(1);
      tagMap.put("Level", levelTag);

      CompoundTag tag = new CompoundTag("", tagMap);
      nbtOut.writeTag(tag);
    } finally {
      out.close();
    }

    // TODO: Close the regionfile
  }

  private CompoundTag chunkToTag(Chunk chunk) {
    final int size = Chunk.WIDTH * Chunk.HEIGHT * Chunk.DEPTH;

    byte[] tileData = new byte[size];
    byte[] skyLightData = new byte[size / 2];
    byte[] blockLightData = new byte[size / 2];
    byte[] metaData = new byte[size / 2];
    byte[] heightMapData = new byte[Chunk.WIDTH * Chunk.HEIGHT];

    for (int cx = 0; cx < Chunk.WIDTH; cx++) {
      for (int cz = 0; cz < Chunk.HEIGHT; cz++) {
        for (int cy = 0; cy < Chunk.DEPTH; cy+=2) {
          int blockOffset = ((cx * Chunk.HEIGHT + cz) * Chunk.DEPTH + cy);
          int offset = blockOffset / 2;
          tileData[blockOffset] = (byte) chunk.getType(cx, cz, cy);
          tileData[blockOffset + 1] = (byte) chunk.getType(cx, cz, cy + 1);
          skyLightData[offset] = (byte) ((chunk.getSkyLight(cx, cz, cy + 1) << 4) | chunk.getSkyLight(cx, cz, cy));
          blockLightData[offset] = (byte) ((chunk.getBlockLight(cx, cz, cy + 1) << 4) | chunk.getBlockLight(cx, cz, cy));
          metaData[offset] = (byte) ((chunk.getMetaData(cx, cz, cy + 1) << 4) | chunk.getMetaData(cx, cz, cy));
        }
      }
    }

    Map<String, Tag> levelTags = new HashMap<String, Tag>();
    levelTags.put("Blocks", new ByteArrayTag("Blocks", tileData));
    levelTags.put("Data", new ByteArrayTag("Data", metaData));
    levelTags.put("SkyLight", new ByteArrayTag("SkyLight", skyLightData));
    levelTags.put("BlockLight", new ByteArrayTag("BlockLight", blockLightData));
    // TODO: Heightmap, entities, tileentities, lastupdate
    levelTags.put("HeightMap", new ByteArrayTag("HeightMap", heightMapData));
    levelTags.put("Entities", chunkEntitiesToTag(chunk));
    levelTags.put("TileEntities", chunkTileEntitiesToTag(chunk));
    levelTags.put("LastUpdate", new LongTag("LastUpdate", 0));

    levelTags.put("xPos", new IntTag("xPos", chunk.getX()));
    levelTags.put("zPos", new IntTag("zPos", chunk.getZ()));
    // TODO: terrainpopulated
    levelTags.put("TerrainPopulated", new ByteTag("TerrainPopulated", (byte) 0));
    return new CompoundTag("Level", levelTags);
  }

  // TODO
  private ListTag<CompoundTag> chunkEntitiesToTag(Chunk chunk) {
    List<CompoundTag> entityTags = new ArrayList<CompoundTag>();
    return new ListTag<CompoundTag>("Entities", CompoundTag.class, entityTags);
  }

  // TODO
  private ListTag<CompoundTag> chunkTileEntitiesToTag(Chunk chunk) {
    List<CompoundTag> entityTags = new ArrayList<CompoundTag>();
    return new ListTag<CompoundTag>("TileEntities", CompoundTag.class, entityTags);
  }

}
TOP

Related Classes of net.lightstone.io.McRegionChunkIoService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.