PocketMine-MP 5.16.1 git-585aee9386a787c95e73dd0a05ffca8329606b68
Chunk.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
25declare(strict_types=1);
26
27namespace pocketmine\world\format;
28
32use function array_map;
33
34class Chunk{
35 public const DIRTY_FLAG_BLOCKS = 1 << 0;
36 public const DIRTY_FLAG_BIOMES = 1 << 3;
37
38 public const DIRTY_FLAGS_ALL = ~0;
39 public const DIRTY_FLAGS_NONE = 0;
40
41 public const MIN_SUBCHUNK_INDEX = -4;
42 public const MAX_SUBCHUNK_INDEX = 19;
43 public const MAX_SUBCHUNKS = self::MAX_SUBCHUNK_INDEX - self::MIN_SUBCHUNK_INDEX + 1;
44
45 public const EDGE_LENGTH = SubChunk::EDGE_LENGTH;
46 public const COORD_BIT_SIZE = SubChunk::COORD_BIT_SIZE;
47 public const COORD_MASK = SubChunk::COORD_MASK;
48
49 private int $terrainDirtyFlags = self::DIRTY_FLAGS_ALL;
50
51 protected ?bool $lightPopulated = false;
52 protected bool $terrainPopulated = false;
53
58 protected \SplFixedArray $subChunks;
59
61 protected array $tiles = [];
62
63 protected HeightArray $heightMap;
64
68 public function __construct(array $subChunks, bool $terrainPopulated){
69 $this->subChunks = new \SplFixedArray(Chunk::MAX_SUBCHUNKS);
70
71 foreach($this->subChunks as $y => $null){
72 //TODO: we should probably require all subchunks to be provided here
73 $this->subChunks[$y] = $subChunks[$y + self::MIN_SUBCHUNK_INDEX] ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
74 }
75
76 $val = (self::MAX_SUBCHUNK_INDEX + 1) * SubChunk::EDGE_LENGTH;
77 $this->heightMap = HeightArray::fill($val); //TODO: what about lazily initializing this?
78
79 $this->terrainPopulated = $terrainPopulated;
80 }
81
85 public function getHeight() : int{
86 return $this->subChunks->getSize();
87 }
88
98 public function getBlockStateId(int $x, int $y, int $z) : int{
99 return $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBlockStateId($x, $y & SubChunk::COORD_MASK, $z);
100 }
101
105 public function setBlockStateId(int $x, int $y, int $z, int $block) : void{
106 $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->setBlockStateId($x, $y & SubChunk::COORD_MASK, $z, $block);
107 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
108 }
109
118 public function getHighestBlockAt(int $x, int $z) : ?int{
119 for($y = self::MAX_SUBCHUNK_INDEX; $y >= self::MIN_SUBCHUNK_INDEX; --$y){
120 $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
121 if($height !== null){
122 return $height | ($y << SubChunk::COORD_BIT_SIZE);
123 }
124 }
125
126 return null;
127 }
128
135 public function getHeightMap(int $x, int $z) : int{
136 return $this->heightMap->get($x, $z);
137 }
138
145 public function setHeightMap(int $x, int $z, int $value) : void{
146 $this->heightMap->set($x, $z, $value);
147 }
148
158 public function getBiomeId(int $x, int $y, int $z) : int{
159 return $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBiomeArray()->get($x, $y, $z);
160 }
161
172 public function setBiomeId(int $x, int $y, int $z, int $biomeId) : void{
173 $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBiomeArray()->set($x, $y, $z, $biomeId);
174 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BIOMES;
175 }
176
177 public function isLightPopulated() : ?bool{
178 return $this->lightPopulated;
179 }
180
181 public function setLightPopulated(?bool $value = true) : void{
182 $this->lightPopulated = $value;
183 }
184
185 public function isPopulated() : bool{
186 return $this->terrainPopulated;
187 }
188
189 public function setPopulated(bool $value = true) : void{
190 $this->terrainPopulated = $value;
191 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
192 }
193
194 public function addTile(Tile $tile) : void{
195 if($tile->isClosed()){
196 throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to a chunk");
197 }
198
199 $pos = $tile->getPosition();
200 if(isset($this->tiles[$index = Chunk::blockHash($pos->x, $pos->y, $pos->z)]) && $this->tiles[$index] !== $tile){
201 throw new \InvalidArgumentException("Another tile is already at this location");
202 }
203 $this->tiles[$index] = $tile;
204 }
205
206 public function removeTile(Tile $tile) : void{
207 $pos = $tile->getPosition();
208 unset($this->tiles[Chunk::blockHash($pos->x, $pos->y, $pos->z)]);
209 }
210
214 public function getTiles() : array{
215 return $this->tiles;
216 }
217
225 public function getTile(int $x, int $y, int $z) : ?Tile{
226 return $this->tiles[Chunk::blockHash($x, $y, $z)] ?? null;
227 }
228
232 public function onUnload() : void{
233 foreach($this->getTiles() as $tile){
234 $tile->close();
235 }
236 }
237
241 public function getHeightMapArray() : array{
242 return $this->heightMap->getValues();
243 }
244
248 public function setHeightMapArray(array $values) : void{
249 $this->heightMap = new HeightArray($values);
250 }
251
252 public function isTerrainDirty() : bool{
253 return $this->terrainDirtyFlags !== self::DIRTY_FLAGS_NONE;
254 }
255
256 public function getTerrainDirtyFlag(int $flag) : bool{
257 return ($this->terrainDirtyFlags & $flag) !== 0;
258 }
259
260 public function getTerrainDirtyFlags() : int{
261 return $this->terrainDirtyFlags;
262 }
263
264 public function setTerrainDirtyFlag(int $flag, bool $value) : void{
265 if($value){
266 $this->terrainDirtyFlags |= $flag;
267 }else{
268 $this->terrainDirtyFlags &= ~$flag;
269 }
270 }
271
272 public function setTerrainDirty() : void{
273 $this->terrainDirtyFlags = self::DIRTY_FLAGS_ALL;
274 }
275
276 public function clearTerrainDirtyFlags() : void{
277 $this->terrainDirtyFlags = self::DIRTY_FLAGS_NONE;
278 }
279
280 public function getSubChunk(int $y) : SubChunk{
281 if($y < self::MIN_SUBCHUNK_INDEX || $y > self::MAX_SUBCHUNK_INDEX){
282 throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
283 }
284 return $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX];
285 }
286
290 public function setSubChunk(int $y, ?SubChunk $subChunk) : void{
291 if($y < self::MIN_SUBCHUNK_INDEX || $y > self::MAX_SUBCHUNK_INDEX){
292 throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
293 }
294
295 $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX] = $subChunk ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
296 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
297 }
298
303 public function getSubChunks() : array{
304 $result = [];
305 foreach($this->subChunks as $yOffset => $subChunk){
306 $result[$yOffset + self::MIN_SUBCHUNK_INDEX] = $subChunk;
307 }
308 return $result;
309 }
310
314 public function collectGarbage() : void{
315 foreach($this->subChunks as $y => $subChunk){
316 $subChunk->collectGarbage();
317 }
318 }
319
320 public function __clone(){
321 //we don't bother cloning entities or tiles since it's impractical to do so (too many dependencies)
322 $this->subChunks = \SplFixedArray::fromArray(array_map(function(SubChunk $subChunk) : SubChunk{
323 return clone $subChunk;
324 }, $this->subChunks->toArray()));
325 $this->heightMap = clone $this->heightMap;
326 }
327
335 public static function blockHash(int $x, int $y, int $z) : int{
336 return ($y << (2 * SubChunk::COORD_BIT_SIZE)) |
337 (($z & SubChunk::COORD_MASK) << SubChunk::COORD_BIT_SIZE) |
338 ($x & SubChunk::COORD_MASK);
339 }
340}
getTile(int $x, int $y, int $z)
Definition: Chunk.php:225
setSubChunk(int $y, ?SubChunk $subChunk)
Definition: Chunk.php:290
setHeightMapArray(array $values)
Definition: Chunk.php:248
static blockHash(int $x, int $y, int $z)
Definition: Chunk.php:335
getHeightMap(int $x, int $z)
Definition: Chunk.php:135
setBiomeId(int $x, int $y, int $z, int $biomeId)
Definition: Chunk.php:172
getHighestBlockAt(int $x, int $z)
Definition: Chunk.php:118
__construct(array $subChunks, bool $terrainPopulated)
Definition: Chunk.php:68
getBiomeId(int $x, int $y, int $z)
Definition: Chunk.php:158
setHeightMap(int $x, int $z, int $value)
Definition: Chunk.php:145
setBlockStateId(int $x, int $y, int $z, int $block)
Definition: Chunk.php:105
getBlockStateId(int $x, int $y, int $z)
Definition: Chunk.php:98