mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 01:46:04 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
@ -212,10 +212,25 @@ class World implements ChunkManager{
|
||||
private int $maxY;
|
||||
|
||||
/**
|
||||
* @var TickingChunkEntry[] chunkHash => TickingChunkEntry
|
||||
* @phpstan-var array<ChunkPosHash, TickingChunkEntry>
|
||||
* @var ChunkTicker[][] chunkHash => [spl_object_id => ChunkTicker]
|
||||
* @phpstan-var array<ChunkPosHash, array<int, ChunkTicker>>
|
||||
*/
|
||||
private array $tickingChunks = [];
|
||||
private array $registeredTickingChunks = [];
|
||||
|
||||
/**
|
||||
* Set of chunks which are definitely ready for ticking.
|
||||
*
|
||||
* @var int[]
|
||||
* @phpstan-var array<ChunkPosHash, ChunkPosHash>
|
||||
*/
|
||||
private array $validTickingChunks = [];
|
||||
|
||||
/**
|
||||
* Set of chunks which might be ready for ticking. These will be checked at the next tick.
|
||||
* @var int[]
|
||||
* @phpstan-var array<ChunkPosHash, ChunkPosHash>
|
||||
*/
|
||||
private array $recheckTickingChunks = [];
|
||||
|
||||
/**
|
||||
* @var ChunkLoader[][] chunkHash => [spl_object_id => ChunkLoader]
|
||||
@ -1153,14 +1168,14 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of chunk position hashes (as returned by World::chunkHash()) which are currently registered for
|
||||
* Returns a list of chunk position hashes (as returned by World::chunkHash()) which are currently valid for
|
||||
* ticking.
|
||||
*
|
||||
* @return int[]
|
||||
* @phpstan-return list<ChunkPosHash>
|
||||
*/
|
||||
public function getTickingChunks() : array{
|
||||
return array_keys($this->tickingChunks);
|
||||
return array_keys($this->validTickingChunks);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1169,11 +1184,8 @@ class World implements ChunkManager{
|
||||
*/
|
||||
public function registerTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void{
|
||||
$chunkPosHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$entry = $this->tickingChunks[$chunkPosHash] ?? null;
|
||||
if($entry === null){
|
||||
$entry = $this->tickingChunks[$chunkPosHash] = new TickingChunkEntry();
|
||||
}
|
||||
$entry->tickers[spl_object_id($ticker)] = $ticker;
|
||||
$this->registeredTickingChunks[$chunkPosHash][spl_object_id($ticker)] = $ticker;
|
||||
$this->recheckTickingChunks[$chunkPosHash] = $chunkPosHash;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1183,42 +1195,40 @@ class World implements ChunkManager{
|
||||
public function unregisterTickingChunk(ChunkTicker $ticker, int $chunkX, int $chunkZ) : void{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
$tickerId = spl_object_id($ticker);
|
||||
if(isset($this->tickingChunks[$chunkHash]->tickers[$tickerId])){
|
||||
unset($this->tickingChunks[$chunkHash]->tickers[$tickerId]);
|
||||
if(count($this->tickingChunks[$chunkHash]->tickers) === 0){
|
||||
unset($this->tickingChunks[$chunkHash]);
|
||||
if(isset($this->registeredTickingChunks[$chunkHash][$tickerId])){
|
||||
unset($this->registeredTickingChunks[$chunkHash][$tickerId]);
|
||||
if(count($this->registeredTickingChunks[$chunkHash]) === 0){
|
||||
unset(
|
||||
$this->registeredTickingChunks[$chunkHash],
|
||||
$this->recheckTickingChunks[$chunkHash],
|
||||
$this->validTickingChunks[$chunkHash]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function tickChunks() : void{
|
||||
if($this->chunkTickRadius <= 0 || count($this->tickingChunks) === 0){
|
||||
if($this->chunkTickRadius <= 0 || count($this->registeredTickingChunks) === 0){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->startTiming();
|
||||
if(count($this->recheckTickingChunks) > 0){
|
||||
$this->timings->randomChunkUpdatesChunkSelection->startTiming();
|
||||
|
||||
/** @var bool[] $chunkTickList chunkhash => dummy */
|
||||
$chunkTickList = [];
|
||||
$chunkTickableCache = [];
|
||||
|
||||
$chunkTickableCache = [];
|
||||
|
||||
foreach($this->tickingChunks as $hash => $entry){
|
||||
if(!$entry->ready){
|
||||
foreach($this->recheckTickingChunks as $hash => $_){
|
||||
World::getXZ($hash, $chunkX, $chunkZ);
|
||||
if($this->isChunkTickable($chunkX, $chunkZ, $chunkTickableCache)){
|
||||
$entry->ready = true;
|
||||
}else{
|
||||
//the chunk has been flagged as temporarily not tickable, so we don't want to tick it this time
|
||||
continue;
|
||||
$this->validTickingChunks[$hash] = $hash;
|
||||
}
|
||||
}
|
||||
$chunkTickList[$hash] = true;
|
||||
$this->recheckTickingChunks = [];
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->stopTiming();
|
||||
}
|
||||
|
||||
$this->timings->randomChunkUpdatesChunkSelection->stopTiming();
|
||||
|
||||
foreach($chunkTickList as $index => $_){
|
||||
foreach($this->validTickingChunks as $index => $_){
|
||||
World::getXZ($index, $chunkX, $chunkZ);
|
||||
|
||||
$this->tickChunk($chunkX, $chunkZ);
|
||||
@ -1267,16 +1277,23 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the 3x3 chunks around the specified chunk as not ready to be ticked. This is used to prevent chunk ticking
|
||||
* while a chunk is being populated, light-populated, or unloaded.
|
||||
* Each chunk will be rechecked every tick until it is ready to be ticked again.
|
||||
* Marks the 3x3 square of chunks centered on the specified chunk for chunk ticking eligibility recheck.
|
||||
*
|
||||
* This should be used whenever the chunk's eligibility to be ticked is changed. This includes:
|
||||
* - Loading/unloading the chunk (the chunk may be registered for ticking before it is loaded)
|
||||
* - Locking/unlocking the chunk (e.g. world population)
|
||||
* - Light populated state change (i.e. scheduled for light population, or light population completed)
|
||||
* - Arbitrary chunk replacement (i.e. setChunk() or similar)
|
||||
*/
|
||||
private function markTickingChunkUnavailable(int $chunkX, int $chunkZ) : void{
|
||||
private function markTickingChunkForRecheck(int $chunkX, int $chunkZ) : void{
|
||||
for($cx = -1; $cx <= 1; ++$cx){
|
||||
for($cz = -1; $cz <= 1; ++$cz){
|
||||
$chunkHash = World::chunkHash($chunkX + $cx, $chunkZ + $cz);
|
||||
if(isset($this->tickingChunks[$chunkHash])){
|
||||
$this->tickingChunks[$chunkHash]->ready = false;
|
||||
unset($this->validTickingChunks[$chunkHash]);
|
||||
if(isset($this->registeredTickingChunks[$chunkHash])){
|
||||
$this->recheckTickingChunks[$chunkHash] = $chunkHash;
|
||||
}else{
|
||||
unset($this->recheckTickingChunks[$chunkHash]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1287,7 +1304,7 @@ class World implements ChunkManager{
|
||||
$lightPopulatedState = $this->chunks[$chunkHash]->isLightPopulated();
|
||||
if($lightPopulatedState === false){
|
||||
$this->chunks[$chunkHash]->setLightPopulated(null);
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
|
||||
$this->workerPool->submitTask(new LightPopulationTask(
|
||||
$this->chunks[$chunkHash],
|
||||
@ -1311,6 +1328,7 @@ class World implements ChunkManager{
|
||||
$chunk->getSubChunk($y)->setBlockSkyLightArray($lightArray);
|
||||
}
|
||||
$chunk->setLightPopulated(true);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
}
|
||||
));
|
||||
}
|
||||
@ -2362,7 +2380,7 @@ class World implements ChunkManager{
|
||||
throw new \InvalidArgumentException("Chunk $chunkX $chunkZ is already locked");
|
||||
}
|
||||
$this->chunkLock[$chunkHash] = $lockId;
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2377,6 +2395,7 @@ class World implements ChunkManager{
|
||||
$chunkHash = World::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunkLock[$chunkHash]) && ($lockId === null || $this->chunkLock[$chunkHash] === $lockId)){
|
||||
unset($this->chunkLock[$chunkHash]);
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -2428,7 +2447,7 @@ class World implements ChunkManager{
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
$chunk->setTerrainDirty();
|
||||
$this->markTickingChunkUnavailable($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking
|
||||
$this->markTickingChunkForRecheck($chunkX, $chunkZ); //this replacement chunk may not meet the conditions for ticking
|
||||
|
||||
if(!$this->isChunkInUse($chunkX, $chunkZ)){
|
||||
$this->unloadChunkRequest($chunkX, $chunkZ);
|
||||
@ -2710,6 +2729,7 @@ class World implements ChunkManager{
|
||||
foreach($this->getChunkListeners($x, $z) as $listener){
|
||||
$listener->onChunkLoaded($x, $z, $this->chunks[$chunkHash]);
|
||||
}
|
||||
$this->markTickingChunkForRecheck($x, $z); //tickers may have been registered before the chunk was loaded
|
||||
|
||||
$this->timings->syncChunkLoad->stopTiming();
|
||||
|
||||
@ -2851,8 +2871,8 @@ class World implements ChunkManager{
|
||||
unset($this->chunks[$chunkHash]);
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
unset($this->tickingChunks[$chunkHash]);
|
||||
$this->markTickingChunkUnavailable($x, $z);
|
||||
unset($this->registeredTickingChunks[$chunkHash]);
|
||||
$this->markTickingChunkForRecheck($x, $z);
|
||||
|
||||
if(array_key_exists($chunkHash, $this->chunkPopulationRequestMap)){
|
||||
$this->logger->debug("Rejecting population promise for chunk $x $z");
|
||||
|
Reference in New Issue
Block a user