[JAVA] minecraft1.14.4 MOD-Entwicklungsmemorandum 1 [Originalrezept]

Einführung

Als ich anfing, MOD für minecraft1.14.4 zu entwickeln, hatte ich mit dem Mangel an Informationen über die Entwicklung zu kämpfen, daher möchte ich mir eine Notiz machen und (vielleicht) denen helfen, die MOD in Zukunft entwickeln. In diesem Artikel werde ich erklären, wann ein für MOD einzigartiges Rezeptformat erstellt wird.

Entwicklungsumgebung

Ausführung
OS Windows10
Forge 28.2.0
JDK AdoptOpenJDK 8u242-b08

Umgebung

Es gibt bereits einen Artikel, in dem die von einer anderen Person geschriebene Umgebungskonstruktion erläutert wird. Bitte beziehen Sie sich darauf.

↓ Dies ist ein Artikel, auf den ich mich beim Aufbau der Umgebung bezogen habe. Erstellung von Minecraft 1.14.4 Forge Mod Teil 1 [Vorbereitung der Entwicklungsumgebung IntelliJ IDEA] TNT Modders: Environment Construction

Originalrezept

Dies ist erforderlich, wenn Sie etwas tun möchten, das mit einem normalen Rezept nicht erreicht werden kann, z. B. zwei Elemente gleichzeitig verfeinern, um eine Legierung zu erhalten.

Verfahren

  1. Implementieren Sie die IRecipe-Schnittstelle
  2. Implementieren Sie einen Serializer, der Rezepte aus JSON-Dateien liest
  3. Registrieren Sie den Serializer bei Forge

Das grobe Verfahren ist so. Es ist notwendig, Kachelentitäten usw. zu betreiben, aber dieses Mal werden wir hauptsächlich das Lesen und Registrieren von Rezepten erklären.

Implementierung

Mir fiel nichts anderes ein, daher ist es mein Ziel, ein Rezept wie das oben beschriebene zu registrieren.

Lassen Sie es uns gemäß der Prozedur implementieren. Implementieren Sie zunächst die IRecipe-Schnittstelle . Der Inhalt ist so.

IRecipe.class


public interface IRecipe<C extends IInventory> {
    //Entspricht der Artikel im Inventar den Zutaten im Rezept?
    boolean matches(C var1, World var2);

    //Gibt eine Kopie des Herstellungsergebnisses zurück
    ItemStack getCraftingResult(C var1);

    //Ich bin mir darüber nicht sicher. Gibt basic true zurück
    boolean canFit(int var1, int var2);

    //Gibt das Ergebnis der Herstellung zurück
    ItemStack getRecipeOutput();

    default NonNullList<ItemStack> getRemainingItems(C p_179532_1_) {
        NonNullList<ItemStack> nonnulllist = NonNullList.withSize(p_179532_1_.getSizeInventory(), ItemStack.EMPTY);

        for(int i = 0; i < nonnulllist.size(); ++i) {
            ItemStack item = p_179532_1_.getStackInSlot(i);
            if (item.hasContainerItem()) {
                nonnulllist.set(i, item.getContainerItem());
            }
        }

        return nonnulllist;
    }

    default NonNullList<Ingredient> getIngredients() {
        return NonNullList.create();
    }

    default boolean isDynamic() {
        return false;
    }

    default String getGroup() {
        return "";
    }

    default ItemStack getIcon() {
        return new ItemStack(Blocks.CRAFTING_TABLE);
    }

    //Gibt die Rezept-ID zurück
    ResourceLocation getId();

    //Gibt einen Serializer zurück, der die zu erstellende JSON-Datei im Rezeptformat liest und schreibt
    IRecipeSerializer<?> getSerializer();

    //Gibt den Rezepttyp zurück
    IRecipeType<?> getType();
}

Da es notwendig ist, andere als Standard zu implementieren,

  • boolean mathes(C var1, World var2);
  • ItemStack getCraftingResult(C var1);
  • boolean canFit(int var1, int var2);
  • ItemStack getRecipeOutput();
  • ResourceLocation getId();
  • IRecipeSerializer<?> getSerializer();
  • IRecipeType<?> getType();

Wir werden die sieben umsetzen. Bei der Implementierung sieht der Code folgendermaßen aus.

ExampleRecipe.java


import com.google.common.collect.Lists;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.RecipeMatcher;

import java.util.List;

public class ExampleRecipe implements IRecipe<IInventory> {
    public static final IRecipeType<ExampleRecipe> RECIPE_TYPE = new IRecipeType<ExampleRecipe>() {
    };
    protected final ResourceLocation id;
    protected NonNullList<Ingredient> ingredients;
    protected ItemStack result;
    protected int cookTime;

    public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){
        this.id = id;
        this.ingredients = ingredients;
        this.result = result;
        this.cookTime = cookTime;
    }

    //Entspricht der Artikel im Inventar den Zutaten im Rezept?
    @Override
    public boolean matches(IInventory inventory, World world) {
        List<ItemStack> inputs = Lists.newArrayList();
        for(int index = 0; index < inventory.getSizeInventory(); ++index){
            ItemStack input = inventory.getStackInSlot(index);
            if(!input.isEmpty()){
                inputs.add(input);
            }
        }
        return RecipeMatcher.findMatches(inputs, this.ingredients) != null;
    }

    //Gibt eine Kopie des Herstellungsergebnisses zurück
    @Override
    public ItemStack getCraftingResult(IInventory inventory) {
        return this.result.copy();
    }

    //Ich bin mir darüber nicht sicher. Gibt basic true zurück
    @Override
    public boolean canFit(int i, int i1) {
        return true;
    }

    //Gibt das Ergebnis der Herstellung zurück
    @Override
    public ItemStack getRecipeOutput() {
        return this.result;
    }

    //Gibt die ID des Rezepts zurück (modid:recipe_name)
    @Override
    public ResourceLocation getId() {
        return this.id;
    }

    //Gibt den Rezepttyp zurück
    @Override
    public IRecipeType<?> getType() {
        return RECIPE_TYPE;
    }
}

Anders als match () und canFit () ist es eine Getter-Funktion, die den Inhalt des Rezepts zurückgibt, daher denke ich nicht, dass es schwierig ist.

mathes () verwendet RecipeMatcher.findMatches (), um festzustellen, ob die Elemente im Inventar mit dem Rezept übereinstimmen.

Ich weiß nicht, wann canFit () verwendet wird, aber soweit ich den Vanille-Code sehen kann, habe ich nichts anderes getan, als true zurückzugeben. Ich weiß auch nicht, warum es zwei Funktionen gibt, die handwerkliche Ergebnisse zurückgeben. Dies bezieht sich auch auf Vanille und gibt eine Kopie des Ergebnisses zurück.

Übrigens habe ich getSerializer () nicht implementiert, da ich den Return Serializer noch nicht implementiert habe. Fügen Sie es hinzu, wenn Sie die Implementierung des Serializers abgeschlossen haben.

import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.util.JSONUtils;

public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{
        public Serializer(){
            this.setRegistryName(new ResourceLocation(modId, "serializer_name"));
        }

        private static NonNullList<Ingredient> readIngredients(JsonArray array){
            NonNullList<Ingredient> ingredients = NonNullList.create();
            for(JsonElement element: array){
                ingredients.add(CraftingHelper.getIngredient(element));
            }
            if(ingredients.isEmpty()){
                throw new JsonParseException("No ingredients for smelting recipe");
            }
            return ingredients;
        }
        
        @Override
        public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) {
            ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true);
            NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients"));
            int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50);
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        //Zur Kommunikation mit dem Server
        @Nullable
        @Override
        public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) {
            final int index = packetBuffer.readVarInt();
            NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY);
            for(int col = 0; col < index; ++col){
                ingredients.set(col, Ingredient.read(packetBuffer));
            }
            ItemStack result = packetBuffer.readItemStack();
            int cookTime = packetBuffer.readVarInt();
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        //Zur Kommunikation mit dem Server
        @Override
        public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) {
            packetBuffer.writeVarInt(exampleRecipe.ingredients.size());
            for (Ingredient ingredient : exampleRecipe.ingredients) {
                ingredient.write(packetBuffer);
            }
            packetBuffer.writeItemStack(exampleRecipe.result);
            packetBuffer.writeVarInt(exampleRecipe.cookTime);
        }
    }

Hier ist der Serializer-Code. Es gibt zwei read (), aber der Unterschied besteht darin, dass der erste von JSON und der zweite von Netzwerkpaketen stammt.

Im Konstruktor wird die ID diesem Serializer zugewiesen. Diese ID ist der Typ, den Sie beim Definieren des Rezepts in der JSON-Datei angeben. Ersetzen Sie modId und serializer_name entsprechend.

readIngredients () ist eine selbst erstellte Funktion, da JSONUtils keine Funktion zum Lesen mehrerer Ingredients hat.


Hinweis

Eine Sache, die Sie beachten sollten, ist die Reihenfolge, in der sie gelesen werden. Das Lesen von JSON hat keine Reihenfolge, sodass Sie sich darüber keine Sorgen machen müssen. Wenn Sie jedoch nicht in der Reihenfolge aus dem Paket lesen, in der Sie es mit write () geschrieben haben, wird beim Anmelden am Server ein Kommunikationsfehler angezeigt. Dies liegt daran, dass das Lesen aus dem Paket von Anfang an in der richtigen Reihenfolge durchgeführt wird. Es ist in Ordnung, um offline zu spielen (da es nur von JSON liest), aber es passt nicht auf den Server, auf dem dieser Fehler auftritt. Dies ist fatal, achten Sie also genau auf die Bestellung.


Jetzt möchte ich den implementierten Serializer in ExampleRecipe.java einfügen.

ExampleRecipe.java


import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.common.util.RecipeMatcher;
import net.minecraftforge.registries.ForgeRegistryEntry;

import javax.annotation.Nullable;
import java.util.List;

public class ExampleRecipe implements IRecipe<IInventory> {
    public static final IRecipeType<ExampleRecipe>
    public static final Serializer SERIALIZER = new Serializer();
    RECIPE_TYPE = new IRecipeType<ExampleRecipe>() {
    };
    protected final ResourceLocation id;
    protected NonNullList<Ingredient> ingredients;
    protected ItemStack result;
    protected int cookTime;

    public ExampleRecipe(ResourceLocation id, NonNullList<Ingredient> ingredients, ItemStack result, int cookTime){
        this.id = id;
        this.ingredients = ingredients;
        this.result = result;
        this.cookTime = cookTime;
    }

    //Entspricht der Artikel im Inventar den Zutaten im Rezept?
    @Override
    public boolean matches(IInventory inventory, World world) {
        List<ItemStack> inputs = Lists.newArrayList();
        for(int index = 0; index < inventory.getSizeInventory(); ++index){
            ItemStack input = inventory.getStackInSlot(index);
            if(!input.isEmpty()){
                inputs.add(input);
            }
        }
        return RecipeMatcher.findMatches(inputs, this.ingredients) != null;
    }

    //Gibt eine Kopie des Herstellungsergebnisses zurück
    @Override
    public ItemStack getCraftingResult(IInventory inventory) {
        return this.result.copy();
    }

    //Ich bin mir darüber nicht sicher. Gibt basic true zurück
    @Override
    public boolean canFit(int i, int i1) {
        return true;
    }

    //Gibt das Ergebnis der Herstellung zurück
    @Override
    public ItemStack getRecipeOutput() {
        return this.result;
    }

    //Gibt die ID des Rezepts zurück (modid:recipe_name)
    @Override
    public ResourceLocation getId() {
        return this.id;
    }

    //Gibt den Rezepttyp zurück
    @Override
    public IRecipeType<?> getType() {
        return RECIPE_TYPE;
    }

    //Geben Sie den Serializer zurück
    @Override
    public IRecipeSerializer<?> getSerializer() {
        return SERIALIZER;
    }

    public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<ExampleRecipe>{
        public Serializer(){
            this.setRegistryName(new ResourceLocation(modId, "serializer_name"));
        }

        private static NonNullList<Ingredient> readIngredients(JsonArray array){
            NonNullList<Ingredient> ingredients = NonNullList.create();
            for(JsonElement element: array){
                ingredients.add(CraftingHelper.getIngredient(element));
            }
            if(ingredients.isEmpty()){
                throw new JsonParseException("No ingredients for smelting recipe");
            }
            return ingredients;
        }

        @Override
        public ExampleRecipe read(ResourceLocation recipeId, JsonObject jsonObject) {
            ItemStack result = CraftingHelper.getItemStack(JSONUtils.getJsonObject(jsonObject, "result"), true);
            NonNullList<Ingredient> ingredients = readIngredients(JSONUtils.getJsonArray(jsonObject, "ingredients"));
            int cookTime = JSONUtils.getInt(jsonObject, "process_time", 50);
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        @Nullable
        @Override
        public ExampleRecipe read(ResourceLocation recipeId, PacketBuffer packetBuffer) {
            final int index = packetBuffer.readVarInt();
            NonNullList<Ingredient> ingredients = NonNullList.withSize(index, Ingredient.EMPTY);
            for(int col = 0; col < index; ++col){
                ingredients.set(col, Ingredient.read(packetBuffer));
            }
            ItemStack result = packetBuffer.readItemStack();
            int cookTime = packetBuffer.readVarInt();
            return new ExampleRecipe(recipeId, ingredients, result, cookTime);
        }

        @Override
        public void write(PacketBuffer packetBuffer, ExampleRecipe exampleRecipe) {
            packetBuffer.writeVarInt(exampleRecipe.ingredients.size());
            for (Ingredient ingredient : exampleRecipe.ingredients) {
                ingredient.write(packetBuffer);
            }
            packetBuffer.writeItemStack(exampleRecipe.result);
            packetBuffer.writeVarInt(exampleRecipe.cookTime);
        }
    }
}

Dies ist die endgültige Form von Code, die die IRecipe -Schnittstelle erbt und Ihre eigenen Rezepte erstellt. GetSerializer () und Variablen wurden zu ExampleRecipe.java hinzugefügt.

Registrieren Sie danach den Serializer in Forge und beenden Sie den Vorgang.

RegisterRecipeTypes.java


import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

@Mod.EventBusSubscriber(modid = modId, bus = Mod.EventBusSubscriber.Bus.MOD)
public class RegisterRecipeTypes {
    @SubscribeEvent
    public static void registerRecipeSerializer(RegistryEvent.Register<IRecipeSerializer<?>> event){
        event.getRegistry().register(ExampleRecipe.SERIALIZER);
    }
}

Definieren Sie ein Rezept

Definieren Sie abschließend das Rezept in dem diesmal erstellten Rezeptformat.

example.json


{
  "type": "modId:serializer_name",
  "ingredients": [
    {
       "item": "itemId"
    },
    {
       "item": "itemId"
    }
  ]
  "result": "itemId"
  "process_time": 100
}

abschließend

Ich weiß nicht, wie man eine Kachelentität erstellt, daher beende ich die Erklärung mit einem Link zu einer Site, die hilfreich zu sein scheint.

Beispiel Mod von Mr. Cadiboo veröffentlicht

↑ Dies ist ein Repository für MOD-Tutorials, die von Ausländern erstellt wurden. Obwohl es auf Englisch ist, ist die Erklärung des Codes geschrieben, also schauen Sie bitte.