本期教程我們要討論的是數據生成器。在之前的編寫過程中,我們手動編寫了一些博客狀態和語言內容,還有一些尚未開始編寫的部分,比如方塊的掉落物列表。目前方塊被破壞時不會掉落任何物品,另外,方塊和物品的合成方式也沒有編寫。
也許我們還想要實現一些進度相關的功能,包括提高與其他模組的兼容性等,這些內容都還沒有著手編寫。當然,如果你對數據包很瞭解,可以在資源包中新建一個名為"data"的文件夾,然後像編寫數據包一樣將這些內容寫入其中,這樣就可以包含掉落物和合成配方等內容了。
Minecraft本身也提供了生成這些數據的方法,今天我們來講解數據生成器,並利用它簡單地生成一些內容。為了應用我們的數據生成器,在項目中新建一個軟件包,命名為"對他進",然後在其中創建一個Java類,命名為"DataGenerator"。
關於數據生成器,我們可以參考Forge的文檔,儘管文檔中有些地方可能並不是很清晰,但仍可以參考一些基本信息。例如,文檔提示我們需要監聽某個事件,並通過AT Provider將我們自己的數據生成類添加進去。
既然我們要監聽一個事件,我們肯定需要使用 EventBus Subscriber 模式,並設置 mod id 為事件總線,以及需要監聽的事件。在觀看代碼後,可以發現這是一個Mod事件,所以我們使用 @Mod.EventBusSubscriber 註解,然後編寫一個靜態方法如下:
@Mod.EventBusSubscriber(modid = "your_mod_id", bus = Mod.EventBusSubscriber.Bus.FORGE)public static void generateGatherDataEvent(GatherDataEvent event) {// 在這裡進行數據生成操作}
在這裡,我們需要添加我們自己的數據生成類。因此,我們可以新建一個名為"Recipes"的類,該類屬於服務器數據。我們可以參考“Recipes Generation”中的內容,這個Recipes類需要作為IRecipeProvider的子類,並實現buildRecipe方法。
我們可以繼承IRecipeProvider,然後實現構造函數並修改參數名。要如何編寫呢?我們可以參考原版的寫法,在net.minecraft.data包中找到Recipes,然後查看其中的配方。在這裡有很多方法,它們主要是通過這種方式來生成配方。
我們可以看到它有ShapedRecipeBuilder(有序合成)和ShapelessRecipeBuilder(無序合成),還有其他一些方法,我們也可以點進去查看最終是如何生成的。我們可以看到它有有序合成、無序合成以及特殊物品合成等。
如果我們想要合成某樣東西,我們可以參考原版類似物品的合成,看看它是如何寫的,然後學著來寫。看了原版之後,我們就知道應該如何寫了。假設我們現在要為小門創建合成配方,我們希望小門是有序合成的,那麼我們可以使用ShapedRecipeBuilder.shapedRecipe,在其中需要提供RecipeType。
接下來我們可以定義合成的樣式,類似於在寫數據包時定義合成表。我們剛才查看原版,也可以看到原版是這樣寫的。對於小門的合成,我們可以選擇特殊的方式,比如用鐵錠和銅錠來合成。定義好合成的樣式後,我們需要確保這些物品的命名都是正確的。
首先,我們需要定義這個鐵錠。我們可以直接使用原版的鐵錠,通過原版的items來獲取。但是,我們需要考慮到其他Mod的兼容性。如果我們硬編碼為原版的鐵錠,而另一個Mod作者認為他們的物品也可以作為鐵錠使用,如果我們寫死了,就無法使用他們的鐵錠,導致不兼容。
為了解決這個問題,Forge提供了一套標籤系統。我們可以在data/forge/tags/items/ingots.json中查看這些標籤。如果有一個Mod認為他們的物品也可用作這類物品時,他們會將自己的物品添加到這些標籤中。為了實現互通性,我們只需使用Forge提供的這些標籤即可。
在編寫合成配方時,儘量使用標籤,因為標籤可以包含一類物品。我們可以使用Forge的標籤,比如#forge:ingots/iron表示鐵錠。繼續定義時,我們可以使用define關鍵字指定具體的物品,例如#forge:ingots/copper表示銅錠。這種做法只適用於Forge的標籤。
如果你想使用原版的標籤,你需要去data/minecraft/tags中找到對應的文本文件。在這裡,你會找到原版的標籤,比如如果你想使用原版的木板,你需要使用#item:planks來調用原版的標籤。它們位於不同的位置,所以要注意你想要使用哪種標籤。
Forge不會重複註冊已經存在的原版標籤,通常會首先查看原版是否已有標籤,然後再查看Forge是否有。如果兩者都沒有,而你認為未來可能會有類似物品出現,那麼最好使用標籤來處理。
如果需要註冊自定義的標籤,可以使用數據生成器來生成一個手寫的方式,並確保它被正確加載和使用。
完成合成配方之後,我們還需要考慮這個配方的解鎖方式。在遊戲中,通常會解鎖一些配方後,在右上角會彈出一個已解鎖的提示,並且在原版的合成表中也能看到。儘管很多人可能會使用JEI來查看所有配方並直接合成,但原版仍有一套內置的遊戲規則,即只能使用已解鎖的配方進行合成。
為了兼容這個系統,我們需要編寫unlock by,這裡我們假設拿到銅錠就可以解鎖這個配方。因此,我們可以定義一個條件叫做has copper,使用has items來指定原版的copper ingot。
首先演示如何使用原版的物品,之後我們可以改用標籤。最後別忘了點擊save consumer保存。如果要生成更多的合成配方,可以繼續按照相同的格式編寫。
除了編寫合成配方外,我們還需要添加文本提供程序(TextProvider),它必須是TX的子類,並實現add text方法。在這裡可以找到許多現有的提供程序,包括原版的文本和Forge的文本。如果想要註冊自定義的內容到Forge中,可以使用BotItemTextProvider並添加相關信息。複製並擴展原版標籤生成類,可以使用BlogTextProvider來確保我們的小門只能使用鐵工具來挖掘。
在這裡我們可以擴展BlockTextProvider,然後實現構造函數方法。修改參數名稱以便理解。接著可以使用鐵工具嗎?實際上這是原版的一個標籤,在net.minecraft.text下,我們可以看到有BlogTextProvider,裡面包含了一些示例。例如,Aaron Top需要不同類型的工具,我們需要使用TagNeedsErial,然後寫入我們需要的內容,比如ModelBlock,點擊Get,再點擊Add來添加我們的方塊MoreDogBlock。
這樣我們就把我們的門添加到需要鐵工具這個標籤裡面了,非常簡單。如果想要添加到其他標籤裡面,也是類似的寫法,都是使用Tag找到對應的標籤提供程序。構造方法會直接生成,TT方法中寫上tag參數,即對應的標籤變量,然後再點擊Add,添加自己的內容即可,非常簡單。
文本提供程序已經註冊好了,接下來我們需要給我們的門添加一個戰利品表,使其能夠掉落所需物品。我們可以在net.minecraft.data中找到Root,這裡有LootTableProvider和BlockLootSubProvider,用來為方塊添加掉落物品。這些內容都是可以學習和應用的,也可以直接使用,比如你想為方塊、實體等添加掉落物品,都可以直接使用。
在Block中,我們可以看到有LootTableProvider,用於創建通用的戰利品表,還有用於給方塊添加戰利品的BlockLootSubProvider。因為這些很常用,所以它們被單獨實現了,可以直接使用。原版有註冊自己的東西,這些戰利品表也可以使用,它們實現了Provider,繼承即可直接使用。
我們直接拿過來,然後新建JavaLootTables,然後擴展AnnilaBlockLte。在這裡我們可以看到,它告訴我們要重寫getKnownBlock方法。我們看看它是怎麼寫的,他使用了DeferRegister這樣寫出來的。我們也有我們的DefaultRegister,我們直接拷貝過來就好了。如果要使用這些塊,我們需要使用public對吧?這裡都可以給大家使用public,用了public之後,我們就可以在這裡選擇testMode,點擊blocks,再點擊getInterests,然後點擊stream,再點flatMap,::atEerator是吧?這一堆我們就直接複製好,然後這個ResistantObject我們也給它導入一下,最後給他return一下,好。然後我們就可以寫public void gentlemen,那我們這個小門肯定要掉落自己嘛,就dropSelfEstimmode,點smallDogBlock,點get,這樣就好了。
當然,如果你想要添加一些附屬條件,比如說需要精準採集什麼的,我們也可以去原本的這裡找一個類似的方塊,比如說我們的鐵礦,鐵礦的方法是createSilkTouchDispatchTable,好。這裡你就照著寫就行了,也不難啊。我們演示就直接用最簡單的就好了,其他東西基本上就不用我演示了。我就寫這幾個吧。
當然,這些其他的東西都像這個BlockStateLong都是可以用代碼生成的。不過這些大家就參考這個文檔,還有原版裡面那些類,看看原版是怎麼寫的,文檔是怎麼寫的,然後自己摸索摸索也就出來了,這個東西真的不難。當然,就算你實在搞不明白,你也可以手寫嘛。MC的wiki上也都有這個數據包和資源包的編寫教程,文件的各種結構都是寫好了,你就可以照著wiki去直接寫文件,也就不寫代碼了。好,這裡我們就可以去註冊我們的事件了。首先我們要wereGenerator等於event.getGenerator(),然後還需要一個變量叫packOutput等於這個generator.getPackOutput(),還需要existingFileHelper就是event.getExistingFileHelper(),然後還需要oneLookupProvider等於event.getLookupProvider()。總共需要這四個變量,我們現在來開始生成我們的文本。
這個就是GeneratorAndProvider,這裡需要的變量是event.includeServerAndClient(),選一個server是生成服務器端資源的,LT是生成客戶端資源的。像我們的這個tag,把我們的戰利品表,我們的合成表都是服務端資源,所以我們都要用這個includeServer。如果我們生成的是語言或者block states這種東西,我們就需要使用includeClient了,然後這裡直接傳入我們的text實例,第一個參數就是我們的packOutput,第二個參數就是我們的lookupProvider,第三個參數是我們的modId,第四個參數就是我們的這個existingFileHelper,這就結束了。
然後我們繼續atProviderEvent,點includeServer。我們在註冊我們的戰利品表NewLootTables。不過戰利品這裡我們要注意,因為我們用的不是這個LootTableProvider,我們用的是前面這些SubProvider,偶爾我們需要的是LootTableProvider,所以我們這裡需要寫new ImplicitListOfNewLootTableProvider,點subproviderIntro,然後使我們的lootTablesNewNoteContextPromSetsBlock。我們可以稍微換一下好,這一行太長了,看得都不太清楚。
這個東西寫完了以後,我們還需要給我們的recipes寫,那就GeneratorAndProviderEvent,點includeServerNewRecipesPoutput。最後,我們再把我們寫的東西都打開檢查一下,看看還有沒有什麼問題。比如說這裡有一個問題啊,就是這個getNoseBlock是讓我們這個類知道有哪些方塊需要生成戰利品表,但是我們這裡呢卻只給我們的這個smallDogBlock生成了戰利品表。而這裡呢我們把我們註冊的兩個block都包含進去了,如果它檢測的我們沒有為這個badBlock寫戰利品表,它就會崩潰。
因此,我們有兩種解決方法。一種是在此處再編寫一個myBlock的戰利品表,另一種是在這裡添加過濾器,點擊filter,然後進行過濾。這裡使用了一個Lambda表達式,為我們的block添加了一個過濾條件,因為我們只需為這一個生成,所以我們可以直接讓它等於該值即可。如果我們只想過濾掉myBlock,也可以這樣做,讓它不等於myBlock,這樣除了myBlock,其他都保持一致。由於我們只有兩個方塊,若有更多方塊,則需要根據需求來調整。修改後保存一下,檢查一下text和recipes部分。
在recipes中,發現了一個小問題,我使用了myBlock,但實際上應該生成小門,因此應該使用smallBlock。檢查無誤後選擇runData,然後運行,如果進程正常結束並且退出代碼為零,說明生成成功。如果退出代碼非零,則表示有錯誤,需要查看報錯信息。生成完成後,在generated文件中查看生成結果。首先會生成needsIronTop,為smallBlock生成合成表和戰利品表,以及smallBlock的配方解鎖方式。
然而,解鎖方式仍然依賴原版銅錠,這可能導致與其他mod不兼容。解決方法很簡單,在recipes中,將相關部分改為text,確保使用正確的標籤。重新生成後查看效果,確認是否生效。檢查後拿出銅錠測試,查看是否解鎖了新的配方,正常情況下應該能夠合成出小門。如果一切順利,表示操作成功,那麼本期教程就到這裡為止。