All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.level.format.generic.BaseChunk Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.level.format.generic;

import cn.nukkit.api.*;
import cn.nukkit.block.Block;
import cn.nukkit.block.BlockID;
import cn.nukkit.block.BlockUnknown;
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockstate.BlockState;
import cn.nukkit.blockstate.exception.InvalidBlockStateException;
import cn.nukkit.level.Level;
import cn.nukkit.level.format.Chunk;
import cn.nukkit.level.format.ChunkSection;
import cn.nukkit.level.format.ChunkSection3DBiome;
import cn.nukkit.level.format.LevelProvider;
import cn.nukkit.level.format.updater.ChunkUpdater;
import cn.nukkit.math.BlockVector3;
import cn.nukkit.utils.ChunkException;
import lombok.extern.log4j.Log4j2;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author MagicDroidX (Nukkit Project)
 */
@Log4j2
public abstract class BaseChunk extends BaseFullChunk implements Chunk {
    protected ChunkSection[] sections;
    protected int sectionLength;
    private boolean delayPaletteUpdates;

    @PowerNukkitOnly("Needed for level backward compatibility")
    @Since("1.3.0.0-PN")
    @Override
    public void backwardCompatibilityUpdate(Level level) {
        ChunkUpdater.backwardCompatibilityUpdate(level, this);
    }

    @PowerNukkitXOnly
    @Since("1.6.0.0-PNX")
    public final int toSectionY(int blockPosY) {
        if (sectionLength == 24) {
            return (blockPosY >> 4) + 4;
        }
        return blockPosY >> 4;
    }

    @Override
    public BaseChunk clone() {
        BaseChunk chunk = (BaseChunk) super.clone();
        if (this.biomes != null) chunk.biomes = this.biomes.clone();
        chunk.heightMap = this.getHeightMapArray().clone();
        if (sections != null && sections[0] != null) {
            chunk.sections = new ChunkSection[sections.length];
            chunk.sectionLength = sections.length;
            for (int i = 0; i < sections.length; i++) {
                chunk.sections[i] = sections[i].copy();
            }
        }
        return chunk;
    }

    @PowerNukkitDifference(info = "using BlockEntity.close() instead of removeBlockEntity() to solve the bug of incomplete cleanup", since = "1.6.0.0-PNX")
    private void removeInvalidTile(int x, int y, int z) {
        BlockEntity entity = getTile(x, y, z);
        if (entity != null) {
            try {
                if (!entity.closed && entity.isBlockEntityValid()) {
                    return;
                }
            } catch (Exception e) {
                try {
                    log.warn("Block entity validation of {} at {}, {} {} {} failed, removing as invalid.",
                            entity.getClass().getName(),
                            getProvider().getLevel().getName(),
                            entity.x,
                            entity.y,
                            entity.z,
                            e
                    );
                } catch (Exception e2) {
                    e.addSuppressed(e2);
                    log.warn("Block entity validation failed", e);
                }
            }
            entity.close();
        }
    }

    @Since("1.4.0.0-PN")
    @PowerNukkitOnly
    @Nonnull
    @Override
    public Stream scanBlocks(BlockVector3 min, BlockVector3 max, BiPredicate condition) {
        int offsetX = getX() << 4;
        int offsetZ = getZ() << 4;
        return IntStream.rangeClosed(min.getChunkSectionY(isOverWorld()), max.getChunkSectionY(isOverWorld()))
                .filter(sectionY -> sectionY >= 0 && sectionY < sections.length)
                .mapToObj(sectionY -> sections[sectionY])
                .filter(section -> !section.isEmpty()).parallel()
                .map(section -> section.scanBlocks(getProvider(), offsetX, offsetZ, min, max, condition))
                .flatMap(Collection::stream);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    public int getFullBlock(int x, int y, int z) {
        return getFullBlock(x, y, z, 0);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @PowerNukkitOnly
    @Override
    public int getFullBlock(int x, int y, int z, int layer) {
        return this.sections[toSectionY(y)].getFullBlock(x, y & 0x0f, z, layer);
    }

    @PowerNukkitOnly
    @Override
    public BlockState getBlockState(int x, int y, int z, int layer) {
        return this.sections[toSectionY(y)].getBlockState(x, y & 0x0f, z, layer);
    }

    @PowerNukkitOnly
    @Override
    public boolean setBlockAtLayer(int x, int y, int z, int layer, int blockId) {
        return this.setBlockStateAtLayer(x, y, z, layer, BlockState.of(blockId));
    }

    @Override
    public boolean setBlock(int x, int y, int z, int blockId) {
        return setBlockState(x, y, z, BlockState.of(blockId));
    }

    @Override
    public Block getAndSetBlock(int x, int y, int z, Block block) {
        return getAndSetBlock(x, y, z, 0, block);
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    @Override
    public BlockState getAndSetBlockState(int x, int y, int z, int layer, BlockState state) {
        int sectionY = toSectionY(y);
        try {
            setChanged();
            return getOrCreateMutableSection(sectionY).getAndSetBlockState(x, y & 0x0f, z, layer, state);
        } finally {
            removeInvalidTile(x, y, z);
        }
    }

    @PowerNukkitOnly
    @Override
    public Block getAndSetBlock(int x, int y, int z, int layer, Block block) {
        BlockState state = getAndSetBlockState(x, y, z, layer, block.getCurrentState());
        try {
            return state.getBlock();
        } catch (InvalidBlockStateException e) {
            return new BlockUnknown(state.getBlockId(), state.getExactIntStorage());
        }
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.3.0.0-PN")
    @Override
    public boolean setFullBlockId(int x, int y, int z, int fullId) {
        return this.setFullBlockId(x, y, z, 0, fullId);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.3.0.0-PN")
    @Override
    @PowerNukkitOnly
    public boolean setFullBlockId(int x, int y, int z, int layer, int fullId) {
        int sectionY = toSectionY(y);
        try {
            setChanged();
            return getOrCreateMutableSection(sectionY).setFullBlockId(x, y & 0x0f, z, layer, fullId);
        } finally {
            removeInvalidTile(x, y, z);
        }
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    public boolean setBlock(int x, int y, int z, int blockId, int meta) {
        return this.setBlockAtLayer(x, y, z, 0, blockId, meta);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @PowerNukkitOnly
    @Override
    public boolean setBlockAtLayer(int x, int y, int z, int layer, int blockId, int meta) {
        return setBlockStateAtLayer(x, y, z, layer, BlockState.of(blockId, meta));
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    @Override
    public boolean setBlockStateAtLayer(int x, int y, int z, int layer, BlockState state) {
        int sectionY = toSectionY(y);
        try {
            setChanged();
            return getOrCreateMutableSection(sectionY).setBlockStateAtLayer(x, y & 0x0f, z, layer, state);
        } finally {
            removeInvalidTile(x, y, z);
        }
    }

    protected ChunkSection getOrCreateMutableSection(int sectionY) {
        ChunkSection section = sections[sectionY];
        if (section.isEmpty()) {
            createChunkSection(sectionY);
            return sections[sectionY];
        }

        return section;
    }

    protected void createChunkSection(int sectionY) {
        try {
            var oldCs = sections[sectionY];
            var newCs = (ChunkSection) this.providerClass.getMethod("createChunkSection", int.class).invoke(this.providerClass, sectionY);
            if (oldCs instanceof ChunkSection3DBiome chunkSection3DBiome && newCs instanceof ChunkSection3DBiome newChunkSection3DBiome) {
                newChunkSection3DBiome.set3DBiomeDataArray(chunkSection3DBiome.get3DBiomeDataArray());
            }
            this.setInternalSection(sectionY, newCs);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            log.error("Failed to create ChunkSection", e);
            throw new ChunkException(e);
        }
    }

    @Override
    public void setBlockId(int x, int y, int z, int id) {
        setBlockStateAtLayer(x, y, z, 0, BlockState.of(id));
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    @PowerNukkitOnly
    public void setBlockId(int x, int y, int z, int layer, int id) {
        int sectionY = toSectionY(y);
        try {
            setChanged();
            getOrCreateMutableSection(sectionY).setBlockId(x, y & 0x0f, z, layer, id);
        } finally {
            removeInvalidTile(x, y, z);
        }
    }

    @Override
    public int getBlockId(int x, int y, int z) {
        return getBlockId(x, y, z, 0);
    }

    @Override
    @PowerNukkitOnly
    public int getBlockId(int x, int y, int z, int layer) {
        return this.sections[toSectionY(y)].getBlockId(x, y & 0x0f, z, layer);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    public int getBlockData(int x, int y, int z) {
        return getBlockData(x, y, z, 0);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    @PowerNukkitOnly
    public int getBlockData(int x, int y, int z, int layer) {
        return this.sections[toSectionY(y)].getBlockData(x, y & 0x0f, z, layer);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @Override
    public void setBlockData(int x, int y, int z, int data) {
        setBlockData(x, y, z, 0, data);
    }

    @Deprecated
    @DeprecationDetails(reason = "The meta is limited to 32 bits", since = "1.4.0.0-PN")
    @PowerNukkitOnly
    @Override
    public void setBlockData(int x, int y, int z, int layer, int data) {
        int sectionY = toSectionY(y);
        try {
            setChanged();
            getOrCreateMutableSection(sectionY).setBlockData(x, y & 0x0f, z, layer, data);
        } finally {
            removeInvalidTile(x, y, z);
        }
    }

    @Override
    public int getBlockSkyLight(int x, int y, int z) {
        return this.sections[toSectionY(y)].getBlockSkyLight(x, y & 0x0f, z);
    }

    @Override
    public void setBlockSkyLight(int x, int y, int z, int level) {
        int sectionY = toSectionY(y);
        getOrCreateMutableSection(sectionY).setBlockSkyLight(x, y & 0x0f, z, level);
        setChanged();
    }

    @Override
    public int getBlockLight(int x, int y, int z) {
        return this.sections[toSectionY(y)].getBlockLight(x, y & 0x0f, z);
    }

    @Override
    public void setBlockLight(int x, int y, int z, int level) {
        int sectionY = toSectionY(y);
        getOrCreateMutableSection(sectionY).setBlockLight(x, y & 0x0f, z, level);
        setChanged();
    }

    @Override
    public boolean isSectionEmpty(float fY) {
        return this.sections[(int) fY].isEmpty();
    }

    @Override
    public ChunkSection getSection(float fY) {
        return this.sections[(int) fY];
    }

    @Override
    public boolean setSection(float fY, ChunkSection section) {
        if (!section.hasBlocks()) {
            this.sections[(int) fY] = EmptyChunkSection.EMPTY[(int) fY];
        } else {
            this.sections[(int) fY] = section;
        }
        setChanged();
        return true;
    }

    protected void setInternalSection(float fY, ChunkSection section) {
        if (isPaletteUpdatesDelayed()) {
            section.delayPaletteUpdates();
        }
        this.sections[(int) fY] = section;
        setChanged();
    }

    @Override
    public boolean load() throws IOException {
        return this.load(true);
    }

    @Override
    public boolean load(boolean generate) throws IOException {
        return this.getProvider() != null && this.getProvider().getChunk(this.getX(), this.getZ(), true) != null;
    }

    @Override
    public byte[] getBlockSkyLightArray() {
        ByteBuffer buffer = ByteBuffer.allocate(2048 * getChunkSectionCount());
        for (int y = 0; y < getChunkSectionCount(); y++) {
            buffer.put(this.sections[y].getSkyLightArray());
        }
        return buffer.array();
    }

    @Override
    public byte[] getBlockLightArray() {
        ByteBuffer buffer = ByteBuffer.allocate(2048 * getChunkSectionCount());
        for (int y = 0; y < getChunkSectionCount(); y++) {
            buffer.put(this.sections[y].getLightArray());
        }
        return buffer.array();
    }

    @Override
    public ChunkSection[] getSections() {
        return sections;
    }

    @Override
    public byte[] getHeightMapArray() {
        return this.heightMap;
    }

    @Override
    public LevelProvider getProvider() {
        return this.provider;
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    @Override
    public boolean setBlockStateAt(int x, int y, int z, int layer, BlockState state) {
        return setBlockStateAtLayer(x & 0xF, y, z & 0XF, layer, state);
    }

    @PowerNukkitOnly
    @Override
    public BlockState getBlockStateAt(int x, int y, int z, int layer) {
        return getBlockState(x & 0xF, y, z & 0xF, layer);
    }

    @PowerNukkitOnly
    @Override
    public boolean isBlockChangeAllowed(int x, int y, int z) {
        for (ChunkSection section : sections) {
            if (section.getBlockChangeStateAbove(x, 0, z) == 3) { // Border
                return false;
            }
        }

        if (y <= 0) {
            return sections[0].getBlockChangeStateAbove(x, 0, z) == 0;
        }

        int sectionY = toSectionY(y);
        y = y & 0xF;
        for (; sectionY >= 0; sectionY--) {
            switch (sections[sectionY].getBlockChangeStateAbove(x, y, z)) {
                case 1: // Deny
                case 3: // Border
                    return false;
                case 2: // Allow
                    if (sectionY == toSectionY(y)) {
                        return sections[sectionY].getBlockId(x, y, z, 0) != BlockID.ALLOW;
                    } else {
                        return true;
                    }
                default:
                    y = 0xF;
            }
        }
        return true;
    }

    @Nonnull
    @PowerNukkitOnly
    @Override
    public List findBorders(int x, int z) {
        List borders = null;
        for (ChunkSection section : sections) {
            if (section.getBlockChangeStateAbove(x, 0, z) == 3) {
                for (int y = 0; y < 0xF; y++) {
                    BlockState blockState = section.getBlockState(x, y, z, 0);
                    if (blockState.getBlockId() == BlockID.BORDER_BLOCK) {
                        if (borders == null) {
                            borders = new ArrayList<>(3);
                        }
                        borders.add(blockState.getBlock(provider.getLevel(), x, y, z, 0));
                    }
                }
            }
        }
        return borders != null ? borders : Collections.emptyList();
    }

    @PowerNukkitOnly
    @Override
    public boolean isBlockedByBorder(int x, int z) {
        for (ChunkSection section : sections) {
            if (section.getBlockChangeStateAbove(x, 0, z) == 3) {
                return true;
            }
        }
        return false;
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    public void delayPaletteUpdates() {
        ChunkSection[] sections = this.sections;
        if (sections != null) {
            for (ChunkSection section : sections) {
                if (section != null) {
                    section.delayPaletteUpdates();
                }
            }
        }
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    public boolean isPaletteUpdatesDelayed() {
        return delayPaletteUpdates;
    }

    @PowerNukkitOnly
    @Since("1.4.0.0-PN")
    public void setPaletteUpdatesDelayed(boolean delayPaletteUpdates) {
        this.delayPaletteUpdates = delayPaletteUpdates;
        if (delayPaletteUpdates) {
            delayPaletteUpdates();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy