Files
SEQUA-Engine/docs/specs/protocole-core-rendu-0.1.md
Ange-Marie MAURIN - A2M Systèmes 98c778aa28 feat(input): entrees du joueur captees par l'adaptateur (ADR 0011)
L'adaptateur traduit clavier et manette en actions SEQUA (PlayerInput) ;
le Core ne recoit et n'enregistre que ces actions, le rejeu reste exact.

- SEQUA 3D : PlayerInput, poll_input(), capacite player_input
  (interface des adaptateurs en version 2)
- protocole Core - rendu 0.2 : render/input, envoye quand les actions
  changent ; entree remise au neutre si le rendu disparait
- Godot : InputMap du projet (ZQSD, fleches, espace, manette) ou script
  SequaInput.sequa_player_input() pour une correspondance avancee ;
  entree neutre sans focus ; exemple examples/sequa_input.gd
- adaptateur debug : entrees scriptees par numero d'image
- Studio : ligne "Entrees du joueur" du panneau Rendu
2026-11-07 20:48:26 +01:00

9.2 KiB
Raw Permalink Blame History

Spécification : protocole Core - rendu 0.2 et sequa-render

  • Statut : brouillon (étape 4). Décisions : ADR 0009 ; entrées du joueur (0.2) : ADR 0011.
  • Historique : 0.1 (scène, mesures) ; 0.2 : message render/input, capacité playerInput, interface des adaptateurs en version 2. Le fichier garde son nom d'origine.
  • Le format reste du JSON provisoire (ADR 0005) en attendant l'ADR du schéma. Enveloppe, entiers 64 bits en hexadécimal et règles d'erreur : comme le protocole Studio - Core 0.2 §2 et §3.
  • Toute ambiguïté se règle en modifiant CE document.

1. Acteurs

Acteur Exécutable / dossier Rôle
Core sequa-core (core/) Serveur WebSocket ; lance et supervise le rendu ; projette le monde en deltas SEQUA 3D
Rendu sequa-render (render/) Client WebSocket du Core ; charge une bibliothèque d'adaptateur ; applique les deltas ; rend les images
Adaptateur sequa_godot_adapter, sequa_debug_adapter (bibliothèques partagées) Implémentent I3DEngineAdapter (3d/)
Studio studio/ Demande au Core de démarrer ou d'arrêter le rendu (protocole 0.2, §6)

2. Lancement de sequa-render

Le Core lance sequa-render avec :

Option Effet
--core <url> URL WebSocket du Core, jeton inclus (ws://127.0.0.1:9100/?token=…)
--adapter <chemin> Bibliothèque d'adaptateur à charger
--adapter-options <json> Réglages propres à l'adaptateur, transmis tels quels (ex. sortie vidéo de l'adaptateur de débogage)
--width <n>, --height <n> Taille de rendu (défaut 1280 × 720)
--headless Rendu sans fenêtre (refusé par un adaptateur qui ne le sait pas)
--fps <n> Cadence imposée par sequa-render (0, défaut : celle du moteur, synchronisation verticale comprise). Sert à l'adaptateur de débogage, qui n'a pas de synchronisation verticale
--lease-grace-ms <n> Délai avant arrêt quand la connexion au Core disparaît (défaut 3000)

Codes de sortie : 0 arrêt normal, 1 erreur de démarrage (connexion, adaptateur, moteur), 2 arguments invalides, 3 bail expiré, 4 fenêtre fermée par l'utilisateur.

Le Core lie le processus de rendu au sien (job object sous Windows, PR_SET_PDEATHSIG sous Linux) : il ne lui survit jamais.

3. Bibliothèque d'adaptateur

Chaque adaptateur exporte (liaison C) :

// Renvoie nullptr si interface_version diffère de la version compilée dans l'adaptateur
// ou si options_json est invalide ; error (facultatif) reçoit alors un message.
sequa::three_d::I3DEngineAdapter* sequa_create_engine_adapter(std::uint32_t interface_version,
                                                               const char* options_json,
                                                               char* error, std::size_t error_size);
void sequa_destroy_engine_adapter(sequa::three_d::I3DEngineAdapter* adapter);

interface_version = sequa::three_d::adapter_interface_version (entier, 1 au départ, 2 depuis poll_input), incrémenté à chaque changement de I3DEngineAdapter ou des types du contrat. Le rendu détruit l'adaptateur, décharge sa bibliothèque, puis seulement ensuite tout ce que l'adaptateur aurait chargé lui-même (le moteur, déchargé par l'adaptateur à sa destruction).

4. Poignée de main

  1. Connexion avec le jeton (protocole 0.2 §3).
  2. Rendu → Core : hello { protocolVersion: "0.2.0", client: "sequa-render" }. Le Core reconnaît le canal de rendu à client et applique la version de CE protocole (compatibilité : même majeure et, en 0.x, même mineure).
  3. Core → rendu : welcome { protocolVersion, coreVersion, instanceId, renderId } (renderId : identifiant de ce lancement, 32 hex).
  4. Rendu → Core : render/ready { capabilities: { engineName, engineVersion, buffers: [noms §5.6], windowed, headless, playerInput }, width, height } (playerInput : le moteur capte les entrées du joueur, facultatif, faux par défaut) après start() réussi de l'adaptateur. En cas d'échec : render/failed { code, message } puis fermeture et code de sortie 1.
  5. Core → rendu : scene/delta avec full: true (scène complète), puis les deltas suivants.

Un seul canal de rendu à la fois : une deuxième connexion sequa-render reçoit error { code: "render-exists" } et est fermée.

5. Messages

Core → rendu

Type Payload
scene/delta { worldVersion: hex, tick: hex, full: bool, sentAtUs: hex, commands: [commande…] }
render/stop {} : le rendu arrête l'adaptateur et sort avec le code 0

full: true : le rendu ajoute en tête du delta un destroy pour chaque objet qu'il connaît encore (il garde son propre SceneState), puis l'applique d'un bloc. worldVersion ne recule jamais, même quand la session du Core redémarre : c'est un compteur du Core, distinct du tick. sentAtUs : horloge monotone du Core en microsecondes (steady_clock, commune aux processus d'un même poste sous Windows et Linux), pour la mesure du délai.

Commandes (champ op) ; identifiants d'objets en hexadécimal, nombres en JSON :

op Champs
create id, transform, visual
destroy id
transform id, transform
visual id, visual
camera camera
  • transform : { position: [x, y, z], rotation: [x, y, z, w], scale: [x, y, z] }
  • visual : { primitive: "box" | "sphere" | "capsule" | "plane", size: [x, y, z], color: [r, g, b, a] (linéaire), semanticClass: int }
  • camera : { transform, verticalFovDeg, near, far }

Rendu → Core

Type Payload
render/frames { frames: int, lastWorldVersion: hex, cpuMsMedian, cpuMsP95, latencyUsMedian, latencyUsP95, applyUsMedian, applyUsP95 } : envoyé toutes les secondes. latencyUs = réception du delta − sentAtUs (transport seul : c'est le critère de l'ADR 0009) ; applyUs = application du delta − sentAtUs (attente de la prochaine image comprise)
render/failed { code, message } : l'adaptateur a échoué ; fermeture, code de sortie 1
render/closed {} : l'utilisateur a fermé la fenêtre ; fermeture, code de sortie 4
render/input { buttons: int [0, 2^32-1], moveX: int, moveY: int ([-32767, 32767]), capturedAtUs: hex } : entrées du joueur déjà traduites en actions par l'adaptateur (§5.1)

Une erreur d'application d'un delta (version qui recule, id inconnu) est une erreur de protocole : render/failed { code: "invalid-delta" }.

5.1 Entrées du joueur (0.2, ADR 0011)

  • Après chaque image, si l'adaptateur déclare playerInput, le rendu appelle poll_input() et envoie render/input seulement si les actions ont changé depuis le dernier envoi (départ : entrée neutre, rien n'est envoyé tant que le joueur ne fait rien).
  • La traduction des appareils (clavier, manette…) en actions appartient à l'adaptateur. Hors focus, l'adaptateur renvoie l'entrée neutre.
  • Le Core applique render/input comme session/input (protocole Studio - Core §4) : prochain tick exécuté, entrée maintenue, la dernière reçue gagne, qu'elle vienne de Studio ou du rendu. Elle est enregistrée : le rejeu est exact.
  • Payload invalide : error { code: "invalid-payload" }, entrée inchangée, connexion conservée. render/input reçu sur une connexion Studio : error unknown-type.
  • Déconnexion du rendu alors que sa dernière entrée n'était pas neutre : le Core remet l'entrée au neutre.
  • capturedAtUs : horloge monotone du rendu à la lecture (comme sentAtUs), pour la mesure future de la latence entre la touche et l'affichage.

6. Ajouts au protocole Studio - Core (0.3)

Requête Payload envoyé Réponse
render/start { adapter: "godot" | "debug", width?, height?, headless?, adapterOptions? } état du rendu
render/stop {} état du rendu
render/status {} état du rendu

État du rendu : { state: "stopped" | "starting" | "running" | "restarting", adapter?, pid?, restarts, capabilities?, frames?, latencyUsP95? }. Le Core résout adapter en chemin de bibliothèque à côté de sequa-render.

7. Supervision

  • Lancement : render/start ou --render <adaptateur>. Le Core attend render/ready (20 s max, le démarrage de Godot prend 1,5 à 2 s).
  • Plantage ou déconnexion inattendue : relance après 1 s, au plus 3 fois par minute ; au-delà, état stopped et erreur visible dans render/status.
  • render/closed (fenêtre fermée par l'auteur) : pas de relance, état stopped.
  • Arrêt du Core : render/stop, attente de la fin (3 s), sinon terminaison forcée.

8. Rythme

  • Le Core envoie un scene/delta après chaque tick qui change la scène. Un tick sans changement n'envoie rien.
  • Le rendu reçoit sur le fil du client, empile, et applique tous les deltas reçus avant chaque image, dans l'ordre, puis appelle render_frame(). Pas d'interpolation en 0.1 (ADR 0009).
  • Si le rendu prend du retard, les deltas s'accumulent mais aucun n'est sauté : la scène reste exacte.

9. Hors 0.2

Tampons de rendu (étape 5), mesures de VRAM, plusieurs rendus simultanés, interpolation, plusieurs joueurs, souris et visée.