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.queue; 020 021import com.google.inject.Inject; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.location.Location; 024import com.plotsquared.core.queue.subscriber.ProgressSubscriber; 025import com.plotsquared.core.util.PatternUtil; 026import com.sk89q.jnbt.CompoundTag; 027import com.sk89q.worldedit.entity.Entity; 028import com.sk89q.worldedit.function.pattern.Pattern; 029import com.sk89q.worldedit.math.BlockVector2; 030import com.sk89q.worldedit.regions.CuboidRegion; 031import com.sk89q.worldedit.util.SideEffectSet; 032import com.sk89q.worldedit.world.World; 033import com.sk89q.worldedit.world.biome.BiomeType; 034import com.sk89q.worldedit.world.block.BaseBlock; 035import com.sk89q.worldedit.world.block.BlockState; 036import org.checkerframework.checker.nullness.qual.NonNull; 037import org.checkerframework.checker.nullness.qual.Nullable; 038 039import java.util.List; 040import java.util.Set; 041import java.util.concurrent.atomic.AtomicBoolean; 042import java.util.function.Consumer; 043 044public abstract class QueueCoordinator { 045 046 private final AtomicBoolean enqueued = new AtomicBoolean(); 047 private boolean forceSync = false; 048 private boolean shouldGen = true; 049 @Nullable 050 private Object chunkObject; 051 @SuppressWarnings({"unused", "FieldCanBeLocal"}) 052 @Inject 053 private GlobalBlockQueue blockQueue; 054 055 /** 056 * Default constructor requires world to indicate any extents given to {@link QueueCoordinator} also need this constructor. 057 * 058 * @param world world as all queues should have this constructor 059 */ 060 public QueueCoordinator(@Nullable World world) { 061 PlotSquared.platform().injector().injectMembers(this); 062 } 063 064 /** 065 * Get a {@link ZeroedDelegateScopedQueueCoordinator} limited to the chunk at the specific chunk Coordinates 066 * 067 * @param x chunk x coordinate 068 * @param z chunk z coordinate 069 * @return a new {@link ZeroedDelegateScopedQueueCoordinator} 070 * @since 7.0.0 071 */ 072 public ZeroedDelegateScopedQueueCoordinator getForChunk(int x, int z, int minY, int maxY) { 073 int bx = x << 4; 074 int bz = z << 4; 075 return new ZeroedDelegateScopedQueueCoordinator(this, Location.at(getWorld().getName(), bx, minY, bz), 076 Location.at(getWorld().getName(), bx + 15, maxY, bz + 15) 077 ); 078 } 079 080 /** 081 * Get the size of the queue in chunks 082 * 083 * @return size 084 */ 085 public abstract int size(); 086 087 /** 088 * Set when the queue was last modified 089 * 090 * @param modified long of system millis 091 */ 092 public abstract void setModified(long modified); 093 094 /** 095 * Returns true if the queue should be forced to be synchronous when enqueued. This is not necessarily synchronous to the 096 * server, and simply effectively makes {@link QueueCoordinator#enqueue()} a blocking operation. 097 * 098 * @return is force sync 099 */ 100 public boolean isForceSync() { 101 return forceSync; 102 } 103 104 /** 105 * Set whether the queue should be forced to be synchronous. This is not necessarily synchronous to the server, and simply 106 * effectively makes {@link QueueCoordinator#enqueue()} a blocking operation. 107 * 108 * @param forceSync force sync or not 109 */ 110 public void setForceSync(boolean forceSync) { 111 this.forceSync = forceSync; 112 } 113 114 115 /** 116 * Get whether chunks should be generated as part of this operation. Default is true. Disabling this may not be supported 117 * depending on server implementation. (i.e. setting to false may not actually disable generation as part of this operation 118 * - this is just a catch-all in case of future differing server implementations; the option will work on Spigot/Paper). 119 * 120 * @since TODO 121 */ 122 public boolean isShouldGen() { 123 return shouldGen; 124 } 125 126 /** 127 * Set whether chunks should be generated as part of this operation. Default is true. Disabling this may not be supported 128 * depending on server implementation. (i.e. setting to false may not actually disable generation as part of this operation 129 * - this is just a catch-all in case of future differing server implementations; the option will work on Spigot/Paper). 130 * 131 * @param shouldGen should generate new chunks or not 132 * @since TODO 133 */ 134 public void setShouldGen(boolean shouldGen) { 135 this.shouldGen = shouldGen; 136 } 137 138 /** 139 * Get the Chunk Object set to the queue 140 * 141 * @return chunk object. Usually the implementation-specific chunk (e.g. bukkit Chunk) 142 */ 143 public @Nullable Object getChunkObject() { 144 return chunkObject; 145 } 146 147 /** 148 * Set a chunk object (e.g. the Bukkit Chunk object) to the queue. This will be used as fallback in case of WNA failure. 149 * Should ONLY be used in specific cases (i.e. generation, where a chunk is being populated) 150 * 151 * @param chunkObject chunk object. Usually the implementation-specific chunk (e.g. bukkit Chunk) 152 */ 153 public void setChunkObject(@NonNull Object chunkObject) { 154 this.chunkObject = chunkObject; 155 } 156 157 /** 158 * Sets the block at the coordinates provided to the given id. 159 * 160 * @param x the x coordinate from from 0 to 15 inclusive 161 * @param y the y coordinate from from 0 (inclusive) - maxHeight(exclusive) 162 * @param z the z coordinate from 0 to 15 inclusive 163 * @param id the BlockState to set the block to 164 * @return success or not 165 */ 166 public abstract boolean setBlock(final int x, final int y, final int z, final @NonNull BlockState id); 167 168 /** 169 * Sets the block at the coordinates provided to the given id. 170 * 171 * @param x the x coordinate from from 0 to 15 inclusive 172 * @param y the y coordinate from from 0 (inclusive) - maxHeight(exclusive) 173 * @param z the z coordinate from 0 to 15 inclusive 174 * @param id the BaseBlock to set the block to 175 * @return success or not 176 */ 177 public abstract boolean setBlock(final int x, final int y, final int z, final @NonNull BaseBlock id); 178 179 /** 180 * Sets the block at the coordinates provided to the given id. 181 * 182 * @param x the x coordinate from from 0 to 15 inclusive 183 * @param y the y coordinate from from 0 (inclusive) - maxHeight(exclusive) 184 * @param z the z coordinate from 0 to 15 inclusive 185 * @param pattern the pattern to set the block to 186 * @return success or not 187 */ 188 public boolean setBlock(final int x, final int y, final int z, final @NonNull Pattern pattern) { 189 return setBlock(x, y, z, PatternUtil.apply(pattern, x, y, z)); 190 } 191 192 /** 193 * Sets a tile entity at the coordinates provided to the given CompoundTag 194 * 195 * @param x the x coordinate from from 0 to 15 inclusive 196 * @param y the y coordinate from from 0 (inclusive) - maxHeight(exclusive) 197 * @param z the z coordinate from 0 to 15 inclusive 198 * @param tag the CompoundTag to set the tile to 199 * @return success or not 200 */ 201 public abstract boolean setTile(int x, int y, int z, @NonNull CompoundTag tag); 202 203 /** 204 * Whether the queue has any tiles being set 205 * 206 * @return if setting tiles 207 */ 208 public abstract boolean isSettingTiles(); 209 210 /** 211 * Get a block at the given coordinates. 212 * 213 * @param x block x 214 * @param y block y 215 * @param z block z 216 * @return WorldEdit BlockState 217 */ 218 public @Nullable 219 abstract BlockState getBlock(int x, int y, int z); 220 221 /** 222 * Set a biome in XZ. This will likely set to the whole column 223 * 224 * @param x x coordinate 225 * @param z z coordinate 226 * @param biome biome 227 * @return success or not 228 * @deprecated Biomes now take XYZ, see {@link #setBiome(int, int, int, BiomeType)} 229 * <br> 230 * Scheduled for removal once we drop the support for versions not supporting 3D biomes, 1.18 and earlier. 231 */ 232 @Deprecated(forRemoval = true, since = "6.0.0") 233 public abstract boolean setBiome(int x, int z, @NonNull BiomeType biome); 234 235 /** 236 * Set a biome in XYZ 237 * 238 * @param x x coordinate 239 * @param y y coordinate 240 * @param z z coordinate 241 * @param biome biome 242 * @return success or not 243 */ 244 public abstract boolean setBiome(int x, int y, int z, @NonNull BiomeType biome); 245 246 /** 247 * Whether the queue has any biomes to be set 248 * 249 * @return if setting biomes 250 */ 251 public abstract boolean isSettingBiomes(); 252 253 /** 254 * If the queue should accept biome placement 255 * 256 * @param enabled If biomes should be enabled 257 * @since 6.8.0 258 */ 259 public abstract void setBiomesEnabled(boolean enabled); 260 261 /** 262 * Add entities to be created 263 * 264 * @param entities list of entities to add to queue 265 */ 266 public void addEntities(@NonNull List<? extends Entity> entities) { 267 for (Entity e : entities) { 268 this.setEntity(e); 269 } 270 } 271 272 /** 273 * Add an entity to be created 274 * 275 * @param entity entity to add to queue 276 * @return success or not 277 */ 278 public abstract boolean setEntity(@NonNull Entity entity); 279 280 /** 281 * Get the list of chunks that are added manually. This usually indicated the queue is "read only". 282 * 283 * @return list of BlockVector2 of chunks that are to be "read" 284 */ 285 public @NonNull 286 abstract List<BlockVector2> getReadChunks(); 287 288 /** 289 * Add a set of {@link BlockVector2} Chunk coordinates to the Read Chunks list 290 * 291 * @param readChunks set of BlockVector2 to add to "read" chunks 292 */ 293 public abstract void addReadChunks(@NonNull Set<BlockVector2> readChunks); 294 295 /** 296 * Add a {@link BlockVector2} Chunk coordinate to the Read Chunks list 297 * 298 * @param chunk BlockVector2 to add to "read" chunks 299 */ 300 public abstract void addReadChunk(@NonNull BlockVector2 chunk); 301 302 /** 303 * Whether chunks should be unloaded after being accessed 304 * 305 * @return if is unloading chunks after accessing them 306 */ 307 public abstract boolean isUnloadAfter(); 308 309 /** 310 * Set whether chunks should be unloaded after being accessed 311 * 312 * @param unloadAfter if to unload chunks after being accessed 313 */ 314 public abstract void setUnloadAfter(boolean unloadAfter); 315 316 /** 317 * Get the {@link CuboidRegion} designated for direct regeneration 318 * 319 * @return CuboidRegion to regenerate 320 */ 321 public @Nullable 322 abstract CuboidRegion getRegenRegion(); 323 324 /** 325 * Set the {@link CuboidRegion} designated for direct regeneration 326 * 327 * @param regenRegion CuboidRegion to regenerate 328 */ 329 public abstract void setRegenRegion(@NonNull CuboidRegion regenRegion); 330 331 /** 332 * Set a specific chunk at the chunk coordinates XZ to be regenerated. 333 * 334 * @param x chunk x 335 * @param z chunk z 336 */ 337 public abstract void regenChunk(int x, int z); 338 339 /** 340 * Get the world the queue is writing to 341 * 342 * @return world of the queue 343 */ 344 public @Nullable 345 abstract World getWorld(); 346 347 /** 348 * Set the queue as having been modified now 349 */ 350 public final void setModified() { 351 setModified(System.currentTimeMillis()); 352 } 353 354 /** 355 * Enqueue the queue to start it 356 * 357 * @return success or not 358 * @since 6.0.10 359 */ 360 public boolean enqueue() { 361 boolean success = false; 362 if (enqueued.compareAndSet(false, true)) { 363 success = true; 364 start(); 365 } 366 return success; 367 } 368 369 /** 370 * Start the queue 371 */ 372 public abstract void start(); 373 374 /** 375 * Cancel the queue 376 */ 377 public abstract void cancel(); 378 379 /** 380 * Get the task to be run when all chunks have been accessed 381 * 382 * @return task to be run when queue is complete 383 */ 384 public abstract Runnable getCompleteTask(); 385 386 /** 387 * Set the task to be run when all chunks have been accessed 388 * 389 * @param whenDone task to be run when queue is complete 390 */ 391 public abstract void setCompleteTask(@Nullable Runnable whenDone); 392 393 /** 394 * Return the chunk consumer set to the queue or null if one is not set 395 * 396 * @return Consumer to be executed on each chunk in queue 397 */ 398 public @Nullable 399 abstract Consumer<BlockVector2> getChunkConsumer(); 400 401 /** 402 * Set the Consumer that will be executed on each chunk in queue 403 * 404 * @param consumer Consumer to be executed on each chunk in queue 405 */ 406 public abstract void setChunkConsumer(@NonNull Consumer<BlockVector2> consumer); 407 408 /** 409 * Add a {@link ProgressSubscriber} to the Queue to subscribe to the relevant Chunk Processor 410 */ 411 public abstract void addProgressSubscriber(@NonNull ProgressSubscriber progressSubscriber); 412 413 /** 414 * Get the {@link LightingMode} to be used when setting blocks 415 */ 416 public @NonNull 417 abstract LightingMode getLightingMode(); 418 419 /** 420 * Set the {@link LightingMode} to be used when setting blocks 421 * 422 * @param mode lighting mode. Null to use default. 423 */ 424 public abstract void setLightingMode(@Nullable LightingMode mode); 425 426 /** 427 * Get the overriding {@link SideEffectSet} to be used by the queue if it exists, else null 428 * 429 * @return Overriding {@link SideEffectSet} or null 430 */ 431 public abstract @Nullable SideEffectSet getSideEffectSet(); 432 433 /** 434 * Set the overriding {@link SideEffectSet} to be used by the queue. Null to use default side effects. 435 * 436 * @param sideEffectSet side effects to override with, or null to use default 437 */ 438 public abstract void setSideEffectSet(@Nullable SideEffectSet sideEffectSet); 439 440 /** 441 * Fill a cuboid between two positions with a BlockState 442 * 443 * @param pos1 1st cuboid position 444 * @param pos2 2nd cuboid position 445 * @param block block to fill 446 */ 447 public void setCuboid(@NonNull Location pos1, @NonNull Location pos2, @NonNull BlockState block) { 448 int yMin = Math.min(pos1.getY(), pos2.getY()); 449 int yMax = Math.max(pos1.getY(), pos2.getY()); 450 int xMin = Math.min(pos1.getX(), pos2.getX()); 451 int xMax = Math.max(pos1.getX(), pos2.getX()); 452 int zMin = Math.min(pos1.getZ(), pos2.getZ()); 453 int zMax = Math.max(pos1.getZ(), pos2.getZ()); 454 for (int y = yMin; y <= yMax; y++) { 455 for (int x = xMin; x <= xMax; x++) { 456 for (int z = zMin; z <= zMax; z++) { 457 setBlock(x, y, z, block); 458 } 459 } 460 } 461 } 462 463 /** 464 * Fill a cuboid between two positions with a Pattern 465 * 466 * @param pos1 1st cuboid position 467 * @param pos2 2nd cuboid position 468 * @param blocks pattern to fill 469 */ 470 public void setCuboid(@NonNull Location pos1, @NonNull Location pos2, @NonNull Pattern blocks) { 471 int yMin = Math.min(pos1.getY(), pos2.getY()); 472 int yMax = Math.max(pos1.getY(), pos2.getY()); 473 int xMin = Math.min(pos1.getX(), pos2.getX()); 474 int xMax = Math.max(pos1.getX(), pos2.getX()); 475 int zMin = Math.min(pos1.getZ(), pos2.getZ()); 476 int zMax = Math.max(pos1.getZ(), pos2.getZ()); 477 for (int y = yMin; y <= yMax; y++) { 478 for (int x = xMin; x <= xMax; x++) { 479 for (int z = zMin; z <= zMax; z++) { 480 setBlock(x, y, z, blocks); 481 } 482 } 483 } 484 } 485 486 /** 487 * Fill a cuboid between two positions with a BiomeType 488 * 489 * @param pos1 1st cuboid position 490 * @param pos2 2nd cuboid position 491 * @param biome biome to fill 492 */ 493 public void setBiomeCuboid(@NonNull Location pos1, @NonNull Location pos2, @NonNull BiomeType biome) { 494 int yMin = Math.min(pos1.getY(), pos2.getY()); 495 int yMax = Math.max(pos1.getY(), pos2.getY()); 496 int xMin = Math.min(pos1.getX(), pos2.getX()); 497 int xMax = Math.max(pos1.getX(), pos2.getX()); 498 int zMin = Math.min(pos1.getZ(), pos2.getZ()); 499 int zMax = Math.max(pos1.getZ(), pos2.getZ()); 500 for (int y = yMin; y <= yMax; y++) { 501 for (int x = xMin; x <= xMax; x++) { 502 for (int z = zMin; z <= zMax; z++) { 503 setBiome(x, y, z, biome); 504 } 505 } 506 } 507 } 508 509 /** 510 * Get the min Y limit associated with the queue 511 */ 512 protected int getMinY() { 513 return getWorld() != null ? getWorld().getMinY() : PlotSquared.platform().versionMinHeight(); 514 } 515 516 /** 517 * Get the max Y limit associated with the queue 518 */ 519 protected int getMaxY() { 520 return getWorld() != null ? getWorld().getMinY() : PlotSquared.platform().versionMaxHeight(); 521 } 522 523 /** 524 * Get the min chunk layer associated with the queue. Usually 0 or -4; 525 */ 526 protected int getMinLayer() { 527 return (getWorld() != null ? getWorld().getMinY() : PlotSquared.platform().versionMinHeight()) >> 4; 528 } 529 530 /** 531 * Get the max chunk layer associated with the queue. Usually 15 or 19 532 */ 533 protected int getMaxLayer() { 534 return (getWorld() != null ? getWorld().getMaxY() : PlotSquared.platform().versionMaxHeight()) >> 4; 535 } 536 537}