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.player.PlotPlayer; 025import com.plotsquared.core.plot.Plot; 026import com.plotsquared.core.plot.PlotArea; 027import com.plotsquared.core.plot.PlotAreaType; 028import com.plotsquared.core.plot.PlotId; 029import org.checkerframework.checker.nullness.qual.NonNull; 030import org.checkerframework.checker.nullness.qual.Nullable; 031 032import java.util.Collections; 033import java.util.List; 034import java.util.concurrent.TimeUnit; 035import java.util.function.Predicate; 036 037public interface AutoService extends Service<AutoService.AutoQuery, List<Plot>> { 038 039 Cache<PlotId, Plot> plotCandidateCache = CacheBuilder.newBuilder() 040 .expireAfterWrite(20, TimeUnit.SECONDS).build(); 041 Object plotLock = new Object(); 042 043 final class AutoQuery { 044 045 private final PlotPlayer<?> player; 046 private final PlotId startId; 047 private final int sizeX; 048 private final int sizeZ; 049 private final PlotArea plotArea; 050 051 /** 052 * Crate a new auto query 053 * 054 * @param player Player to claim for 055 * @param startId Plot ID to start searching from 056 * @param sizeX Number of plots along the X axis 057 * @param sizeZ Number of plots along the Z axis 058 * @param plotArea Plot area to search in 059 */ 060 public AutoQuery( 061 final @NonNull PlotPlayer<?> player, final @Nullable PlotId startId, 062 final int sizeX, final int sizeZ, final @NonNull PlotArea plotArea 063 ) { 064 this.player = player; 065 this.startId = startId; 066 this.sizeX = sizeX; 067 this.sizeZ = sizeZ; 068 this.plotArea = plotArea; 069 } 070 071 /** 072 * Get the player that the plots are meant for 073 * 074 * @return Player 075 */ 076 public @NonNull PlotPlayer<?> getPlayer() { 077 return this.player; 078 } 079 080 /** 081 * Get the plot ID to start searching from 082 * 083 * @return Start ID 084 */ 085 public @Nullable PlotId getStartId() { 086 return this.startId; 087 } 088 089 /** 090 * Get the number of plots along the X axis 091 * 092 * @return Number of plots along the X axis 093 */ 094 public int getSizeX() { 095 return this.sizeX; 096 } 097 098 /** 099 * Get the number of plots along the Z axis 100 * 101 * @return Number of plots along the Z axis 102 */ 103 public int getSizeZ() { 104 return this.sizeZ; 105 } 106 107 /** 108 * Get the plot area to search in 109 * 110 * @return Plot area 111 */ 112 public @NonNull PlotArea getPlotArea() { 113 return this.plotArea; 114 } 115 116 } 117 118 119 final class DefaultAutoService implements AutoService { 120 121 @Override 122 public List<Plot> handle(final @NonNull AutoQuery autoQuery) { 123 return Collections.emptyList(); 124 } 125 126 } 127 128 129 final class SinglePlotService implements AutoService, Predicate<AutoQuery> { 130 131 @Nullable 132 @Override 133 public List<Plot> handle(@NonNull AutoQuery autoQuery) { 134 Plot plot; 135 PlotId nextId = autoQuery.getStartId(); 136 do { 137 synchronized (plotLock) { 138 plot = autoQuery.getPlotArea().getNextFreePlot(autoQuery.getPlayer(), nextId); 139 if (plot != null && plotCandidateCache.getIfPresent(plot.getId()) == null) { 140 plotCandidateCache.put(plot.getId(), plot); 141 return Collections.singletonList(plot); 142 } 143 // if the plot is already in the cache, we want to make sure we skip it the next time 144 if (plot != null) { 145 nextId = plot.getId(); 146 } 147 } 148 } while (plot != null); 149 return null; 150 } 151 152 @Override 153 public boolean test(final @NonNull AutoQuery autoQuery) { 154 return autoQuery.sizeX == 1 && autoQuery.sizeZ == 1; 155 } 156 157 } 158 159 160 final class MultiPlotService implements AutoService, Predicate<AutoQuery> { 161 162 @Override 163 public List<Plot> handle(final @NonNull AutoQuery autoQuery) { 164 /* TODO: Add timeout? */ 165 outer: 166 while (true) { 167 synchronized (plotLock) { 168 final PlotId start = 169 autoQuery.getPlotArea().getMeta("lastPlot", PlotId.of(0, 0)).getNextId(); 170 final PlotId end = PlotId.of( 171 start.getX() + autoQuery.getSizeX() - 1, 172 start.getY() + autoQuery.getSizeZ() - 1 173 ); 174 final List<Plot> plots = 175 autoQuery.getPlotArea().canClaim(autoQuery.getPlayer(), start, end); 176 autoQuery.getPlotArea().setMeta("lastPlot", start); // set entry point for next try 177 if (plots != null && !plots.isEmpty()) { 178 for (final Plot plot : plots) { 179 if (plotCandidateCache.getIfPresent(plot.getId()) != null) { 180 continue outer; 181 } 182 plotCandidateCache.put(plot.getId(), plot); 183 } 184 return plots; 185 } 186 } 187 } 188 } 189 190 @Override 191 public boolean test(final @NonNull AutoQuery autoQuery) { 192 return autoQuery.getPlotArea().getType() != PlotAreaType.PARTIAL; 193 } 194 195 } 196 197}