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.common.base.Preconditions; 022import com.google.inject.Inject; 023import com.plotsquared.core.configuration.Settings; 024import com.plotsquared.core.inject.factory.ChunkCoordinatorFactory; 025import com.plotsquared.core.location.Location; 026import com.plotsquared.core.queue.subscriber.ProgressSubscriber; 027import com.sk89q.worldedit.math.BlockVector2; 028import com.sk89q.worldedit.world.World; 029import org.checkerframework.checker.nullness.qual.NonNull; 030import org.checkerframework.checker.nullness.qual.Nullable; 031 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.function.Consumer; 037 038/** 039 * Builds a {@link ChunkCoordinator} instance 040 */ 041public class ChunkCoordinatorBuilder { 042 043 private final List<BlockVector2> requestedChunks = new LinkedList<>(); 044 private final List<ProgressSubscriber> progressSubscribers = new ArrayList<>(); 045 private final ChunkCoordinatorFactory chunkCoordinatorFactory; 046 private Consumer<Throwable> throwableConsumer = Throwable::printStackTrace; 047 private World world; 048 private Consumer<BlockVector2> chunkConsumer; 049 private Runnable whenDone = () -> { 050 }; 051 private long maxIterationTime = Settings.QUEUE.MAX_ITERATION_TIME; // A little over 1 tick; 052 private int initialBatchSize = Settings.QUEUE.INITIAL_BATCH_SIZE; 053 private boolean unloadAfter = true; 054 private boolean forceSync = false; 055 056 @Inject 057 public ChunkCoordinatorBuilder(@NonNull ChunkCoordinatorFactory chunkCoordinatorFactory) { 058 this.chunkCoordinatorFactory = chunkCoordinatorFactory; 059 } 060 061 /** 062 * Set the world 063 * 064 * @param world world 065 * @return this ChunkCoordinatorBuilder instance 066 */ 067 public @NonNull ChunkCoordinatorBuilder inWorld(final @NonNull World world) { 068 this.world = Preconditions.checkNotNull(world, "World may not be null"); 069 return this; 070 } 071 072 /** 073 * Add a chunk to be accessed 074 * 075 * @param chunkLocation BlockVector2 of chunk to add 076 * @return this ChunkCoordinatorBuilder instance 077 */ 078 public @NonNull ChunkCoordinatorBuilder withChunk(final @NonNull BlockVector2 chunkLocation) { 079 this.requestedChunks.add(Preconditions.checkNotNull(chunkLocation, "Chunk location may not be null")); 080 return this; 081 } 082 083 /** 084 * Add a Collection of chunks to be accessed 085 * 086 * @param chunkLocations Collection of BlockVector2 to add 087 * @return this ChunkCoordinatorBuilder instance 088 */ 089 public @NonNull ChunkCoordinatorBuilder withChunks(final @NonNull Collection<BlockVector2> chunkLocations) { 090 chunkLocations.forEach(this::withChunk); 091 return this; 092 } 093 094 /** 095 * Add chunks within a region to be accessed 096 * 097 * @param pos1 minimum region location 098 * @param pos2 maximum region location 099 * @return this ChunkCoordinatorBuilder instance 100 */ 101 public @NonNull ChunkCoordinatorBuilder withRegion(@NonNull Location pos1, @NonNull Location pos2) { 102 final int p1x = pos1.getX(); 103 final int p1z = pos1.getZ(); 104 final int p2x = pos2.getX(); 105 final int p2z = pos2.getZ(); 106 final int bcx = p1x >> 4; 107 final int bcz = p1z >> 4; 108 final int tcx = p2x >> 4; 109 final int tcz = p2z >> 4; 110 final ArrayList<BlockVector2> chunks = new ArrayList<>(); 111 112 for (int x = bcx; x <= tcx; x++) { 113 for (int z = bcz; z <= tcz; z++) { 114 chunks.add(BlockVector2.at(x, z)); 115 } 116 } 117 118 chunks.forEach(this::withChunk); 119 return this; 120 } 121 122 /** 123 * Set the consumer to be used when a chunk is loaded 124 * 125 * @param chunkConsumer Consumer to be used by the ChunkCoordinator 126 * @return this ChunkCoordinatorBuilder instance 127 */ 128 public @NonNull ChunkCoordinatorBuilder withConsumer(final @NonNull Consumer<BlockVector2> chunkConsumer) { 129 this.chunkConsumer = Preconditions.checkNotNull(chunkConsumer, "Chunk consumer may not be null"); 130 return this; 131 } 132 133 /** 134 * Set the Runnable to run when all chunks have been accessed 135 * 136 * @param whenDone task to run when all chunks are accessed 137 * @return this ChunkCoordinatorBuilder instance 138 */ 139 public @NonNull ChunkCoordinatorBuilder withFinalAction(final @Nullable Runnable whenDone) { 140 if (whenDone == null) { 141 return this; 142 } 143 this.whenDone = whenDone; 144 return this; 145 } 146 147 /** 148 * Set the max time taken while iterating over and accessing loaded chunks 149 * 150 * @param maxIterationTime max iteration time 151 * @return this ChunkCoordinatorBuilder instance 152 */ 153 public @NonNull ChunkCoordinatorBuilder withMaxIterationTime(final long maxIterationTime) { 154 Preconditions.checkArgument(maxIterationTime > 0, "Max iteration time must be positive"); 155 this.maxIterationTime = maxIterationTime; 156 return this; 157 } 158 159 /** 160 * Set the initial batch size to be used for loading chunks 161 * 162 * @param initialBatchSize initial batch size 163 * @return this ChunkCoordinatorBuilder instance 164 */ 165 public @NonNull ChunkCoordinatorBuilder withInitialBatchSize(final int initialBatchSize) { 166 Preconditions.checkArgument(initialBatchSize > 0, "Initial batch size must be positive"); 167 this.initialBatchSize = initialBatchSize; 168 return this; 169 } 170 171 /** 172 * Set the consumer to be used to handle {@link Throwable}s 173 * 174 * @param throwableConsumer consumer to hanble throwables 175 * @return this ChunkCoordinatorBuilder instance 176 */ 177 public @NonNull ChunkCoordinatorBuilder withThrowableConsumer(final @NonNull Consumer<Throwable> throwableConsumer) { 178 this.throwableConsumer = Preconditions.checkNotNull(throwableConsumer, "Throwable consumer may not be null"); 179 return this; 180 } 181 182 /** 183 * Set whether the chunks should be allow to unload after being accessed. This should only be used where the chunks are read from 184 * and then written to from a separate queue where they're consequently unloaded. 185 * 186 * @param unloadAfter if to unload chuns afterwards 187 * @return this ChunkCoordinatorBuilder instance 188 */ 189 public @NonNull ChunkCoordinatorBuilder unloadAfter(final boolean unloadAfter) { 190 this.unloadAfter = unloadAfter; 191 return this; 192 } 193 194 /** 195 * Set whether the chunks coordinator should be forced to be synchronous. This is not necessarily synchronous to the server, 196 * and simply effectively makes {@link ChunkCoordinator#start()} ()} a blocking operation. 197 * 198 * @param forceSync force sync or not 199 * @since 6.9.0 200 */ 201 public @NonNull ChunkCoordinatorBuilder forceSync(final boolean forceSync) { 202 this.forceSync = forceSync; 203 return this; 204 } 205 206 public @NonNull ChunkCoordinatorBuilder withProgressSubscriber(ProgressSubscriber progressSubscriber) { 207 this.progressSubscribers.add(progressSubscriber); 208 return this; 209 } 210 211 public @NonNull ChunkCoordinatorBuilder withProgressSubscribers(Collection<ProgressSubscriber> progressSubscribers) { 212 this.progressSubscribers.addAll(progressSubscribers); 213 return this; 214 } 215 216 /** 217 * Create a new {@link ChunkCoordinator} instance based on the values in the Builder instance. 218 * 219 * @return a new ChunkCoordinator 220 */ 221 public @NonNull ChunkCoordinator build() { 222 Preconditions.checkNotNull(this.world, "No world was supplied"); 223 Preconditions.checkNotNull(this.chunkConsumer, "No chunk consumer was supplied"); 224 Preconditions.checkNotNull(this.whenDone, "No final action was supplied"); 225 Preconditions.checkNotNull(this.throwableConsumer, "No throwable consumer was supplied"); 226 return chunkCoordinatorFactory 227 .create( 228 this.maxIterationTime, 229 this.initialBatchSize, 230 this.chunkConsumer, 231 this.world, 232 this.requestedChunks, 233 this.whenDone, 234 this.throwableConsumer, 235 this.unloadAfter, 236 this.progressSubscribers, 237 this.forceSync 238 ); 239 } 240 241}