Search in sources :

Example 1 with NbtDataContainerInputStream

use of org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream in project LanternServer by LanternPowered.

the class AnvilChunkIOService method getGeneratedChunks.

@Override
public ChunkDataStream getGeneratedChunks() {
    return new ChunkDataStream() {

        // All the region files
        private Path[] paths;

        // The current region file that we opened
        @Nullable
        private RegionFile region;

        // The coordinates of the chunk inside the region
        private int chunkX;

        private int chunkZ;

        // The next index of the chunk in the region file
        private int regionChunkIndex;

        // The next index that we are in the file array
        private int regionFileIndex;

        // Whether the current fields are cached
        private boolean cached;

        // Done, no new chunks can be found
        private boolean done;

        {
            // Use the reset to initialize
            this.reset();
        }

        @Override
        public DataContainer next() {
            if (!this.hasNext()) {
                throw new NoSuchElementException();
            }
            try {
                final DataInputStream is = this.region.getChunkDataInputStream(this.chunkX, this.chunkZ);
                final DataContainer data;
                try (NbtDataContainerInputStream nbt = new NbtDataContainerInputStream(is)) {
                    data = nbt.read();
                }
                this.cached = false;
                return data;
            } catch (IOException e) {
                // This shouldn't happen
                throw new IllegalStateException(e);
            }
        }

        @Override
        public boolean hasNext() {
            // Fast fail
            if (this.done) {
                return false;
            }
            // Use the cached index if set
            if (this.cached) {
                return true;
            }
            // Try first to search for more chunks in the current region
            while (true) {
                if (this.region != null) {
                    while (++this.regionChunkIndex < REGION_AREA) {
                        this.chunkX = this.regionChunkIndex / REGION_SIZE;
                        this.chunkZ = this.regionChunkIndex % REGION_SIZE;
                        if (this.region.hasChunk(this.chunkX, this.chunkZ)) {
                            this.cached = true;
                            return true;
                        }
                    }
                }
                // There no chunk available in the current region,
                // reset the chunk index for the next one
                this.regionChunkIndex = -1;
                // try the next region
                if (++this.regionFileIndex >= this.paths.length) {
                    this.region = null;
                    this.done = true;
                    return false;
                }
                final Path nextRegionFile = this.paths[this.regionFileIndex];
                if (Files.exists(nextRegionFile)) {
                    Matcher matcher = cache.getFilePattern().matcher(nextRegionFile.getFileName().toString());
                    int regionX = Integer.parseInt(matcher.group(0));
                    int regionZ = Integer.parseInt(matcher.group(1));
                    try {
                        this.region = cache.getRegionFile(regionX, regionZ);
                    } catch (IOException e) {
                        logger.error("Failed to read the region file ({};{}) in the world folder {}", regionX, regionZ, baseDir.getFileName().toString(), e);
                        this.region = null;
                    }
                } else {
                    this.region = null;
                }
            }
        }

        @Override
        public int available() {
            // the region files
            throw new UnsupportedOperationException();
        }

        @Override
        public void reset() {
            this.paths = cache.getRegionFiles();
            this.regionFileIndex = -1;
            this.regionChunkIndex = -1;
            this.region = null;
            this.cached = false;
            this.done = false;
        }
    };
}
Also used : Path(java.nio.file.Path) DataContainer(org.spongepowered.api.data.DataContainer) ChunkDataStream(org.spongepowered.api.world.storage.ChunkDataStream) NbtDataContainerInputStream(org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream) Matcher(java.util.regex.Matcher) IOException(java.io.IOException) DataInputStream(java.io.DataInputStream) NoSuchElementException(java.util.NoSuchElementException)

Example 2 with NbtDataContainerInputStream

use of org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream in project LanternServer by LanternPowered.

the class AnvilChunkIOService method getChunkData.

@Override
public CompletableFuture<Optional<DataContainer>> getChunkData(Vector3i chunkCoords) {
    return this.scheduler.submitAsyncTask(() -> {
        final int x = chunkCoords.getX();
        final int z = chunkCoords.getZ();
        final RegionFile region = cache.getRegionFileByChunk(x, z);
        final int regionX = x & REGION_MASK;
        final int regionZ = z & REGION_MASK;
        final DataInputStream is = region.getChunkDataInputStream(regionX, regionZ);
        if (is == null) {
            return Optional.empty();
        }
        final DataContainer data;
        try (NbtDataContainerInputStream nbt = new NbtDataContainerInputStream(is)) {
            data = nbt.read();
        }
        return Optional.of(data);
    });
}
Also used : DataContainer(org.spongepowered.api.data.DataContainer) NbtDataContainerInputStream(org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream) DataInputStream(java.io.DataInputStream)

Example 3 with NbtDataContainerInputStream

use of org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream in project LanternServer by LanternPowered.

the class AnvilChunkIOService method read.

@Override
public boolean read(LanternChunk chunk) throws IOException {
    final int x = chunk.getX();
    final int z = chunk.getZ();
    final RegionFile region = this.cache.getRegionFileByChunk(x, z);
    final int regionX = x & REGION_MASK;
    final int regionZ = z & REGION_MASK;
    final DataInputStream is = region.getChunkDataInputStream(regionX, regionZ);
    if (is == null) {
        return false;
    }
    final DataView levelDataView;
    try (NbtDataContainerInputStream nbt = new NbtDataContainerInputStream(is)) {
        levelDataView = nbt.read().getView(LEVEL).get();
    }
    // read the vertical sections
    final List<DataView> sectionList = levelDataView.getViewList(SECTIONS).get();
    final ChunkSection[] sections = new ChunkSection[16];
    // noinspection unchecked
    final Short2ObjectOpenHashMap<LanternTileEntity>[] tileEntitySections = new Short2ObjectOpenHashMap[sections.length];
    for (DataView sectionTag : sectionList) {
        final int y = sectionTag.getInt(Y).get();
        final byte[] rawTypes = (byte[]) sectionTag.get(BLOCKS).get();
        final byte[] extTypes = sectionTag.contains(BLOCKS_EXTRA) ? (byte[]) sectionTag.get(BLOCKS_EXTRA).get() : null;
        final byte[] data = (byte[]) sectionTag.get(DATA).get();
        final byte[] blockLight = (byte[]) sectionTag.get(BLOCK_LIGHT).get();
        final byte[] skyLight = (byte[]) sectionTag.get(SKY_LIGHT).get();
        final NibbleArray dataArray = new NibbleArray(rawTypes.length, data, true);
        final NibbleArray extTypesArray = extTypes == null ? null : new NibbleArray(rawTypes.length, extTypes, true);
        final short[] types = new short[rawTypes.length];
        for (int i = 0; i < rawTypes.length; i++) {
            types[i] = (short) ((extTypesArray == null ? 0 : extTypesArray.get(i)) << 12 | ((rawTypes[i] & 0xff) << 4) | dataArray.get(i));
        }
        tileEntitySections[y] = new Short2ObjectOpenHashMap<>();
        sections[y] = new ChunkSection(types, new NibbleArray(rawTypes.length, skyLight, true), new NibbleArray(rawTypes.length, blockLight, true), tileEntitySections[y]);
    }
    levelDataView.getViewList(TILE_ENTITIES).ifPresent(tileEntityViews -> {
        final ObjectSerializer<LanternTileEntity> tileEntitySerializer = ObjectSerializerRegistry.get().get(LanternTileEntity.class).get();
        for (DataView tileEntityView : tileEntityViews) {
            final int tileY = tileEntityView.getInt(TILE_ENTITY_Y).get();
            final int section = tileY >> 4;
            if (tileEntitySections[section] == null) {
                continue;
            }
            final int tileZ = tileEntityView.getInt(TILE_ENTITY_Z).get();
            final int tileX = tileEntityView.getInt(TILE_ENTITY_X).get();
            try {
                final LanternTileEntity tileEntity = tileEntitySerializer.deserialize(tileEntityView);
                tileEntity.setLocation(new Location<>(this.world, tileX, tileY, tileZ));
                tileEntity.setValid(true);
                tileEntitySections[section].put((short) ChunkSection.index(tileX & 0xf, tileY & 0xf, tileZ & 0xf), tileEntity);
            } catch (InvalidDataException e) {
                this.logger.warn("Error loading tile entity at ({};{};{}) in the chunk ({},{}) in the world {}", tileX & 0xf, tileY & 0xf, tileZ & 0xf, x, z, getWorldProperties().getWorldName(), e);
            }
        }
    });
    final DataView spongeDataView = levelDataView.getView(DataQueries.SPONGE_DATA).orElse(null);
    final List<DataView> trackerDataViews = spongeDataView == null ? null : levelDataView.getViewList(TRACKER_DATA_TABLE).orElse(null);
    // noinspection unchecked
    final Short2ObjectMap<LanternChunk.TrackerData>[] trackerData = chunk.getTrackerData().getRawObjects();
    if (trackerDataViews != null) {
        for (DataView dataView : trackerDataViews) {
            final Optional<Short> optIndex = dataView.getShort(TRACKER_BLOCK_POS);
            if (!optIndex.isPresent()) {
                continue;
            }
            final int creatorId = dataView.getInt(TRACKER_ENTRY_CREATOR).orElse(-1);
            final int notifierId = dataView.getInt(TRACKER_ENTRY_NOTIFIER).orElse(-1);
            // index = z << 12 | y << 4 | x
            int index = optIndex.get() & 0xffff;
            final int section = (index >> 8) & 0xf;
            // Convert the index to the section based system
            // index = y << 8 | z << 4 | x
            index = ChunkSection.index(index & 0xf, (index >> 4) & 0xf, index >> 12);
            trackerData[section].put((short) index, new LanternChunk.TrackerData(creatorId, notifierId));
        }
    }
    // initialize the chunk
    chunk.initializeSections(sections);
    chunk.setPopulated(levelDataView.getInt(TERRAIN_POPULATED).orElse(0) > 0);
    if (levelDataView.contains(BIOMES)) {
        final byte[] biomes = (byte[]) levelDataView.get(BIOMES).get();
        final byte[] biomesExtra = (byte[]) (levelDataView.contains(BIOMES_EXTRA) ? levelDataView.get(BIOMES_EXTRA).get() : null);
        final short[] newBiomes = new short[biomes.length];
        for (int i = 0; i < biomes.length; i++) {
            newBiomes[i] = (short) ((biomesExtra == null ? 0 : biomesExtra[i]) << 8 | biomes[i]);
        }
        chunk.initializeBiomes(newBiomes);
    }
    final Object heightMap;
    if (levelDataView.contains(HEIGHT_MAP) && (heightMap = levelDataView.get(HEIGHT_MAP).get()) instanceof int[]) {
        chunk.initializeHeightMap((int[]) heightMap);
    } else {
        chunk.initializeHeightMap(null);
    }
    levelDataView.getLong(INHABITED_TIME).ifPresent(time -> chunk.setInhabitedTime(time.intValue()));
    chunk.setLightPopulated(levelDataView.getInt(LIGHT_POPULATED).orElse(0) > 0);
    chunk.initializeLight();
    levelDataView.getViewList(ENTITIES).ifPresent(entityViews -> {
        final ObjectSerializer<LanternEntity> entitySerializer = ObjectSerializerRegistry.get().get(LanternEntity.class).get();
        for (DataView entityView : entityViews) {
            try {
                final LanternEntity entity = entitySerializer.deserialize(entityView);
                final int ySection = fixEntityYSection(entity.getPosition().getFloorY() >> 4);
                chunk.addEntity(entity, ySection);
            } catch (InvalidDataException e) {
                this.logger.warn("Error loading entity in the chunk ({},{}) in the world {}", x, z, getWorldProperties().getWorldName(), e);
            }
        }
    });
    return true;
}
Also used : NbtDataContainerInputStream(org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream) Short2ObjectOpenHashMap(it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap) LanternChunk(org.lanternpowered.server.world.chunk.LanternChunk) NibbleArray(org.lanternpowered.server.util.collect.array.NibbleArray) InvalidDataException(org.spongepowered.api.data.persistence.InvalidDataException) LanternEntity(org.lanternpowered.server.entity.LanternEntity) DataInputStream(java.io.DataInputStream) Short2ObjectMap(it.unimi.dsi.fastutil.shorts.Short2ObjectMap) DataView(org.spongepowered.api.data.DataView) LanternTileEntity(org.lanternpowered.server.block.tile.LanternTileEntity) ChunkSection(org.lanternpowered.server.world.chunk.LanternChunk.ChunkSection)

Example 4 with NbtDataContainerInputStream

use of org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream in project LanternServer by LanternPowered.

the class LanternByteBuffer method readLimitedDataView.

@Nullable
@Override
public DataView readLimitedDataView(int maximumDepth, int maxBytes) {
    final int index = this.buf.readerIndex();
    if (this.buf.readByte() == 0) {
        return null;
    }
    this.buf.readerIndex(index);
    try {
        try (NbtDataContainerInputStream input = new NbtDataContainerInputStream(new LimitInputStream(new ByteBufInputStream(this.buf), maxBytes), false, maximumDepth)) {
            return input.read();
        }
    } catch (IOException e) {
        throw new CodecException(e);
    }
}
Also used : NbtDataContainerInputStream(org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream) CodecException(io.netty.handler.codec.CodecException) LimitInputStream(org.lanternpowered.server.util.io.LimitInputStream) ByteBufInputStream(io.netty.buffer.ByteBufInputStream) IOException(java.io.IOException) Nullable(javax.annotation.Nullable)

Aggregations

NbtDataContainerInputStream (org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream)4 DataInputStream (java.io.DataInputStream)3 IOException (java.io.IOException)2 DataContainer (org.spongepowered.api.data.DataContainer)2 ByteBufInputStream (io.netty.buffer.ByteBufInputStream)1 CodecException (io.netty.handler.codec.CodecException)1 Short2ObjectMap (it.unimi.dsi.fastutil.shorts.Short2ObjectMap)1 Short2ObjectOpenHashMap (it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap)1 Path (java.nio.file.Path)1 NoSuchElementException (java.util.NoSuchElementException)1 Matcher (java.util.regex.Matcher)1 Nullable (javax.annotation.Nullable)1 LanternTileEntity (org.lanternpowered.server.block.tile.LanternTileEntity)1 LanternEntity (org.lanternpowered.server.entity.LanternEntity)1 NibbleArray (org.lanternpowered.server.util.collect.array.NibbleArray)1 LimitInputStream (org.lanternpowered.server.util.io.LimitInputStream)1 LanternChunk (org.lanternpowered.server.world.chunk.LanternChunk)1 ChunkSection (org.lanternpowered.server.world.chunk.LanternChunk.ChunkSection)1 DataView (org.spongepowered.api.data.DataView)1 InvalidDataException (org.spongepowered.api.data.persistence.InvalidDataException)1