C ABI 與宿主服務

模組匯出

每個腳本程式庫匯出 7 個 C 函式(mtr_native.h;註冊巨集自動生成全部):

匯出說明
uint32_t mtrNativeAbiVersion()傳回編譯時的 ABI 版本(現時 13);宿主拒絕不相容的版本——與 JCM 對 JS 腳本的版本失配重新載入策略一致
const char* mtrScriptType()"vehicle" / "pids" / "eyecandy" / "lift" / "psd"
const char* mtrScriptId()腳本 id(須與資源包宣告一致)
size_t mtrStateSize()逐實例狀態塊大小;宿主為每個實例配置一塊
void mtrInit(const JcmFrameInput*)在宿主剛配置狀態塊後呼叫一次,placement-new 建構 State(ABI 6 起必需;宿主漏調時模組惰性建構兜底)
int32_t mtrCreate / mtrRender / mtrDispose(const JcmFrameInput*, …)生命週期入口;傳回 0 成功、-1 空入參、-2 ABI 不符、-3 資源類型不符。宿主在每模組單一背景執行緒上按實例序列呼叫

幀輸入與幀輸出

JcmFrameInput(宿主 → 模組)

typedef struct JcmFrameInput {
    uint32_t abi_version;              /* 協商後的 ABI 版本 */
    int32_t  resource_kind;            /* MtrResourceKind:vehicle/pids/eyecandy/lift/psd */
    const void* snapshot;              /* 對應的 Jcm*Snapshot */
    void*    state;                    /* 宿主配置的逐實例狀態塊 */
    size_t   state_size;
    const struct JcmHostServices* host;/* 渲染執行緒可呼叫的宿主服務 */
} JcmFrameInput;

快照內部約定:所有 *_offset 欄位都是相對快照首個位元組的位元組偏移;string_pool_offset/len 描述共享 UTF-8 字串池的位置。SDK 包裝器替你完成了全部定址。

JcmFrameOutput(模組 → 宿主)

輸出是扁平記錄流 + 五個輸出記憶體區——「記憶體區」(arena)指一大塊預先申請的記憶體,幀內按順序配置、幀末整體交還,mtrRender 傳回後宿主立即讀取並回收幀(模組間雙緩衝由宿主完成):

記憶體區內容容量(SDK 預設)
records順序排列的繪製記錄(變長,按 record_size 步進遍歷)256 條
matrix_arena矩陣流(列主序 4×4,每矩陣 16 個 float)128 組
string_arenaUTF-8 字串流(貼圖 id、文字、聲音 id…)32 KB
pixel_arenaBGRA8 像素流(紋理上載)初始 1 MB,按需增長至 16 MB,閒置後衰減回落
float_arenafloat 流(體素碰撞箱等)1024 個

ABI 9 起輸出同時給出各記憶體區的完整容量——宿主為每個記憶體區只建一個 ByteBuffer、每幀改 limit 重用,不再逐幀配置五個包裝物件。

繪製記錄

每條記錄以 JcmRecordHeader{kind, flags, record_size} 開頭,變長順序排列。flags 承載記錄級開關:目前定義 MTR_RECORD_FLAG_NATURAL_LIGHT(Text / Texture / Rect 的 natural_light()——取環境光照而非最大渲染亮度)。該位元組由原 padding 就地升格:舊 SDK 編譯的模組恆寫 0、舊宿主忽略之——雙向二進制相容,無 ABI 跳版。8 種類型:

kind結構用途 / 對應的 JS 呼叫
JCM_DRAW_TEXTJcmDrawTextPIDS / 通用文字(TextWrapper 欄位逐一對齊:對齊、溢出模式、跑馬燈、z 序、層、字型 id)
JCM_DRAW_TEXTUREJcmDrawTexture貼圖面片(UV、染色、z 序)
JCM_DRAW_MODELJcmDrawModel車輛車廂 / 轉向架 / 裝飾模型(宿主模型控制代碼 + 姿態矩陣組)
JCM_DRAW_SOUNDJcmDrawSound定位音效(車廂局部座標偏移)
JCM_DRAW_LOCAL_SOUNDJcmDrawSound報站廣播(僅乘坐玩家可聞)
JCM_DRAW_TEXTURE_UPLOADJcmDrawTextureUploadGraphicsTexture 像素上載(BGRA8 + 髒矩形)
JCM_DRAW_OUTLINE_SHAPEJcmDrawShape裝飾輪廓 / 碰撞箱(每箱 6 個 float;碰撞箱上限 1.5 格)
JCM_DRAW_BLOCK_EVENT—裝飾 onBlockUse 事件握手

宿主回放這些記錄用的是與 JS 腳本完全相同的渲染程式碼路徑(PIDSDrawCall / ScriptRenderManager)——畫出來就是同一個東西。

宿主服務

JcmHostServices 是一張函式指標表(in->host),渲染執行緒內可呼叫。舊宿主上新插槽為 null,呼叫前判斷是否為 null 即可降級:

服務起始版本說明
acquire_model / release_modelABI 1模型 JSON 控制代碼生命週期(對應 ModelJS 控制代碼)
create_texture / release_textureABI 1GraphicsTexture 生命週期(對應 Java 類別)
rasterize_textABI 1宿主 TTF 光柵化回呼(CJK 等),BGRA8 寫入呼叫方緩衝區;傳回 -1 表示字型不覆蓋該碼點(應回退內置點陣字型)
logABI 1console.log / console.error 通道
acquire_quad_modelABI 4宿主建構貼圖面片(4 頂點 + UV + 渲染階段 + 紋理控制代碼)——JS DisplayHelper 插槽模型的原生等價物;頂點為模型空間、UV 以貼圖左上角為原點、y 向下。無頭驅動傳回 -1,此時跳過繪製
measure_font_text / draw_font_textABI 7資源字型的度量與基線光柵化(與 JS Graphics2D 同一字型、同一壓縮與裁剪語義;髒邊界出參)
invoke_apiABI 8mtr::Api 的底層通道:UTF-8 JSON 請求進、結果出至執行緒區域緩衝區(下次呼叫前有效)
defer_freeABI 10把已死亡的大塊記憶體交給宿主背景回收執行緒(詳見 效能與除錯)

ABI 版本演進

宿主接受 ABI 6–13 模組(各自按其時代的佈局餵資料、回報自己的 ABI 號)。用現時 SDK 編譯即產出 ABI 13:

ABI引入內容
6mtrInit():宿主必須建構狀態物件(零填充塊對非平凡類型是未定義行為)
7資源字型度量 + 基線文字光柵化加入宿主服務
8invoke_api(完整 Java API 通道)、電梯快照、轉乘型別化模式;ABI 8 程式庫要求 JCM 2.3.0-beta-2.yanyang.7 及以上宿主
9轉乘 / 車輛的規範交通類型 id(MAGIC MTR 擴展);宿主停止逐幀複製快照、記憶體區容量出參
10defer_free 延遲回收掛鉤(腳本自有大緩衝區的離 JVM 執行緒釋放)
11車輛快照 door_direction(開門方向 0..3 —— 原 _pad0 位元組就地升格,佈局與尺寸不變,舊模組照常工作)
12MTR_RESOURCE_PSD(第 5 类腳本)與 JcmPsdSnapshot / JcmPsdArrival / JcmPsdRoute / JcmPsdDoor 快照——纯增量,既有結構零變化;ABI 12 库要求晏阳 hotfix.2 以上宿主
13JcmPsdSnapshot.station_offset(位元組偏移 124;-1 = 無——該欄位在 ABI 12 中是填補位)與其後 96 位元組嘅 JcmPsdStation 記錄,支撐 psd.stop() / mtr::Psd::stop():站名、顏色(ARGB + 十六進位)、票價區 1–3、停靠本站嘅全部線路、鄰站線路分組、實體連通站名、出口同去向、月台編號——純增量,既有結構零變化;ABI 6–12 模組照常載入
為甚麼必須拒絕舊宿主

函式指標表隨 ABI 增長:新模組會呼叫舊宿主不存在的插槽。宿主按模組回報的 ABI 號選擇對應的結構佈局與餵料方式,不符就拒載——這是刻意的快速失敗策略。

傳輸層(宿主內部,腳本不可見)

自 2.3.0-beta-2.yanyang.6(layout v2)起模組內完全無 JNI:兩個呼叫平面承載全部跨語言流量——Panama FFM 平面(JDK 22+,libraryLookup + jcm_* downcall + upcall vtable)與共享記憶體輪詢平面(任何 JDK ≥ 16,橋以建構函式啟動的背景 poller 執行緒經檔案系統握手發現區域,一切經共享頁服務)。腳本程式碼對此完全無感——模組只看到上述 C ABI。


接下來:效能與除錯 —— 把腳本跑得又快又穩。