001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.listener;
020
021import com.plotsquared.core.configuration.Settings;
022import com.plotsquared.core.util.WEManager;
023import com.plotsquared.core.util.WorldUtil;
024import com.sk89q.worldedit.WorldEditException;
025import com.sk89q.worldedit.entity.BaseEntity;
026import com.sk89q.worldedit.entity.Entity;
027import com.sk89q.worldedit.extent.AbstractDelegateExtent;
028import com.sk89q.worldedit.extent.Extent;
029import com.sk89q.worldedit.extent.NullExtent;
030import com.sk89q.worldedit.math.BlockVector2;
031import com.sk89q.worldedit.math.BlockVector3;
032import com.sk89q.worldedit.regions.CuboidRegion;
033import com.sk89q.worldedit.util.Location;
034import com.sk89q.worldedit.world.biome.BiomeType;
035import com.sk89q.worldedit.world.block.BaseBlock;
036import com.sk89q.worldedit.world.block.BlockState;
037import com.sk89q.worldedit.world.block.BlockStateHolder;
038import org.checkerframework.checker.nullness.qual.NonNull;
039
040import java.lang.reflect.Field;
041import java.util.HashMap;
042import java.util.Map;
043import java.util.Set;
044
045public class ProcessedWEExtent extends AbstractDelegateExtent {
046
047    private final Set<CuboidRegion> mask;
048    private final String world;
049    private final int max;
050    private final WorldUtil worldUtil;
051    private final Map<Long, Integer[]> tileEntityCount = new HashMap<>();
052    int Ecount = 0;
053    boolean Eblocked = false;
054    private int count;
055    private Extent parent;
056
057    public ProcessedWEExtent(
058            String world,
059            Set<CuboidRegion> mask,
060            int max,
061            Extent child,
062            Extent parent,
063            final @NonNull WorldUtil worldUtil
064    ) {
065        super(child);
066        this.mask = mask;
067        this.world = world;
068        this.worldUtil = worldUtil;
069        if (max == -1) {
070            max = Integer.MAX_VALUE;
071        }
072        this.max = max;
073        this.count = 0;
074        this.parent = parent;
075    }
076
077    private static long getChunkKey(final BlockVector3 location) {
078        return (long) (location.getBlockX() >> 4) & 4294967295L | ((long) (location.getBlockZ() >> 4) & 4294967295L) << 32;
079    }
080
081    @Override
082    public BlockState getBlock(BlockVector3 position) {
083        if (WEManager.maskContains(this.mask, position.getX(), position.getY(), position.getZ())) {
084            return super.getBlock(position);
085        }
086        return WEExtent.AIRSTATE;
087    }
088
089    @Override
090    public BaseBlock getFullBlock(BlockVector3 position) {
091        if (WEManager.maskContains(this.mask, position.getX(), position.getY(), position.getZ())) {
092            return super.getFullBlock(position);
093        }
094        return WEExtent.AIRBASE;
095    }
096
097    @Override
098    public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block)
099            throws WorldEditException {
100
101        final boolean isTile = this.worldUtil.getTileEntityTypes().contains(block.getBlockType());
102        if (isTile) {
103            final Integer[] tileEntityCount = this.tileEntityCount.computeIfAbsent(
104                    getChunkKey(location),
105                    key -> new Integer[]{this.worldUtil.getTileEntityCount(
106                            world,
107                            BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4)
108                    )}
109            );
110            if (tileEntityCount[0] >= Settings.Chunk_Processor.MAX_TILES) {
111                return false;
112            } else {
113                tileEntityCount[0]++;
114            }
115        }
116        if (WEManager.maskContains(this.mask, location.getX(), location.getY(), location.getZ())) {
117            if (this.count++ > this.max) {
118                if (this.parent != null) {
119                    try {
120                        Field field =
121                                AbstractDelegateExtent.class.getDeclaredField("extent");
122                        field.setAccessible(true);
123                        field.set(this.parent, new NullExtent());
124                    } catch (Exception e) {
125                        e.printStackTrace();
126                    }
127                    this.parent = null;
128                }
129                return false;
130            }
131            return super.setBlock(location, block);
132        }
133
134        return !isTile;
135    }
136
137    @Override
138    public Entity createEntity(Location location, BaseEntity entity) {
139        if (this.Eblocked) {
140            return null;
141        }
142        this.Ecount++;
143        if (this.Ecount > Settings.Chunk_Processor.MAX_ENTITIES) {
144            this.Eblocked = true;
145        }
146        if (WEManager.maskContains(this.mask, location.getBlockX(), location.getBlockY(),
147                location.getBlockZ()
148        )) {
149            return super.createEntity(location, entity);
150        }
151        return null;
152    }
153
154    @SuppressWarnings("deprecation")
155    @Override
156    public boolean setBiome(BlockVector2 position, BiomeType biome) {
157        return WEManager.maskContains(this.mask, position.getX(), position.getZ()) && super
158                .setBiome(position, biome);
159    }
160
161}