LoginSignup
0
0

More than 3 years have passed since last update.

minecraft1.7.10左右を入れ替えたときに別のアイテムを出すクラフトレシピを登録したい

Last updated at Posted at 2020-11-21

環境

minecraft 1.7.10

多分他のバージョンでも同じ。それこそ1.2.5とかでも

背景

例えばしたの2つの例を見てほしい。このように左右逆に並べた時に別々のアイテムを作るレシピを登録したいとする。

image.png image.png

何も考えずに
1.7のレシピ追加 - Minecraft Modding Wiki
を真似して

package com.example.examplemod;

import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
@Mod(modid = ExampleMod.MODID, version = ExampleMod.VERSION)
public class ExampleMod
{
    public static final String MODID = "examplemod";
    public static final String VERSION = "1.0";

    @EventHandler
    public void init(FMLInitializationEvent event)
    {
        GameRegistry.addShapedRecipe(new ItemStack(Blocks.stone),
                "WA",
                'A',Blocks.dirt,
                'W',Blocks.grass);
        GameRegistry.addShapedRecipe(new ItemStack(Blocks.cobblestone),
                "AW",
                'A',Blocks.dirt,
                'W',Blocks.grass);
    }
}

のようにすると思う。このとき、予想に反して両方とも焼石が生成されてしまう。

原因

左右反転レシピが自動的に登録される仕様がMinecraftにはある。このよけいなおせっかい機能が原因である。

そもそもGameRegistry.addShapedRecipeがどう実装されているかというと
https://gitlab.zveronline.ru/Mirrors/Minecraft/MinecraftForge/-/blob/1.7.10/fml/src/main/java/cpw/mods/fml/common/registry/GameRegistry.java#L248-251

    public static IRecipe addShapedRecipe(ItemStack output, Object... params)
    {
        return CraftingManager.getInstance().addRecipe(output, params);
    }

net.minecraft.item.crafting.CraftingManagerに丸投げされている。

これをデコンパイルして追跡した先人たち曰く、内部でいろいろ処理された後、net.minecraft.item.crafting.ShapedRecipesのインスタンスが作られるのだそうだ。この中にmatchesというメソッドがある。でコンパイルした結果を読むと、matchesが呼び出しているcheckMatchというprivate methodの第4引数にtrueが渡るときとfalseが渡るときがあり、先にtrueのときが処理されている。反転が先に判定される状態だ。これを書き換えることでなんとかできそうだ。

そこでShapedRecipesをoverrideしてmatchesを書き換えることにする。ただ、checkMatchがprivate methodで呼び出せないので仕方ないからデコンパイルしたものを持ってきた。

解決方法

デコンパイルしたコードを結構引っ張ってきたようなコードとなる

NonReversedShapedRecipe.java
package com.example.examplemod;

import java.util.HashMap;

import net.minecraft.block.Block;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ShapedRecipes;
import net.minecraft.world.World;
import cpw.mods.fml.common.registry.GameRegistry;

public class NonReversedShapedRecipe {
    public static ShapedRecipes addShapedRecipe(ItemStack output, Object... params) {
        String s = "";
        int i = 0;
        int j = 0;
        int k = 0;

        if (params[i] instanceof String[]) {
            String[] astring = (String[]) ((String[]) params[i++]);

            for (int l = 0; l < astring.length; ++l) {
                String s1 = astring[l];
                ++k;
                j = s1.length();
                s = s + s1;
            }
        } else {
            while (params[i] instanceof String) {
                String s2 = (String) params[i++];
                ++k;
                j = s2.length();
                s = s + s2;
            }
        }

        HashMap<Character, ItemStack> hashmap;

        for (hashmap = new HashMap<>(); i < params.length; i += 2) {
            Character character = (Character) params[i];
            ItemStack itemstack1 = null;

            if (params[i + 1] instanceof Item) {
                itemstack1 = new ItemStack((Item) params[i + 1]);
            } else if (params[i + 1] instanceof Block) {
                itemstack1 = new ItemStack((Block) params[i + 1], 1, 32767);
            } else if (params[i + 1] instanceof ItemStack) {
                itemstack1 = (ItemStack) params[i + 1];
            }

            hashmap.put(character, itemstack1);
        }

        ItemStack[] aitemstack = new ItemStack[j * k];

        for (int i1 = 0; i1 < j * k; ++i1) {
            char c0 = s.charAt(i1);

            if (hashmap.containsKey(Character.valueOf(c0))) {
                aitemstack[i1] = ((ItemStack) hashmap.get(Character.valueOf(c0))).copy();
            } else {
                aitemstack[i1] = null;
            }
        }

        ShapedRecipes shapedrecipes = new ShapedRecipes(j, k, aitemstack, output) {
            @Override
            public boolean matches(InventoryCrafting inv, World worldIn) {
                for (int i = 0; i <= 3 - this.recipeWidth; ++i) {
                    for (int j = 0; j <= 3 - this.recipeHeight; ++j) {
                        // When you uncomment this, you can get vanilla's behavior.
                        // if (this.checkMatch(inv, i, j, true)) {
                        //  return true;
                        // }

                        if (this.checkMatch(inv, i, j, false)) {
                            return true;
                        }

                    }
                }
                return false;
            }

            private boolean checkMatch(InventoryCrafting inv, int i, int j, boolean p_77573_4_) {
                for (int k = 0; k < 3; ++k) {
                    for (int l = 0; l < 3; ++l) {
                        int i1 = k - i;
                        int j1 = l - j;
                        ItemStack itemstack = null;

                        if (i1 >= 0 && j1 >= 0 && i1 < this.recipeWidth && j1 < this.recipeHeight) {
                            if (p_77573_4_) {
                                itemstack = this.recipeItems[this.recipeWidth - i1 - 1 + j1 * this.recipeWidth];
                            } else {
                                itemstack = this.recipeItems[i1 + j1 * this.recipeWidth];
                            }
                        }

                        ItemStack itemstack1 = inv.getStackInRowAndColumn(k, l);

                        if (itemstack1 != null || itemstack != null) {
                            if (itemstack1 == null && itemstack != null || itemstack1 != null && itemstack == null) {
                                return false;
                            }

                            if (itemstack.getItem() != itemstack1.getItem()) {
                                return false;
                            }

                            if (itemstack.getItemDamage() != 32767
                                    && itemstack.getItemDamage() != itemstack1.getItemDamage()) {
                                return false;
                            }
                        }
                    }
                }

                return true;
            }
        };
        GameRegistry.addRecipe(shapedrecipes);
        return shapedrecipes;
    }
}

なんとこの記事を書いている今日、はじめてまともにJavaに触ったのだが、ShapedRecipes shapedrecipes = new ShapedRecipes(j, k, aitemstack, output) {以下の部分は匿名(無名)クラスという機能らしい。ここでは無名のShapedRecipesを継承したクラス作ってることになる。

あとはGameRegistry.addShapedRecipeNonReversedShapedRecipe.addShapedRecipeに置き換えるだけでよい。

image.png
image.png

ちゃんと左右を入れ替えたときに別のアイテムを出すことができた

謝辞

この記事は解析を担当した某Discord鯖のE氏を始めとする方々の成果を私が記事としてまとめたものです。サンプルコードが動くことは自力で再現していますがそれ以上のことは私は行っていません。解析に感謝します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0