# Arquitectura de NovelasVM Este documento describe la arquitectura técnica de NovelasVM, sus componentes y el flujo de publicación de una novela visual. ## Componentes principales ``` ┌──────────────────────────────────────────────────────────────────────┐ │ Cliente │ │ (navegador web) │ └───────────────────────────────┬──────────────────────────────────────┘ │ HTTP ┌───────────────────────────────▼──────────────────────────────────────┐ │ nginx (puerto 80) │ │ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐ │ │ │ Portal web │ │ Juegos web │ │ /games/umineko/ │ │ │ │ /var/www/... │ │ /var/www/games/ │ │ → redirect :8081 │ │ │ └─────────────────┘ └──────────────────┘ └─────────────────────┘ │ └───────────────────────────────┬──────────────────────────────────────┘ │ ┌───────────────────────┼───────────────────────┐ │ │ │ ┌───────▼───────┐ ┌──────────▼─────────┐ ┌────────▼────────┐ │ build-novela │ │ detect-engine │ │ Umineko Web │ │ .sh │ │ .sh │ │ Docker :8081 │ └───────┬───────┘ └────────────────────┘ └─────────────────┘ │ ┌───────▼───────┐ │ Ren'Py SDK │ │ /opt/novelas │ │ /tools/renpy│ └───────────────┘ ``` ## Flujo de publicación 1. El administrador coloca el proyecto en `/opt/novelas/projects//`. 2. Opcionalmente añade `meta.json` con metadatos. 3. Ejecuta `/opt/novelas/bin/build-novela.sh [motor]`. 4. `detect-engine.sh` identifica el motor si no se especifica. 5. Según el motor: - **Ren'Py**: `renpy.sh launcher web_build` genera archivos web. - **Unity/Web**: se copian tal cual. - **ONScripter**: se copia motor OnscripterYuri + índice. 6. Se genera `game.json` con metadatos. 7. Se copia a `/var/www/novelas/games//`. 8. Se genera snippet nginx si requiere COOP/COEP. 9. Se actualiza `/var/www/novelas/games.json`. 10. Se recarga nginx. ## Archivos clave | Ruta | Propósito | |------|-----------| | `/opt/novelas/bin/build-novela.sh` | Pipeline de build y publicación | | `/opt/novelas/bin/detect-engine.sh` | Detección de motor | | `/opt/novelas/bin/umineko-web.sh` | Gestión del contenedor Umineko | | `/opt/novelas/config/nginx-snippet.template` | Plantilla de snippet COOP/COEP | | `/var/www/novelas/index.html` | Portal principal | | `/var/www/novelas/assets/app.js` | Lógica del portal | | `/var/www/novelas/assets/styles.css` | Estilos y temas | | `/var/www/novelas/games.json` | Catálogo global | | `/var/www/novelas/games//game.json` | Metadatos por juego | | `/etc/nginx/snippets/novelas-games/*.conf` | Headers específicos por juego | ## Catálogo global `games.json` ```json { "games": [ { "slug": "demo", "title": "The Question", "subtitle": "Demo oficial de Ren'Py", "engine": "renpy", "description": "...", "cover": "/games/demo/web-presplash.jpg", "version": "7.0", "author": "Ren'Py Team", "createdAt": "2026-06-14T10:35:09Z", "entryPoint": "/games/demo/index.html", "coopCoep": true } ], "updatedAt": "2026-06-14T22:07:10Z", "version": "1.0" } ``` ## Seguridad de headers - `COOP`/`COEP` se aplican **solo** a juegos Ren'Py porque requieren `SharedArrayBuffer`. - Unity WebGL y juegos web genéricos no los reciben para evitar bloqueo de recursos cross-origin. ## Escalabilidad - Cada juego es independiente en `/var/www/novelas/games//`. - El portal carga el catálogo asíncronamente, por lo que el número de juegos solo afecta el tamaño de `games.json`. - Para Umineko, el contenedor Docker es independiente y puede escalarse o moverse a otro host.