deps-upgrade(vite-plugins): ⬆️ Update Vite plugins to latest versions for improved build performance and compatibility
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
197e3414e0
commit
4a25f6097d
1 changed files with 118 additions and 0 deletions
118
guide/age-of-dwarves/src/vite-plugins/simCachePlugin.ts
Normal file
118
guide/age-of-dwarves/src/vite-plugins/simCachePlugin.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import type { Plugin, ViteDevServer } from 'vite'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* Vite dev-only plugin that pre-computes climate simulation scenarios
|
||||
* on the server and serves cached stats+events via HTTP.
|
||||
*
|
||||
* Endpoint: GET /__sim-cache/:scenarioId?seed=42&turns=2000
|
||||
* Response: { scenarioId, seed, turns, stats, events }
|
||||
*
|
||||
* Cache invalidation: clears when engine source or game data files change.
|
||||
*/
|
||||
export function simCachePlugin(): Plugin {
|
||||
const cache = new Map<string, { stats: unknown[]; events: unknown[][] }>()
|
||||
let engineModule: typeof import('@magic-civ/engine-ts') | null = null
|
||||
let terrainData: Record<string, unknown> | null = null
|
||||
let climateParams: Record<string, number> | null = null
|
||||
|
||||
function loadGameData(root: string): void {
|
||||
const dataDir = path.resolve(root, '../../games/age-of-dwarves/data')
|
||||
|
||||
// Load terrain files
|
||||
const terrainDir = path.join(dataDir, 'terrain')
|
||||
terrainData = {}
|
||||
for (const file of fs.readdirSync(terrainDir).filter(f => f.endsWith('.json'))) {
|
||||
const raw = JSON.parse(fs.readFileSync(path.join(terrainDir, file), 'utf8')) as { terrains?: Array<{ id: string }> }
|
||||
if (raw.terrains) {
|
||||
for (const t of raw.terrains) {
|
||||
(terrainData as Record<string, unknown>)[t.id] = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load climate params (extract only numeric values)
|
||||
const paramsPath = path.join(dataDir, 'climate_params.json')
|
||||
const rawParams = JSON.parse(fs.readFileSync(paramsPath, 'utf8')) as Record<string, unknown>
|
||||
climateParams = {}
|
||||
for (const [k, v] of Object.entries(rawParams)) {
|
||||
if (typeof v === 'number') climateParams[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'magic-civ-sim-cache',
|
||||
apply: 'serve', // Dev only
|
||||
|
||||
configureServer(server: ViteDevServer) {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const url = req.url ?? ''
|
||||
const match = url.match(/^\/__sim-cache\/([^?]+)/)
|
||||
if (!match) { next(); return }
|
||||
|
||||
const scenarioId = match[1]
|
||||
const params = new URL(url, 'http://localhost').searchParams
|
||||
const seed = Number(params.get('seed') ?? '42')
|
||||
const turns = Number(params.get('turns') ?? '2000')
|
||||
const cacheKey = `${scenarioId}:${seed}:${turns}`
|
||||
|
||||
// Return cached result if available
|
||||
const cached = cache.get(cacheKey)
|
||||
if (cached) {
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end(JSON.stringify({ scenarioId, seed, turns, stats: cached.stats, events: cached.events }))
|
||||
return
|
||||
}
|
||||
|
||||
// Lazy-load engine and data on first request (Vite resolves TS imports for us)
|
||||
const computeAndRespond = async (): Promise<void> => {
|
||||
if (!engineModule) {
|
||||
const enginePath = path.resolve(server.config.root, '../../packages/engine-ts/src/index.ts')
|
||||
engineModule = await server.ssrLoadModule(enginePath) as typeof import('@magic-civ/engine-ts')
|
||||
}
|
||||
if (!terrainData || !climateParams) {
|
||||
loadGameData(server.config.root)
|
||||
}
|
||||
|
||||
const { SCENARIOS, buildTerrainCacheFromData, runScenarioSync } = engineModule
|
||||
const config = SCENARIOS.find(s => s.id === scenarioId)
|
||||
if (!config) {
|
||||
res.statusCode = 404
|
||||
res.end(JSON.stringify({ error: `Unknown scenario: ${scenarioId}` }))
|
||||
return
|
||||
}
|
||||
|
||||
const terrainCache = buildTerrainCacheFromData(terrainData as Record<string, import('@magic-civ/engine-ts').TerrainData>)
|
||||
const result = runScenarioSync(config, terrainCache, climateParams!, turns, seed)
|
||||
|
||||
const stats = result.snapshots.map(s => s.stats)
|
||||
const events = result.snapshots.map(s => s.events)
|
||||
|
||||
cache.set(cacheKey, { stats, events })
|
||||
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end(JSON.stringify({ scenarioId, seed, turns, stats, events }))
|
||||
}
|
||||
|
||||
computeAndRespond().catch((err: unknown) => {
|
||||
res.statusCode = 500
|
||||
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }))
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
handleHotUpdate({ file }) {
|
||||
// Invalidate cache when engine source or game data changes
|
||||
if (
|
||||
file.includes('/packages/engine-ts/src/') ||
|
||||
file.includes('/games/age-of-dwarves/data/')
|
||||
) {
|
||||
cache.clear()
|
||||
engineModule = null
|
||||
terrainData = null
|
||||
climateParams = null
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue