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.command; 020 021import com.google.inject.Inject; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.configuration.Settings; 024import com.plotsquared.core.configuration.caption.TranslatableCaption; 025import com.plotsquared.core.database.DBFunc; 026import com.plotsquared.core.listener.PlotListener; 027import com.plotsquared.core.player.PlotPlayer; 028import com.plotsquared.core.plot.Plot; 029import com.plotsquared.core.plot.PlotArea; 030import com.plotsquared.core.plot.PlotId; 031import com.plotsquared.core.plot.world.PlotAreaManager; 032import com.plotsquared.core.util.StringMan; 033import com.plotsquared.core.util.query.PlotQuery; 034import com.plotsquared.core.util.task.TaskManager; 035import com.plotsquared.core.uuid.UUIDMapping; 036import net.kyori.adventure.text.minimessage.Template; 037import org.apache.logging.log4j.LogManager; 038import org.apache.logging.log4j.Logger; 039import org.checkerframework.checker.nullness.qual.NonNull; 040 041import java.util.HashMap; 042import java.util.HashSet; 043import java.util.Iterator; 044import java.util.Map.Entry; 045import java.util.UUID; 046import java.util.concurrent.atomic.AtomicBoolean; 047 048@CommandDeclaration(usage = "/plot purge world:<world> area:<area> id:<id> owner:<owner> shared:<shared> unknown:[true | false] clear:[true | false]", 049 command = "purge", 050 permission = "plots.admin", 051 category = CommandCategory.ADMINISTRATION, 052 requiredType = RequiredType.CONSOLE, 053 confirmation = true) 054public class Purge extends SubCommand { 055 056 private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Purge.class.getSimpleName()); 057 058 private final PlotAreaManager plotAreaManager; 059 private final PlotListener plotListener; 060 061 @Inject 062 public Purge( 063 final @NonNull PlotAreaManager plotAreaManager, 064 final @NonNull PlotListener plotListener 065 ) { 066 this.plotAreaManager = plotAreaManager; 067 this.plotListener = plotListener; 068 } 069 070 @Override 071 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 072 if (args.length == 0) { 073 sendUsage(player); 074 return false; 075 } 076 077 String world = null; 078 PlotArea area = null; 079 PlotId id = null; 080 UUID owner = null; 081 UUID added = null; 082 boolean clear = false; 083 boolean unknown = false; 084 for (String arg : args) { 085 String[] split = arg.split(":"); 086 if (split.length != 2) { 087 sendUsage(player); 088 return false; 089 } 090 switch (split[0].toLowerCase()) { 091 case "world": 092 case "w": 093 world = split[1]; 094 break; 095 case "area": 096 case "a": 097 area = this.plotAreaManager.getPlotAreaByString(split[1]); 098 if (area == null) { 099 player.sendMessage( 100 TranslatableCaption.of("errors.not_valid_plot_world"), 101 Template.of("value", split[1]) 102 ); 103 return false; 104 } 105 break; 106 case "plotid": 107 case "id": 108 try { 109 id = PlotId.fromString(split[1]); 110 } catch (IllegalArgumentException ignored) { 111 player.sendMessage( 112 TranslatableCaption.of("invalid.not_valid_plot_id"), 113 Template.of("value", split[1]) 114 ); 115 return false; 116 } 117 break; 118 case "owner": 119 case "o": 120 UUIDMapping ownerMapping = PlotSquared.get().getImpromptuUUIDPipeline().getImmediately(split[1]); 121 if (ownerMapping == null) { 122 player.sendMessage( 123 TranslatableCaption.of("errors.invalid_player"), 124 Template.of("value", split[1]) 125 ); 126 return false; 127 } 128 owner = ownerMapping.getUuid(); 129 break; 130 case "shared": 131 case "s": 132 UUIDMapping addedMapping = PlotSquared.get().getImpromptuUUIDPipeline().getImmediately(split[1]); 133 if (addedMapping == null) { 134 player.sendMessage( 135 TranslatableCaption.of("errors.invalid_player"), 136 Template.of("value", split[1]) 137 ); 138 return false; 139 } 140 added = addedMapping.getUuid(); 141 break; 142 case "clear": 143 case "c": 144 case "delete": 145 case "d": 146 case "del": 147 clear = Boolean.parseBoolean(split[1]); 148 break; 149 case "unknown": 150 case "?": 151 case "u": 152 unknown = Boolean.parseBoolean(split[1]); 153 break; 154 default: 155 sendUsage(player); 156 return false; 157 } 158 } 159 final HashSet<Plot> toDelete = new HashSet<>(); 160 for (Plot plot : PlotQuery.newQuery().whereBasePlot()) { 161 if (world != null && !plot.getWorldName().equalsIgnoreCase(world)) { 162 continue; 163 } 164 if (area != null && !plot.getArea().equals(area)) { 165 continue; 166 } 167 if (id != null && !plot.getId().equals(id)) { 168 continue; 169 } 170 if (owner != null && !plot.isOwner(owner)) { 171 continue; 172 } 173 if (added != null && !plot.isAdded(added)) { 174 continue; 175 } 176 if (unknown) { 177 UUIDMapping uuidMapping = PlotSquared.get().getImpromptuUUIDPipeline().getImmediately(plot.getOwner()); 178 if (uuidMapping != null) { 179 continue; 180 } 181 } 182 toDelete.addAll(plot.getConnectedPlots()); 183 } 184 if (PlotSquared.get().plots_tmp != null) { 185 for (Entry<String, HashMap<PlotId, Plot>> entry : PlotSquared.get().plots_tmp 186 .entrySet()) { 187 String worldName = entry.getKey(); 188 if (world != null && !world.equalsIgnoreCase(worldName)) { 189 continue; 190 } 191 for (Entry<PlotId, Plot> entry2 : entry.getValue().entrySet()) { 192 Plot plot = entry2.getValue(); 193 if (area != null && !plot.getArea().equals(area)) { 194 continue; 195 } 196 if (id != null && !plot.getId().equals(id)) { 197 continue; 198 } 199 if (owner != null && !plot.isOwner(owner)) { 200 continue; 201 } 202 if (added != null && !plot.isAdded(added)) { 203 continue; 204 } 205 if (unknown) { 206 UUIDMapping addedMapping = PlotSquared.get().getImpromptuUUIDPipeline().getImmediately(plot.getOwner()); 207 if (addedMapping != null) { 208 continue; 209 } 210 } 211 toDelete.add(plot); 212 } 213 } 214 } 215 if (toDelete.isEmpty()) { 216 player.sendMessage(TranslatableCaption.of("invalid.found_no_plots")); 217 return false; 218 } 219 String cmd = 220 "/plot purge " + StringMan.join(args, " ") + " (" + toDelete.size() + " plots)"; 221 boolean finalClear = clear; 222 Runnable run = () -> { 223 LOGGER.info("Calculating plots to purge, please wait..."); 224 HashSet<Integer> ids = new HashSet<>(); 225 Iterator<Plot> iterator = toDelete.iterator(); 226 AtomicBoolean cleared = new AtomicBoolean(true); 227 Runnable runasync = new Runnable() { 228 @Override 229 public void run() { 230 while (iterator.hasNext() && cleared.get()) { 231 cleared.set(false); 232 Plot plot = iterator.next(); 233 if (plot.temp != Integer.MAX_VALUE) { 234 try { 235 ids.add(plot.temp); 236 if (finalClear) { 237 plot.getPlotModificationManager().clear(false, true, player, () -> { 238 LOGGER.info("Plot {} cleared by purge", plot.getId()); 239 }); 240 } else { 241 plot.getPlotModificationManager().removeSign(); 242 } 243 plot.getArea().removePlot(plot.getId()); 244 for (PlotPlayer<?> pp : plot.getPlayersInPlot()) { 245 Purge.this.plotListener.plotEntry(pp, plot); 246 } 247 } catch (NullPointerException e) { 248 LOGGER.error("NullPointer during purge detected. This is likely" 249 + " because you are deleting a world that has been removed", e); 250 } 251 } 252 cleared.set(true); 253 } 254 if (iterator.hasNext()) { 255 TaskManager.runTaskAsync(this); 256 } else { 257 TaskManager.runTask(() -> { 258 DBFunc.purgeIds(ids); 259 player.sendMessage( 260 TranslatableCaption.of("purge.purge_success"), 261 Template.of("amount", ids.size() + "/" + toDelete.size()) 262 ); 263 }); 264 } 265 } 266 }; 267 TaskManager.runTaskAsync(runasync); 268 }; 269 if (hasConfirmation(player)) { 270 if (unknown) { 271 if (Settings.UUID.BACKGROUND_CACHING_ENABLED) { 272 player.sendMessage(TranslatableCaption.of("purge.confirm_purge_unknown_bg_enabled")); 273 } else { 274 player.sendMessage(TranslatableCaption.of("purge.confirm_purge_unknown_bg_disabled")); 275 } 276 } 277 CmdConfirm.addPending(player, cmd, run); 278 } else { 279 run.run(); 280 } 281 return true; 282 } 283 284}