[MC1.12]MOD製作チュートリアル > レシピの追加

概要

レシピを追加する。
前のチュートリアルまで実装している段階でのコードである。

ソースコード

残念!ソースコードの追加実装などいらないのだよ!!
あくまでも前のチュートリアルまで実装できていればだが、レシピの実装はjson側で行うため追加コードは必要ない。

アセット

  • aluminium_block.json[recipe]
src/main/resources/assets/aluminiummod/recipes/に配置する
{
  "type": "minecraft:crafting_shaped",
  "pattern": [
    "###",
    "###",
    "###"
  ],
  "key": {
    "#": {
      "item": "aluminiummod:aluminium"
    }
  },
  "result": {
    "item": "aluminiummod:aluminium_block"
  }
}
 

  • aluminium.json[recipe]
{
  "type": "minecraft:crafting_shapeless",
  "ingredients": [
    {
      "item": "aluminiummod:aluminium_block"
    }
  ],
  "result": {
    "item": "aluminiummod:aluminium",
    "count":9
  }
}
 


解説

MC1.12より、今までのレシピの登録方法が非推奨となり、代わりにjson形式でレシピを登録することになった。
その為、バニラにレシピを追加するだけのMODなら最悪@Modをつけたクラスを作成するだけでjavaに触れるのは終わる。

名称はそのままforge側に登録され、/recipeコマンドで使われるため作られるアイテムのレジスター名にするのが最適である。
まず、type要素でクラフトタイプを指定する。"minecraft:crafting_shaped"で定形レシピ、minecraft:crafting_shapelessで不定形レシピを指定する。
次に、定形レシピではpatternでレシピの形を指定し、その文字に当てはまるアイテムをkeyで指定する。
不定形レシピではingredientsで指定する。
最後に、resultで結果を登録する。
それぞれ : で区切ってある前がMapのkey、後ろがvalueであると考えれば良い。
item要素で<modid>:<レジスター名>、count要素で個数、data要素でメタデータ(アイテムなのでblockstateは登録できない)を指定する。
アイテムの種類を増やしたいときはkey若しくはingredientsを下のように変更すれば良い。
+ ...
  • 定形
"key": {
    "#": {
      "item": "aluminiummod:aluminium"
    },
    "A": {
      "item": "minecraft:skull",
      "data": 4
  },
 

    • 不定形
"ingredients": [
    {
      "item": "aluminiummod:aluminium_block"
    },
    {
      "item": "minecraft:skull",
      "data": 4
  ],
 


レシピ追加通知

MC1.12からの新機能として、「レシピブック」がある。
また、バニラのアイテムは入手したとき右上に「新規レシピ追加」という通知が出る。
今回はアイテムを大量に追加するMOD用に「レシピブック」とイベントを使って通知を出す。
(進捗のシステムを使ってレシピを出すことも出来るが、書くべきjsonファイル数が多くなる。そのやり方は進捗の追加チュートリアルで記載予定。)
+ ...

AluminiumMod.java

クラス末尾に追加する。
@Mod.Instance("aluminiummod")
    public static AluminiumMod aluminiumInstance;
 
    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {
        HOLDER.register();
    }
 
    //アイテムを拾ったときのイベント。
    @SubscribeEvent
    public void onPickupItem(EntityItemPickupEvent event) {
        this.aluminiumUnlockRecipes(event.getItem().getItem(), event.getEntityPlayer());
    }
 
    //コンテナを閉じたとき(チェストやプレイヤーインベントリなど)のイベント。
    @SubscribeEvent
    public void onCloseContainer(PlayerContainerEvent.Close event) {
        for (ItemStack itemStack : event.getEntityPlayer().inventoryContainer.getInventory()) {
            this.aluminiumUnlockRecipes(itemStack, event.getEntityPlayer());
        }
    }
 
    private void aluminiumUnlockRecipes(ItemStack stack, EntityPlayer player) {
        if (FMLCommonHandler.instance().getSide().isClient()) {
            Item item = stack.getItem();
            int meta = stack.getMetadata();
            ItemStack itemStack = new ItemStack(item, 1, meta);
            //もしレシピを保持するリストに合致すれば
            if (!this.HOLDER.map.isEmpty() && this.HOLDER.map.containsKey(itemStack)) {
                List<ResourceLocation> list = this.HOLDER.map.get(itemStack);
                //player.unlockRecipes(ResourceLocation[] locations)でレシピブックに追加する。
                player.unlockRecipes(list.toArray(new ResourceLocation[list.size()]));
            }
        }
    }
 

AluminiumRecipeHolder.java

レシピをリソースから読み取って保持するクラス。
package com.tntmodders.tutorial;
 
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.FMLCommonHandler;
 
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
 
public class AluminiumRecipeHolder {
    //ItemStackよりそのアイテムが鍵となるレシピを取得できるようにする。
    public static final Map<ItemStack, List<ResourceLocation>> map = new ItemStackHashMap();
 
    public void register() {
        if (FMLCommonHandler.instance().getSide().isClient()) {
            //assets/<modid>/recipes/よりリソースを取得する。
            this.getResource("assets/aluminiummod/recipes/");
        }
    }
 
    public void getResource(String path) {
        ClassLoader loader = AluminiumMod.class.getClassLoader();
        URL url = loader.getResource(path);
        //jarファイル内か否かで処理が変化する。
        if (url.getProtocol().equals("jar")) {
            String[] strings = url.getPath().split(":");
            String leadPath = strings[strings.length - 1].split("!")[0];
            File f = new File(leadPath);
            JarFile jarFile;
            try {
                //jarファイル自体を取得する。(zipファイル・jarファイルとして扱う事ができる。)
                jarFile = new JarFile(f);
                Enumeration<JarEntry> enumeration = jarFile.entries();
                while (enumeration.hasMoreElements()) {
                    JarEntry entry = enumeration.nextElement();
                    String s = entry.getName();
                    if (s != null && s.startsWith(path) && s.endsWith(".json")) {
                        InputStream stream = null;
                        try {
                            stream = loader.getResourceAsStream(s);
                            //inputstreamを使ってjarファイル内のjsonを読み込む。
                            this.readStream(stream, s);
                            stream.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            List<File> list = this.getListFile(path);
            if (list.size() > 0) {
                for (File recipe : list) {
                    InputStream stream = null;
                    try {
                        stream = new FileInputStream(recipe);
                        this.readStream(stream, recipe.getName());
                        stream.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
 
    //ファイルを全て取得する。これもjarか否かによって変わる。
    private List<File> getListFile(String path) {
        List<File> files = new ArrayList<>();
        ClassLoader loader = AluminiumMod.class.getClassLoader();
        URL url = loader.getResource(path);
        if (url.getProtocol().equals("jar")) {
            String[] strings = url.getPath().split(":");
            String leadPath = strings[strings.length - 1].split("!")[0];
            File f = new File(leadPath);
            JarFile jarFile;
            try {
                jarFile = new JarFile(f);
                Enumeration<JarEntry> enumeration = jarFile.entries();
                while (enumeration.hasMoreElements()) {
                    JarEntry entry = enumeration.nextElement();
                    String s = entry.getName();
                    if (s != null && s.startsWith(path) && s.endsWith(".json")) {
                        files.add(new File(loader.getResource(s).getPath()));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            File packFile = FMLCommonHandler.instance().findContainerFor(AluminiumMod.aluminiumInstance).getSource();
            File newFile = new File(packFile.toURI().getPath() + path);
            files = Arrays.asList(newFile.listFiles());
        }
        return files;
    }
 
    private void readStream(InputStream stream, String name) {
        //inputstreamよりJSONを読み込む。
        JsonReader reader = new JsonReader(new InputStreamReader(stream));
        JsonObject jsonObject = new Gson().fromJson(reader, JsonObject.class);
        //文字列の中にパスが紛れ込んだ場合それを消す。また、".json"を抜いてResourceLocationとして保存する。
        ResourceLocation location = new ResourceLocation("aluminiummod", name.replaceAll("assets/aluminiummod/recipes/", "")
                .replaceAll(".json", ""));
 
        //定形レシピでキーを"#"にしたアイテムを鍵とする。
        if (jsonObject.has("key") && jsonObject.getAsJsonObject("key").has("#")) {
            Item item = Item.getByNameOrId(jsonObject.getAsJsonObject("key").getAsJsonObject("#").get("item").getAsString());
            int i = 0;
            if (jsonObject.getAsJsonObject("key").getAsJsonObject("#").has("data")) {
                i = jsonObject.getAsJsonObject("key").getAsJsonObject("#").get("data").getAsInt();
            }
            ItemStack stack = new ItemStack(item, 1, i);
            List<ResourceLocation> locations = map.containsKey(stack) ? map.get(stack) : new ArrayList<ResourceLocation>();
            locations.add(location);
            map.put(stack, locations);
        } 
        //不定形レシピで一番上に書いたアイテムを鍵とする。
        else if (jsonObject.has("ingredients") && jsonObject.getAsJsonArray("ingredients").get(0).getAsJsonObject().has("item")) {
            String s = jsonObject.getAsJsonArray("ingredients").get(0).getAsJsonObject().get("item").getAsString();
            Item item = Item.getByNameOrId(s);
            int i = 0;
            if (jsonObject.getAsJsonArray("ingredients").get(0).getAsJsonObject().has("data")) {
                i = jsonObject.getAsJsonArray("ingredients").get(0).getAsJsonObject().get("data").getAsInt();
            }
            ItemStack stack = new ItemStack(item, 1, i);
            List<ResourceLocation> locations = map.containsKey(stack) ? map.get(stack) : new ArrayList<ResourceLocation>();
            locations.add(location);
            map.put(stack, locations);
        }
    }
 
    //ItemStackを使ったマップを定義する。
    public static class ItemStackHashMap<K extends ItemStack, V extends List<ResourceLocation>> extends HashMap<K, V> {
 
        public V get(Object key) {
            if (key instanceof ItemStack && this.containsKey(key)) {
                for (Map.Entry<K, V> entry : this.entrySet()) {
                    if (entry.getKey().getItem() == ((ItemStack) key).getItem() && entry.getKey().getMetadata() == ((ItemStack) key).getMetadata()) {
                        return entry.getValue();
                    }
                }
            }
            return null;
        }
 
        @Override
        public boolean containsKey(Object key) {
            if (key instanceof ItemStack) {
                ItemStack itemStack = ((ItemStack) key);
                for (ItemStack stack : this.keySet()) {
                    if (stack.getItem() == itemStack.getItem() && stack.getMetadata() == itemStack.getMetadata()) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
 
 

コメント

この項目に関する質問などをどうぞ。
最終更新:2017年08月13日 23:30