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