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