feat: omni-morph sprite swap, bullet time showboats, music tuning, server hardening

- Creator omni-morph now generates actual sprite sheets for morphed archetypes
- 3 new Creator showboats: bullet time attack, ₿ throne summon, disco dance
- Music: subtle tempo shift (+10 BPM max), longer phrases (8/16/24 bars),
  smoother crossfades, less chaotic hi-hat at high intensity
- Server: security headers, body size limit, production error masking,
  CORS origin warning, graceful shutdown with drain
- Payments: atomic consume (eliminates SELECT/UPDATE race), release reverts DB
- Fight loop: round events for live TUI, retro displayPrompt
- Frontend: pass pubkey in payment/queue requests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 16:23:47 +00:00
co-authored by Claude Opus 4.6
parent 0acfa4417f
commit e12fb94ae7
13 changed files with 565 additions and 514 deletions
+20
View File
@@ -3,6 +3,8 @@ import { app } from './app.js'
import { runMigrations } from './db/startup.js'
import { seedMockBots, seedClassicBots } from './engine/mock.js'
import { startBackgroundFights } from './engine/background.js'
import { getActiveFighterCount } from './engine/orchestrator.js'
import { clearAllPending } from './engine/human-responses.js'
// Production env validation — warn but don't crash (wallet features degrade gracefully)
if (process.env.NODE_ENV === 'production') {
@@ -11,6 +13,9 @@ if (process.env.NODE_ENV === 'production') {
if (missing.length > 0) {
console.warn(`[botfights] WARNING: Missing env vars: ${missing.join(', ')} — wallet/payment features disabled`)
}
if (!process.env.CORS_ORIGIN || process.env.CORS_ORIGIN === '*') {
console.warn('[botfights] WARNING: CORS_ORIGIN not set — allowing all origins')
}
}
// Run migrations and seed mock bots before starting the server
@@ -26,3 +31,18 @@ serve({ fetch: app.fetch, port }, () => {
// Start background fight loop so the site always has fresh activity
startBackgroundFights()
})
// Graceful shutdown — drain in-flight fights before exiting
const shutdown = async (signal: string) => {
console.log(`[botfights] ${signal} received, shutting down...`)
const active = getActiveFighterCount()
if (active > 0) {
console.log(`[botfights] ${active} fighters still active, waiting 10s...`)
}
await new Promise(r => setTimeout(r, active > 0 ? 10_000 : 500))
clearAllPending()
console.log('[botfights] shutdown complete')
process.exit(0)
}
process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))