diff --git a/src/world/format/Chunk.php b/src/world/format/Chunk.php index 9f001fd37..2bef86b26 100644 --- a/src/world/format/Chunk.php +++ b/src/world/format/Chunk.php @@ -42,6 +42,8 @@ class Chunk{ public const COORD_BIT_SIZE = SubChunk::COORD_BIT_SIZE; public const COORD_MASK = SubChunk::COORD_MASK; + private int $modificationCount; + /** @var int */ private $terrainDirtyFlags = 0; @@ -68,7 +70,7 @@ class Chunk{ /** * @param SubChunk[] $subChunks */ - public function __construct(array $subChunks, BiomeArray $biomeIds, bool $terrainPopulated){ + public function __construct(array $subChunks, BiomeArray $biomeIds, bool $terrainPopulated, int $modificationCount = 0){ $this->subChunks = new \SplFixedArray(Chunk::MAX_SUBCHUNKS); foreach($this->subChunks as $y => $null){ @@ -80,6 +82,7 @@ class Chunk{ $this->biomeIds = $biomeIds; $this->terrainPopulated = $terrainPopulated; + $this->modificationCount = $modificationCount; } /** @@ -108,6 +111,7 @@ class Chunk{ public function setFullBlock(int $x, int $y, int $z, int $block) : void{ $this->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)->setFullBlock($x, $y & SubChunk::COORD_MASK, $z, $block); $this->terrainDirtyFlags |= self::DIRTY_FLAG_TERRAIN; + $this->modificationCount++; } /** @@ -171,6 +175,7 @@ class Chunk{ public function setBiomeId(int $x, int $z, int $biomeId) : void{ $this->biomeIds->set($x, $z, $biomeId); $this->terrainDirtyFlags |= self::DIRTY_FLAG_BIOMES; + $this->modificationCount++; } public function isLightPopulated() : ?bool{ @@ -188,6 +193,7 @@ class Chunk{ public function setPopulated(bool $value = true) : void{ $this->terrainPopulated = $value; $this->terrainDirtyFlags |= self::DIRTY_FLAG_TERRAIN; + $this->modificationCount++; } public function addTile(Tile $tile) : void{ @@ -270,16 +276,24 @@ class Chunk{ }else{ $this->terrainDirtyFlags &= ~$flag; } + $this->modificationCount++; } public function setTerrainDirty() : void{ $this->terrainDirtyFlags = ~0; + $this->modificationCount++; } public function clearTerrainDirtyFlags() : void{ $this->terrainDirtyFlags = 0; } + /** + * Returns the modcount for this chunk. Any saveable change to the chunk will cause this number to be incremented, + * so you can use this to detect when the chunk has been modified. + */ + public function getModificationCount() : int{ return $this->modificationCount; } + public function getSubChunk(int $y) : SubChunk{ if($y < 0 || $y >= $this->subChunks->getSize()){ throw new \InvalidArgumentException("Invalid subchunk Y coordinate $y"); diff --git a/src/world/format/io/FastChunkSerializer.php b/src/world/format/io/FastChunkSerializer.php index bb8bec687..0f3db73d1 100644 --- a/src/world/format/io/FastChunkSerializer.php +++ b/src/world/format/io/FastChunkSerializer.php @@ -51,6 +51,8 @@ final class FastChunkSerializer{ */ public static function serializeTerrain(Chunk $chunk) : string{ $stream = new BinaryStream(); + $stream->putLong($chunk->getModificationCount()); + $stream->putByte( ($chunk->isPopulated() ? self::FLAG_POPULATED : 0) ); @@ -88,6 +90,7 @@ final class FastChunkSerializer{ */ public static function deserializeTerrain(string $data) : Chunk{ $stream = new BinaryStream($data); + $modificationCounter = $stream->getLong(); $flags = $stream->getByte(); $terrainPopulated = (bool) ($flags & self::FLAG_POPULATED); @@ -115,6 +118,6 @@ final class FastChunkSerializer{ $biomeIds = new BiomeArray($stream->get(256)); - return new Chunk($subChunks, $biomeIds, $terrainPopulated); + return new Chunk($subChunks, $biomeIds, $terrainPopulated, $modificationCounter); } }