PocketMine-MP 5.23.3 git-976fc63567edab7a6fb6aeae739f43cf9fe57de4
Loading...
Searching...
No Matches
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
64 protected array $tiles = [];
65
66 protected HeightArray $heightMap;
67
71 public function __construct(array $subChunks, bool $terrainPopulated){
72 $this->subChunks = new \SplFixedArray(Chunk::MAX_SUBCHUNKS);
73
74 foreach($this->subChunks as $y => $null){
75 //TODO: we should probably require all subchunks to be provided here
76 $this->subChunks[$y] = $subChunks[$y + self::MIN_SUBCHUNK_INDEX] ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
77 }
78
79 $val = (self::MAX_SUBCHUNK_INDEX + 1) * SubChunk::EDGE_LENGTH;
80 $this->heightMap = HeightArray::fill($val); //TODO: what about lazily initializing this?
81
82 $this->terrainPopulated = $terrainPopulated;
83 }
84
88 public function getHeight() : int{
89 return $this->subChunks->getSize();
90 }
91
101 public function getBlockStateId(int $x, int $y, int $z) : int{
102 return $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBlockStateId($x, $y & SubChunk::COORD_MASK, $z);
103 }
104
108 public function setBlockStateId(int $x, int $y, int $z, int $block) : void{
109 $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->setBlockStateId($x, $y & SubChunk::COORD_MASK, $z, $block);
110 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
111 }
112
121 public function getHighestBlockAt(int $x, int $z) : ?int{
122 for($y = self::MAX_SUBCHUNK_INDEX; $y >= self::MIN_SUBCHUNK_INDEX; --$y){
123 $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z);
124 if($height !== null){
125 return $height | ($y << SubChunk::COORD_BIT_SIZE);
126 }
127 }
128
129 return null;
130 }
131
138 public function getHeightMap(int $x, int $z) : int{
139 return $this->heightMap->get($x, $z);
140 }
141
148 public function setHeightMap(int $x, int $z, int $value) : void{
149 $this->heightMap->set($x, $z, $value);
150 }
151
161 public function getBiomeId(int $x, int $y, int $z) : int{
162 return $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBiomeArray()->get($x, $y, $z);
163 }
164
175 public function setBiomeId(int $x, int $y, int $z, int $biomeId) : void{
176 $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->getBiomeArray()->set($x, $y, $z, $biomeId);
177 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BIOMES;
178 }
179
180 public function isLightPopulated() : ?bool{
181 return $this->lightPopulated;
182 }
183
184 public function setLightPopulated(?bool $value = true) : void{
185 $this->lightPopulated = $value;
186 }
187
188 public function isPopulated() : bool{
189 return $this->terrainPopulated;
190 }
191
192 public function setPopulated(bool $value = true) : void{
193 $this->terrainPopulated = $value;
194 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
195 }
196
197 public function addTile(Tile $tile) : void{
198 if($tile->isClosed()){
199 throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to a chunk");
200 }
201
202 $pos = $tile->getPosition();
203 if(isset($this->tiles[$index = Chunk::blockHash($pos->x, $pos->y, $pos->z)]) && $this->tiles[$index] !== $tile){
204 throw new \InvalidArgumentException("Another tile is already at this location");
205 }
206 $this->tiles[$index] = $tile;
207 }
208
209 public function removeTile(Tile $tile) : void{
210 $pos = $tile->getPosition();
211 unset($this->tiles[Chunk::blockHash($pos->x, $pos->y, $pos->z)]);
212 }
213
218 public function getTiles() : array{
219 return $this->tiles;
220 }
221
229 public function getTile(int $x, int $y, int $z) : ?Tile{
230 return $this->tiles[Chunk::blockHash($x, $y, $z)] ?? null;
231 }
232
236 public function onUnload() : void{
237 foreach($this->getTiles() as $tile){
238 $tile->close();
239 }
240 }
241
246 public function getHeightMapArray() : array{
247 return $this->heightMap->getValues();
248 }
249
254 public function setHeightMapArray(array $values) : void{
255 $this->heightMap = new HeightArray($values);
256 }
257
258 public function isTerrainDirty() : bool{
259 return $this->terrainDirtyFlags !== self::DIRTY_FLAGS_NONE;
260 }
261
262 public function getTerrainDirtyFlag(int $flag) : bool{
263 return ($this->terrainDirtyFlags & $flag) !== 0;
264 }
265
266 public function getTerrainDirtyFlags() : int{
267 return $this->terrainDirtyFlags;
268 }
269
270 public function setTerrainDirtyFlag(int $flag, bool $value) : void{
271 if($value){
272 $this->terrainDirtyFlags |= $flag;
273 }else{
274 $this->terrainDirtyFlags &= ~$flag;
275 }
276 }
277
278 public function setTerrainDirty() : void{
279 $this->terrainDirtyFlags = self::DIRTY_FLAGS_ALL;
280 }
281
282 public function clearTerrainDirtyFlags() : void{
283 $this->terrainDirtyFlags = self::DIRTY_FLAGS_NONE;
284 }
285
286 public function getSubChunk(int $y) : SubChunk{
287 if($y < self::MIN_SUBCHUNK_INDEX || $y > self::MAX_SUBCHUNK_INDEX){
288 throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
289 }
290 return $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX];
291 }
292
296 public function setSubChunk(int $y, ?SubChunk $subChunk) : void{
297 if($y < self::MIN_SUBCHUNK_INDEX || $y > self::MAX_SUBCHUNK_INDEX){
298 throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y");
299 }
300
301 $this->subChunks[$y - self::MIN_SUBCHUNK_INDEX] = $subChunk ?? new SubChunk(Block::EMPTY_STATE_ID, [], new PalettedBlockArray(BiomeIds::OCEAN));
302 $this->terrainDirtyFlags |= self::DIRTY_FLAG_BLOCKS;
303 }
304
309 public function getSubChunks() : array{
310 $result = [];
311 foreach($this->subChunks as $yOffset => $subChunk){
312 $result[$yOffset + self::MIN_SUBCHUNK_INDEX] = $subChunk;
313 }
314 return $result;
315 }
316
320 public function collectGarbage() : void{
321 foreach($this->subChunks as $y => $subChunk){
322 $subChunk->collectGarbage();
323 }
324 }
325
326 public function __clone(){
327 //we don't bother cloning entities or tiles since it's impractical to do so (too many dependencies)
328 $this->subChunks = \SplFixedArray::fromArray(array_map(function(SubChunk $subChunk) : SubChunk{
329 return clone $subChunk;
330 }, $this->subChunks->toArray()));
331 $this->heightMap = clone $this->heightMap;
332 }
333
341 public static function blockHash(int $x, int $y, int $z) : int{
342 return ($y << (2 * SubChunk::COORD_BIT_SIZE)) |
343 (($z & SubChunk::COORD_MASK) << SubChunk::COORD_BIT_SIZE) |
344 ($x & SubChunk::COORD_MASK);
345 }
346}
getTile(int $x, int $y, int $z)
Definition Chunk.php:229
setSubChunk(int $y, ?SubChunk $subChunk)
Definition Chunk.php:296
setHeightMapArray(array $values)
Definition Chunk.php:254
static blockHash(int $x, int $y, int $z)
Definition Chunk.php:341
getHeightMap(int $x, int $z)
Definition Chunk.php:138
setBiomeId(int $x, int $y, int $z, int $biomeId)
Definition Chunk.php:175
getHighestBlockAt(int $x, int $z)
Definition Chunk.php:121
__construct(array $subChunks, bool $terrainPopulated)
Definition Chunk.php:71
getBiomeId(int $x, int $y, int $z)
Definition Chunk.php:161
setHeightMap(int $x, int $z, int $value)
Definition Chunk.php:148
setBlockStateId(int $x, int $y, int $z, int $block)
Definition Chunk.php:108
getBlockStateId(int $x, int $y, int $z)
Definition Chunk.php:101