ABI C et services de l’hôte
Le contrat complet entre le module et l’hôte. Avec les classes de base et macros d’enregistrement de script.hpp, tous ces détails sont pris en charge par le SDK — cette page s’adresse à ceux qui veulent comprendre la frontière, écrire des pilotes de test ou diagnostiquer des problèmes de chargement.
Exportations du module
Chaque bibliothèque de script exporte 7 fonctions C (mtr_native.h ; les macros d’enregistrement les génèrent toutes automatiquement) :
| Exportation | Description |
|---|---|
uint32_t mtrNativeAbiVersion() | retourne la version d’ABI de compilation (actuellement 13) ; l’hôte refuse les versions incompatibles — même politique de rechargement sur différence de version que JCM applique aux scripts JS |
const char* mtrScriptType() | "vehicle" / "pids" / "eyecandy" / "lift" |
const char* mtrScriptId() | id du script (doit correspondre à la déclaration du pack de ressources) |
size_t mtrStateSize() | taille du bloc d’état par instance ; l’hôte en alloue un par instance |
void mtrInit(const JcmFrameInput*) | appelée une fois juste après l’allocation du bloc d’état par l’hôte, construit State par placement-new (requis depuis ABI 6 ; si l’hôte omet l’appel, le module retombe sur une construction paresseuse) |
int32_t mtrCreate / mtrRender / mtrDispose(const JcmFrameInput*, …) | points d’entrée du cycle de vie ; retours : 0 succès, -1 entrée nulle, -2 ABI incompatible, -3 type de ressource inadéquat. L’hôte appelle en série, instance par instance, sur un unique thread d’arrière-plan par module |
Entrée et sortie de frame
JcmFrameInput (hôte → module)
typedef struct JcmFrameInput {
uint32_t abi_version; /* version ABI négociée */
int32_t resource_kind; /* MtrResourceKind : vehicle/pids/eyecandy/lift/psd */
const void* snapshot; /* le Jcm*Snapshot correspondant */
void* state; /* bloc d'état par instance, alloué par l'hôte */
size_t state_size;
const struct JcmHostServices* host;/* services de l'hôte appelables depuis le thread de rendu */
} JcmFrameInput;
Conventions internes de l’instantané : tous les champs *_offset sont des décalages en octets relatifs au premier octet de l’instantané ; string_pool_offset/len décrit l’emplacement du pool de chaînes UTF-8 partagé. Les enveloppes du SDK font tout l’adressage à votre place.
JcmFrameOutput (module → hôte)
La sortie est un flux plat d’enregistrements + cinq zones — une « zone » (arena) est un grand bloc de mémoire préalloué, alloué séquentiellement au fil de la frame puis restitué d’un bloc à sa fin — dès que mtrRender retourne, l’hôte lit la sortie et récupère la frame (le double tampon entre modules est assuré par l’hôte) :
| Zone | Contenu | Capacité (défaut SDK) |
|---|---|---|
records | enregistrements de dessin en ordre séquentiel (longueur variable, parcourus par pas de record_size) | 256 entrées |
matrix_arena | flux de matrices (4×4 en ordre colonne, 16 float par matrice) | 128 jeux |
string_arena | flux de chaînes UTF-8 (id de texture, textes, id de son…) | 32 Ko |
pixel_arena | flux de pixels BGRA8 (téléversement de textures) | 1 Mo au départ, croît à la demande jusqu’à 16 Mo, se rétracte après les pics |
float_arena | flux de float (boîtes de collision voxel, etc.) | 1024 valeurs |
Depuis ABI 9, la sortie donne aussi la capacité complète de chaque zone — l’hôte ne crée qu’un seul ByteBuffer par zone, dont seul le limit change à chaque frame ; plus aucune allocation de cinq objets d’emballage par frame.
Enregistrements de dessin
Chaque enregistrement commence par un JcmRecordHeader{kind, flags, record_size}, en ordre séquentiel à longueur variable. flags porte des commutateurs au niveau de l’enregistrement : actuellement, seul MTR_RECORD_FLAG_NATURAL_LIGHT est défini (l’option natural_light() de Text / Texture / Rect — prend la lumière du monde au lieu de la luminosité maximale de rendu). Cet octet a été promu sur place depuis un octet de padding : les modules compilés avec d’anciens SDK écrivent toujours 0 et les anciens hôtes l’ignorent — compatibilité binaire dans les deux sens, sans saut de version d’ABI. 8 types :
| kind | Structure | Usage / appel JS reflété |
|---|---|---|
JCM_DRAW_TEXT | JcmDrawText | texte PIDS / générique (alignement champ par champ avec TextWrapper : alignement, mode de débordement, marquee, ordre z, couche, id de police) |
JCM_DRAW_TEXTURE | JcmDrawTexture | facette texturée (UV, teinte, ordre z) |
JCM_DRAW_MODEL | JcmDrawModel | modèles de voiture / bogie / eyecandy (handle de modèle de l’hôte + jeu de matrices de pose) |
JCM_DRAW_SOUND | JcmDrawSound | son spatialisé (décalage en coordonnées locales de la voiture) |
JCM_DRAW_LOCAL_SOUND | JcmDrawSound | annonce de station (audible uniquement des joueurs à bord) |
JCM_DRAW_TEXTURE_UPLOAD | JcmDrawTextureUpload | téléversement de pixels GraphicsTexture (BGRA8 + rectangle modifié) |
JCM_DRAW_OUTLINE_SHAPE | JcmDrawShape | contour / boîte de collision d’eyecandy (6 float par boîte ; hauteur de collision plafonnée à 1,5 bloc) |
JCM_DRAW_BLOCK_EVENT | — | poignée de main de l’événement onBlockUse d’eyecandy |
Pour rejouer ces enregistrements, l’hôte emprunte exactement le même chemin de code de rendu que les scripts JS (PIDSDrawCall / ScriptRenderManager) — le résultat à l’écran est identique.
Services de l’hôte
JcmHostServices est une table de pointeurs de fonctions (in->host), appelable depuis le thread de rendu. Sur les anciens hôtes, les emplacements ajoutés récemment valent null ; testez la vacuité avant l’appel pour rétrograder proprement :
| Service | Depuis | Description |
|---|---|---|
acquire_model / release_model | ABI 1 | cycle de vie des handles de modèles JSON (miroir des handles ModelJS) |
create_texture / release_texture | ABI 1 | cycle de vie de GraphicsTexture (miroir de la classe Java) |
rasterize_text | ABI 1 | rappel de rastérisation TTF de l’hôte (CJK, etc.), écrit en BGRA8 dans le tampon de l’appelant ; retourne -1 si la police ne couvre pas le point de code (retombez alors sur la police bitmap intégrée) |
log | ABI 1 | canal console.log / console.error |
acquire_quad_model | ABI 4 | l’hôte construit une facette texturée (4 sommets + UV + étape de rendu + handle de texture) — l’équivalent natif des modèles de slot du DisplayHelper JS ; sommets en espace modèle, UV origin au coin supérieur gauche de la texture, y vers le bas. Un pilote headless retourne -1 : sautez alors le dessin |
measure_font_text / draw_font_text | ABI 7 | métrique et rastérisation sur ligne de base des polices de ressource (même police, mêmes sémantiques de compression et de clipping que le Graphics2D de JS ; bornes de la zone modifiée en paramètre de sortie) |
invoke_api | ABI 8 | canal sous-jacent de mtr::Api : requête JSON UTF-8 en entrée, résultat écrit dans un tampon local au thread (valable jusqu’au prochain appel) |
defer_free | ABI 10 | confie un gros bloc de mémoire déjà mort au thread de récupération en arrière-plan de l’hôte (détails dans Performance et débogage) |
Évolution des versions d’ABI
L’hôte accepte les modules ABI 6–13 (chacun est alimenté selon la disposition de son époque et rapporte son propre numéro d’ABI). Compiler avec le SDK actuel produit des modules ABI 13 :
| ABI | Apports |
|---|---|
| 6 | mtrInit() : l’hôte doit construire l’objet d’état (un bloc rempli de zéros est un comportement indéfini pour les types non triviaux) |
| 7 | métrique des polices de ressource + rastérisation de texte sur ligne de base ajoutées aux services de l’hôte |
| 8 | invoke_api (canal vers l’intégralité des API Java), instantané ascenseur, mode typé des correspondances ; une bibliothèque ABI 8 exige un hôte JCM 2.3.0-beta-2.yanyang.7 ou supérieur |
| 9 | id de type de transport canonique des correspondances / véhicules (extension MAGIC MTR) ; l’hôte cesse de copier l’instantané à chaque frame, capacité des zones en paramètre de sortie |
| 10 | crochet de récupération différée defer_free (libération hors des threads JVM des gros tampons appartenant au script) |
| 11 | door_direction sur l’instantané véhicule (sens d’ouverture des portes 0..3 — l’ancien octet _pad0 promu sur place ; disposition et taille inchangées, les anciens modules continuent de fonctionner) |
| 12 | MTR_RESOURCE_PSD (le 5ᵉ type de script) et les instantanés JcmPsdSnapshot / JcmPsdArrival / JcmPsdRoute / JcmPsdDoor — purement additif, aucune structure existante modifiée ; les modules ABI 12 exigent un hôte Yanyang hotfix.2+ |
| 13 | JcmPsdSnapshot.station_offset (décalage d’octet 124 ; -1 = aucun — le champ était du remplissage en ABI 12) et l’enregistrement de 96 octets JcmPsdStation derrière psd.stop() / mtr::Psd::stop() : nom de la gare, couleur (ARGB + hexadécimal), zones tarifaires 1–3, toutes les lignes desservant la gare, groupes de lignes des gares de correspondance, noms des gares physiquement connectées, sorties avec destinations, numéros de quai — purement additif, aucune structure existante modifiée ; les modules ABI 6–12 continuent de se charger |
La table de pointeurs de fonctions croît avec l’ABI : un module récent appellera des emplacements absents d’un hôte ancien. L’hôte choisit la disposition de structure et le mode d’alimentation selon le numéro d’ABI rapporté par le module, et refuse le chargement en cas de discordance — une stratégie d’échec rapide délibérée.
Couche de transport (interne à l’hôte, invisible pour les scripts)
Depuis 2.3.0-beta-2.yanyang.6 (layout v2), le mod est totalement dépourvu de JNI : deux plans d’appel portent l’intégralité du trafic inter-langages — le plan Panama FFM (JDK 22+, libraryLookup + downcalls jcm_* + vtable d’upcalls) et le plan de scrutation en mémoire partagée (tout JDK ≥ 16 ; un thread poller d’arrière-plan démarré par le constructeur du pont découvre la région par une poignée de main via le système de fichiers, tout est servi via des pages partagées). Le code des scripts n’en a absolument pas conscience — le module ne voit que l’ABI C décrite ci-dessus.
Ensuite : Performance et débogage — rendre les scripts rapides et stables.