【真正的和平模式】一、模組簡(jiǎn)介與開(kāi)發(fā)環(huán)境

你可曾想過(guò),為何在Minecraft這片大陸上的怪物總會(huì)無(wú)差別或有條件地攻擊玩家們?生活在這里,難免會(huì)感覺(jué)到孤獨(dú)和恐懼——難道只有不停地戰(zhàn)斗才是生存下去的唯一途徑?不!或許戰(zhàn)斗會(huì)讓你血脈噴張,或許戰(zhàn)斗會(huì)給你不一樣的體驗(yàn),但這遠(yuǎn)遠(yuǎn)不是熱鬧,而是另一種孤獨(dú)。
如果,你能夠去完成怪物們的心愿,與他們和諧共處,一起愉快地跳舞,一起在陽(yáng)光下自由地奔跑——這才是真正的和平,這才是真正的熱鬧。
《真正的和平模式》這個(gè)模組添加了十余種自然結(jié)構(gòu),近十種生物實(shí)體,數(shù)十個(gè)方塊和物品,以及對(duì)原版的怪物們分別添加了劇情任務(wù)。玩家要運(yùn)用自己的腦力與技術(shù),去完成怪物們的委托,或擊敗強(qiáng)敵,或探尋珍寶,或生產(chǎn)勞作,或?qū)ひ捳胬怼?dāng)玩家完成一種怪物的全部委托后,這種怪物將不再與玩家對(duì)立。
去吧,玩家,讓這片冷酷而孤獨(dú)的大陸變得熱鬧起來(lái)吧!
沒(méi)錯(cuò),這段內(nèi)容是我開(kāi)發(fā)的這個(gè)新模組的簡(jiǎn)介內(nèi)容。由于近日比賽和項(xiàng)目較多,沒(méi)什么時(shí)間來(lái)知乎創(chuàng)作了。但TeaCon 2023的這場(chǎng)比賽增設(shè)了【言傳身教】獎(jiǎng),要求開(kāi)發(fā)者通過(guò)編寫(xiě)技術(shù)博客為萌新開(kāi)發(fā)者們快速入門(mén)、答疑解惑,那我何不寫(xiě)(shuǐ)幾篇文章呢?
項(xiàng)目以MIT協(xié)議開(kāi)源,github倉(cāng)庫(kù)地址:
https://github.com/Viola-Siemens/Real-Peaceful-Mode
本文首發(fā)于知乎,筆者是作者本人,第二轉(zhuǎn)載到bilibili平臺(tái)。原文鏈接:https://zhuanlan.zhihu.com/p/644074598
背景
首先我們來(lái)看TeaCon 2023的題目:
「熱鬧」仿佛是中國(guó)人的向往的生活目標(biāo)。春節(jié)時(shí)節(jié),人們放煙花、貼春聯(lián),這是「熱鬧」的新年;夜市里,人頭攢動(dòng)、攤販吆喝聲不絕于耳,這是「熱鬧」的集市;閑暇時(shí)間,三五好友相約一起,去郊游、去唱歌,這是「熱鬧」的聚會(huì);燈紅酒綠的鬧市里,悄然步入遠(yuǎn)離喧囂人群的小巷,這是反差的「熱鬧」。
那么你對(duì)「熱鬧」的體會(huì)和感覺(jué)是什么?什么才是讓你感覺(jué)身心愉悅的「熱鬧」?請(qǐng)以此為出發(fā)點(diǎn),寫(xiě)一個(gè)模組,自圓其說(shuō)即可。
示例模組:末影龍舟。
審題
題目首先提供了四種熱鬧:
放煙花、貼春聯(lián),以節(jié)日氛圍為主,通過(guò)喜慶的元素烘托出【熱鬧】。由此立意大概率會(huì)做出別具特色的裝飾mod。
鬧市中的叫賣(mài)聲、豐富而吸引人的商品,通過(guò)物質(zhì)的屬性吸引人們一起【熱鬧】。由此立意,結(jié)合前幾天我老家淄博燒烤的爆火,說(shuō)不定可以寫(xiě)出夜市、燒烤等風(fēng)格的食物mod,比如可以取名為淄博樂(lè)事。
三五好友相約郊游唱歌,通過(guò)玩家間的交互來(lái)體現(xiàn)【熱鬧】。這是本題中最為擴(kuò)大開(kāi)發(fā)者立意范圍的一條,只要豐富了玩家間的交流,都可以算作熱鬧,甚至還保留了冒險(xiǎn)mod等題材的選擇可能。示例模組末影龍舟則是通過(guò)玩家間一起齊心協(xié)力劃龍舟、互相組隊(duì)賽龍舟體現(xiàn)出了【熱鬧】的氛圍。
遠(yuǎn)離喧囂,背棄世俗,選擇孤身奮戰(zhàn)——也許這是孤獨(dú),也許這十分冷清,但這又何嘗不是反差的【熱鬧】?這是本題最難立意的一條,稍有不慎則會(huì)跑題,不少參賽者選擇放棄從它立意。既然如此,我選擇反其道而行之,從這一條上立意,畢竟我頭鐵,我怕誰(shuí)!
另外題目的表述似乎在給予開(kāi)發(fā)者們一個(gè)暗示,熱鬧體現(xiàn)在玩家與玩家之間,體現(xiàn)在物品的使用與交互中,事實(shí)上并非如此。我也是考慮到了這一點(diǎn),萌生了一個(gè)大膽的想法——玩家與怪物之間,是否也算熱鬧?
基于這個(gè)想法,我便在考慮,如果在Minecraft的世界,玩家和怪物能夠和平共處,互相幫助,從此玩家不再孤身一人,而是有著無(wú)數(shù)的旅途伙伴,而無(wú)需多人聯(lián)機(jī),那這樣的世界該有多熱鬧!
這便是反向立意法,在立意時(shí)考慮【熱鬧】的反義詞是什么,我認(rèn)為是【冷清】、【孤獨(dú)】,而解決掉冷清與孤獨(dú)之后,便是符合題意的熱鬧!
進(jìn)一步的,這片大陸中不止有一種怪物,玩家實(shí)現(xiàn)了一種怪物的心愿后,悄然離開(kāi)去幫助其它的生物,正是符合了第四條的立意。
說(shuō)干就干!模組策劃好了,內(nèi)容便是,玩家需要通過(guò)不斷探索世界,幫助怪物們完成他們的心愿,從此對(duì)應(yīng)的怪物將不再攻擊玩家,實(shí)現(xiàn)真真正正的和平模式!
環(huán)境配置
Minecraft與Forge版本的選擇
根據(jù)要求,模組的目標(biāo)平臺(tái)為Minecraft 1.20.1,F(xiàn)orge 47.0.1以上——事實(shí)上TeaCon開(kāi)幕不久后,F(xiàn)orge 47.1.0穩(wěn)定版問(wèn)世,于是我選擇使用它來(lái)編譯和運(yùn)行模組。
編寫(xiě)build.gradle
1.20+與先前版本不同,需要使用ForgeGradle 6.0,而gradle的版本也變成了8.1.1,我用之前寫(xiě)1.16.5~1.19.4的build.gradle的寫(xiě)法來(lái)配置項(xiàng)目時(shí)直接失敗了,因此需要注意這一點(diǎn)。
由于需要使用spongepowered mixin,因而需要添加一些dependencies:
為了方便調(diào)試和生成內(nèi)容,需添加一些runs的配置,這里我寫(xiě)了四個(gè)runs,分別是運(yùn)行客戶端、運(yùn)行服務(wù)端、運(yùn)行服務(wù)端測(cè)試和生成內(nèi)容:
最后則是forge版本、mixin的依賴,打jar包和和mixin的配置,還有最重要的標(biāo)題、作者、版本與發(fā)布設(shè)置:
模組核心架構(gòu)
常言道,萬(wàn)事開(kāi)頭難,那么編寫(xiě)一個(gè)模組該從何處開(kāi)頭呢?
首先,F(xiàn)orge模組通過(guò)一個(gè)注解了“@Mod(value = "modid")”的類作為模組入口,通過(guò)實(shí)例化這樣一個(gè)類來(lái)對(duì)Minecraft的游戲內(nèi)容進(jìn)行修改。這個(gè)類中,你可以監(jiān)聽(tīng)Forge事件、注冊(cè)項(xiàng)目,也可以調(diào)用和加載其它的類來(lái)進(jìn)行各種修改等。例如,我可以這樣編寫(xiě)構(gòu)造函數(shù):
注意,這里我調(diào)用了Forge的兩個(gè)不同的bus——ForgeBus和ModBus。ModBus一般執(zhí)行模組生命周期中發(fā)生的注冊(cè)、加載等事件,而ForgeBus則一般執(zhí)行游戲過(guò)程中Forge的各種Hooks捕獲到的事件,如TagsUpdatedEvent(數(shù)據(jù)包被重新加載)、ServerStartedEvent(服務(wù)端,啟動(dòng)?。?、VillagerTradesEvent(村民職業(yè)對(duì)應(yīng)交易表的添加)等。前者往往是靜態(tài)的,而后者往往是動(dòng)態(tài)的。這些Event的監(jiān)聽(tīng)則構(gòu)成了Forge的API,你可以通過(guò)查閱Forge文檔,了解Forge的Hooks和注冊(cè)機(jī)制,以及參閱minecraftforge的jar包的源代碼內(nèi)容來(lái)了解它們。總之能不mixin盡量不要mixin,除非有一些類中沒(méi)有Forge的Hooks,或者你需要魔改的部分找不到API(如弓的附魔、新的音符盒樂(lè)器等等),而accesstransformer又無(wú)法單獨(dú)完成修改,才考慮去使用mixin。
個(gè)人喜歡采用的實(shí)現(xiàn)一個(gè)功能的邏輯如下:
確定需求
從minecraft源代碼中找到實(shí)現(xiàn)這個(gè)需求可行的注入點(diǎn)
確定有無(wú)Forge的Event可以通過(guò)監(jiān)聽(tīng)來(lái)實(shí)現(xiàn)需求
若無(wú),考慮可否使用AT(Access Transformer)來(lái)修改成員、函數(shù)或類的權(quán)限(也可以用mixin寫(xiě)Accessor)
若否,再考慮mixin,盡量使用Inject而避免用Redirect,絕對(duì)避免Overwrite和捕獲局域變量的方法!
回到構(gòu)造函數(shù),這里調(diào)用了RPMContent.modConstruction,這是各種項(xiàng)目注冊(cè)的入口:
以創(chuàng)造模式物品欄為例,模組可以選擇使用延遲注冊(cè)機(jī)制實(shí)現(xiàn)項(xiàng)目的注冊(cè):
這里定義了一個(gè)名為"real_peaceful_mode:real_peaceful_mode"的創(chuàng)造模式物品欄,對(duì)照本地化文件(以英語(yǔ)en_us.json為例),我們可以看到這個(gè)物品欄標(biāo)簽的名字:
"itemGroup.real_peaceful_mode": "Real Peaceful Mode"
沒(méi)錯(cuò),這是Minecraft原版的本地化系統(tǒng),最早是自定義格式的.lang文件,如今則是JSON格式。代碼中調(diào)用時(shí),只需要使用Component.translatable("<本地化鍵名>")即可。
總結(jié)
我們的模組框架成功搭建起來(lái)了,那么模組開(kāi)發(fā)的基礎(chǔ)部分到此為止,下一篇我將講解一些或核心或邊緣的模組功能的實(shí)現(xiàn)。模組仍在持續(xù)制作中,敬請(qǐng)期待!