openvibers/openvibe.games Commits

Games: physgun texture as a 320 KB JPEG (was a 1.43 MB PNG), .obj served as text, only hashed /assets/ immutable (#67)

OP OpenVibers committed 30 days ago · 4ece44dd05 · 5 files, +20 −6

Three static-asset fixes, found measuring M6's first load.

- **The physgun texture is a JPEG** (`public/assets/physgun_tex.jpg`, quality 90, 4:4:4): 320 KB instead of a 1.43 MB PNG. Its alpha was never read (the material sets no `hasAlpha`), and every player downloads it at spawn.
- **`.obj` is served as text** (`text/plain; charset=utf-8`): the physgun's 472 KB model went out as `application/octet-stream`, which the CDN does not compress.
- **Only Vite-hashed files under `/assets/` are immutable.** The files `public/assets/` copies there (textures, the model) keep their names, yet they were cached `immutable` a year: one changed in place would have stayed stale that long. They get `max-age=86400, stale-while-revalidate=604800`; hashed build files keep `immutable`.

Tests: `httpServer.test.ts` pins the public-asset cache header (33/33). Typecheck is clean. `npm test` 123/124: `instanceHub.test.ts` failed under the full-suite load and passes alone; it does not touch these files.

Changes

ddiff --git a/apps/client/public/assets/physgun_tex.jpg b/apps/client/public/assets/physgun_tex.jpg
nnew file mode 100644
iindex 0000000..fd37f35
BBinary files /dev/null and b/apps/client/public/assets/physgun_tex.jpg differ
ddiff --git a/apps/client/public/assets/physgun_tex.png b/apps/client/public/assets/physgun_tex.png
ddeleted file mode 100755
iindex 5941783..0000000
BBinary files a/apps/client/public/assets/physgun_tex.png and /dev/null differ
ddiff --git a/apps/client/src/render/viewmodel.ts b/apps/client/src/render/viewmodel.ts
iindex 9f275ca..aa362e7 100644
---- a/apps/client/src/render/viewmodel.ts
++++ b/apps/client/src/render/viewmodel.ts
@@@ -187,7 +187,7 @@ export class Viewmodel {
try {
const result = await SceneLoader.ImportMeshAsync('', '/assets/', 'physgun.obj', this.scene)
const mat = new StandardMaterial('physgun-vm', this.scene)
-- mat.diffuseTexture = new Texture('/assets/physgun_tex.png', this.scene)
++ mat.diffuseTexture = new Texture('/assets/physgun_tex.jpg', this.scene)
mat.specularColor = new Color3(0.15, 0.15, 0.15)
mat.emissiveColor = new Color3(0.25, 0.28, 0.32)
mat.backFaceCulling = false
ddiff --git a/apps/server/src/net/httpServer.test.ts b/apps/server/src/net/httpServer.test.ts
iindex b09fbd6..760ac91 100644
---- a/apps/server/src/net/httpServer.test.ts
++++ b/apps/server/src/net/httpServer.test.ts
@@@ -139,7 +139,11 @@ describe('real routes on the apex', () => {
expect(asset.headers['content-type']).toBe('text/javascript')
expect(asset.headers['cache-control']).toBe('public, max-age=31536000, immutable')
expect((await send('/frame.js')).status).toBe(200)
-- expect((await send('/assets/tex/wood_planks.jpg')).status).toBe(200)
++ const tex = await send('/assets/tex/wood_planks.jpg')
++ expect(tex.status).toBe(200)
++ expect(tex.headers['cache-control']).toBe(
++ 'public, max-age=86400, stale-while-revalidate=604800',
++ )
})
it('answers health, readiness, metrics and the API', async () => {
ddiff --git a/apps/server/src/net/httpServer.ts b/apps/server/src/net/httpServer.ts
iindex 7e0c2ad..cc3f68a 100644
---- a/apps/server/src/net/httpServer.ts
++++ b/apps/server/src/net/httpServer.ts
@@@ -74,8 +74,14 @@ const MIME: Record<string, string> = {
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.map': 'application/json',
++ // Wavefront models are text: a text type lets the CDN compress them (the physgun's 472 KB went out raw as
++ // application/octet-stream).
++ '.obj': 'text/plain; charset=utf-8',
}
++/** A file name Vite content-hashed (`main-CE0R2rej.js`): its bytes can never change under that name. */
++const HASHED_BUILD_FILE = /-[A-Za-z0-9_-]{8}\.[a-z0-9]+$/
++
/**
* Authorization for the editor HTTP routes. Owner rule (ADR-0007 deleted
* the shared secret): a Network staff session with `staff.games.manage` is
@@@ -811,13 +817,17 @@ export function createHttpServer(
res.writeHead(200, {
'content-type': type,
// HTML must always revalidate (a stale page pins an old protocol
-- // version and can't join). Vite content-hashes /assets/, so those
-- // are immutable; everything else gets a modest TTL.
++ // version and can't join). Vite content-hashes what it builds into
++ // /assets/, so those are immutable. The files public/assets/ copies
++ // there keep their names (textures, the physgun model), so they get a
++ // day: one of them changed in place would otherwise stay stale a year.
'cache-control': filePath.endsWith('.html')
? 'no-cache'
-- : filePath.includes('/assets/')
++ : filePath.includes('/assets/') && HASHED_BUILD_FILE.test(filePath)
? 'public, max-age=31536000, immutable'
-- : 'public, max-age=3600',
++ : filePath.includes('/assets/')
++ ? 'public, max-age=86400, stale-while-revalidate=604800'
++ : 'public, max-age=3600',
})
createReadStream(filePath)
.on('error', (err) => {