Démarrage rapide

Prérequis

OutilVersionRemarques
Compilateur C++C++17MSVC (Windows) / GCC / Clang, au choix
CMake≥ 3.16recommandé, mais non obligatoire — le SDK est entièrement constitué d’en-têtes
JDKfacultatifuniquement nécessaire pour exécuter les tests d’intégration JVM livrés avec le SDK
Astuce

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)
À noter

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 cibleProduitRemarques
Windows x64my_pids.dllMSVC ; /utf-8 est préréglé par le projet du SDK
Linux x64 / ARM64libmy_pids.soGCC / Clang
macOS x64 / arm64libmy_pids.dylibarchitecture choisie via -DCMAKE_OSX_ARCHITECTURES=arm64 (ou x86_64), -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0
Android arm64libmy_pids.socompilation 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 arm64libmy_pids.dylibcompilation croisée Mach-O, aucune signature requise (re-signature au moment du sideload)
Astuce

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 scriptLocationsRé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 / ….dylibchemin 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 / liftScripts dans mtr_custom_resources.json, ainsi que les préréglages pids_images (via scriptLocations) dans joban_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 / nativeLibraries restent 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++ lit input via api.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.
Note

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

  1. 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).
  2. 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.
  3. Ouvrez logs/latest.log et recherchez native script — au chargement, le mod y imprime un résumé census des modules (nombre de modules, types de scripts, canal de transport, etc.). Si votre example:arrivals y figure, le chargement a réussi.
  4. 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.
Important

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.