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(hybridPlotWorld.getWorldName(), new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()), biome); 145 } 146 } 147 148 private void createSchemAbs( 149 final @NonNull QueueCoordinator queue, 150 final @NonNull Location pos1, 151 final @NonNull Location pos2, 152 boolean isRoad 153 ) { 154 int size = hybridPlotWorld.SIZE; 155 int minY; 156 if ((isRoad && Settings.Schematics.PASTE_ROAD_ON_TOP) || (!isRoad && Settings.Schematics.PASTE_ON_TOP)) { 157 minY = hybridPlotWorld.SCHEM_Y; 158 } else { 159 minY = hybridPlotWorld.getMinBuildHeight(); 160 } 161 BaseBlock airBlock = BlockTypes.AIR.getDefaultState().toBaseBlock(); 162 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 163 short absX = (short) ((x - hybridPlotWorld.ROAD_OFFSET_X) % size); 164 if (absX < 0) { 165 absX += size; 166 } 167 for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { 168 short absZ = (short) ((z - hybridPlotWorld.ROAD_OFFSET_Z) % size); 169 if (absZ < 0) { 170 absZ += size; 171 } 172 BaseBlock[] blocks = hybridPlotWorld.G_SCH.get(MathMan.pair(absX, absZ)); 173 if (blocks != null) { 174 for (int y = 0; y < blocks.length; y++) { 175 if (blocks[y] != null) { 176 queue.setBlock(x, minY + y, z, blocks[y]); 177 } else if (!isRoad) { 178 // This is necessary, otherwise any blocks not specified in the schematic will remain after a clear 179 // Do not set air for road as this may cause cavernous roads when debugroadregen is used 180 queue.setBlock(x, minY + y, z, airBlock); 181 } 182 } 183 } 184 BiomeType biome = hybridPlotWorld.G_SCH_B.get(MathMan.pair(absX, absZ)); 185 if (biome != null) { 186 queue.setBiome(x, z, biome); 187 } else { 188 queue.setBiome(x, z, hybridPlotWorld.getPlotBiome()); 189 } 190 } 191 } 192 } 193 194 @Override 195 public boolean createRoadSouth(final @NonNull Plot plot, @Nullable QueueCoordinator queue) { 196 boolean enqueue = false; 197 if (queue == null) { 198 enqueue = true; 199 queue = hybridPlotWorld.getQueue(); 200 } 201 super.createRoadSouth(plot, queue); 202 PlotId id = plot.getId(); 203 PlotId id2 = PlotId.of(id.getX(), id.getY() + 1); 204 Location bot = getPlotBottomLocAbs(id2); 205 Location top = getPlotTopLocAbs(id); 206 Location pos1 = Location.at(hybridPlotWorld.getWorldName(), bot.getX() - 1, hybridPlotWorld.getMinGenHeight(), top.getZ() + 1); 207 Location pos2 = Location.at(hybridPlotWorld.getWorldName(), top.getX() + 1, hybridPlotWorld.getMaxGenHeight(), bot.getZ()); 208 this.resetBiome(hybridPlotWorld, pos1, pos2); 209 if (!hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 210 return true; 211 } 212 createSchemAbs(queue, pos1, pos2, true); 213 return !enqueue || queue.enqueue(); 214 } 215 216 @Override 217 public boolean createRoadSouthEast(final @NonNull Plot plot, @Nullable QueueCoordinator queue) { 218 boolean enqueue = false; 219 if (queue == null) { 220 enqueue = true; 221 queue = hybridPlotWorld.getQueue(); 222 } 223 super.createRoadSouthEast(plot, queue); 224 PlotId id = plot.getId(); 225 PlotId id2 = PlotId.of(id.getX() + 1, id.getY() + 1); 226 Location pos1 = getPlotTopLocAbs(id).add(1, 0, 1); 227 Location pos2 = getPlotBottomLocAbs(id2); 228 createSchemAbs(queue, pos1, pos2, true); 229 if (hybridPlotWorld.ROAD_SCHEMATIC_ENABLED) { 230 createSchemAbs(queue, pos1, pos2, true); 231 } 232 return !enqueue || queue.enqueue(); 233 } 234 235 @Override 236 public boolean clearPlot( 237 final @NonNull Plot plot, 238 final @Nullable Runnable whenDone, 239 @Nullable PlotPlayer<?> actor, 240 @Nullable QueueCoordinator queue 241 ) { 242 if (this.regionManager.notifyClear(this)) { 243 //If this returns false, the clear didn't work 244 if (this.regionManager.handleClear(plot, whenDone, this, actor)) { 245 return true; 246 } 247 } 248 final Location pos1 = plot.getBottomAbs(); 249 final Location pos2 = plot.getExtendedTopAbs(); 250 // If augmented 251 final boolean canRegen = 252 (hybridPlotWorld.getType() == PlotAreaType.AUGMENTED) && (hybridPlotWorld.getTerrain() != PlotAreaTerrainType.NONE) && REGENERATIVE_CLEAR; 253 // The component blocks 254 final Pattern plotfloor = hybridPlotWorld.TOP_BLOCK.toPattern(); 255 final Pattern filling = hybridPlotWorld.MAIN_BLOCK.toPattern(); 256 257 final Pattern bedrock; 258 if (hybridPlotWorld.PLOT_BEDROCK) { 259 bedrock = BlockTypes.BEDROCK.getDefaultState(); 260 } else { 261 bedrock = hybridPlotWorld.MAIN_BLOCK.toPattern(); 262 } 263 264 final BiomeType biome = hybridPlotWorld.getPlotBiome(); 265 boolean enqueue = false; 266 if (queue == null) { 267 enqueue = true; 268 queue = hybridPlotWorld.getQueue(); 269 } 270 if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { 271 queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); 272 } 273 if (whenDone != null) { 274 queue.setCompleteTask(whenDone); 275 } 276 if (!canRegen) { 277 queue.setCuboid( 278 pos1.withY(hybridPlotWorld.getMinGenHeight()), 279 pos2.withY(hybridPlotWorld.getMinGenHeight()), 280 hybridPlotWorld.PLOT_BEDROCK ? bedrock : filling 281 ); 282 // Each component has a different layer 283 queue.setCuboid( 284 pos1.withY(hybridPlotWorld.getMinGenHeight() + 1), 285 pos2.withY(hybridPlotWorld.PLOT_HEIGHT - 1), 286 filling 287 ); 288 queue.setCuboid(pos1.withY(hybridPlotWorld.PLOT_HEIGHT), pos2.withY(hybridPlotWorld.PLOT_HEIGHT), plotfloor); 289 queue.setCuboid( 290 pos1.withY(hybridPlotWorld.PLOT_HEIGHT + 1), 291 pos2.withY(hybridPlotWorld.getMaxGenHeight()), 292 BlockTypes.AIR.getDefaultState() 293 ); 294 queue.setBiomeCuboid(pos1, pos2, biome); 295 } else { 296 queue.setRegenRegion(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3())); 297 } 298 pastePlotSchematic(queue, pos1, pos2); 299 return !enqueue || queue.enqueue(); 300 } 301 302 public void pastePlotSchematic( 303 final @NonNull QueueCoordinator queue, 304 final @NonNull Location bottom, 305 final @NonNull Location top 306 ) { 307 if (!hybridPlotWorld.PLOT_SCHEMATIC) { 308 return; 309 } 310 createSchemAbs(queue, bottom, top, false); 311 } 312 313 /** 314 * Retrieves the location of where a sign should be for a plot. 315 * 316 * @param plot The plot 317 * @return The location where a sign should be 318 */ 319 @Override 320 public Location getSignLoc(final @NonNull Plot plot) { 321 return hybridPlotWorld.getSignLocation(plot); 322 } 323 324 public HybridPlotWorld getHybridPlotWorld() { 325 return this.hybridPlotWorld; 326 } 327 328}