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.plot.world;
020
021import com.plotsquared.core.location.Location;
022import com.plotsquared.core.plot.PlotArea;
023import com.plotsquared.core.plot.PlotWorld;
024import com.plotsquared.core.util.PlotAreaConverter;
025import com.plotsquared.core.util.RegionUtil;
026import com.sk89q.worldedit.math.BlockVector3;
027import com.sk89q.worldedit.regions.CuboidRegion;
028import org.checkerframework.checker.nullness.qual.NonNull;
029import org.checkerframework.checker.nullness.qual.Nullable;
030import org.khelekore.prtree.MBR;
031import org.khelekore.prtree.PRTree;
032import org.khelekore.prtree.SimpleMBR;
033
034import java.util.Collection;
035import java.util.Collections;
036import java.util.LinkedList;
037import java.util.List;
038
039/**
040 * Plot world that contains several plot areas (clusters)
041 */
042public class ScatteredPlotWorld extends PlotWorld {
043
044    private static final PlotAreaConverter MBR_CONVERTER = new PlotAreaConverter();
045    private static final int BRANCH_FACTOR = 30;
046
047    private final List<PlotArea> areas = new LinkedList<>();
048    private final Object treeLock = new Object();
049    private PRTree<PlotArea> areaTree;
050
051    /**
052     * Create a new plot world with a given world name
053     *
054     * @param world World name
055     */
056    public ScatteredPlotWorld(final @NonNull String world) {
057        super(world);
058    }
059
060    @Override
061    public @Nullable PlotArea getArea(final @NonNull Location location) {
062        if (this.areas.isEmpty()) {
063            return null;
064        }
065        synchronized (this.treeLock) {
066            for (final PlotArea area : this.areaTree.find(location.toMBR())) {
067                if (area.contains(location)) {
068                    return area;
069                }
070            }
071        }
072        return null;
073    }
074
075    @Override
076    public @NonNull Collection<PlotArea> getAreas() {
077        return Collections.unmodifiableCollection(this.areas);
078    }
079
080    @Override
081    public void addArea(final @NonNull PlotArea area) {
082        this.areas.add(area);
083        this.buildTree();
084    }
085
086    @Override
087    public void removeArea(final @NonNull PlotArea area) {
088        this.areas.remove(area);
089        this.buildTree();
090    }
091
092    @Override
093    public @NonNull Collection<PlotArea> getAreasInRegion(final @NonNull CuboidRegion region) {
094        if (this.areas.isEmpty()) {
095            return Collections.emptyList();
096        }
097        synchronized (this.treeLock) {
098            final List<PlotArea> areas = new LinkedList<>();
099
100            final BlockVector3 min = region.getMinimumPoint();
101            final BlockVector3 max = region.getMaximumPoint();
102            final MBR mbr = new SimpleMBR(min.getX(), max.getX(), min.getY(), max.getY(), min.getZ(), max.getZ());
103
104            for (final PlotArea area : this.areaTree.find(mbr)) {
105                if (RegionUtil.intersects(area.getRegion(), region)) {
106                    areas.add(area);
107                }
108            }
109
110            return areas;
111        }
112    }
113
114    /**
115     * Rebuild the area tree
116     */
117    private void buildTree() {
118        synchronized (this.treeLock) {
119            this.areaTree = new PRTree<>(MBR_CONVERTER, BRANCH_FACTOR);
120            this.areaTree.load(this.areas);
121        }
122    }
123
124}