Class EventRolloutDifferTest
對應計畫 C7 的出口條件「先過併發 gate + 鎖序 assert + Tier2/3」。event 與 npc/quest/portal/reactor 的關鍵差異:
- 引擎是行程級、開機即建並 eval:不同於 npc/quest/reactor 的 lazy per-client、portal 的 per-name
SAM ——
EventScriptManager在頻道啟動時(每頻道一具)對每個scripts/event/<name>.js立即getInvocable(eval 頂層)+init(),並把引擎iv共用給該事件的所有 instance。 該iv被 Netty 封包緒 + EventTimer 池多執行緒呼叫。 - 唯一需要序列化的類別:C1 已對 event 的
iv套ScriptEngines.serialized(Invocable)(per-engineReentrantLock, 橫跨整個invokeFunction)。Nashorn 路徑回傳裸 iv(容忍並發)→ 並存期行為不變。 - 鎖序不變式(C7 新增 runtime assert):腳本從引擎鎖內回呼 Java(如
eim.registerPlayer取EventInstanceManager的結構鎖 wL)→ 既定鎖序「engine-lock → structural-lock」。任何「持結構鎖時 invoke 引擎」的反向路徑都會死結。對抗審查(workflow,3 lens × refute-by-default)揪出一條真實違規:EventInstanceManager.playerDisconnected在 wL 鎖內呼叫removePlayer→playerExitinvoke(KerningPQ/LudiPQ/ZakumPQ 等回傳負值、隊長離線即觸發)——C7 已修(把 invoke 移出鎖區), 並加ScriptEngines.trackStructuralLock(Lock)/assertLockOrder守護防回歸。本檔下半即驗該守護。
同一 JVM 並存 nashorn-core(黃金參考)與 GraalJS(候選),由
ScriptEngines.fresh() 建立(C8 後正式環境一律 GraalJS;黃金參考見 GoldenReference.nashorn())。不需 DB / wz / 網路 / Timer。
(語料偵察 + 鎖序審查由 workflow 完成:全 102 檔零真 E4X / Java.type / importClass /
JavaAdapter;interop 僅 7 檔 load('nashorn:mozilla_compat.js') + importPackage
——兩引擎在 js.nashorn-compat=true 下走相同路徑(C0 的 GraalJsSeamTest 已證 mozilla_compat
在 GraalJS 可解析、PVP.js 在其解析清單),Tier3 全檔 eval parity 為最終仲裁。零頂層 em/eim 參照
(event 引擎 eval 在綁定 em 之前;伺服器今日於 Nashorn 開機即證)。parity 邊角:2xEvent.em.DoubleRateEvent
/BossQuest.saveBossQuestPoints/lolcastle.saveWinner/enterDisguise3.getMapInstance(int,int)
皆為不存在方法,僅 invoke 時兩引擎同樣拋,Tier3 解析/綁定不受影響。)
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classstatic final class錄製式EventInstanceManagerstub(腳本參數eim)。static final class -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoid全scripts/event/*.js在 nashorn-core 與 GraalJS 必須等價(非「全部成功」): 解析分歧(XOR)= 零:不得有任何腳本「一引擎解析成功、另一引擎失敗」。void合成微 fixture(不依賴部署內容):把 event 最具差異風險的「多載選擇 + 數值強制 + 數字→String 強制」 集中於一處隔離直驗,並對確切轉錄下斷言。void(3) C7 鎖序守護:持 EventInstanceManager 結構鎖(以 trackStructuralLock 包覆)時 invoke serialized() 引擎 → 立即拋 IllegalStateException;鎖外 invoke 正常;釋鎖後恢復(計數正確遞減)。void(4) Nashorn 路徑:serialized() 回傳裸 iv(無代理)→ 即使持結構鎖也不觸發守護(並存期行為不變)。void(2) 線上機制:ScriptEngines.serialized(Invocable)(EventScriptManager 對 iv 所套)→ 無例外、 maxInFlightinvalid input: '<'=1、跨呼叫狀態保留(證持久 context,非 per-invocation 隔離 → PVP/PQ 計數不被清)。void(1) 重現通用危害:單一共用 GraalJS Context 被兩執行緒同時invokeFunction("setup")。void
-
Constructor Details
-
EventRolloutDifferTest
public EventRolloutDifferTest()
-
-
Method Details
-
corpusParseAndEntryBindingAreEngineEquivalent
全scripts/event/*.js在 nashorn-core 與 GraalJS 必須等價(非「全部成功」):- 解析分歧(XOR)= 零:不得有任何腳本「一引擎解析成功、另一引擎失敗」。這條同時是
mozilla_compat 的最終仲裁 —— 7 個
load('nashorn:mozilla_compat.js')檔的頂層 load 在 eval (見下方綁定步驟)時若 GraalJS 不解析,該檔函式不會 hoist →typeof init在兩引擎分歧 → 本測試失敗。 C0–C6 已證兩引擎皆解析 mozilla_compat,故預期零分歧。 - 進入函式綁定逐檔等價:eval(容忍頂層丟)後,
ENTRY_FNS每個typeof fn兩引擎須一致 —— 驗 event 線上實際走的invokeFunction(...)路徑(init/setup/playerEntry/monsterValue/…)。
「兩引擎皆解析失敗」≠ 分歧(記到
build/event-parse-broken-both.txt,不擋 rollout;如BossQuest.js既有語法錯)。注意自 2026-06-04 起,事件腳本改由EventScriptManager.discoverEventScripts()掃描整個scripts/event/目錄載入(原channel.events清單已移除),故解析失敗的腳本由EventScriptManager載入時優雅略過(getInvocable→null),而非靠載入清單排除。- Throws:
Exception
- 解析分歧(XOR)= 零:不得有任何腳本「一引擎解析成功、另一引擎失敗」。這條同時是
mozilla_compat 的最終仲裁 —— 7 個
-
tier2TranscriptsAndReturnsMatchAcrossEngines
-
eventOverloadAndCoercionFixtureMatchesAcrossEngines
合成微 fixture(不依賴部署內容):把 event 最具差異風險的「多載選擇 + 數值強制 + 數字→String 強制」 集中於一處隔離直驗,並對確切轉錄下斷言。涵蓋:em.setProperty(String,String)與eim.setProperty(String,String)—— String 引數身份。- 數字 → String 強制:
em.setProperty("num", 0)/eim.setProperty("n", 7)—— 對應 BossQuest 系列(13 處setProperty(_,0))。Nashorn 把數字 0/7 coerce 成 "0"/"7"; 本 fixture 以 parity 為硬閘(gold==cand)抓任何 GraalJS 分歧,並以 gold 為黃金釘住確切轉錄。 eim.broadcastPlayerMsg(int,String)多載(含繁中 String 身份)。eim.startEventTimer(long)—— JS 數字 → long。- 數字回傳(
return 1)。
- Throws:
Exception
-
serializedEventEngineSerializesConcurrentEntry
(2) 線上機制:ScriptEngines.serialized(Invocable)(EventScriptManager 對 iv 所套)→ 無例外、 maxInFlightinvalid input: '<'=1、跨呼叫狀態保留(證持久 context,非 per-invocation 隔離 → PVP/PQ 計數不被清)。- Throws:
Exception
-
lockOrderAssertFiresWhenInvokingUnderStructuralLock
-
nashornPathNeverFiresLockOrderAssert
-