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.caption.StaticCaption; 024import com.plotsquared.core.configuration.caption.TranslatableCaption; 025import com.plotsquared.core.location.Location; 026import com.plotsquared.core.player.PlotPlayer; 027import com.plotsquared.core.plot.Plot; 028import com.plotsquared.core.plot.world.PlotAreaManager; 029import com.plotsquared.core.queue.GlobalBlockQueue; 030import com.plotsquared.core.queue.QueueCoordinator; 031import com.plotsquared.core.util.RegionManager; 032import com.plotsquared.core.util.RegionUtil; 033import com.plotsquared.core.util.WorldUtil; 034import com.plotsquared.core.util.query.PlotQuery; 035import com.plotsquared.core.util.task.RunnableVal; 036import com.plotsquared.core.util.task.RunnableVal2; 037import com.plotsquared.core.util.task.TaskManager; 038import com.plotsquared.core.util.task.TaskTime; 039import com.sk89q.worldedit.math.BlockVector2; 040import com.sk89q.worldedit.regions.CuboidRegion; 041import org.apache.logging.log4j.LogManager; 042import org.apache.logging.log4j.Logger; 043import org.checkerframework.checker.nullness.qual.NonNull; 044 045import java.util.HashSet; 046import java.util.Iterator; 047import java.util.List; 048import java.util.Set; 049 050@CommandDeclaration(command = "trim", 051 permission = "plots.admin", 052 usage = "/plot trim <world> [regenerate]", 053 requiredType = RequiredType.CONSOLE, 054 category = CommandCategory.ADMINISTRATION) 055public class Trim extends SubCommand { 056 057 private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + Trim.class.getSimpleName()); 058 private static volatile boolean TASK = false; 059 060 private final PlotAreaManager plotAreaManager; 061 private final WorldUtil worldUtil; 062 private final GlobalBlockQueue blockQueue; 063 private final RegionManager regionManager; 064 065 @Inject 066 public Trim( 067 final @NonNull PlotAreaManager plotAreaManager, 068 final @NonNull WorldUtil worldUtil, 069 final @NonNull GlobalBlockQueue blockQueue, 070 final @NonNull RegionManager regionManager 071 ) { 072 this.plotAreaManager = plotAreaManager; 073 this.worldUtil = worldUtil; 074 this.blockQueue = blockQueue; 075 this.regionManager = regionManager; 076 } 077 078 /** 079 * Runs the result task with the parameters (viable, nonViable). 080 * 081 * @param world The world 082 * @param result (viable = .mcr to trim, nonViable = .mcr keep) 083 * @return success or not 084 */ 085 public static boolean getTrimRegions( 086 String world, 087 final RunnableVal2<Set<BlockVector2>, Set<BlockVector2>> result 088 ) { 089 if (result == null) { 090 return false; 091 } 092 TranslatableCaption.of("trim.trim_starting"); 093 final List<Plot> plots = PlotQuery.newQuery().inWorld(world).asList(); 094 if (PlotSquared.platform().expireManager() != null) { 095 plots.removeAll(PlotSquared.platform().expireManager().getPendingExpired()); 096 } 097 result.value1 = new HashSet<>(PlotSquared.platform().worldUtil().getChunkChunks(world)); 098 result.value2 = new HashSet<>(); 099 StaticCaption.of(" - MCA #: " + result.value1.size()); 100 StaticCaption.of(" - CHUNKS: " + (result.value1.size() * 1024) + " (max)"); 101 StaticCaption.of(" - TIME ESTIMATE: 12 Parsecs"); 102 TaskManager.getPlatformImplementation().objectTask(plots, new RunnableVal<>() { 103 @Override 104 public void run(Plot plot) { 105 Location pos1 = plot.getCorners()[0]; 106 Location pos2 = plot.getCorners()[1]; 107 int ccx1 = pos1.getX() >> 9; 108 int ccz1 = pos1.getZ() >> 9; 109 int ccx2 = pos2.getX() >> 9; 110 int ccz2 = pos2.getZ() >> 9; 111 for (int x = ccx1; x <= ccx2; x++) { 112 for (int z = ccz1; z <= ccz2; z++) { 113 BlockVector2 loc = BlockVector2.at(x, z); 114 if (result.value1.remove(loc)) { 115 result.value2.add(loc); 116 } 117 } 118 } 119 } 120 }).thenAccept(ignore -> 121 TaskManager.getPlatformImplementation().taskLater(result, TaskTime.ticks(1L))); 122 return true; 123 } 124 125 @Override 126 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 127 if (args.length == 0) { 128 sendUsage(player); 129 return false; 130 } 131 final String world = args[0]; 132 if (!this.worldUtil.isWorld(world) || !this.plotAreaManager.hasPlotArea(world)) { 133 player.sendMessage(TranslatableCaption.of("errors.not_valid_world")); 134 return false; 135 } 136 if (Trim.TASK) { 137 player.sendMessage(TranslatableCaption.of("trim.trim_in_progress")); 138 return false; 139 } 140 Trim.TASK = true; 141 final boolean regen = args.length == 2 && Boolean.parseBoolean(args[1]); 142 getTrimRegions(world, new RunnableVal2<>() { 143 @Override 144 public void run(Set<BlockVector2> viable, final Set<BlockVector2> nonViable) { 145 Runnable regenTask; 146 if (regen) { 147 LOGGER.info("Starting regen task"); 148 LOGGER.info(" - This is a VERY slow command"); 149 LOGGER.info(" - It will say 'Trim done!' when complete"); 150 regenTask = new Runnable() { 151 @Override 152 public void run() { 153 if (nonViable.isEmpty()) { 154 Trim.TASK = false; 155 player.sendMessage(TranslatableCaption.of("trim.trim_done")); 156 LOGGER.info("Trim done!"); 157 return; 158 } 159 Iterator<BlockVector2> iterator = nonViable.iterator(); 160 BlockVector2 mcr = iterator.next(); 161 iterator.remove(); 162 int cbx = mcr.getX() << 5; 163 int cbz = mcr.getZ() << 5; 164 // get all 1024 chunks 165 HashSet<BlockVector2> chunks = new HashSet<>(); 166 for (int x = cbx; x < cbx + 32; x++) { 167 for (int z = cbz; z < cbz + 32; z++) { 168 BlockVector2 loc = BlockVector2.at(x, z); 169 chunks.add(loc); 170 } 171 } 172 int bx = cbx << 4; 173 int bz = cbz << 4; 174 CuboidRegion region = 175 RegionUtil.createRegion(bx, bx + 511, 0, 0, bz, bz + 511); 176 for (Plot plot : PlotQuery.newQuery().inWorld(world)) { 177 Location bot = plot.getBottomAbs(); 178 Location top = plot.getExtendedTopAbs(); 179 CuboidRegion plotReg = RegionUtil 180 .createRegion(bot.getX(), top.getX(), 0, 0, bot.getZ(), top.getZ()); 181 if (!RegionUtil.intersects(region, plotReg)) { 182 continue; 183 } 184 for (int x = plotReg.getMinimumPoint().getX() >> 4; 185 x <= plotReg.getMaximumPoint().getX() >> 4; x++) { 186 for (int z = plotReg.getMinimumPoint().getZ() >> 4; 187 z <= plotReg.getMaximumPoint().getZ() >> 4; z++) { 188 BlockVector2 loc = BlockVector2.at(x, z); 189 chunks.remove(loc); 190 } 191 } 192 } 193 final QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(world)); 194 TaskManager.getPlatformImplementation().objectTask(chunks, new RunnableVal<>() { 195 @Override 196 public void run(BlockVector2 value) { 197 queue.regenChunk(value.getX(), value.getZ()); 198 } 199 }).thenAccept(ignore -> TaskManager.getPlatformImplementation() 200 .taskLater(this, TaskTime.ticks(1L))); 201 } 202 }; 203 } else { 204 regenTask = () -> { 205 Trim.TASK = false; 206 player.sendMessage(TranslatableCaption.of("trim.trim_done")); 207 LOGGER.info("Trim done!"); 208 }; 209 } 210 regionManager.deleteRegionFiles(world, viable, regenTask); 211 212 } 213 }); 214 return true; 215 } 216 217}