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;
94 private function __construct(
96 private Compressor $compressor,
97 private int $dimensionId = DimensionIds::OVERWORLD
100 private function prepareChunkAsync(
int $chunkX,
int $chunkZ,
int $chunkHash) : CompressBatchPromise{
101 $this->world->registerChunkListener($this, $chunkX, $chunkZ);
102 $chunk = $this->world->getChunk($chunkX, $chunkZ);
104 throw new \InvalidArgumentException(
"Cannot request an unloaded chunk");
108 $this->world->timings->syncChunkSendPrepare->startTiming();
110 $promise =
new CompressBatchPromise();
112 $this->world->getServer()->getAsyncPool()->submitTask(
113 new ChunkRequestTask(
122 $this->caches[$chunkHash] = $promise;
123 $promise->onResolve(
function(CompressBatchPromise $promise) use ($chunkHash) :
void{
125 if(($this->caches[$chunkHash] ??
null) === $promise){
126 $this->caches[$chunkHash] = $promise->getResult();
132 $this->world->timings->syncChunkSendPrepare->stopTiming();
142 $chunkHash =
World::chunkHash($chunkX, $chunkZ);
143 if(isset($this->caches[$chunkHash])){
145 return $this->caches[$chunkHash];
148 return $this->prepareChunkAsync($chunkX, $chunkZ, $chunkHash);
151 private function destroy(
int $chunkX,
int $chunkZ) : bool{
152 $chunkHash =
World::chunkHash($chunkX, $chunkZ);
153 $existing = $this->caches[$chunkHash] ??
null;
154 unset($this->caches[$chunkHash]);
156 return $existing !==
null;
162 private function destroyOrRestart(
int $chunkX,
int $chunkZ) : void{
163 $chunkPosHash =
World::chunkHash($chunkX, $chunkZ);
164 $cache = $this->caches[$chunkPosHash] ??
null;
166 if(!is_string($cache)){
169 unset($this->caches[$chunkPosHash]);
171 $this->prepareChunkAsync($chunkX, $chunkZ, $chunkPosHash)->onResolve(...$cache->getResolveCallbacks());
174 $this->destroy($chunkX, $chunkZ);
179 use ChunkListenerNoOpTrait {
181 onChunkChanged as
private;
182 onBlockChanged as
private;
183 onChunkUnloaded as
private;
190 $this->destroyOrRestart($chunkX, $chunkZ);
199 $this->destroy($block->getFloorX() >>
Chunk::COORD_BIT_SIZE, $block->getFloorZ() >>
Chunk::COORD_BIT_SIZE);
206 $this->destroy($chunkX, $chunkZ);
207 $this->world->unregisterChunkListener($this, $chunkX, $chunkZ);
216 foreach($this->caches as $cache){
217 if(is_string($cache)){
218 $result += strlen($cache);
228 $total = $this->hits + $this->misses;
229 return $total > 0 ? $this->hits / $total : 0.0;