mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 07:40:43 +00:00
Fix NameLayer atlas asset references
This commit is contained in:
@@ -52,6 +52,7 @@ const DYNAMIC_MOVER_ZOOM_HYSTERESIS = 0.2;
|
||||
const DYNAMIC_MOVER_SCALE_SETTLE_MS = 160;
|
||||
const DYNAMIC_MOVER_SCALE_COOLDOWN_MS = 300;
|
||||
const DYNAMIC_MOVER_SUBPIXEL_SNAP = false;
|
||||
const SPRITELESS_CELL_MARKER_SIZE = 3;
|
||||
const SMALL_SHIP_MASK_SIZE = 5;
|
||||
const TRANSPORT_SHIP_MASK = [
|
||||
"..B..",
|
||||
@@ -2191,14 +2192,15 @@ export class UnitLayer implements Layer {
|
||||
customTerritoryColor?: Colord,
|
||||
): MoverSpriteRect {
|
||||
if (this.isSpriteLessCellUnit(unit)) {
|
||||
const outX = roundCoords ? Math.round(x) : x;
|
||||
const outY = roundCoords ? Math.round(y) : y;
|
||||
const markerOffset = Math.floor(SPRITELESS_CELL_MARKER_SIZE / 2);
|
||||
const outX = (roundCoords ? Math.round(x) : x) - markerOffset;
|
||||
const outY = (roundCoords ? Math.round(y) : y) - markerOffset;
|
||||
const pad = 2;
|
||||
return {
|
||||
x: outX - pad,
|
||||
y: outY - pad,
|
||||
w: 1 + pad * 2,
|
||||
h: 1 + pad * 2,
|
||||
w: SPRITELESS_CELL_MARKER_SIZE + pad * 2,
|
||||
h: SPRITELESS_CELL_MARKER_SIZE + pad * 2,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2248,10 +2250,16 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
if (this.isSpriteLessCellUnit(unit)) {
|
||||
const outX = roundCoords ? Math.round(x) : x;
|
||||
const outY = roundCoords ? Math.round(y) : y;
|
||||
const markerOffset = Math.floor(SPRITELESS_CELL_MARKER_SIZE / 2);
|
||||
const outX = (roundCoords ? Math.round(x) : x) - markerOffset;
|
||||
const outY = (roundCoords ? Math.round(y) : y) - markerOffset;
|
||||
ctx.fillStyle = this.cellUnitFillStyle(unit);
|
||||
ctx.fillRect(outX, outY, 1, 1);
|
||||
ctx.fillRect(
|
||||
outX,
|
||||
outY,
|
||||
SPRITELESS_CELL_MARKER_SIZE,
|
||||
SPRITELESS_CELL_MARKER_SIZE,
|
||||
);
|
||||
ctx.restore();
|
||||
|
||||
return this.computeSpriteRect(
|
||||
|
||||
@@ -195,11 +195,63 @@ function renderBitmapFontAsset({
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextureAtlasJsonAsset({
|
||||
resourcesDir,
|
||||
relativePath,
|
||||
assetManifest,
|
||||
}: DerivedPublicAssetRenderContext): string {
|
||||
const atlas = JSON.parse(readPublicAssetText(resourcesDir, relativePath)) as {
|
||||
meta?: { image?: unknown };
|
||||
};
|
||||
|
||||
const imagePath = atlas.meta?.image;
|
||||
if (imagePath === undefined) {
|
||||
return `${JSON.stringify(atlas, null, 2)}\n`;
|
||||
}
|
||||
|
||||
if (typeof imagePath !== "string") {
|
||||
throw new Error(
|
||||
`Derived asset ${relativePath} contains a non-string atlas image reference`,
|
||||
);
|
||||
}
|
||||
|
||||
if (imagePath.trim().length === 0) {
|
||||
throw new Error(
|
||||
`Derived asset ${relativePath} contains a blank atlas image reference`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isExternalAssetReference(imagePath)) {
|
||||
const referencedAssetPath = resolveDerivedAssetReference(
|
||||
relativePath,
|
||||
imagePath,
|
||||
);
|
||||
const referencedHashedUrl = assetManifest[referencedAssetPath];
|
||||
if (!referencedHashedUrl) {
|
||||
throw new Error(
|
||||
`Derived asset ${relativePath} references ${referencedAssetPath}, but it is missing from the asset manifest`,
|
||||
);
|
||||
}
|
||||
|
||||
atlas.meta!.image = getEmittedAssetRelativePath(
|
||||
relativePath,
|
||||
referencedHashedUrl,
|
||||
);
|
||||
}
|
||||
|
||||
return `${JSON.stringify(atlas, null, 2)}\n`;
|
||||
}
|
||||
|
||||
const DERIVED_PUBLIC_ASSET_RENDERERS: DerivedPublicAssetRenderer[] = [
|
||||
{
|
||||
matches: (relativePath) => relativePath === "manifest.json",
|
||||
render: renderWebManifestAsset,
|
||||
},
|
||||
{
|
||||
matches: (relativePath) =>
|
||||
relativePath.startsWith("images/") && relativePath.endsWith(".json"),
|
||||
render: renderTextureAtlasJsonAsset,
|
||||
},
|
||||
{
|
||||
matches: (relativePath) =>
|
||||
relativePath.startsWith("fonts/") && relativePath.endsWith(".xml"),
|
||||
|
||||
@@ -59,6 +59,36 @@ describe("PublicAssetManifest", () => {
|
||||
await fs.writeFile(pagePath, pageContent);
|
||||
}
|
||||
|
||||
async function writeTextureAtlasFixture(
|
||||
resourcesDir: string,
|
||||
jsonRelativePath: string,
|
||||
imageFilePath: string,
|
||||
imageContent: string = "png-v1",
|
||||
): Promise<void> {
|
||||
const jsonPath = path.join(resourcesDir, jsonRelativePath);
|
||||
const imagePath = path.join(path.dirname(jsonPath), imageFilePath);
|
||||
const atlasImagePath = imageFilePath.split(path.sep).join(path.posix.sep);
|
||||
|
||||
await fs.mkdir(path.dirname(imagePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
jsonPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
frames: {},
|
||||
meta: {
|
||||
image: atlasImagePath,
|
||||
format: "RGBA8888",
|
||||
size: { w: 1, h: 1 },
|
||||
scale: "1",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
await fs.writeFile(imagePath, imageContent);
|
||||
}
|
||||
|
||||
async function emitHashedAsset(
|
||||
outDir: string,
|
||||
assetHref: string,
|
||||
@@ -189,6 +219,79 @@ describe("PublicAssetManifest", () => {
|
||||
expect(emittedManifest).toContain("data:image/png;base64,AAA");
|
||||
});
|
||||
|
||||
test("rewrites TexturePacker atlas image refs to hashed relative paths", async () => {
|
||||
const { resourcesDir, outDir } = await createTempResources();
|
||||
|
||||
await writeTextureAtlasFixture(
|
||||
resourcesDir,
|
||||
path.join("images", "namelayer-icons.json"),
|
||||
"namelayer-icons.png",
|
||||
);
|
||||
|
||||
const assetManifest = buildPublicAssetManifest([resourcesDir]);
|
||||
createHashedPublicAssetFiles([resourcesDir], outDir, assetManifest);
|
||||
|
||||
const jsonHref = assetManifest["images/namelayer-icons.json"];
|
||||
const pngHref = assetManifest["images/namelayer-icons.png"];
|
||||
const emittedJson = await emitHashedAsset(outDir, jsonHref);
|
||||
const emittedAtlas = JSON.parse(emittedJson) as {
|
||||
meta: { image: string };
|
||||
};
|
||||
|
||||
expect(emittedAtlas.meta.image).toBe(
|
||||
getExpectedRelativeEmittedPath(jsonHref, pngHref),
|
||||
);
|
||||
expect(emittedAtlas.meta.image).not.toBe("namelayer-icons.png");
|
||||
});
|
||||
|
||||
test("TexturePacker atlas JSON hash changes when its image changes", async () => {
|
||||
const { resourcesDir } = await createTempResources();
|
||||
|
||||
await writeTextureAtlasFixture(
|
||||
resourcesDir,
|
||||
path.join("images", "namelayer-icons.json"),
|
||||
"namelayer-icons.png",
|
||||
);
|
||||
|
||||
const firstManifest = buildPublicAssetManifest([resourcesDir]);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(resourcesDir, "images", "namelayer-icons.png"),
|
||||
"png-v2",
|
||||
);
|
||||
clearPublicAssetManifestCache();
|
||||
|
||||
const secondManifest = buildPublicAssetManifest([resourcesDir]);
|
||||
|
||||
expect(firstManifest["images/namelayer-icons.png"]).not.toBe(
|
||||
secondManifest["images/namelayer-icons.png"],
|
||||
);
|
||||
expect(firstManifest["images/namelayer-icons.json"]).not.toBe(
|
||||
secondManifest["images/namelayer-icons.json"],
|
||||
);
|
||||
});
|
||||
|
||||
test("fails when TexturePacker atlas JSON references a missing image", async () => {
|
||||
const { resourcesDir } = await createTempResources();
|
||||
|
||||
await fs.mkdir(path.join(resourcesDir, "images"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(resourcesDir, "images", "namelayer-icons.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
frames: {},
|
||||
meta: { image: "missing.png" },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
expect(() => buildPublicAssetManifest([resourcesDir])).toThrow(
|
||||
/images\/namelayer-icons\.json references images\/missing\.png/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("rewrites BMFont XML page filenames to hashed relative paths", async () => {
|
||||
const { resourcesDir, outDir } = await createTempResources();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user