Enhance map rendering with WebP support and loading feedback

- Update tile rendering to support both PNG and WebP formats.
- Introduce a loading spinner and progress bar for build feedback.
- Remove the manual build button; builds now trigger automatically on map selection.
- Add a phase plan document outlining future improvements and goals.
This commit is contained in:
Marco 2026-03-27 11:02:30 +01:00
commit 549ff38334
6 changed files with 243 additions and 45 deletions

View file

@ -343,7 +343,7 @@ export class BuildManager {
DOWNLOAD_CACHE_ROOT,
gameId,
`map-${mapId}`,
`${buildOptionSuffix(job.options)}.png`
`${gameId}-map-${mapId}-${buildOptionSuffix(job.options)}.png`
);
if (fs.existsSync(outputPath)) {
return outputPath;
@ -354,7 +354,7 @@ export class BuildManager {
for (let tileY = 0; tileY < job.metadata.tileCountY; tileY += 1) {
for (let tileX = 0; tileX < job.metadata.tileCountX; tileX += 1) {
composites.push({
input: this.renderTile(jobId, gameId, mapId, tileX, tileY),
input: await this.renderTile(jobId, gameId, mapId, tileX, tileY, "png"),
left: tileX * job.metadata.tileSize,
top: tileY * job.metadata.tileSize
});
@ -377,19 +377,21 @@ export class BuildManager {
return outputPath;
}
renderTile(jobId, gameId, mapId, tileX, tileY) {
async renderTile(jobId, gameId, mapId, tileX, tileY, format = "webp") {
const job = this.requireReadyJob(jobId, gameId, mapId);
const tileKey = `${job.id}:${tileX}:${tileY}`;
const tileKey = `${job.id}:${format}:${tileX}:${tileY}`;
if (this.tileCache.has(tileKey)) {
return this.tileCache.get(tileKey);
}
const extension = format === "png" ? "png" : "webp";
const tilePath = path.join(
TILE_CACHE_ROOT,
gameId,
`map-${mapId}`,
buildOptionSuffix(job.options),
`${tileX}-${tileY}.png`
`${tileX}-${tileY}.${extension}`
);
if (fs.existsSync(tilePath)) {
const cached = fs.readFileSync(tilePath);
@ -428,11 +430,25 @@ export class BuildManager {
);
}
const png = encodePng(tileWidth, tileHeight, buffer);
let output;
if (format === "png") {
output = encodePng(tileWidth, tileHeight, buffer);
} else {
output = await sharp(buffer, {
raw: {
width: tileWidth,
height: tileHeight,
channels: 4
},
limitInputPixels: false
})
.webp({ lossless: true, effort: 4 })
.toBuffer();
}
ensureDir(path.dirname(tilePath));
fs.writeFileSync(tilePath, png);
this.tileCache.set(tileKey, png);
return png;
fs.writeFileSync(tilePath, output);
this.tileCache.set(tileKey, output);
return output;
}
requireReadyJob(jobId, gameId, mapId) {