OpenStrike: un FPS estilo Counter-Strike sobre la PSP de 2004 con JavaScript

Fuentes: Shipping OpenStrike: a Counter-Strike-Shaped FPS on a 2004 Handheld, xataka.com

Un grupo de desarrolladores independientes ha presentado OpenStrike, un videojuego de disparos en primera persona que reproduce la experiencia clásica de Counter-Strike sobre la PlayStation Portable original de 2004, pero programado íntegramente con tecnologías web modernas como JavaScript y TypeScript. El proyecto, de código abierto y disponible en el repositorio pocket-stack/open-strike, supone la primera aplicación construida sobre la familia de runtimes Pocket y pretende demostrar que la ergonomía del ecosistema web, sin toda su maquinaria pesada, es capaz de escalar más allá de la interfaz de usuario hasta alcanzar un juego tridimensional en tiempo real.

El hardware objetivo no puede ser más exigente. La PSP-1000 dispone de un único núcleo MIPS a 333 MHz sin ejecución especulativa, 32 MB de memoria RAM de los cuales solo 24 MB quedan para el programa, y una unidad gráfica fija sin soporte para shaders programables. La pantalla nativa de 480 por 272 píxeles se alimenta desde apenas 2 MB de memoria de vídeo. Además, no existe un sistema operativo al uso: no hay procesos, ni memoria virtual, ni enlazador dinámico. El juego completo, motor en Rust, intérprete de JavaScript, el bundle JS y el mapa, se compilan en un único ejecutable EBOOT.PBP que recibe la máquina en exclusiva. Si se cuelga, la máquina se cuelga con él.

La arquitectura interna replica el reparto típico de las aplicaciones nativas modernas. El núcleo en Rust, bautizado como openstrike-core y acompañado del renderizador Pocket3D, se encarga de todo lo que no puede fallar ni un solo frame: el movimiento del jugador, las colisiones, la inteligencia artificial de los bots, el disparo y el dibujado del mundo. La capa de JavaScript, por su parte, define qué hace único al juego: el archivo rules.ts contiene el flujo de rondas, la puntuación y las tablas de armas y bots, mientras que hud.tsx implementa el HUD completo, salud, munición, mira, marcador, como un árbol de componentes de Solid estilado con clases de Tailwind, ejecutándose sobre PocketJS. El motor de JavaScript embebido es QuickJS, el motor ES2023 completo de Fabrice Bellard, capaz de ejecutarse interpretado sin JIT y ocupando unos pocos cientos de kilobytes.

El ciclo de ejecución está cuidadosamente orquestado para respetar los 16,7 milisegundos de cada frame. Primero se lee el pad, después el simulador en Rust avanza la física y los bots, luego QuickJS recibe un único despacho con el estado y los eventos del frame, las reglas reaccionan y el HUD solo rerenderiza los bindings que cambiaron, y por último los comandos resultantes vuelven al núcleo en Rust para configurar la siguiente fase. JavaScript se consulta exactamente una vez por frame, lo que garantiza que la simulación nunca puede bloquearse.

Los mapas reutilizan el formato BSP de la era GoldSrc, el mismo de Half-Life y Counter-Strike original, que almacena los polígonos organizados en un árbol de partición espacial binaria. Su característica estrella es el PVS, el conjunto potencialmente visible: para cada hoja del árbol, un bitset precalculado indica qué otras hojas pueden verse desde ella. El renderizador ni siquiera considera la geometría que no es visible. Para evitar que la PSP pierda tiempo descodificando texturas o reorganizando datos al cargar, el proyecto incluye un cooker, pocket3d-cook, que se ejecuta en el ordenador del desarrollador y emite un archivo .p3d con los bytes exactos que la GPU desea leer. La iluminación se hornea en colores de vértices, las posiciones se cuantifican a enteros de 16 bits, y las texturas se convierten al formato CLUT8 con paletas de 256 colores, reduciendo su tamaño cuatro veces frente a RGBA.

OpenStrike se publica como un único archivo que se puede copiar tal cual a una Memory Stick, y mantiene 60 FPS estables en el hardware original. Se trata, en definitiva, de una demostración de que las herramientas que los desarrolladores web usan a diario pueden adaptarse a entornos extremadamente limitados cuando el compilador hace el trabajo pesado en tiempo de build y la capa de scripting se disciplina con un único cruce por frame.