前人建立的道路 === 如果要我選出對Forge模組開發最大貢獻的三個東西 那我會毫不猶豫地選擇Minecraft、Forge、MCP 前兩個你可能很熟悉,但MCP是一切的根基 默默地支撐起整個模組開發卻鮮為人知,這是我對MCP的印象(♡˙︶˙♡) 最後,強烈建議你在看完這篇之後,也將[這篇文的這個段落](https://hackmd.io/@immortalmice/Hkj9s-CvU/https%3A%2F%2Fhackmd.io%2FwsPcqONwQ8CNUG5UhOTCHQ#Minecraft%E5%AE%98%E6%96%B9%E5%B0%8D%E6%96%BC%E6%A8%A1%E7%B5%84%E9%96%8B%E7%99%BC%E7%9A%84%E6%85%8B%E5%BA%A6)也一起看完 在本章節你會了解 --- - Mincraft的原始程式碼的樣子 - MCP是什麼 - Searge Name、Notch Name與MCP Name - Forge/Forge Gradle是什麼 Mincraft的原始程式碼的樣子 --- 雖然你可能對這件事情是知道,但沒有很大的實感 那就是Minecraft終究到底還是個商業軟體 這意味著Minecraft官方有立場,也有責任保護好他們的產品 對於商業軟體,基本上編譯後,只要不公開原始碼,就能先少少的提供一點點保護 (除非這是個開源專案) 除此之外,第一個會想的到的就是使用[混淆(Obfuscation)](https://en.wikipedia.org/wiki/Obfuscation_(software))技術 **是的,你手上拿到的Minecraft JAR檔是經過混淆後再編譯後的產物** 面對這樣的一個東西,要對他進行開發模組是很難的 沒有人能面對混淆後的程式碼甚至是已經編譯成二進位的檔案進行開發的 可以做,但是不會有人想做,就算是抖M的M鼠也一樣 ╮(╯_╰)╭ 真正的原始碼仍然是握在Minecraft官方手中,官方也沒有公開發表過,這並不是一個開源專案 MCP是什麼 --- 你可能已經想到了,有個技術叫[逆向工程(Reverse Engineering)](https://en.wikipedia.org/wiki/Reverse_engineering) 裡面有著大名鼎鼎的反編譯和反混淆這兩個技術 反編譯有專門的反編譯工具,這沒什麼問題,特別這是個Java專案,比起其他語言容易多了 但反混淆就不是了,難度提升許多,儘管一樣有反混淆工具,但通常效果並不彰顯 想想就知道了,你要讓一個程式告訴你abg.class就是Block.class,你自己都很難做到了,更何況是現今還沒有彈性思考能力的電腦 所以一定程度上,反混淆還是要透過人工才能完成 那麼,有人對Minecraft做過這些事了嗎? **恩,有的,看看這段的標題吧** MCP(Mod Coder Pack)為此而生,藉由他們的努力,我們有了~~用一堆人的肝~~換來的MCP Mapping Mapping是一個表,裡面紀錄了反混淆前和反混淆後的類別/欄位/方法名稱 反混淆後的名稱是怎麼得來的?你可以說是用猜的,但其實還是可以透過一些線索來推導 不過要注意,反混淆後的名稱不一定和原始碼的名稱一模一樣 可能MCP覺得這個方法名稱應該原先叫做dropFood,但其實Minecraft員工當初寫的其實是dropApple σ ゚∀ ゚) ゚∀゚)σ 那有了這個Mapping,我們可以得到可讀性高的程式碼了,總該可以進行開發了吧? **不,並沒有。**~~不要瞎掰好嗎~~ヾ(⌒(_´・ㅅ・\`\)\_ Searge Name、Notch Name與MCP Name --- Notch Name指的是混淆狀態下的名稱 MCP Name指的是透過MCP團隊反混淆後決定的名稱 诶?那Searge Name是誰?來打醬油的? 你要知道,每一個Minecraft版本的發布,背後都進行了不同的混淆程序 也就是比如說,dropApple這方法在1.7.10和1.12.2都存在 但可能經過混淆後,1.7.10的版本變成了sdr,而1.12.2版本變成了koi Searge Name為此而生,他保證了相同的東西,會得到相同的名稱 簡單來說,上面舉例的sdr和koi這兩個Notch Name對應的Searge Name會都叫做func_149662_c 欸等等等等,func_149662_c是啥鬼?說好的dropApple呢?(╯‵□′)╯︵┴─┴ 你要知道,MCP Name是會改變的,可能今天MCP團隊中有個人提出了 > "我覺得這個方法應該要叫dropFood" > 大家同意了,於是這版本的Mapping發布了出去,但幾天後另一個人跳出來說了 >"欸你看這方法,他是用來讓樹葉掉東西的,裡面還用到了Item.Apple,所以我覺得應該要改叫做dropFruit。" > 於是大家又同意了,這個版本的Mapping發了出去 為了讓最後的名稱更好,更能描述方法的作用,MCP Name是會變來變去的,就算是在同一個Minecraft版本也一樣 但是這樣很頭痛阿......明明是同一個版本,但名字不一樣,很難搞的(。ŏ_ŏ) 所以Searge Name出生了,因為他有一個特性,他是唯一的,就算跨版本也是唯一 所以這個dropApple方法的人生會是這樣的: 1. Minecraft員工誕生了他,叫做dropApple 2. Minecraft做了混淆,他變成了sdr 3. MCP得到檔案並反編譯後,先用程式工具給sdr賦予了func_149662_c這個名字 4. Forge這邊的開發得知了這個版本的sdr是func_149662_c 5. MCP幾天後發布了一個Mapping,他們認為func_149662_c應該要叫做dropFood 6. 模組作者(你)開始著手開發,你用了這版本的Mapping,於是你用dropFood名稱呼叫此方法 7. 編譯前,你的程式碼dropFood透過Mapping變回了func_149662_c 8. 執行時,Forge首先載入原版Minecraft的檔案,用了黑魔法([ASM修改字節碼](https://blog.csdn.net/sweatOtt/article/details/88114002)),讓sdr以func_149662_c的名稱載入 9. Forge開始載入你的模組,沒有用黑魔法,但此時你原本寫下的dropFood早已因為Mapping變成了func_149662_c 10. 現在,一切都對上了,可喜可賀可喜可賀 你可能會說,欸,多一個名字,多這麼多步驟,真的有好處嗎? 當然有的,想想看你哪一天,看到新的Mapping發出了,你覺得dropFruit很新潮,想拿來用 你需要重新下載Forge嗎?不用,你只要下載Mapping就好。 再來,新的Mapping出來後,Forge需要為他多做一個版本嗎?不用,因為Forge只要知道Searge Name就好,Forge只需要根據他的Bug修正和新功能更新版本,完全與Mapping的版本無關。 Forge甚至可以用和你開發環境不同版本的MCP Mapping,在你開發的時候同時有兩個Mapping存在,但對於你的開發來說不會有太大的影響 這聽起來很棒,不是嗎?ヽ(✿゚▽゚)ノ > 以上的名稱皆為舉例,如有任何類別/方法因此自認遭個資洩漏,純屬意外´030\` Forge/Forge Gradle是什麼 --- 玩模組,走在路上都會被天外飛來的Forge這個詞砸到臉上 那究竟Forge做了哪些事呢? 現在,每個模組包都一次包了一兩百個模組在裡面,但都能成功的運行 這一兩百的模組作者都這麼厲害,互相深度溝通來維持模組的相容性嗎? 當然不是,Forge建立了一個架構,提供給各模組使用,最後再由Forge載入時處理各種衝突。 你到時候的開發環境中,包含了Minecraft的原始檔案,也包含了Forge自己寫的檔案,最後,也包含了Minecraft原始檔案卻被Forge動過手腳的檔案。 如果套件包(Package)的名稱是net.minecraft開頭,那代表這是Minecraft官方開發的檔案 如果套件包的名稱是net.minecraftforge開頭,那這代表必定是Forge寫的檔案 如果是Forge動過手腳的檔案,通常在Forge動手腳的部分,會加上註解。 ```java= /* ================================ FORGE START ==============================*/ //Forge在這個區段做事 /* ================================= FORGE END ===============================*/ ``` 剛剛我說了通常,那如果不是一個很明確動手腳的區塊怎麼辦?恩.....沒怎麼辦XD 簡單來說,Forge做了三種事 1. 如果模組作者要做某些事,Forge提供一套工具讓模組作者使用 2. 針對Minecraft原生的類別,Forge提供一些新的方法 3. Forge擴充Minecraft原本的方法供模組作者使用 一的部分,比方說註冊方塊、註冊物品、在某某某事情發生的時候額外做一些模組作者想要的事 二的部分,比方說Forge為`Block.class`增加了`getHarvestTool`方法,他會回傳由Forge建立的`ToolType.class`,目前ToolType有AXE、PICKAXE、SHOVEL這三個被靜態宣告的實例 ```java= public net.minecraftforge.common.ToolType getHarvestTool(BlockState state) ``` 三的部分,`Block.class`中的原版`getExplosionResistance`方法,不需要傳入任何參數 可是如果對於某個模組,他需要覆寫(Override)這方法,但同時這模組需要根據某些其他的資訊來決定爆炸抗性怎麼辦? 於是Forge提供了這方法的擴充版本,呼叫時會傳入 方塊狀態、世界、方塊位置、引爆者、爆炸本身實體 這五個參數 ```java= default float getExplosionResistance(BlockState state, IWorldReader world, BlockPos pos, Entity exploder, Explosion explosion) ``` 如此一來,模組作者只要覆寫(Override)這方法,就可以用這五種資訊決定他的回傳結果 對於Forge擴充的方法,Forge通常會在原版方法上寫下註解讓你注意到有另一個版本可以使用,比如說會像是這樣 ```java= //Forge: State sensitive version or //Forge: Use more sensitive version or //Forge: Use more sensitive version {@link IForgeBlockState#getSoundType(IWorldReader, BlockPos, Entity)} ``` 如果遇到不是第三種,沒告訴你Forge指的sensitive version在哪裡怎麼辦? 首先,你可以看看同一個類別中,是否有Forge重載(Overload)這個方法的東西。 再者,看一下該類別是否被Forge偷塞了一個Forge寫的介面(Interface),他通常會長這樣: ```java= public class Block extends ... implements ..., net.minecraftforge.common.extensions.IForgeBlock ``` 你就可以去這個IForgeBlock看,通常裡面會有大量用default定義的介面(Interface)方法 這些default會用一些預設的參數去呼叫原先Minecraft的方法 也就是說,你什麼都不做,這東西就會照原版的方法執行 如果模組作者覆寫(Overide)這方法,就會以模組寫的方法執行 事實上,如果你仔細觀察所有的程式碼,你會發現其實Forge做的東西很多 全部東西的目的都只有一個,方便模組作者開發他的模組 不管是方便性還是相容性,所以感謝Forge團隊大大們~~的肝~~吧\*ଘ(੭*ˊᵕˋ)੭\* ੈ✩‧₊˚ --- *本頁面撰寫於2020/04/12,目前最後更新日期為2020/05/23* *若上述時間與你閱讀的時間相距過遠,請自行斟酌是否採用本頁面的資訊*
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up