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.bukkit.generator; 020 021import com.plotsquared.bukkit.queue.GenChunk; 022import com.plotsquared.bukkit.util.BukkitUtil; 023import com.plotsquared.bukkit.util.BukkitWorld; 024import com.plotsquared.core.PlotSquared; 025import com.plotsquared.core.generator.GeneratorWrapper; 026import com.plotsquared.core.generator.IndependentPlotGenerator; 027import com.plotsquared.core.generator.SingleWorldGenerator; 028import com.plotsquared.core.location.ChunkWrapper; 029import com.plotsquared.core.plot.PlotArea; 030import com.plotsquared.core.plot.world.PlotAreaManager; 031import com.plotsquared.core.queue.ScopedQueueCoordinator; 032import com.plotsquared.core.util.ChunkManager; 033import com.sk89q.worldedit.math.BlockVector2; 034import org.bukkit.World; 035import org.bukkit.block.Biome; 036import org.bukkit.generator.BlockPopulator; 037import org.bukkit.generator.ChunkGenerator; 038import org.checkerframework.checker.nullness.qual.NonNull; 039 040import java.util.ArrayList; 041import java.util.List; 042import java.util.Random; 043import java.util.Set; 044 045public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper<ChunkGenerator> { 046 047 @SuppressWarnings("unused") 048 public final boolean PAPER_ASYNC_SAFE = true; 049 050 private final PlotAreaManager plotAreaManager; 051 private final IndependentPlotGenerator plotGenerator; 052 private final ChunkGenerator platformGenerator; 053 private final boolean full; 054 private final String levelName; 055 private List<BlockPopulator> populators; 056 private boolean loaded = false; 057 058 public BukkitPlotGenerator( 059 final @NonNull String name, 060 final @NonNull IndependentPlotGenerator generator, 061 final @NonNull PlotAreaManager plotAreaManager 062 ) { 063 this.plotAreaManager = plotAreaManager; 064 this.levelName = name; 065 this.plotGenerator = generator; 066 this.platformGenerator = this; 067 this.populators = new ArrayList<>(); 068 int minecraftMinorVersion = PlotSquared.platform().serverVersion()[1]; 069 if (minecraftMinorVersion >= 17) { 070 this.populators.add(new BlockStatePopulator(this.plotGenerator)); 071 } else { 072 this.populators.add(new LegacyBlockStatePopulator(this.plotGenerator)); 073 } 074 this.full = true; 075 } 076 077 public BukkitPlotGenerator(final String world, final ChunkGenerator cg, final @NonNull PlotAreaManager plotAreaManager) { 078 if (cg instanceof BukkitPlotGenerator) { 079 throw new IllegalArgumentException("ChunkGenerator: " + cg.getClass().getName() 080 + " is already a BukkitPlotGenerator!"); 081 } 082 this.plotAreaManager = plotAreaManager; 083 this.levelName = world; 084 this.full = false; 085 this.platformGenerator = cg; 086 this.plotGenerator = new DelegatePlotGenerator(cg, world); 087 } 088 089 @Override 090 public void augment(PlotArea area) { 091 BukkitAugmentedGenerator.get(BukkitUtil.getWorld(area.getWorldName())); 092 } 093 094 @Override 095 public boolean isFull() { 096 return this.full; 097 } 098 099 @Override 100 public IndependentPlotGenerator getPlotGenerator() { 101 return this.plotGenerator; 102 } 103 104 @Override 105 public ChunkGenerator getPlatformGenerator() { 106 return this.platformGenerator; 107 } 108 109 @Override 110 public @NonNull List<BlockPopulator> getDefaultPopulators(@NonNull World world) { 111 try { 112 checkLoaded(world); 113 } catch (Exception e) { 114 e.printStackTrace(); 115 } 116 ArrayList<BlockPopulator> toAdd = new ArrayList<>(); 117 List<BlockPopulator> existing = world.getPopulators(); 118 if (populators == null && platformGenerator != null) { 119 populators = new ArrayList<>(platformGenerator.getDefaultPopulators(world)); 120 } 121 if (populators != null) { 122 for (BlockPopulator populator : this.populators) { 123 if (!existing.contains(populator)) { 124 toAdd.add(populator); 125 } 126 } 127 } 128 return toAdd; 129 } 130 131 private synchronized void checkLoaded(@NonNull World world) { 132 // Do not attempt to load configurations until WorldEdit has a platform ready. 133 if (!PlotSquared.get().isWeInitialised()) { 134 return; 135 } 136 if (!this.loaded) { 137 String name = world.getName(); 138 PlotSquared.get().loadWorld(name, this); 139 final Set<PlotArea> areas = this.plotAreaManager.getPlotAreasSet(name); 140 if (!areas.isEmpty()) { 141 PlotArea area = areas.iterator().next(); 142 if (!area.isMobSpawning()) { 143 if (!area.isSpawnEggs()) { 144 world.setSpawnFlags(false, false); 145 } 146 setSpawnLimits(world, 0); 147 } else { 148 world.setSpawnFlags(true, true); 149 setSpawnLimits(world, -1); 150 } 151 } 152 this.loaded = true; 153 } 154 } 155 156 @SuppressWarnings("deprecation") 157 private void setSpawnLimits(@NonNull World world, int limit) { 158 world.setAmbientSpawnLimit(limit); 159 world.setAnimalSpawnLimit(limit); 160 world.setMonsterSpawnLimit(limit); 161 world.setWaterAnimalSpawnLimit(limit); 162 } 163 164 /** 165 * The entire method is deprecated, but kept for compatibility with versions lower than or equal to 1.16.2. 166 * The method will be removed in future versions, because WorldEdit and FastAsyncWorldEdit only support the latest point 167 * release. 168 */ 169 @Deprecated(forRemoval = true, since = "6.11.0") 170 @Override 171 public @NonNull ChunkData generateChunkData( 172 @NonNull World world, @NonNull Random random, int x, int z, 173 @NonNull BiomeGrid biome 174 ) { 175 int minY = BukkitWorld.getMinWorldHeight(world); 176 int maxY = BukkitWorld.getMaxWorldHeight(world); 177 GenChunk result = new GenChunk(minY, maxY); 178 if (this.getPlotGenerator() instanceof SingleWorldGenerator) { 179 if (result.getChunkData() != null) { 180 for (int chunkX = 0; chunkX < 16; chunkX++) { 181 for (int chunkZ = 0; chunkZ < 16; chunkZ++) { 182 for (int y = minY; y < maxY; y++) { 183 biome.setBiome(chunkX, y, chunkZ, Biome.PLAINS); 184 185 } 186 } 187 } 188 return result.getChunkData(); 189 } 190 } 191 // Set the chunk location 192 result.setChunk(new ChunkWrapper(world.getName(), x, z)); 193 // Set the result data 194 result.setChunkData(createChunkData(world)); 195 result.biomeGrid = biome; 196 result.result = null; 197 198 // Catch any exceptions (as exceptions usually thrown) 199 try { 200 // Fill the result data if necessary 201 if (this.platformGenerator != this) { 202 return this.platformGenerator.generateChunkData(world, random, x, z, biome); 203 } else { 204 generate(BlockVector2.at(x, z), world, result); 205 } 206 } catch (Throwable e) { 207 e.printStackTrace(); 208 } 209 // Return the result data 210 return result.getChunkData(); 211 } 212 213 private void generate(BlockVector2 loc, World world, ScopedQueueCoordinator result) { 214 // Load if improperly loaded 215 if (!this.loaded) { 216 checkLoaded(world); 217 } 218 // Process the chunk 219 if (ChunkManager.preProcessChunk(loc, result)) { 220 return; 221 } 222 PlotArea area = this.plotAreaManager.getPlotArea(world.getName(), null); 223 if (area == null && (area = this.plotAreaManager.getPlotArea(this.levelName, null)) == null) { 224 throw new IllegalStateException( 225 "Cannot regenerate chunk that does not belong to a plot area." + " Location: " + loc 226 + ", world: " + world); 227 } 228 try { 229 this.plotGenerator.generateChunk(result, area); 230 } catch (Throwable e) { 231 // Recover from generator error 232 e.printStackTrace(); 233 } 234 ChunkManager.postProcessChunk(loc, result); 235 } 236 237 @Override 238 public boolean canSpawn(final @NonNull World world, final int x, final int z) { 239 return true; 240 } 241 242 public boolean shouldGenerateCaves() { 243 return false; 244 } 245 246 public boolean shouldGenerateDecorations() { 247 return false; 248 } 249 250 public boolean isParallelCapable() { 251 return true; 252 } 253 254 public boolean shouldGenerateMobs() { 255 return false; 256 } 257 258 public boolean shouldGenerateStructures() { 259 return true; 260 } 261 262 @Override 263 public String toString() { 264 if (this.platformGenerator == this) { 265 return this.plotGenerator.getName(); 266 } 267 if (this.platformGenerator == null) { 268 return "null"; 269 } else { 270 return this.platformGenerator.getClass().getName(); 271 } 272 } 273 274 @Override 275 public boolean equals(final Object obj) { 276 if (obj == null) { 277 return false; 278 } 279 return toString().equals(obj.toString()) || toString().equals(obj.getClass().getName()); 280 } 281 282 public String getLevelName() { 283 return this.levelName; 284 } 285 286}