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.generator; 020 021import com.google.common.collect.Sets; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.command.Template; 024import com.plotsquared.core.configuration.Settings; 025import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; 026import com.plotsquared.core.location.Location; 027import com.plotsquared.core.player.PlotPlayer; 028import com.plotsquared.core.plot.Plot; 029import com.plotsquared.core.plot.PlotAreaTerrainType; 030import com.plotsquared.core.plot.PlotAreaType; 031import com.plotsquared.core.plot.PlotId; 032import com.plotsquared.core.queue.QueueCoordinator; 033import com.plotsquared.core.util.FileBytes; 034import com.plotsquared.core.util.FileUtils; 035import com.plotsquared.core.util.MathMan; 036import com.plotsquared.core.util.RegionManager; 037import com.plotsquared.core.util.WorldUtil; 038import com.sk89q.worldedit.function.pattern.Pattern; 039import com.sk89q.worldedit.regions.CuboidRegion; 040import com.sk89q.worldedit.world.biome.BiomeType; 041import com.sk89q.worldedit.world.block.BaseBlock; 042import com.sk89q.worldedit.world.block.BlockTypes; 043import org.checkerframework.checker.nullness.qual.NonNull; 044import org.checkerframework.checker.nullness.qual.Nullable; 045 046import java.io.File; 047import java.io.IOException; 048import java.nio.file.Files; 049import java.util.HashSet; 050import java.util.Objects; 051 052public class HybridPlotManager extends ClassicPlotManager { 053 054 public static boolean REGENERATIVE_CLEAR = true; 055 056 private final HybridPlotWorld hybridPlotWorld; 057 private final RegionManager regionManager; 058 private final ProgressSubscriberFactory subscriberFactory; 059 060 public HybridPlotManager( 061 final @NonNull HybridPlotWorld hybridPlotWorld, 062 final @NonNull RegionManager regionManager, 063 @NonNull ProgressSubscriberFactory subscriberFactory 064 ) { 065 super(hybridPlotWorld, regionManager); 066 this.hybridPlotWorld = hybridPlotWorld; 067 this.regionManager = regionManager; 068 this.subscriberFactory = subscriberFactory; 069 } 070 071 @Override 072 public void exportTemplate() throws IOException { 073 HashSet<FileBytes> files = Sets.newHashSet(new FileBytes( 074 Settings.Paths.TEMPLATES + "/tmp-data.yml", 075 Template.getBytes(hybridPlotWorld) 076 )); 077 String dir = 078 Settings.Paths.SCHEMATICS + File.separator + "GEN_ROAD_SCHEMATIC" + File.separator + hybridPlotWorld.getWorldName() + File.separator; 079 try { 080 File sideRoad = FileUtils.getFile(PlotSquared.platform().getDirectory(), dir + "sideroad.schem"); 081 String newDir = 082 Settings.Paths.SCHEMATICS + File.separator + "GEN_ROAD_SCHEMATIC" + File.separator + "__TEMP_DIR__" + File.separator; 083 if (sideRoad.exists()) { 084 files.add(new FileBytes(newDir + "sideroad.schem", Files.readAllBytes(sideRoad.toPath()))); 085 } 086 File intersection = FileUtils.getFile(PlotSquared.platform().getDirectory(), dir + "intersection.schem"); 087 if (intersection.exists()) { 088 files.add(new FileBytes(newDir + "intersection.schem", Files.readAllBytes(intersection.toPath()))); 089 } 090 File plot = FileUtils.getFile(PlotSquared.platform().getDirectory(), dir + "plot.schem"); 091 if (plot.exists()) { 092 files.add(new FileBytes(newDir + "plot.schem", Files.readAllBytes(plot.toPath()))); 093 } 094 } catch (IOException e) { 095 e.printStackTrace(); 096 } 097 Template.zipAll(hybridPlotWorld.getWorldName(), files); 098 } 099 100 @Override 101 public boolean createRoadEast(final @NonNull Plot plot, @Nullable QueueCoordinator queue) { 102 boolean enqueue = false; 103 if (queue == null) { 104 queue = hybridPlotWorld.getQueue(); 105 enqueue = true; 106 } 107 super.createRoadEast(plot, queue); 108 PlotId id = plot.getId(); 109 PlotId id2 = PlotId.of(id.getX() + 1, id.getY()); 110 Location bot = getPlotBottomLocAbs(id2); 111 Location top = getPlotTopLocAbs(id); 112 Location pos1 = Location.at( 113 hybridPlotWorld.getWorldName(), 114 top.getX() + 1, 115 hybridPlotWorld.getMinGenHeight(), 116 bot.getZ() - 1 117 ); 118 Location pos2 = Location.at( 119 hybridPlotWorld.getWorldName(), 120 bot.getX(), 121 hybridPlotWorld.getMaxGenHeight(), 122 top.getZ() + 1 123 ); 124 this.resetBiome(hybridPlotWorld, pos1, pos2); 125 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 126 return true; 127 } 128 createSchemAbs(queue, pos1, pos2, true); 129 return !enqueue || queue.enqueue(); 130 } 131 132 private void resetBiome( 133 final @NonNull HybridPlotWorld hybridPlotWorld, 134 final @NonNull Location pos1, 135 final @NonNull Location pos2 136 ) { 137 BiomeType biome = hybridPlotWorld.getPlotBiome(); 138 if (!Objects.equals(PlotSquared.platform().worldUtil() 139 .getBiomeSynchronous( 140 hybridPlotWorld.getWorldName(), 141 (pos1.getX() + pos2.getX()) / 2, 142 (pos1.getZ() + pos2.getZ()) / 2 143 ), biome)) { 144 WorldUtil.setBiome( 145 hybridPlotWorld.getWorldName(), 146 new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()), 147 biome 148 ); 149 } 150 } 151 152 private void createSchemAbs( 153 final @NonNull QueueCoordinator queue, 154 final @NonNull Location pos1, 155 final @NonNull Location pos2, 156 boolean isRoad 157 ) { 158 int size = hybridPlotWorld.SIZE; 159 int minY; 160 if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { 161 minY = hybridPlotWorld.SCHEM_Y; 162 } else { 163 minY = hybridPlotWorld.getMinBuildHeight(); 164 } 165 int schemYDiff = (isRoad ? hybridPlotWorld.getRoadYStart() : hybridPlotWorld.getPlotYStart()) - minY; 166 BaseBlock airBlock = BlockTypes.AIR.getDefaultState().toBaseBlock(); 167 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 168 short absX = (short) ((x - hybridPlotWorld.ROAD_OFFSET_X) % size); 169 if (absX < 0) { 170 absX += size; 171 } 172 for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { 173 short absZ = (short) ((z - hybridPlotWorld.ROAD_OFFSET_Z) % size); 174 if (absZ < 0) { 175 absZ += size; 176 } 177 BaseBlock[] blocks = hybridPlotWorld.G_SCH.get(MathMan.pair(absX, absZ)); 178 if (blocks != null) { 179 for (int y = 0; y < blocks.length; y++) { 180 if (blocks[y] != null) { 181 queue.setBlock(x, minY + y, z, blocks[y]); 182 } else if (y > schemYDiff) { 183 // This is necessary, otherwise any blocks not specified in the schematic will remain after a clear. 184 // This should only be done where the schematic has actually "started" 185 queue.setBlock(x, minY + y, z, airBlock); 186 } else if (isRoad) { 187 queue.setBlock(x, minY + y, z, hybridPlotWorld.ROAD_BLOCK.toPattern()); 188 } else { 189 queue.setBlock(x, minY + y, z, hybridPlotWorld.MAIN_BLOCK.toPattern()); 190 } 191 } 192 } 193 BiomeType biome = hybridPlotWorld.G_SCH_B.get(MathMan.pair(absX, absZ)); 194 if (biome != null) { 195 queue.setBiome(x, z, biome); 196 } else { 197 queue.setBiome(x, z, hybridPlotWorld.getPlotBiome()); 198 } 199 } 200 } 201 } 202 203 @Override 204 public boolean createRoadSouth(final @NonNull Plot plot, @Nullable QueueCoordinator queue) { 205 boolean enqueue = false; 206 if (queue == null) { 207 enqueue = true; 208 queue = hybridPlotWorld.getQueue(); 209 } 210 super.createRoadSouth(plot, queue); 211 PlotId id = plot.getId(); 212 PlotId id2 = PlotId.of(id.getX(), id.getY() + 1); 213 Location bot = getPlotBottomLocAbs(id2); 214 Location top = getPlotTopLocAbs(id); 215 Location pos1 = Location.at( 216 hybridPlotWorld.getWorldName(), 217 bot.getX() - 1, 218 hybridPlotWorld.getMinGenHeight(), 219 top.getZ() + 1 220 ); 221 Location pos2 = Location.at( 222 hybridPlotWorld.getWorldName(), 223 top.getX() + 1, 224 hybridPlotWorld.getMaxGenHeight(), 225 bot.getZ() 226 ); 227 this.resetBiome(hybridPlotWorld, pos1, pos2); 228 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 229 return true; 230 } 231 createSchemAbs(queue, pos1, pos2, true); 232 return !enqueue || queue.enqueue(); 233 } 234 235 @Override 236 public boolean createRoadSouthEast(final @NonNull Plot plot, @Nullable QueueCoordinator queue) { 237 boolean enqueue = false; 238 if (queue == null) { 239 enqueue = true; 240 queue = hybridPlotWorld.getQueue(); 241 } 242 super.createRoadSouthEast(plot, queue); 243 PlotId id = plot.getId(); 244 PlotId id2 = PlotId.of(id.getX() + 1, id.getY() + 1); 245 Location pos1 = getPlotTopLocAbs(id).add(1, 0, 1); 246 Location pos2 = getPlotBottomLocAbs(id2); 247 createSchemAbs(queue, pos1, pos2, true); 248 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 249 createSchemAbs(queue, pos1, pos2, true); 250 } 251 return !enqueue || queue.enqueue(); 252 } 253 254 @Override 255 public boolean clearPlot( 256 final @NonNull Plot plot, 257 final @Nullable Runnable whenDone, 258 @Nullable PlotPlayer<?> actor, 259 @Nullable QueueCoordinator queue 260 ) { 261 if (this.regionManager.notifyClear(this)) { 262 //If this returns false, the clear didn't work 263 if (this.regionManager.handleClear(plot, whenDone, this, actor)) { 264 return true; 265 } 266 } 267 final Location pos1 = plot.getBottomAbs(); 268 final Location pos2 = plot.getExtendedTopAbs(); 269 // If augmented 270 final boolean canRegen = 271 (hybridPlotWorld.getType() == PlotAreaType.AUGMENTED) && (hybridPlotWorld.getTerrain() != PlotAreaTerrainType.NONE) && REGENERATIVE_CLEAR; 272 // The component blocks 273 final Pattern plotfloor = hybridPlotWorld.TOP_BLOCK.toPattern(); 274 final Pattern filling = hybridPlotWorld.MAIN_BLOCK.toPattern(); 275 276 final Pattern bedrock; 277 if (hybridPlotWorld.PLOT_BEDROCK) { 278 bedrock = BlockTypes.BEDROCK.getDefaultState(); 279 } else { 280 bedrock = hybridPlotWorld.MAIN_BLOCK.toPattern(); 281 } 282 283 final BiomeType biome = hybridPlotWorld.getPlotBiome(); 284 boolean enqueue = false; 285 if (queue == null) { 286 enqueue = true; 287 queue = hybridPlotWorld.getQueue(); 288 } 289 if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { 290 queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); 291 } 292 if (whenDone != null) { 293 queue.setCompleteTask(whenDone); 294 } 295 if (!canRegen) { 296 if (hybridPlotWorld.getMinBuildHeight() < hybridPlotWorld.getMinGenHeight()) { 297 queue.setCuboid( 298 pos1.withY(hybridPlotWorld.getMinBuildHeight()), 299 pos2.withY(hybridPlotWorld.getMinGenHeight()), 300 BlockTypes.AIR.getDefaultState() 301 ); 302 } 303 queue.setCuboid( 304 pos1.withY(hybridPlotWorld.getMinGenHeight()), 305 pos2.withY(hybridPlotWorld.getMinGenHeight()), 306 hybridPlotWorld.PLOT_BEDROCK ? bedrock : filling 307 ); 308 // Each component has a different layer 309 queue.setCuboid( 310 pos1.withY(hybridPlotWorld.getMinGenHeight() + 1), 311 pos2.withY(hybridPlotWorld.PLOT_HEIGHT - 1), 312 filling 313 ); 314 queue.setCuboid(pos1.withY(hybridPlotWorld.PLOT_HEIGHT), pos2.withY(hybridPlotWorld.PLOT_HEIGHT), plotfloor); 315 queue.setCuboid( 316 pos1.withY(hybridPlotWorld.PLOT_HEIGHT + 1), 317 pos2.withY(hybridPlotWorld.getMaxGenHeight()), 318 BlockTypes.AIR.getDefaultState() 319 ); 320 if (hybridPlotWorld.getMaxGenHeight() < hybridPlotWorld.getMaxBuildHeight() - 1) { 321 queue.setCuboid( 322 pos1.withY(hybridPlotWorld.getMaxGenHeight()), 323 pos2.withY(hybridPlotWorld.getMaxBuildHeight() - 1), 324 BlockTypes.AIR.getDefaultState() 325 ); 326 } 327 queue.setBiomeCuboid(pos1, pos2, biome); 328 } else { 329 queue.setRegenRegion(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3())); 330 } 331 pastePlotSchematic(queue, pos1, pos2); 332 return !enqueue || queue.enqueue(); 333 } 334 335 public void pastePlotSchematic( 336 final @NonNull QueueCoordinator queue, 337 final @NonNull Location bottom, 338 final @NonNull Location top 339 ) { 340 if (!hybridPlotWorld.PLOT_SCHEMATIC) { 341 return; 342 } 343 createSchemAbs(queue, bottom, top, false); 344 } 345 346 /** 347 * Retrieves the location of where a sign should be for a plot. 348 * 349 * @param plot The plot 350 * @return The location where a sign should be 351 */ 352 @Override 353 public Location getSignLoc(final @NonNull Plot plot) { 354 return hybridPlotWorld.getSignLocation(plot); 355 } 356 357 public HybridPlotWorld getHybridPlotWorld() { 358 return this.hybridPlotWorld; 359 } 360 361}