openvibers/openvibe.games Commits

Map: answer the second built-in place with its own map, not a 404 (#283)

OP OpenVibers committed 7 days ago · 1a727169c2 · 4 files, +58 −5

Changes

ddiff --git a/apps/server/src/config.test.ts b/apps/server/src/config.test.ts
iindex d3b151d..cdf6cce 100644
---- a/apps/server/src/config.test.ts
++++ b/apps/server/src/config.test.ts
@@@ -69,6 +69,23 @@ describe('loadConfig surf place', () => {
})
})
++describe('loadConfig editor maps', () => {
++ it('gives the other built-in place a sibling map document, never a 404', () => {
++ const scraplandia = loadConfig({
++ GAMES_PLACE_ID: 'scraplandia',
++ MAP_PATH: '/opt/openvibe.games/data/map.json',
++ })
++ expect(scraplandia.mapPath).toBe('/opt/openvibe.games/data/map.json')
++ expect(scraplandia.secondaryMapPath).toBe('/opt/openvibe.games/data/salvage-rush-map.json')
++
++ const rush = loadConfig({
++ GAMES_PLACE_ID: 'salvage-rush',
++ MAP_PATH: '/opt/openvibe.games/data/salvage-rush-map.json',
++ })
++ expect(rush.secondaryMapPath).toBe('/opt/openvibe.games/data/map.json')
++ })
++})
++
describe('loadConfig scriptMods', () => {
it('is off unless the place enables it', () => {
expect(loadConfig({}).scriptMods).toBe(false)
ddiff --git a/apps/server/src/config.ts b/apps/server/src/config.ts
iindex 307e099..97764ce 100644
---- a/apps/server/src/config.ts
++++ b/apps/server/src/config.ts
@@@ -1,3 +1,4 @@
++import { dirname, join } from 'node:path'
import { surfMap } from '@openvibe/content'
import { MIGRATIONS_DIR } from '@openvibe/persistence'
import { SIM_TICK_RATE, SURF_SNAPSHOT_EVERY, SURF_TICK_RATE } from '@openvibe/shared'
@@@ -30,6 +31,11 @@ export interface ServerConfig {
/** Directory of built client assets to serve, or null for API/WS only. */
staticDir: string | null
mapPath: string
++ /**
++ * The editor map of the other built-in place this process runs (main.ts serves both): a sibling of `mapPath` named
++ * after its place. A missing file is an empty map, not a 404, so the client never reports a failed map request.
++ */
++ secondaryMapPath: string
/**
* TRUST_PROXY: which proxies may set the client-address headers. null = none
* (the TCP peer is the client). 'all' = the server is only reachable through
@@@ -209,6 +215,14 @@ export function loadConfig(env: NodeJS.ProcessEnv): ServerConfig {
// A surf deployment's place id is its map id; the old `surf-lagoon` env value
// boots the canonical place rather than starting a second, aliased one.
const placeId = surfMap(requestedPlace)?.id ?? requestedPlace
++ const mapPath =
++ env.MAP_PATH ?? (placeId === 'salvage-rush' ? 'data/salvage-rush-map.json' : 'data/map.json')
++ // main.ts also runs the other built-in place in this process. Its map document is the sibling of the primary one;
++ // the file not existing yet is an empty map (mapStore.loadMap), where a 404 would show up as a failed request.
++ const secondaryMapPath = join(
++ dirname(mapPath),
++ placeId === 'salvage-rush' ? 'map.json' : 'salvage-rush-map.json',
++ )
return {
port: intEnv(env, 'PORT', 8000),
host: env.HOST ?? '0.0.0.0',
@@@ -223,9 +237,8 @@ export function loadConfig(env: NodeJS.ProcessEnv): ServerConfig {
valkeyPrefix: env.VALKEY_PREFIX ?? 'ov:games:',
},
staticDir: env.STATIC_DIR ?? null,
-- mapPath:
-- env.MAP_PATH ??
-- (placeId === 'salvage-rush' ? 'data/salvage-rush-map.json' : 'data/map.json'),
++ mapPath,
++ secondaryMapPath,
trustProxy: parseProxyTrust(env.TRUST_PROXY),
/** openvibe.network auth endpoint; when set, editor tokens validate there. */
networkAuthUrl:
ddiff --git a/apps/server/src/main.ts b/apps/server/src/main.ts
iindex 08e33bd..4e874c2 100644
---- a/apps/server/src/main.ts
++++ b/apps/server/src/main.ts
@@@ -1356,13 +1356,14 @@ async function main(): Promise<void> {
log.child({ system: 'indexnow' }),
),
config.placeId,
-- // One map document per place: surf maps are separate files, the rest
-- // share the configured editor map.
++ // One map document per place: surf maps are separate files, each built-in place has its own document (an absent
++ // file is an empty map, not a 404), and anything else has no map.
(place) => {
const surf = place ? surfMapOfPlace(place) : null
if (surf) return surfMapPath(surf.id)
if (!place || place === config.placeId)
return primarySurf ? surfMapPath(primarySurf.id) : config.mapPath
++ if (place === secondaryPlace) return config.secondaryMapPath
return undefined
},
)
ddiff --git a/apps/server/src/net/httpServer.test.ts b/apps/server/src/net/httpServer.test.ts
iindex 7370d7c..b2afd4a 100644
---- a/apps/server/src/net/httpServer.test.ts
++++ b/apps/server/src/net/httpServer.test.ts
@@@ -87,6 +87,9 @@ beforeAll(async () => {
writeFileSync(join(dist, 'assets', 'tex', 'wood_planks.jpg'), 'jpeg')
writeFileSync(join(dist, 'assets', 'tex', 'wood_planks.ktx2'), 'ktx2')
const mapPath = join(dir, 'data', 'map.json')
++ // The process runs both built-in places. This one has no file yet: /map.json?game=salvage-rush must still be an
++ // empty map document, not the 404 the play client logged (main.ts's mapPathFor answers it from here).
++ const secondaryMapPath = join(dir, 'data', 'salvage-rush-map.json')
mkdirSync(join(dir, 'data', 'map-assets'), { recursive: true })
writeFileSync(join(dir, 'data', 'map-assets', 'tex-1700000000000.png'), 'png')
@@@ -108,6 +111,14 @@ beforeAll(async () => {
async () => [{ slot: 0, name: 'Ana', appearance: null }],
null,
{ handle: (req, res) => readiness.handle(req, res) },
++ undefined,
++ undefined,
++ 'scraplandia',
++ (place) => {
++ if (!place || place === 'scraplandia') return mapPath
++ if (place === 'salvage-rush') return secondaryMapPath
++ return undefined
++ },
)
bare = createHttpServer(null, metrics, log)
port = await listen(server)
@@@ -167,6 +178,17 @@ describe('real routes on the apex', () => {
expect(tex.headers['content-type']).toBe('image/png')
})
++ it('answers the other built-in place with an empty map, not a 404', async () => {
++ const map = await send('/map.json?game=salvage-rush')
++ expect(map.status).toBe(200)
++ expect(map.headers['content-type']).toBe('application/json')
++ expect(map.headers.etag).toBeTruthy()
++ const body = JSON.parse(map.body) as { v?: number }
++ expect(body.v).toBe(2)
++ // A place this process does not run still has no map.
++ expect((await send('/map.json?game=not-a-place')).status).toBe(404)
++ })
++
it('answers the SSO routes', async () => {
const me = await send('/auth/me')
expect(me.status).toBe(200)