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