44 private static array $instances = [];
50 $worldId = spl_object_id($world);
51 $compressorId = spl_object_id($compressor);
52 if(!isset(self::$instances[$worldId])){
53 self::$instances[$worldId] = [];
55 foreach(self::$instances[$worldId] as $cache){
58 unset(self::$instances[$worldId]);
59 \GlobalLogger::get()->debug(
"Destroyed chunk packet caches for world#$worldId");
62 if(!isset(self::$instances[$worldId][$compressorId])){
63 \GlobalLogger::get()->debug(
"Created new chunk packet cache (world#$worldId, compressor#$compressorId)");
64 self::$instances[$worldId][$compressorId] =
new self($world, $compressor);
66 return self::$instances[$worldId][$compressorId];
69 public static function pruneCaches() : void{
70 foreach(self::$instances as $compressorMap){
71 foreach($compressorMap as $chunkCache){
72 foreach($chunkCache->caches as $chunkHash => $promise){
73 if(is_string($promise)){
75 unset($chunkCache->caches[$chunkHash]);
86 private array $caches = [];
88 private int $hits = 0;
89 private int $misses = 0;
91 private function __construct(
93 private Compressor $compressor
96 private function prepareChunkAsync(
int $chunkX,
int $chunkZ,
int $chunkHash) : CompressBatchPromise{
97 $this->world->registerChunkListener($this, $chunkX, $chunkZ);
98 $chunk = $this->world->getChunk($chunkX, $chunkZ);
100 throw new \InvalidArgumentException(
"Cannot request an unloaded chunk");
104 $this->world->timings->syncChunkSendPrepare->startTiming();
106 $promise =
new CompressBatchPromise();
108 $this->world->getServer()->getAsyncPool()->submitTask(
109 new ChunkRequestTask(
112 DimensionIds::OVERWORLD,
118 $this->caches[$chunkHash] = $promise;
119 $promise->onResolve(
function(CompressBatchPromise $promise) use ($chunkHash) :
void{
121 if(($this->caches[$chunkHash] ??
null) === $promise){
122 $this->caches[$chunkHash] = $promise->getResult();
128 $this->world->timings->syncChunkSendPrepare->stopTiming();
138 $chunkHash =
World::chunkHash($chunkX, $chunkZ);
139 if(isset($this->caches[$chunkHash])){
141 return $this->caches[$chunkHash];
144 return $this->prepareChunkAsync($chunkX, $chunkZ, $chunkHash);
147 private function destroy(
int $chunkX,
int $chunkZ) : bool{
148 $chunkHash =
World::chunkHash($chunkX, $chunkZ);
149 $existing = $this->caches[$chunkHash] ??
null;
150 unset($this->caches[$chunkHash]);
152 return $existing !==
null;
158 private function destroyOrRestart(
int $chunkX,
int $chunkZ) : void{
159 $chunkPosHash =
World::chunkHash($chunkX, $chunkZ);
160 $cache = $this->caches[$chunkPosHash] ??
null;
162 if(!is_string($cache)){
165 unset($this->caches[$chunkPosHash]);
167 $this->prepareChunkAsync($chunkX, $chunkZ, $chunkPosHash)->onResolve(...$cache->getResolveCallbacks());
170 $this->destroy($chunkX, $chunkZ);
175 use ChunkListenerNoOpTrait {
177 onChunkChanged as
private;
178 onBlockChanged as
private;
179 onChunkUnloaded as
private;
186 $this->destroyOrRestart($chunkX, $chunkZ);
195 $this->destroy($block->getFloorX() >>
Chunk::COORD_BIT_SIZE, $block->getFloorZ() >>
Chunk::COORD_BIT_SIZE);
202 $this->destroy($chunkX, $chunkZ);
203 $this->world->unregisterChunkListener($this, $chunkX, $chunkZ);
212 foreach($this->caches as $cache){
213 if(is_string($cache)){
214 $result += strlen($cache);
224 $total = $this->hits + $this->misses;
225 return $total > 0 ? $this->hits / $total : 0.0;