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.queue; 020 021import com.google.common.base.Preconditions; 022import com.intellectualsites.annotations.DoNotUse; 023import com.plotsquared.bukkit.util.BukkitBlockUtil; 024import com.plotsquared.bukkit.util.BukkitUtil; 025import com.plotsquared.core.location.ChunkWrapper; 026import com.plotsquared.core.location.Location; 027import com.plotsquared.core.queue.ScopedQueueCoordinator; 028import com.plotsquared.core.util.ChunkUtil; 029import com.plotsquared.core.util.PatternUtil; 030import com.sk89q.worldedit.bukkit.BukkitAdapter; 031import com.sk89q.worldedit.function.pattern.Pattern; 032import com.sk89q.worldedit.world.biome.BiomeType; 033import com.sk89q.worldedit.world.block.BaseBlock; 034import com.sk89q.worldedit.world.block.BlockState; 035import com.sk89q.worldedit.world.block.BlockTypes; 036import org.bukkit.Bukkit; 037import org.bukkit.Chunk; 038import org.bukkit.World; 039import org.bukkit.block.Biome; 040import org.bukkit.generator.ChunkGenerator.BiomeGrid; 041import org.bukkit.generator.ChunkGenerator.ChunkData; 042import org.checkerframework.checker.nullness.qual.NonNull; 043import org.checkerframework.checker.nullness.qual.Nullable; 044 045import java.util.Arrays; 046 047@DoNotUse 048public class GenChunk extends ScopedQueueCoordinator { 049 050 public final Biome[] biomes; 051 public BlockState[][] result; 052 public BiomeGrid biomeGrid; 053 public Chunk chunk; 054 public String world; 055 public int chunkX; 056 public int chunkZ; 057 private ChunkData chunkData = null; 058 059 /** 060 * @param minY minimum world Y, inclusive 061 * @param maxY maximum world Y, inclusive 062 * 063 * @since 6.6.0 064 */ 065 public GenChunk(int minY, int maxY) { 066 super(null, Location.at("", 0, minY, 0), Location.at("", 15, maxY, 15)); 067 this.biomes = Biome.values(); 068 } 069 070 public @Nullable ChunkData getChunkData() { 071 return this.chunkData; 072 } 073 074 /** 075 * Set the internal Bukkit chunk data 076 * 077 * @param chunkData Bukkit ChunkData 078 */ 079 public void setChunkData(@NonNull ChunkData chunkData) { 080 this.chunkData = chunkData; 081 } 082 083 public @NonNull Chunk getChunk() { 084 if (chunk == null) { 085 World worldObj = BukkitUtil.getWorld(world); 086 if (worldObj != null) { 087 this.chunk = worldObj.getChunkAt(chunkX, chunkZ); 088 } 089 } 090 return chunk; 091 } 092 093 /** 094 * Set the chunk being represented 095 * 096 * @param chunk Bukkit Chunk 097 */ 098 public void setChunk(@NonNull Chunk chunk) { 099 this.chunk = chunk; 100 } 101 102 103 /** 104 * Set the world and XZ of the chunk being represented via {@link ChunkWrapper} 105 * 106 * @param wrap PlotSquared ChunkWrapper 107 */ 108 public void setChunk(@NonNull ChunkWrapper wrap) { 109 chunk = null; 110 world = wrap.world; 111 chunkX = wrap.x; 112 chunkZ = wrap.z; 113 } 114 115 @Override 116 public void fillBiome(@NonNull BiomeType biomeType) { 117 if (biomeGrid == null) { 118 return; 119 } 120 Biome biome = BukkitAdapter.adapt(biomeType); 121 for (int y = getMin().getY(); y <= getMax().getY(); y++) { 122 for (int x = 0; x < 16; x++) { 123 for (int z = 0; z < 16; z++) { 124 this.biomeGrid.setBiome(x, y, z, biome); 125 } 126 } 127 } 128 } 129 130 @Override 131 public void setCuboid(@NonNull Location pos1, @NonNull Location pos2, @NonNull BlockState block) { 132 if (result != null && pos1.getX() == 0 && pos1.getZ() == 0 && pos2.getX() == 15 && pos2.getZ() == 15) { 133 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 134 int layer = getLayerIndex(y); 135 BlockState[] data = result[layer]; 136 if (data == null) { 137 result[layer] = data = new BlockState[4096]; 138 } 139 int start = y << 8; 140 int end = start + 256; 141 Arrays.fill(data, start, end, block); 142 } 143 } 144 int minX = Math.min(pos1.getX(), pos2.getX()); 145 int minY = Math.min(pos1.getY(), pos2.getY()); 146 int minZ = Math.min(pos1.getZ(), pos2.getZ()); 147 int maxX = Math.max(pos1.getX(), pos2.getX()); 148 int maxY = Math.max(pos1.getY(), pos2.getY()); 149 int maxZ = Math.max(pos1.getZ(), pos2.getZ()); 150 chunkData.setRegion(minX, minY, minZ, maxX + 1, maxY + 1, maxZ + 1, BukkitAdapter.adapt(block)); 151 } 152 153 @Override 154 public boolean setBiome(int x, int z, @NonNull BiomeType biomeType) { 155 return setBiome(x, z, BukkitAdapter.adapt(biomeType)); 156 } 157 158 /** 159 * Set the in the whole column of XZ 160 * 161 * @param x Relative x location within the chunk (0 - 15) 162 * @param z Relative z location within the chunk (0 - 15) 163 * @param biome Bukkit biome to set 164 * @return if successful 165 */ 166 public boolean setBiome(int x, int z, @NonNull Biome biome) { 167 if (this.biomeGrid != null) { 168 for (int y = getMin().getY(); y <= getMax().getY(); y++) { 169 this.setBiome(x, y, z, biome); 170 } 171 return true; 172 } 173 return false; 174 } 175 176 public boolean setBiome(int x, int y, int z, @NonNull Biome biome) { 177 if (this.biomeGrid != null) { 178 this.biomeGrid.setBiome(x, y, z, biome); 179 return true; 180 } 181 return false; 182 } 183 184 @Override 185 public boolean setBlock(int x, int y, int z, @NonNull Pattern pattern) { 186 final BaseBlock block = PatternUtil.apply(Preconditions.checkNotNull( 187 pattern, 188 "Pattern may not be null" 189 ), x + (chunkX << 4), y, z + (chunkZ << 4)); 190 return setBlock(x, y, z, block); 191 } 192 193 @Override 194 public boolean setBlock(int x, int y, int z, @NonNull BlockState id) { 195 if (this.result == null) { 196 this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id)); 197 return true; 198 } 199 this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id)); 200 this.storeCache(x, y, z, id); 201 return true; 202 } 203 204 private void storeCache(final int x, final int y, final int z, final @NonNull BlockState id) { 205 int i = getLayerIndex(y); 206 BlockState[] v = this.result[i]; 207 if (v == null) { 208 this.result[i] = v = new BlockState[4096]; 209 } 210 int j = ChunkUtil.getJ(x, y, z); 211 v[j] = id; 212 } 213 214 @Override 215 public boolean setBlock(int x, int y, int z, @NonNull BaseBlock id) { 216 if (this.result == null) { 217 this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id)); 218 return true; 219 } 220 this.chunkData.setBlock(x, y, z, BukkitAdapter.adapt(id)); 221 this.storeCache(x, y, z, id.toImmutableState()); 222 return true; 223 } 224 225 @Override 226 public @Nullable BlockState getBlock(int x, int y, int z) { 227 int i = getLayerIndex(y); 228 if (result == null) { 229 return BukkitBlockUtil.get(chunkData.getType(x, y, z)); 230 } 231 BlockState[] array = result[i]; 232 if (array == null) { 233 return BlockTypes.AIR.getDefaultState(); 234 } 235 int j = ChunkUtil.getJ(x, y, z); 236 return array[j]; 237 } 238 239 public int getX() { 240 return chunk == null ? chunkX : chunk.getX(); 241 } 242 243 public int getZ() { 244 return chunk == null ? chunkZ : chunk.getZ(); 245 } 246 247 @Override 248 public com.sk89q.worldedit.world.@NonNull World getWorld() { 249 return chunk == null ? BukkitAdapter.adapt(Bukkit.getWorld(world)) : BukkitAdapter.adapt(chunk.getWorld()); 250 } 251 252 @Override 253 public @NonNull Location getMax() { 254 return Location.at(getWorld().getName(), 15 + (getX() << 4), super.getMax().getY(), 15 + (getZ() << 4)); 255 } 256 257 @Override 258 public @NonNull Location getMin() { 259 return Location.at(getWorld().getName(), getX() << 4, super.getMin().getY(), getZ() << 4); 260 } 261 262 public @NonNull GenChunk clone() { 263 GenChunk toReturn = new GenChunk(getMin().getY(), getMax().getY()); 264 if (this.result != null) { 265 for (int i = 0; i < this.result.length; i++) { 266 BlockState[] matrix = this.result[i]; 267 if (matrix != null) { 268 toReturn.result[i] = new BlockState[matrix.length]; 269 System.arraycopy(matrix, 0, toReturn.result[i], 0, matrix.length); 270 } 271 } 272 } 273 toReturn.chunkData = this.chunkData; 274 return toReturn; 275 } 276 277 private int getLayerIndex(int y) { 278 return (y - getMin().getY()) >> 4; 279 } 280 281}