最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

MC 1.20.1 ForgeMod開發(fā)小記:從mod界面的config按鈕說起

2023-07-19 23:04 作者:CloudVoid  | 我要投稿

寫在開頭

本文的所有內(nèi)容基于 Minecraft 1.20.1?Forge?47.1.0 版本,使用?CC BY-NC-ND 4.0 協(xié)議發(fā)布至公有領(lǐng)域。

如果你是一個(gè) Mod 開發(fā)老手,那么我建議你看完前言后直接跳到解決方法部分,我相信你已經(jīng)在?Mod?開發(fā)的歷程中有自己的思考和感悟,細(xì)枝末節(jié)的過程不再重要。

如果你是一個(gè) Mod 開發(fā)新手,那么我還是建議你仔細(xì)閱讀這篇文章的內(nèi)容,重要的不是具體的問題,而是解決問題的過程和方法,希望我的講解可以帶你一窺在抄寫教程和瘋狂畫餅之外的、獨(dú)立解決問題的 Modding 魅力。

* 建議使用 Bilibili 網(wǎng)頁版閱讀

* 本文參與 TeaCon 2023?“言傳身教” 獎(jiǎng)項(xiàng)評(píng)選

前言

今年,筆者以參賽者(也不一定,萬一到時(shí)候造不出來展館就變成參展者了,樂.jpg)的身份參加了 2023 TeaCon 模組開發(fā)茶會(huì),參賽作品是一個(gè)很簡(jiǎn)單的客戶端 Mod:彈幕聊天。在這里,筆者無意詳述?Mod 功能實(shí)現(xiàn)的具體細(xì)節(jié),而是想與各位一起討論一個(gè)在 Mod 開發(fā)中經(jīng)常被許多開發(fā)者忽略的一個(gè)用戶體驗(yàn)上的小細(xì)節(jié),即 Mod 的配置項(xiàng)。

有一定項(xiàng)目經(jīng)驗(yàn)的開發(fā)者肯定對(duì) Forge 提供的配置文件系統(tǒng)有了不少了解,或許也都上手寫過自己 Mod 的配置文件。但是就筆者的經(jīng)驗(yàn)來看,許多開發(fā)者都沒有注意到 Forge 的一個(gè)小細(xì)節(jié):Forge 的 Mod 列表界面其實(shí)是有一個(gè)寫著“配置”的按鈕的。

至少截至發(fā)稿,TeaCon 2023 測(cè)試服的?Mod 界面里也只有彈幕聊天適配了這個(gè) Forge 提供的按鈕。作為對(duì)比,測(cè)試服的 config 文件夾內(nèi)共有 29 個(gè)文件(不計(jì)算文件夾嵌套的情況下)

有的開發(fā)者可能覺得寫出來配置文件就已經(jīng)足夠方便玩家了,但筆者本人還是比較推薦大家積極地去適配 Forge 提供的功能,這不僅能夠方便玩家,也能夠方便你被問到各種奇葩問題的血壓(當(dāng)然筆者想去適配這個(gè)按鈕的部分原因可能是因?yàn)樽约旱膹?qiáng)迫癥)。

那么接下來就直接進(jìn)入我們的正題:這個(gè)按鈕我們到底該如何去適配呢?

開發(fā)中遇到了困難,那自然是去翻源碼。Minecraft 的游戲內(nèi)容顯示是以 Screen 為基本單元的,那與 Mod 列表界面強(qiáng)相關(guān)的自然就是 ModListScreen 類了,讓我們來看看這個(gè)類的源碼:

我們一眼就看到這個(gè)類下面有三個(gè) Button 類型的字段,對(duì)應(yīng)的名字也正好就是我們剛剛在 Mod 列表界面看到的三個(gè)按鈕,那配置按鈕就是里面的 configButton 沒跑了。那么這個(gè)按鈕是怎么初始化的呢?

每個(gè) Screen 在被渲染到屏幕上之前都會(huì)調(diào)用一遍 init() 方法完成內(nèi)容的初始化,Mod 列表界面自然也不例外,在這里我們順利的找到了這個(gè)按鈕的初始化方法??梢钥吹?,這里調(diào)用了 Button 類的構(gòu)造器方法生成了一個(gè) Button 實(shí)例。bounds() 方法很好理解,就算看參數(shù)都能猜出來這是一個(gè)定義按鈕位置的方法,那么定義按鈕按下邏輯的方法自然就在構(gòu)造器方法的參數(shù)中了,我們來看看構(gòu)造器方法的定義:

很好,參數(shù)類型把作用都告訴我們了,很顯然這個(gè)方法的第二個(gè)參數(shù)傳遞的就是一個(gè)定義按鈕按下后邏輯的實(shí)例,由于第二個(gè)參數(shù)類型是一個(gè)函數(shù)式接口(即只有一個(gè)方法的接口)Forge 在這里直接傳入了一個(gè) Lambda 表達(dá)式:

?接下來我們只需要找到?displayModConfig() 這個(gè)方法,它的代碼是這樣的:

這段代碼有些復(fù)雜,我們一行一行分析,首先看 ConfigScreenHandler.getScreenFactoryFor() 方法:

可以看到,這個(gè)方法的返回值是一個(gè)?Optional<BiFunction<Minecraft, Screen, Screen>>?類型的對(duì)象,然后調(diào)用了 map() 方法將這個(gè)對(duì)象映射為了一個(gè)?Optional<Screen> 對(duì)象,最后判斷這個(gè) Screen 是否存在,若存在則調(diào)用 minecraft.setScreen() 方法替換游戲內(nèi)的界面。

可以想當(dāng)然地認(rèn)為(這部分的討論請(qǐng)見后文),只要?map()?方法能夠返回一個(gè)存在 Screen 的?Optional<Screen>?對(duì)象,便可以讓 Mod 列表界面的 config 按鈕正常工作,所以我們還是需要返回?ConfigScreenHandler.getScreenFactoryFor()?方法,看看它到底返回了一個(gè)什么樣的對(duì)象:

首先,它調(diào)用了 ModList.get() 方法得到了游戲的 Mod 列表,然后調(diào)用 getModContainerById()?方法,獲取到了對(duì)應(yīng) Mod 的 Container(可以簡(jiǎn)單理解為一個(gè) Mod 對(duì)應(yīng)一個(gè) Container ),最后調(diào)用 flatMap() 方法將對(duì)應(yīng)的 Container 實(shí)例映射為我們需要的?Optional<BiFunction<Minecraft, Screen, Screen>> 對(duì)象(在這里我們可以認(rèn)為 flatMap()?和 map() 方法效果是一樣的)。

那么具體的映射方法這里還是使用了 Lambda 表達(dá)式進(jìn)行了定義:

當(dāng)然這里也用了一個(gè)?map()?方法,不過結(jié)合?ConfigScreenHandler?類中對(duì)?ConfigScreenFactory?類(?record?類,JDK 16 中引入的新特性)的定義相信讀者不難看出這段代碼的作用,這里就不再贅述了(注意:這里的 mc 指的不是minecraft ,而是 modContainer )。

那么我們只需要知道 mc.getCustomExtension() 究竟返回了什么,就能知道我們?cè)撛趺唇o config 按鈕添加功能了,直接上源碼:

從代碼中我們很容易就能看出來,其實(shí)這就是一個(gè)從鍵是類,值是?Supplier?對(duì)象的 map 中取值的方法,對(duì)應(yīng)放鍵值對(duì)進(jìn)入這個(gè) map 的方法正好挨在一塊,也省得我們繼續(xù)找了。

不過這里還有一個(gè)問題:我們?cè)撛趺传@取自己 Mod 對(duì)應(yīng)的 Container 呢?

可以看到 ModContainer?類是位于?net.minecraftforge.fml?包下的,都屬于 fml 了那自然是去找模組加載上下文( ModLoadingContext )啦。查閱對(duì)應(yīng)代碼后可以找到?ModLoadingContext?類中有一個(gè)?getActiveContainer()?方法正好可以拿到我們 Mod?對(duì)應(yīng)的 Container?,不過這里我們還有一個(gè)更棒的選擇:ModLoadingContext?類已經(jīng)給我們提供了往?extensionPoints?中添加鍵值對(duì)的方法,我們只需要直接調(diào)用?registerExtensionPoint()?方法就可以了。

想當(dāng)然?

當(dāng)然,現(xiàn)在回頭看我們會(huì)發(fā)現(xiàn)還有一個(gè)小問題沒有解決,那就是前文提到的?“想當(dāng)然” ,那么我們的判斷究竟對(duì)不對(duì)呢?其實(shí)寫出來跑一遍就肯定能知道答案,但這里我們還是想從代碼層面找出證據(jù)證明我們的 “想當(dāng)然” 是對(duì)的。

文章都寫到這了,那我們想的肯定是對(duì)的,這里我也就不賣關(guān)子,直接把對(duì)應(yīng)的源碼貼出來大家就能明白。

其實(shí)在 init() 方法的后面部分,我們能看到這里對(duì) configButton 做了一個(gè)奇怪的操作:

從字段名稱我們就能看出來,這個(gè)語句是禁用?configButton?的功能的,那么這個(gè)字段在什么時(shí)候被設(shè)置為 true?了呢?在后面的 updateCache()?方法里:

這與我們?cè)?displayModConfig()?方法中找到的代碼不謀而合,補(bǔ)上了這最后一塊漏洞。

解決方法

根據(jù)我們?cè)谏厦娴耐评?,只需要在自己?Mod?主類中添加這樣一段代碼,就可以實(shí)現(xiàn)按下 config 按鈕完成我們想要的邏輯了,在這里我就以最簡(jiǎn)單的實(shí)現(xiàn):按下按鈕打開配置文件為例(這是彈幕聊天里的代碼,不過現(xiàn)在已經(jīng)不是這樣的了)。

在這里單獨(dú)寫出一個(gè) @OnlyIn()?注解的類是為了防止在服務(wù)端上觸發(fā)客戶端的類加載導(dǎo)致的崩服,不過我想應(yīng)該也不會(huì)有人把彈幕聊天裝到服務(wù)器上吧(笑 TeaCon 除外)。

打開游戲,就可以看到 Mod 列表界面的 config 按鈕已經(jīng)亮起來了,按下就可以彈出配置文件啦!

后記

本來計(jì)劃用一篇文章講完 Forge?的 config 按鈕和原版風(fēng)格配置界面的,但是寫完第一部分才發(fā)現(xiàn)字?jǐn)?shù)已經(jīng)不少了,所以今天就先寫到這兒吧(第二篇寫不寫還兩說呢,一是最近實(shí)在是抽不開身來,二是關(guān)于 Screen?的內(nèi)容其實(shí)前人已經(jīng)有很多介紹了)。

其實(shí) Forge 提供的配置文件系統(tǒng)在 1.12.2 版本后進(jìn)行了一次大改,原來的自動(dòng)配置界面系統(tǒng)被舍棄了,這也是讓我想要適配這個(gè)界面的原因之一吧(順帶一提,這玩意前段時(shí)間還有人給 Forge?提 PR 想把它加回來,結(jié)果被否了)。各位讀者如果對(duì)?1.12.2 版本及以前的自動(dòng)配置界面系統(tǒng)有興趣可以參考 3Tusk 所撰寫的 Harbinger Mod 開發(fā)教程中的相關(guān)內(nèi)容,這里我就不再貼鏈接了。

當(dāng)然,也希望在?TeaCon?2023?開發(fā)截止前看到這篇專欄的參賽者們積極修改自己的 Mod(s) (笑

這里是 Locus_Natit(你也可以叫我 Loci_Natit ),我們下次再見~

MC 1.20.1 ForgeMod開發(fā)小記:從mod界面的config按鈕說起的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
鹿泉市| 山西省| 绍兴市| 曲沃县| 永仁县| 敦煌市| 兴国县| 黑水县| 怀远县| 章丘市| 安泽县| 老河口市| 勃利县| 遂川县| 徐闻县| 贡觉县| 凤台县| 高雄市| 交口县| 江华| 长阳| 福贡县| 延边| 安国市| 大冶市| 翼城县| 福州市| 云阳县| 中西区| 郧西县| 合江县| 北辰区| 虎林市| 镇康县| 桐乡市| 柘城县| 苗栗市| 水富县| 江陵县| 韶山市| 黄大仙区|