Package net.glowstone.io.anvil

Source Code of net.glowstone.io.anvil.AnvilChunkIoService

package net.glowstone.io.anvil;

import net.glowstone.GlowChunk;
import net.glowstone.GlowChunk.ChunkSection;
import net.glowstone.GlowChunkSnapshot;
import net.glowstone.GlowServer;
import net.glowstone.block.entity.TileEntity;
import net.glowstone.entity.GlowEntity;
import net.glowstone.io.ChunkIoService;
import net.glowstone.io.entity.EntityStorage;
import net.glowstone.util.NibbleArray;
import net.glowstone.util.nbt.CompoundTag;
import net.glowstone.util.nbt.NBTInputStream;
import net.glowstone.util.nbt.NBTOutputStream;
import net.glowstone.util.nbt.TagType;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

/**
* An implementation of the {@link ChunkIoService} which reads and writes Anvil maps,
* an improvement on the McRegion file format.
*/
public final class AnvilChunkIoService 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 final File dir;

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

    // todo: consider the session.lock file

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

    /**
     * Reads a chunk from its region file.
     * @param chunk The GlowChunk to read into.
     * @return Whether the
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public boolean read(GlowChunk chunk) throws IOException {
        int x = chunk.getX(), z = chunk.getZ();
        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 false;
        }

        DataInputStream in = region.getChunkDataInputStream(regionX, regionZ);

        CompoundTag levelTag;
        try (NBTInputStream nbt = new NBTInputStream(in, false)) {
            CompoundTag root = nbt.readCompound();
            levelTag = root.getCompound("Level");
        }

        // read the vertical sections
        List<CompoundTag> sectionList = levelTag.getCompoundList("Sections");
        ChunkSection[] sections = new ChunkSection[16];
        for (CompoundTag sectionTag : sectionList) {
            int y = sectionTag.getByte("Y");
            byte[] rawTypes = sectionTag.getByteArray("Blocks");
            NibbleArray extTypes = sectionTag.containsKey("Add") ? new NibbleArray(sectionTag.getByteArray("Add")) : null;
            NibbleArray data = new NibbleArray(sectionTag.getByteArray("Data"));
            NibbleArray blockLight = new NibbleArray(sectionTag.getByteArray("BlockLight"));
            NibbleArray skyLight = new NibbleArray(sectionTag.getByteArray("SkyLight"));

            char[] types = new char[rawTypes.length];
            for (int i = 0; i < rawTypes.length; i++) {
                types[i] = (char) (((extTypes == null ? 0 : extTypes.get(i)) << 12) | ((rawTypes[i] & 0xff) << 4) | data.get(i));
            }
            sections[y] = new ChunkSection(types, skyLight, blockLight);
        }

        // initialize the chunk
        chunk.initializeSections(sections);
        chunk.setPopulated(levelTag.getBool("TerrainPopulated"));

        // read biomes
        if (levelTag.isByteArray("Biomes")) {
            chunk.setBiomes(levelTag.getByteArray("Biomes"));
        }

        // read entities
        if (levelTag.isList("Entities", TagType.COMPOUND)) {
            for (CompoundTag entityTag : levelTag.getCompoundList("Entities")) {
                try {
                    // note that creating the entity is sufficient to add it to the world
                    EntityStorage.loadEntity(chunk.getWorld(), entityTag);
                } catch (Exception e) {
                    GlowServer.logger.log(Level.WARNING, "Error loading entity in " + chunk, e);
                }
            }
        }

        // read "HeightMap" if we need to

        // read tile entities
        List<CompoundTag> storedTileEntities = levelTag.getCompoundList("TileEntities");
        for (CompoundTag tileEntityTag : storedTileEntities) {
            int tx = tileEntityTag.getInt("x");
            int ty = tileEntityTag.getInt("y");
            int tz = tileEntityTag.getInt("z");
            TileEntity tileEntity = chunk.getEntity(tx & 0xf, ty, tz & 0xf);
            if (tileEntity != null) {
                try {
                    tileEntity.loadNbt(tileEntityTag);
                } catch (Exception ex) {
                    GlowServer.logger.log(Level.SEVERE, "Error loading TileEntity at " + tileEntity.getBlock(), ex);
                }
            } else {
                GlowServer.logger.warning("No tile entity at " + chunk.getWorld() + "," + tx + "," + ty + "," + tz);
            }
        }

        return true;
    }

    /**
     * Writes a chunk to its region file.
     * @param chunk The {@link GlowChunk} to write from.
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public void write(GlowChunk chunk) throws IOException {
        int x = chunk.getX(), z = chunk.getZ();
        RegionFile region = cache.getRegionFile(dir, x, z);
        int regionX = x & (REGION_SIZE - 1);
        int regionZ = z & (REGION_SIZE - 1);

        CompoundTag levelTags = new CompoundTag();

        // core properties
        levelTags.putInt("xPos", chunk.getX());
        levelTags.putInt("zPos", chunk.getZ());
        levelTags.putBool("TerrainPopulated", chunk.isPopulated());
        levelTags.putLong("LastUpdate", 0);

        // chunk sections
        List<CompoundTag> sectionTags = new ArrayList<>();
        GlowChunkSnapshot snapshot = chunk.getChunkSnapshot(true, true, false);
        ChunkSection[] sections = snapshot.getRawSections();
        for (byte i = 0; i < sections.length; ++i) {
            ChunkSection sec = sections[i];
            if (sec == null) continue;

            CompoundTag sectionTag = new CompoundTag();
            sectionTag.putByte("Y", i);

            byte[] rawTypes = new byte[sec.types.length];
            NibbleArray extTypes = null;
            NibbleArray data = new NibbleArray(sec.types.length);
            for (int j = 0; j < sec.types.length; j++) {
                rawTypes[j] = (byte) ((sec.types[j] >> 4) & 0xFF);
                byte extType = (byte) (sec.types[j] >> 12);
                if (extType > 0) {
                    if (extTypes == null) {
                        extTypes = new NibbleArray(sec.types.length);
                    }
                    extTypes.set(j, extType);
                }
                data.set(j, (byte) (sec.types[j] & 0xF));
            }
            sectionTag.putByteArray("Blocks", rawTypes);
            if (extTypes != null) {
                sectionTag.putByteArray("Add", extTypes.getRawData());
            }
            sectionTag.putByteArray("Data", data.getRawData());
            sectionTag.putByteArray("BlockLight", sec.blockLight.getRawData());
            sectionTag.putByteArray("SkyLight", sec.skyLight.getRawData());

            sectionTags.add(sectionTag);
        }
        levelTags.putCompoundList("Sections", sectionTags);

        // height map and biomes
        levelTags.putIntArray("HeightMap", snapshot.getRawHeightmap());
        levelTags.putByteArray("Biomes", snapshot.getRawBiomes());

        // entities
        List<CompoundTag> entities = new ArrayList<>();
        for (GlowEntity entity : chunk.getRawEntities()) {
            if (!entity.shouldSave()) {
                continue;
            }
            try {
                CompoundTag tag = new CompoundTag();
                EntityStorage.save(entity, tag);
                entities.add(tag);
            } catch (Exception e) {
                GlowServer.logger.log(Level.WARNING, "Error saving " + entity + " in " + chunk, e);
            }
        }
        levelTags.putCompoundList("Entities", entities);

        // tile entities
        List<CompoundTag> tileEntities = new ArrayList<>();
        for (TileEntity entity : chunk.getRawTileEntities()) {
            try {
                CompoundTag tag = new CompoundTag();
                entity.saveNbt(tag);
                tileEntities.add(tag);
            } catch (Exception ex) {
                GlowServer.logger.log(Level.SEVERE, "Error saving tile entity at " + entity.getBlock(), ex);
            }
        }
        levelTags.putCompoundList("TileEntities", tileEntities);

        CompoundTag levelOut = new CompoundTag();
        levelOut.putCompound("Level", levelTags);

        try (NBTOutputStream nbt = new NBTOutputStream(region.getChunkDataOutputStream(regionX, regionZ), false)) {
            nbt.writeTag(levelOut);
        }
    }

    @Override
    public void unload() throws IOException {
        cache.clear();
    }

}
TOP

Related Classes of net.glowstone.io.anvil.AnvilChunkIoService

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.