腳本體例與生命週期
五類腳本之基類,逐實例之狀態,生命週期之時序,暨線程與崩潰隔離之義,備於此篇。
五類腳本
納 <mtr/script.hpp>,承其所應之基類,以註冊宏而導出之。基類模板之參,即逐實例狀態之型:
| 資源 | 基類 | 註冊宏 | 數據包裝器 |
|---|---|---|---|
| 車輛 | mtr::VehicleScript<State> | MTR_REGISTER_VEHICLE_SCRIPT | mtr::Vehicle |
| PIDS | mtr::PidsScript<State> | MTR_REGISTER_PIDS_SCRIPT | mtr::Pids |
| 屏蔽之門 | mtr::PsdScript<State> | MTR_REGISTER_PSD_SCRIPT | mtr::Psd |
| 電梯 | mtr::LiftScript<State> | MTR_REGISTER_LIFT_SCRIPT | mtr::Lift |
| 裝飾 | mtr::EyecandyScript<State> | MTR_REGISTER_EYECANDY_SCRIPT | mtr::EyeCandy |
車輛腳本之骨架一首(餘三類,惟易其基類與參數之型而已):
#include <mtr/script.hpp>
struct MyVehicleState {
mtr::CycleTracker dest{"KOWLOON|TSUEN WAN"}; // 君自定之任意 C++ 類型
mtr::Api::Object tracker; // Java 物件之柄
std::unique_ptr<mtr::GraphicsTexture> lcd; // 所持之資源
};
struct MyVehicleScript : mtr::VehicleScript<MyVehicleState> {
static constexpr auto ID = "demo:vehicle";
void create(mtr::VehicleContext& ctx, MyVehicleState& state,
const mtr::Vehicle& vehicle) override {
// 首幀之前一呼:造資源、註回呼
}
void render(mtr::VehicleContext& ctx, MyVehicleState& state,
const mtr::Vehicle& vehicle) override {
// 每幀一呼:讀快照、書繪製記錄
}
void dispose(mtr::VehicleContext& ctx, MyVehicleState& state,
const mtr::Vehicle& vehicle) override {
// 實例銷毀前一呼:釋放資源
}
};
MTR_REGISTER_VEHICLE_SCRIPT(MyVehicleScript)
ID者,腳本命名空間之 id 也(如demo:vehicle),必與資源包mtr_custom_resources.json中宣告條目之id相合。- 三生命週期函數,override 皆可省(基類固有虛而空之實現),惟參數之簽名不可易。
- 一翻譯單元,惟註一腳本;腳本之眾者,分為數模塊之庫。
State:逐實例之狀態
每腳本實例(每車也,每 PIDS 方塊實體也……)各有獨立之 State 一塊,由宿主分而構之:
- 宿主按
mtrStateSize()之數(註冊宏自sizeof(State)生之)而配其塊;首用之前,經mtrInit()以 placement-new 構之——慎勿視零填充之記憶體為可用(std::string諸非平凡之型,施於零記憶體,未定義之行也)。 - 塊中之物,跨幀恆存:紋理之柄、模型之柄、回呼、緩存,悉置諸此。
- 至若腳本類之成員(君於
MyVehicleScript所書之欄),則該模塊諸實例所共——宜惟置唯讀之常數與全域之配置。 dispose既返,狀態隨即而析;實例自客戶端之列而沒者,約五秒乃除役;資源包重載(F3+T)之時,則盡毀之。
要訣
後臺回呼之物(mtr::Api::Callback),必存於 State,以終其身——蓋 Java 側者,任意之時皆得呼之也。
生命週期之時序
分配+構造
宿主為實例配狀態之塊,呼 mtrInit(),則 State() 構焉。
create
實例未繪,先此一呼。create 之際所呈之繪製記錄,必留至首幀 render 而復用——一次之靜態內容(形狀也,常駐之貼圖也)置諸此;render 惟畫其動者。資源之取、Api 之呼、回呼之註,亦皆宜在焉。
render(每幀)
讀快照而運算,錄繪製記錄於幀之之區。快照與包裝器,惟本次一呼之內為效;宿主於 render 既返,即讀其輸出而收其幀。實例不可見,則 render 不呼,而狀態恆存焉。
dispose
實例除役或重載之時,乃呼之。於此閉其所持紋理、模型諸資源;State 之析構隨之。縱 dispose 擲異常,狀態亦必安然就析。
與 JS 相應
此 create / render / dispose + 逐實例 state 之制,即 JCM 為 JS 腳本所設之模型(每 ScriptInstance 一 state 物件)原生之映也;遷 JS 腳本之時,邏輯可逐段對譯焉。
線程模型
- 宿主之於每模塊,立單一後臺之線程,串行呼其生命週期諸函(映 JCM 於 JS 腳本逐腳本單線程執行之器)——同一實例之
create / render / dispose,永不並發。 - 然 render 與非同步之回呼,或得並發(
BackgroundWorker所呈之任務,方行於他線程者也)——共享可變之狀態而欲其安,腳本自任其責。 - 資源包重載,必先止回呼,俟方行呼者既返,而後毀其狀態、卸其碼。
崩潰隔離
註冊宏者,裹每一導出函數於崩潰之防護(crash_guard.hpp):
- 段錯誤(segmentation fault)諸硬體之疾,惟禁該模塊而已,遊戲行程行如故;
latest.log留native script faulted … module disabled一行。 - 未獲之 C++ 異常,亦為防護所截——然請自為 try/catch,以保其語義之明。
- POSIX 之訊號,不以 longjmp 之式「復」之:越訊號處理器而跳,其危甚於崩潰本身;故模塊逕下線者,蓋刻意之設也。
返回之碼
| mtrCreate / mtrRender / mtrDispose 所返 | 其義 |
|---|---|
0 | 成功 |
-1 | 入參為空 |
-2 | ABI 版本不合(宿主與模塊商議不成) |
-3 | 資源類型不合(如車輛之模塊,誤以 PIDS 呼之) |
此諸返回之碼,SDK 所生也;尋常腳本,無足掛懷。惟君自撰宿主或測試之驅,則按表而解之可也(詳見 C ABI 與宿主服務)。