Skip to content

Commit 814f837

Browse files
committed
feat: Add bounds cache
1 parent 4b01fd0 commit 814f837

6 files changed

Lines changed: 133 additions & 36 deletions

File tree

index.php

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Arakne\MapParser\Loader\MapStructure;
99
use Arakne\MapParser\Renderer\CellShape;
1010
use Arakne\MapParser\Renderer\MapRenderer;
11+
use Arakne\MapParser\Sprite\Cache\SqliteSpriteCache;
1112
use Arakne\MapParser\Tile\Cache\SqliteCache;
1213
use Arakne\MapParser\Tile\Coordinate\Bounds;
1314
use Arakne\MapParser\Tile\Coordinate\LatLongBounds;
@@ -63,7 +64,7 @@
6364
return MapStructure::fromSwfFile(new SwfFile($mapFile), new MapKey($map['key']), new MapCoordinates($map['MAP_X'], $map['MAP_Y'], $map['SUBAREA_ID']));
6465
},
6566
tileCache: new SqliteCache($cacheDir . '/tiles.db'),
66-
spriteCache: new \Arakne\MapParser\Sprite\Cache\SqliteSpriteCache($cacheDir . '/sprites.db'),
67+
spriteCache: new SqliteSpriteCache($cacheDir . '/sprites.db'),
6768
attachmentsProviders: [
6869
function (MapStructure $map) {
6970
if ($map->attachments) {
@@ -212,26 +213,24 @@ function (string $name, int $current, int $total) {
212213
exit(0);
213214
}
214215

215-
$worker = new \Workerman\Worker('http://0.0.0.0:5000');
216-
$worker->count = 16;
216+
function run(string $path, array $get, Closure $sender): void {
217+
global $dmp, $dofusMapsDir, $amaknaRenderer, $incarnamRenderer;
217218

218-
$worker->onMessage = function (TcpConnection $connection, Request $request) use ($dofusMapsDir, $amaknaRenderer, $incarnamRenderer): void {
219-
global $dmp;
220-
switch ($request->path()) {
219+
switch ($path) {
221220
case '/':
222221
case '/amakna':
223222
ob_start();
224223
$maxZoom = $amaknaRenderer->maxZoom + 1;
225224
$map = 'amakna';
226225
include __DIR__.'/worldmap.html.php';
227-
$connection->send(new Response(body: ob_get_clean(), headers: ['Content-Type' => 'text/html']));
226+
$sender(ob_get_clean(), ['Content-Type' => 'text/html']);
228227
return;
229228
case '/incarnam':
230229
ob_start();
231230
$maxZoom = $incarnamRenderer->maxZoom + 1;
232231
$map = 'incarnam';
233232
include __DIR__.'/worldmap.html.php';
234-
$connection->send(new Response(body: ob_get_clean(), headers: ['Content-Type' => 'text/html']));
233+
$sender(ob_get_clean(), ['Content-Type' => 'text/html']);
235234
return;
236235
case '/tiles/amakna':
237236
$tileRenderer = $amaknaRenderer;
@@ -241,7 +240,7 @@ function (string $name, int $current, int $total) {
241240
break;
242241

243242
case '/markers/incarnam':
244-
$bbox = LatLongBounds::fromString($request->get('bbox'));
243+
$bbox = LatLongBounds::fromString($get['bbox']);
245244
$bounds = $incarnamRenderer->coordinate->toMapBounds($bbox);
246245
$maps = incarnamMapsInBounds($bounds);
247246
$triggers = loadTriggers(array_map(fn (Map $m) => $m->id, $maps));
@@ -269,19 +268,19 @@ function (string $name, int $current, int $total) {
269268
}
270269
}
271270

272-
$connection->send(new Response(
273-
headers: ['Content-Type' => 'application/json'],
274-
body: json_encode([
271+
$sender(
272+
json_encode([
275273
'type' => 'FeatureCollection',
276274
'features' => [
277275
...$points,
278276
],
279277
]),
280-
));
278+
['Content-Type' => 'application/json'],
279+
);
281280
return;
282281

283282
case '/markers/amakna':
284-
$bbox = LatLongBounds::fromString($request->get('bbox'));
283+
$bbox = LatLongBounds::fromString($get['bbox']);
285284
$bounds = $amaknaRenderer->coordinate->toMapBounds($bbox);
286285
$maps = amaknaMapsInBounds($bounds);
287286
$triggers = loadTriggers(array_map(fn (Map $m) => $m->id, $maps));
@@ -312,19 +311,19 @@ function (string $name, int $current, int $total) {
312311
}
313312
}
314313

315-
$connection->send(new Response(
316-
headers: ['Content-Type' => 'application/json'],
317-
body: json_encode([
314+
$sender(
315+
json_encode([
318316
'type' => 'FeatureCollection',
319317
'features' => [
320318
...$points,
321319
],
322320
]),
323-
));
321+
['Content-Type' => 'application/json'],
322+
);
324323
return;
325324

326325
case '/showmap':
327-
$mapId = (int) $request->get('id', 0);
326+
$mapId = (int) $get['id'] ?? 0;
328327
$map = $dmp->load($mapId);
329328

330329
$triggers = array_map(function ($trigger) use ($map) {
@@ -344,45 +343,60 @@ function (string $name, int $current, int $total) {
344343
require __DIR__ . '/map.html.php';
345344
$content = ob_get_clean();
346345

347-
$connection->send(new Response(body: $content));
346+
$sender($content);
348347
return;
349348

350349
case '/render':
351-
$mapId = (int) $request->get('id', 0);
350+
$mapId = (int) $get['id'] ?? 0;
352351

353352
$rendered = $dmp->render($mapId);
354353

355354
ob_start();
356355
imagepng($rendered);
357356
$imageData = ob_get_clean();
358357

359-
$connection->send(new Response(
360-
headers: ['Content-Type' => 'image/png'],
361-
body: $imageData,
362-
));
358+
$sender(
359+
$imageData,
360+
['Content-Type' => 'image/png'],
361+
);
363362
return;
364363

365364
default:
366-
$connection->send(new Response(404, body: 'Not found'));
365+
$sender('Not found');
367366
return;
368367
}
369368

370-
$x = (int) $request->get('x', 0);
371-
$y = (int) $request->get('y', 0);
372-
$zoom = (int) $request->get('z', 0);
369+
$x = (int) $get['x'] ?? 0;
370+
$y = (int) $get['y'] ?? 0;
371+
$zoom = (int) $get['z'] ?? 0;
373372

374373
$img = $tileRenderer->render($x, $y, $zoom);
375374

376375
ob_start();
377376
imagepng($img);
378377
$imageData = ob_get_clean();
379378

380-
$connection->send(
381-
new Response(
382-
headers: ['Content-Type' => 'image/png'],
383-
body: $imageData,
384-
)
385-
);
379+
$sender($imageData, ['Content-Type' => 'image/png']);
380+
}
381+
382+
if (php_sapi_name() === 'cli-server') {
383+
run($_SERVER['PATH_INFO'], $_GET, function (string $data, array $headers = []) {
384+
foreach ($headers as $name => $value) {
385+
header($name . ': ' . $value);
386+
}
387+
echo $data;
388+
});
389+
return;
390+
}
391+
392+
$worker = new \Workerman\Worker('http://0.0.0.0:5000');
393+
$worker->count = 16;
394+
395+
$worker->onMessage = function (TcpConnection $connection, Request $request) {;
396+
run($request->path(), $request->get(), function (string $data, array $headers = []) use ($connection) {
397+
$response = new Response(200, $headers, $data);
398+
$connection->send($response);
399+
});
386400
};
387401

388402
\Workerman\Worker::runAll();

src/Tile/Cache/FilesystemTileCache.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
namespace Arakne\MapParser\Tile\Cache;
44

5+
use Arakne\MapParser\Tile\Coordinate\Bounds;
56
use Arakne\MapParser\Tile\TileMapCoordinates;
67
use Closure;
78
use GdImage;
89
use Override;
910

1011
use function dirname;
12+
use function file_get_contents;
13+
use function file_put_contents;
1114
use function imagecreatefrompng;
1215
use function imagepng;
1316
use function is_dir;
@@ -27,6 +30,32 @@ public function __construct(
2730
private string $path,
2831
) {}
2932

33+
#[Override]
34+
public function bounds(Closure $compute): Bounds
35+
{
36+
$path = $this->path . '/bounds';
37+
38+
if (is_file($path)) {
39+
$content = file_get_contents($path);
40+
41+
if ($content !== false) {
42+
[$xMin, $xMax, $yMin, $yMax] = explode(',', $content, 4);
43+
return new Bounds((int) $xMin, (int) $xMax, (int) $yMin, (int) $yMax);
44+
}
45+
}
46+
47+
$bounds = $compute();
48+
$dir = dirname($path);
49+
50+
if (!is_dir($dir)) {
51+
mkdir($dir, 0777, true);
52+
}
53+
54+
file_put_contents($path, $bounds->xMin . ',' . $bounds->xMax . ',' . $bounds->yMin . ',' . $bounds->yMax);
55+
56+
return $bounds;
57+
}
58+
3059
#[Override]
3160
public function map(TileMapCoordinates $coordinates, Closure $compute): ?GdImage
3261
{

src/Tile/Cache/NullTileCache.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Arakne\MapParser\Tile\Cache;
44

5+
use Arakne\MapParser\Tile\Coordinate\Bounds;
56
use Arakne\MapParser\Tile\TileMapCoordinates;
67
use Closure;
78
use GdImage;
@@ -13,6 +14,12 @@
1314
*/
1415
final readonly class NullTileCache implements TileCacheInterface
1516
{
17+
#[Override]
18+
public function bounds(Closure $compute): Bounds
19+
{
20+
return $compute();
21+
}
22+
1623
#[Override]
1724
public function map(TileMapCoordinates $coordinates, Closure $compute): ?GdImage
1825
{

src/Tile/Cache/SqliteCache.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Arakne\MapParser\Tile\Cache;
44

5+
use Arakne\MapParser\Tile\Coordinate\Bounds;
56
use Arakne\MapParser\Tile\TileMapCoordinates;
67
use Closure;
78
use GdImage;
@@ -41,6 +42,30 @@ public function __construct(
4142
$this->initSchema();
4243
}
4344

45+
#[Override]
46+
public function bounds(Closure $compute): Bounds
47+
{
48+
$stmt = $this->pdo->prepare('SELECT * FROM bounds WHERE namespace = ?');
49+
$stmt->execute([$this->namespace]);
50+
51+
if (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
52+
return new Bounds((int) $row['xMin'], (int) $row['xMax'], (int) $row['yMin'], (int) $row['yMax']);
53+
}
54+
55+
$bounds = $compute();
56+
57+
$stmt = $this->pdo->prepare('INSERT INTO bounds (namespace, xMin, xMax, yMin, yMax) VALUES (?, ?, ?, ?, ?)');
58+
$stmt->execute([
59+
$this->namespace,
60+
$bounds->xMin,
61+
$bounds->xMax,
62+
$bounds->yMin,
63+
$bounds->yMax,
64+
]);
65+
66+
return $bounds;
67+
}
68+
4469
#[Override]
4570
public function map(TileMapCoordinates $coordinates, Closure $compute): ?GdImage
4671
{
@@ -187,5 +212,17 @@ private function initSchema(): void
187212
);
188213
SQL,
189214
);
215+
216+
$this->pdo->exec(
217+
<<<'SQL'
218+
CREATE TABLE IF NOT EXISTS bounds (
219+
namespace TEXT NOT NULL,
220+
xMin INTEGER NOT NULL,
221+
xMax INTEGER NOT NULL,
222+
yMin INTEGER NOT NULL,
223+
yMax INTEGER NOT NULL
224+
);
225+
SQL,
226+
);
190227
}
191228
}

src/Tile/Cache/TileCacheInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Arakne\MapParser\Tile\Cache;
44

5+
use Arakne\MapParser\Tile\Coordinate\Bounds;
56
use Arakne\MapParser\Tile\TileMapCoordinates;
67
use Closure;
78
use GdImage;
@@ -11,6 +12,15 @@
1112
*/
1213
interface TileCacheInterface
1314
{
15+
/**
16+
* Get (or compute and store) the map bounds
17+
* If the bounds are passed as parameter on the tile renderer, this method will not be called.
18+
*
19+
* @param Closure():Bounds $compute The function to compute the bounds if not cached
20+
* @return Bounds
21+
*/
22+
public function bounds(Closure $compute): Bounds;
23+
1424
/**
1525
* Get (or compute and store) the rendered map image
1626
*

src/WorldMap/WorldMapTileRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function __construct(
3333
) {
3434
parent::__construct(
3535
$this->renderChunk(...),
36-
$worldMap->bounds(),
36+
$cache->bounds($worldMap->bounds(...)),
3737
mapWidth: MapRenderer::DISPLAY_WIDTH,
3838
mapHeight: MapRenderer::DISPLAY_HEIGHT,
3939
tileSize: $tileSize,

0 commit comments

Comments
 (0)