Démarrage rapide
De zéro à l’exécution en jeu : récupérer le SDK → écrire un premier module → compiler → l’intégrer à un pack de ressources.
Prérequis
| Outil | Version | Remarques |
|---|---|---|
| Compilateur C++ | C++17 | MSVC (Windows) / GCC / Clang, au choix |
| CMake | ≥ 3.16 | recommandé, mais non obligatoire — le SDK est entièrement constitué d’en-têtes |
| JDK | facultatif | uniquement nécessaire pour exécuter les tests d’intégration JVM livrés avec le SDK |
Le SDK est fourni intégralement sous forme d’en-têtes (native/include/mtr/), sans la moindre dépendance à une bibliothèque précompilée ; ajoutez simplement include/ au chemin d’inclusion de votre système de construction habituel.
Récupérer le SDK
Le SDK est publié à chaque Release sous la forme jcm-native-sdk-<version>.zip (voir les pièces jointes des GitHub Releases) ; son contenu correspond aux sources du répertoire native/ du dépôt :
jcm-native-sdk-2.3.0-1.0.2/
├── include/mtr/ # tous les en-têtes du SDK (script.hpp est le point d'entrée principal)
│ ├── mtr_native.h # ABI C : fonctions exportées, instantanés, enregistrements de dessin, services de l'hôte
│ ├── script.hpp # classes de base de script + macros d'enregistrement (un seul #include suffit)
│ ├── vehicle.hpp pids.hpp lift.hpp eyecandy.hpp
│ ├── gfx.hpp gfx2d.hpp font5x7.hpp text.hpp matrices.hpp
│ ├── api.hpp # mtr::Api (accès complet aux API Java)
│ └── crash_guard.hpp ...
├── examples/ # trois exemples de modules minimaux
├── bench/ # tests et outils
└── CMakeLists.txt
Créer le projet de module
Le plus simple : décompressez le SDK dans votre projet de pack de ressources et ajoutez vos propres cibles à la fin de son CMakeLists.txt — le SDK a déjà encapsulé dans la fonction mtr_add_script() tous les détails de liaison propres à chaque plateforme (Windows .dll sans préfixe, .dylib et -undefined dynamic_lookup sur macOS, alignement de page de 16 Ko sur Android) :
# À ajouter à la fin du native/CMakeLists.txt du SDK
mtr_add_script(my_pids src/my_pids.cpp)
Pour un projet autonome, voici le modèle complet équivalent à cette fonction :
cmake_minimum_required(VERSION 3.16)
project(my_native_scripts LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# chemin des en-têtes du SDK (pointez vers le répertoire include du SDK décompressé)
add_library(mtr_script_sdk INTERFACE)
target_include_directories(mtr_script_sdk INTERFACE path/to/sdk/include)
function(mtr_add_script name) # fonction de cible de module, identique à celle du SDK
add_library(${name} MODULE ${ARGN})
target_link_libraries(${name} PRIVATE mtr_script_sdk)
set_target_properties(${name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
if(WIN32)
set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX ".dll")
elseif(APPLE)
set_target_properties(${name} PROPERTIES PREFIX "lib" SUFFIX ".dylib")
target_link_options(${name} PRIVATE "-Wl,-undefined,dynamic_lookup")
elseif(ANDROID)
target_link_options(${name} PRIVATE "-Wl,-z,max-page-size=16384")
endif()
endfunction()
mtr_add_script(my_pids src/my_pids.cpp)
Un script est une bibliothèque MODULE (aucun binaire du SDK n’est lié) ; par défaut, les symboles sont masqués et seules les 7 fonctions mtr* exigées par l’ABI sont exportées (c’est le rôle des macros d’enregistrement de script.hpp). C++17 est le standard minimal.
Premier module
Un module PIDS minimal d’affichage des arrivées — complet, compilable et directement utilisable en jeu :
#include <mtr/script.hpp>
struct ArrivalState {}; // état persistant par instance (vide dans cet exemple)
struct ArrivalDisplay : mtr::PidsScript<ArrivalState> {
static constexpr auto ID = "example:arrivals"; // doit correspondre à la déclaration du pack de ressources
void render(mtr::PidsContext& ctx, ArrivalState&,
const mtr::Pids& pids) override {
for (int row = 0; row < pids.rows(); row++) {
if (pids.is_row_hidden(row)) continue;
std::string text = pids.get_custom_message(row).str();
if (text.empty() && row < pids.arrivals().size())
text = pids.arrivals().at(row).destination().str();
if (!text.empty())
mtr::Text::create()
.text(text.c_str())
.pos(2, row * 10.0 + 2)
.size(pids.width() - 4, 9)
.draw(ctx);
}
}
};
MTR_REGISTER_PIDS_SCRIPT(ArrivalDisplay)
Points essentiels :
- La classe de base détermine le type de script :
PidsScript/VehicleScript/LiftScript/EyecandyScript; le paramètre modèle est le type d’état par instance. - Le cycle de vie est identique à JS : surchargez selon vos besoins les trois fonctions virtuelles
create/render/dispose(toutes facultatives). - La macro d’enregistrement développe l’intégralité des exportations ABI ; une unité de traduction n’enregistre qu’un seul script.
D’autres exemples se trouvent dans examples/ du SDK : véhicule / ascenseur / eyecandy, chacun avec son exemple minimal.
Compilation
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/build --config Release --parallel 4
ctest --test-dir native/build -C Release --output-on-failure # facultatif : exécuter les tests fournis avec le SDK
| Plateforme cible | Produit | Remarques |
|---|---|---|
| Windows x64 | my_pids.dll | MSVC ; /utf-8 est préréglé par le projet du SDK |
| Linux x64 / ARM64 | libmy_pids.so | GCC / Clang |
| macOS x64 / arm64 | libmy_pids.dylib | architecture choisie via -DCMAKE_OSX_ARCHITECTURES=arm64 (ou x86_64), -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 |
| Android arm64 | libmy_pids.so | compilation croisée avec la chaîne d’outils NDK, API 21+ ; l’alignement de page de 16 Ko est déjà pris en charge par le modèle de projet |
| iOS / iPadOS arm64 | libmy_pids.dylib | compilation croisée Mach-O, aucune signature requise (re-signature au moment du sideload) |
Dans un pack de ressources, les bibliothèques sont associées à la plateforme selon l’extension du fichier (.dll → Windows, .so → Linux/Android, .dylib → macOS/iOS) ; les règles précises de répertoire sont détaillées dans la section suivante.
Intégration dans un pack de ressources
Arborescence
Placez les produits de compilation de chaque plateforme dans le répertoire natives/<platform>/ du pack de ressources :
my_pack/
├── natives/
│ ├── windows-x64/my_pids.dll
│ ├── windows-arm64/my_pids.dll
│ ├── linux-x64/libmy_pids.so
│ ├── linux-arm64/libmy_pids.so
│ ├── macos-x64/libmy_pids.dylib
│ ├── macos-arm64/libmy_pids.dylib
│ ├── android-arm64/libmy_pids.so
│ └── ios-arm64/libmy_pids.dylib
└── ... (textures, modèles et autres ressources)
Déclaration dans joban_custom_resources.json (préréglage PIDS)
Aucun champ language n’est nécessaire — le mod détermine automatiquement la langue d’après les caractéristiques de chaque entrée de scriptLocations ; JS et C++ peuvent être mélangés en toute proportion au sein d’une même entrée :
{
"pids_images": [{
"id": "example:arrivals",
"scriptLocations": [
"mtr:my_pack/natives/my_pids"
]
}]
}
Règles de résolution des entrées :
Entrée scriptLocations | Résultat |
|---|---|
…/main.js (insensible à la casse) | JavaScript |
mtr:my_pack/natives/my_pids (sans extension) | racine (stem) C++ indépendante de la plateforme → résolue automatiquement pour chaque plateforme en natives/linux-x64/libmy_pids.so, natives/windows-x64/my_pids.dll, natives/macos-arm64/libmy_pids.dylib, etc. ; dans un environnement Linux ARM64, les candidats ios-arm64 / android-arm64 sont automatiquement ajoutés |
…/libfoo.so / ….dll / ….dylib | chemin complet C++ (associé à la plateforme correspondant à l’extension ; ignoré sur les autres plateformes) |
{"windows": …, "linux": …, "macos": …} | table de correspondance C++ par plateforme ; les clés précises telles que macos-arm64 sont prises en charge (les clés précises ont priorité sur les clés génériques) |
- Mêmes règles pour les quatre catégories :
vehicleScripts/objectScripts/liftScriptsdansmtr_custom_resources.json, ainsi que les préréglagespids_images(viascriptLocations) dansjoban_custom_resources.json, prennent tous en charge les déclarations mixtes décrites ci-dessus. - Compatibilité avec l’ancien format : les anciennes déclarations
"language": "cpp"+nativeLibrary/nativeLibrariesrestent valides et peuvent également être mélangées avec du.js;"language": "javascript"force l’analyse de l’entrée entière comme JS. - Champs JS inchangés : la signification des champs
prependExpressions/input/dataFetchMode, etc. ne change pas ; le côté C++ litinputviaapi.input(). - Rendre hors champ : le champ de preset PIDS
outOfBound: true(amont v2.3.0-prerelease.3) maintient le rendu du preset lorsque l’écran sort du cadre de la caméra — indispensable aux presets projecteur qui doivent tourner sans interruption.
Les modules de script (vos .dll/.so/.dylib compilés) appartiennent au pack de ressources : placez-les dans natives/<platform>/ ; le assets/jcm/natives/ embarqué dans le jar du mod ne contient que les ponts natifs — ne les confondez pas. Le jeu ne charge que les bibliothèques de la plateforme correspondant au système d’exploitation courant.
Vérification en jeu
- Installez JCM Yanyang et MTR, placez le pack de ressources dans
resourcepacks/et activez-le (ou distribuez-le en tant que ressource intégrée avec la sauvegarde). - Placez le bloc correspondant (par exemple un PIDS), puis sélectionnez votre script dans la configuration / faites en sorte que l’entrée déclarée soit active.
- Ouvrez
logs/latest.loget rechercheznative script— au chargement, le mod y imprime un résumé census des modules (nombre de modules, types de scripts, canal de transport, etc.). Si votreexample:arrivalsy figure, le chargement a réussi. - Après avoir modifié une bibliothèque, appuyez sur F3+T pour recharger le pack de ressources, ce qui recharge également les scripts C++ (ou utilisez le raccourci du mod « Recharger tous les scripts MTR ») — nul besoin de redémarrer le jeu.
Utilisez impérativement un jar Release ou un produit Complete Build. Les artefacts de la CI rapide (push/PR) ne contiennent pas les ponts natifs : le journal affiche native bridge unavailable et tous les scripts C++ sont ignorés — ce n’est pas une erreur de votre module.
Étape suivante : Modèle de script et cycle de vie — la sémantique complète des quatre catégories de scripts ; ou passez directement aux instantanés de données pour consulter les données accessibles en lecture.