跳转至内容

[原创][1.20.1][EidolonJS开发实录]基于链式调用达成在Schema注册中使用回调函数构建配方

灵感大王
2 2 67 1
  • 项目地址:PickAID/EidolonJS: as the name says


    在为Eidolon中的坩埚注册相应的Schema时,我发现了一个问题:

    坩埚配方的输入并不能够使用简单的RecipeKey添加,相关的代码如下:

    public static class Step {
            public final List<Ingredient> matches = new ArrayList<>();
            public final int stirs;
    
            public Step(int stirs, List<Ingredient> matches) {
                this.stirs = stirs;
                this.matches.addAll(matches);
            }
        }
    

    而最初一版的RecipeKey与Schema设计如下:

    public interface CrucibleSchema {
        RecipeKey<OutputItem> OUTPUT = ItemComponents.OUTPUT_ID_WITH_COUNT.key("result");
        RecipeKey<OutputItem[]> OUTPUT = ItemComponents.OUTPUT_ARRAY.key("result");
    
        RecipeComponentBuilder STEP_BUILDER = new RecipeComponentBuilder(2)
                .add(NumberComponent.INT.key("stirs").optional(1))
                .add(NumberComponent.INT.key("stirs").defaultOptional())
                .add(ItemComponents.INPUT_ARRAY.key("items"));
        RecipeKey<RecipeComponentBuilderMap[]> STEPS = STEP_BUILDER.inputRole().asArray().key("steps");
        RecipeSchema SCHEMA = new RecipeSchema(OUTPUT, STEPS);
    

    进行注册后简单对代码进行测试:

    ServerEvents.recipes(event => {
        event.recipes.eidolon.crucible("2x stone", [
            {stirs: 1, items: ["#forge:dusts/redstone", "2x eidolon:soul_shard"]},
            {items: ["#forge:dusts/redstone", "eidolon:soul_shard"]},
            {stirs: 1},
            {stirs: 3}
        ])
    }
    

    配方的确可以正常注册,但是可以看到,Step部分的格式与json几乎一致,不够优雅。
    于是笔者开始尝试在RecipeKey中使用回调函数,然而失败了(也有可能是我菜),
    而我在阅读了SummoningRitual的源代码之后(AlmostReliable/summoningrituals at 1.20.1-forge)有了不一样的想法。
    或许可以使用链式调用形式的配方,并在链式调用的方法中使用回调函数。
    完整的代码实现如下:

    //篇幅原因省去import
    
    //CrucibleSchema.java
    public interface CrucibleSchema {
        @FunctionalInterface
        interface StepBuilderCallback {
            void apply(StepBuilderJS builder);
        }
        class CrucibleRecipeJS extends RecipeJS {
            public CrucibleRecipeJS steps(StepBuilderCallback callback) {
                var builder = new StepBuilderJS();
                callback.apply(builder);
                setValue(STEPS, builder.getStepList().toArray(CrucibleRecipe.Step[]::new));
                return this;
            }
    
    
        }
    
        RecipeKey<CrucibleRecipe.Step[]> STEPS = new RecipeComponent<CrucibleRecipe.Step[]>() {
            @Override
            public Class<?> componentClass() {
                return CrucibleRecipe.Step[].class;
            }
    
            @Override
            public JsonElement write(RecipeJS recipe, CrucibleRecipe.Step[] value) {
                if (value == null) {
                    return null;
                }
    
                JsonArray stepsArray = new JsonArray();
                for (CrucibleRecipe.Step step : value) {
                    JsonObject stepObj = new JsonObject();
    
                    // Add stirs
                    stepObj.addProperty("stirs", step.stirs);
    
                    // Add items
                    if (!step.matches.isEmpty()) {
                        JsonArray itemsArray = new JsonArray();
                        for (Ingredient ingredient : step.matches) {
                            itemsArray.add(ingredient.toJson());
                        }
                        stepObj.add("items", itemsArray);
                    }
    
                    stepsArray.add(stepObj);
                }
    
                return stepsArray;
            }
    
            @Override
            public CrucibleRecipe.Step[] read(RecipeJS recipe, Object from) {
                if (from instanceof JsonElement) {
                    JsonArray stepsArray = ((JsonElement) from).getAsJsonArray();
                    List<CrucibleRecipe.Step> steps = new ArrayList<>();
    
                    for (JsonElement element : stepsArray) {
                        if (!element.isJsonObject()) {
                            throw new JsonSyntaxException("Each step must be a JSON object");
                        }
    
                        JsonObject stepObj = element.getAsJsonObject();
                        int stirs = stepObj.has("stirs") ? stepObj.get("stirs").getAsInt() : 0;
    
                        List<Ingredient> ingredients = new ArrayList<>();
                        if (stepObj.has("items")) {
                            JsonArray itemsArray = stepObj.getAsJsonArray("items");
                            for (JsonElement item : itemsArray) {
                                ingredients.add(Ingredient.fromJson(item));
                            }
                        }
    
                        steps.add(new CrucibleRecipe.Step(stirs, ingredients));
                    }
    
                    return steps.toArray(CrucibleRecipe.Step[]::new);
                } else {
                    throw new JsonSyntaxException("Each step must be a JSON object");
                }
            }
        }.key("steps").noBuilders();
    
        RecipeKey<OutputItem> OUTPUT = ItemComponents.OUTPUT.key("result").noBuilders();
    
        RecipeSchema SCHEMA = new RecipeSchema(CrucibleRecipeJS.class, CrucibleRecipeJS::new, OUTPUT, STEPS).constructor(((recipe, schemaType, keys, from) -> {
            recipe.setValue(OUTPUT, from.getValue(recipe, OUTPUT));
            recipe.setValue(STEPS, new CrucibleRecipe.Step[0]);
        }), OUTPUT);
    
    }
    
    
    //StepBuilderJS.java
    public class StepBuilderJS {
        List<CrucibleRecipe.Step> stepList = new ArrayList<>();
        List<Ingredient> item = new ArrayList<>();
        int stirs = 0;
    
        public StepBuilderJS stirs(int count) {
            stirs = count;
            return this;
        }
    
        public StepBuilderJS stirs() {
            return stirs(1);
        }
    
        public StepBuilderJS items(Ingredient... items) {
            item.addAll(List.of(items));
            return this;
        }
    
        public void step(int count, Ingredient... items) {
            var step = new CrucibleRecipe.Step(count, item);
            stepList.add(step);
        }
    
        public void build(){
            var step = new CrucibleRecipe.Step(stirs, item);
            stepList.add(step);
            stirs(0);
            item.clear();
        }
    
        @HideFromJS
        public List<CrucibleRecipe.Step> getStepList() {
            return stepList;
        }
    }
    

    最终实现的效果如下:

    event.recipes.eidolon.crucible("2x stone").steps((step) => {
            step.stirs(1).items("#forge:dusts/redstone", "eidolon:soul_shard").build()
            step.items("#forge:dusts/redstone","eidolon:soul_shard").build()
            step.stirs(3).build()
        })
    

    或许直接使用链式调用传入每一步的设计更为直接,但是出于个人偏好,笔者仍然选择使用回调函数的形式处理这部分。
    (//TODO 代码详解施工中)

  • 是幻梦吉斯,我们有救了❤


相关推荐


  • [原创][KubeJS]利用事件监听制作弹幕武器

    灵感大王 kubejs
    7
    3 赞同
    7 帖子
    318 浏览
    不是客服M
    flowchart TD A((玩家空挥)) --> B{是否为特定武器} B -->|是| C["计算发射数据 发包服务端<br/>(坐标,弹幕实体id)"] B -->|不是| D["服务端生成相应弹幕"] C --> D D --> E((结束))
  • 养老向的物品抽奖机:虚空草莓

    灵感大王 kubejs 作物 forge
    2
    0 赞同
    2 帖子
    72 浏览
    忆然
    呜,硬编码名称
  • 1 赞同
    1 帖子
    171 浏览
    忆然
    本文使用:CC-BY-NC-SA 4.0协议 kjs本身提供的物品注册比较有限,在注册某些kjs未提供的item的时候我们就需要用到createCustom 如果有需要的item注册可以在本文下方留言,会考虑更新 createCustom注册model是没有的,也就是你还得去写一份model.json(这部分可以参考原版wiki) 下面是使用createCustom去注册一个弓的例子 let $BowItem = Java.loadClass("net.minecraft.world.item.BowItem") let $Item$Properties = Java.loadClass("net.minecraft.world.item.Item$Properties") StartupEvents.registry("item", event => { event.createCustom("modid:item_name", () => { let properties = new $Item$Properties() //修改耐久 为0则无耐久属性 properties.durability(0) //修改最大堆叠数量 properties.stacksTo(1) //修改稀有度 properties.rarity("epic") //创建新的bowitem let item = new $BowItem(properties) //返回新的bowitem进行注册 return item }) })