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.services.plots;
020
021import cloud.commandframework.services.types.Service;
022import com.google.common.cache.Cache;
023import com.google.common.cache.CacheBuilder;
024import com.plotsquared.core.plot.Plot;
025import com.plotsquared.core.plot.PlotAreaType;
026import com.plotsquared.core.plot.PlotId;
027import org.checkerframework.checker.nullness.qual.NonNull;
028import org.checkerframework.checker.nullness.qual.Nullable;
029
030import java.util.Collections;
031import java.util.List;
032import java.util.concurrent.TimeUnit;
033import java.util.function.Predicate;
034
035public interface AutoService extends Service<AutoQuery, List<Plot>> {
036
037    Cache<PlotId, Plot> plotCandidateCache = CacheBuilder.newBuilder()
038            .expireAfterWrite(20, TimeUnit.SECONDS).build();
039    Object plotLock = new Object();
040
041    final class DefaultAutoService implements AutoService {
042
043        @Override
044        public List<Plot> handle(final @NonNull AutoQuery autoQuery) {
045            return Collections.emptyList();
046        }
047
048    }
049
050    final class SinglePlotService implements AutoService, Predicate<AutoQuery> {
051
052        @Nullable
053        @Override
054        public List<Plot> handle(@NonNull AutoQuery autoQuery) {
055            Plot plot;
056            PlotId nextId = autoQuery.startId();
057            do {
058                synchronized (plotLock) {
059                    plot = autoQuery.plotArea().getNextFreePlot(autoQuery.player(), nextId);
060                    if (plot != null && plotCandidateCache.getIfPresent(plot.getId()) == null) {
061                        plotCandidateCache.put(plot.getId(), plot);
062                        return Collections.singletonList(plot);
063                    }
064                    // if the plot is already in the cache, we want to make sure we skip it the next time
065                    if (plot != null) {
066                        nextId = plot.getId();
067                    }
068                }
069            } while (plot != null);
070            return null;
071        }
072
073        @Override
074        public boolean test(final @NonNull AutoQuery autoQuery) {
075            return autoQuery.sizeX() == 1 && autoQuery.sizeZ() == 1;
076        }
077
078    }
079
080    final class MultiPlotService implements AutoService, Predicate<AutoQuery> {
081
082        @Override
083        public List<Plot> handle(final @NonNull AutoQuery autoQuery) {
084            /* TODO: Add timeout? */
085            outer:
086            while (true) {
087                synchronized (plotLock) {
088                    final PlotId start =
089                            autoQuery.plotArea().getMeta("lastPlot", PlotId.of(0, 0)).getNextId();
090                    final PlotId end = PlotId.of(
091                            start.getX() + autoQuery.sizeX() - 1,
092                            start.getY() + autoQuery.sizeZ() - 1
093                    );
094                    final List<Plot> plots =
095                            autoQuery.plotArea().canClaim(autoQuery.player(), start, end);
096                    autoQuery.plotArea().setMeta("lastPlot", start); // set entry point for next try
097                    if (plots != null && !plots.isEmpty()) {
098                        for (final Plot plot : plots) {
099                            if (plotCandidateCache.getIfPresent(plot.getId()) != null) {
100                                continue outer;
101                            }
102                            plotCandidateCache.put(plot.getId(), plot);
103                        }
104                        return plots;
105                    }
106                }
107            }
108        }
109
110        @Override
111        public boolean test(final @NonNull AutoQuery autoQuery) {
112            return autoQuery.plotArea().getType() != PlotAreaType.PARTIAL;
113        }
114
115    }
116
117}