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