diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java index 4f8e68a97..5bd11d54c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitCustomItem.java @@ -21,12 +21,12 @@ public class BukkitCustomItem extends AbstractCustomItem { private final Object item; private final Object clientItem; - public BukkitCustomItem(UniqueKey id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey, + public BukkitCustomItem(boolean isVanillaItem, UniqueKey id, Object item, Object clientItem, Key materialKey, Key clientBoundMaterialKey, List behaviors, List> modifiers, List> clientBoundModifiers, ItemSettings settings, Map>> events) { - super(id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events); + super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events); this.item = item; this.clientItem = clientItem; } @@ -64,6 +64,7 @@ public class BukkitCustomItem extends AbstractCustomItem { } public static class BuilderImpl implements Builder { + private boolean isVanillaItem; private UniqueKey id; private Key itemKey; private final Object item; @@ -80,6 +81,12 @@ public class BukkitCustomItem extends AbstractCustomItem { this.clientBoundItem = clientBoundItem; } + @Override + public Builder isVanillaItem(boolean is) { + this.isVanillaItem = is; + return this; + } + @Override public Builder id(UniqueKey id) { this.id = id; @@ -150,7 +157,7 @@ public class BukkitCustomItem extends AbstractCustomItem { public CustomItem build() { this.modifiers.addAll(this.settings.modifiers()); this.clientBoundModifiers.addAll(this.settings.clientBoundModifiers()); - return new BukkitCustomItem(this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors), + return new BukkitCustomItem(this.isVanillaItem, this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors), List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 9a060037f..219e20ef5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -17,7 +17,7 @@ import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.*; -import net.momirealms.craftengine.core.item.modifier.IdModifier; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.plugin.config.Config; @@ -70,10 +70,9 @@ public class BukkitItemManager extends AbstractItemManager { this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get(); this.registerCustomTrimMaterial(); this.loadLastRegisteredPatterns(); - ItemStack emptyStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.instance$ItemStack$EMPTY); - this.emptyItem = this.wrap(emptyStack); - this.emptyUniqueItem = new UniqueIdItem<>(UniqueKey.AIR, this.emptyItem); + this.emptyItem = this.factory.wrap(emptyStack); + this.emptyUniqueItem = UniqueIdItem.of(this.emptyItem); this.decoratedHashOpsGenerator = VersionHelper.isOrAbove1_21_5() ? (Function) FastNMS.INSTANCE.createDecoratedHashOpsGenerator(MRegistryOps.HASHCODE) : null; } @@ -158,13 +157,33 @@ public class BukkitItemManager extends AbstractItemManager { } } + @Override + public Item build(DatapackRecipeResult result) { + if (result.components() == null) { + ItemStack itemStack = createVanillaItemStack(Key.of(result.id())); + return wrap(itemStack).count(result.count()); + } else { + // 低版本无法应用nbt或组件,所以这里是1.20.5+ + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("id", result.id()); + jsonObject.addProperty("count", result.count()); + jsonObject.add("components", result.components()); + Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject) + .resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null); + if (nmsStack == null) { + return this.emptyItem; + } + return wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack)); + } + } + @Override public Optional> getVanillaItem(Key key) { ItemStack vanilla = createVanillaItemStack(key); if (vanilla == null) { return Optional.empty(); } - return Optional.of(new CloneableConstantItem(key, this.wrap(vanilla))); + return Optional.of(CloneableConstantItem.of(this.wrap(vanilla))); } @Override @@ -356,23 +375,10 @@ public class BukkitItemManager extends AbstractItemManager { @Override public @NotNull Item wrap(ItemStack itemStack) { - if (itemStack == null) return this.emptyItem; + if (itemStack == null || itemStack.isEmpty()) return this.emptyItem; return this.factory.wrap(itemStack); } - @Override - public Key itemId(ItemStack itemStack) { - Item wrapped = wrap(itemStack); - return wrapped.id(); - } - - @Override - public Key customItemId(ItemStack itemStack) { - Item wrapped = wrap(itemStack); - if (!wrapped.hasTag(IdModifier.CRAFT_ENGINE_ID)) return null; - return wrapped.id(); - } - @Override protected CustomItem.Builder createPlatformItemBuilder(UniqueKey id, Key materialId, Key clientBoundMaterialId) { Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(materialId)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java deleted file mode 100644 index 13fb185c6..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/CloneableConstantItem.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.momirealms.craftengine.bukkit.item; - -import net.momirealms.craftengine.core.item.BuildableItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.inventory.ItemStack; - -public class CloneableConstantItem implements BuildableItem { - private final Item item; - private final Key id; - - public CloneableConstantItem(Key id, Item item) { - this.item = item; - this.id = id; - } - - @Override - public Key id() { - return this.id; - } - - @Override - public Item buildItem(ItemBuildContext context, int count) { - return this.item.copyWithCount(count); - } - - @Override - public ItemStack buildItemStack(ItemBuildContext context, int count) { - return this.item.copyWithCount(count).getItem(); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java index 7b2ded9df..b042fe12c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/BukkitItemFactory.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.util.ItemTags; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.item.ExternalItemSource; import net.momirealms.craftengine.core.item.ItemFactory; -import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.setting.EquipmentData; @@ -66,6 +65,11 @@ public abstract class BukkitItemFactory> extend } } + @Override + protected boolean isEmpty(W item) { + return FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject()); + } + @SuppressWarnings("deprecation") @Override protected byte[] toByteArray(W item) { @@ -80,12 +84,15 @@ public abstract class BukkitItemFactory> extend @Override protected Key vanillaId(W item) { Object i = FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()); - if (i == null) return ItemKeys.AIR; + if (i == null) return null; return KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.method$Registry$getKey(MBuiltInRegistries.ITEM, i)); } @Override protected Key id(W item) { + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) { + return null; + } return customId(item).orElse(vanillaId(item)); } @@ -96,6 +103,9 @@ public abstract class BukkitItemFactory> extend @Override protected UniqueKey recipeIngredientID(W item) { + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(item.getLiteralObject())) { + return null; + } if (this.hasExternalRecipeSource) { for (ExternalItemSource source : this.recipeIngredientSources) { String id = source.id(item.getItem()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 21f37ec25..d5552f058 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -31,11 +31,6 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory { return item.remove(path); } - @Override - protected boolean isEmpty(LegacyItemWrapper item) { - return item.getItem().isEmpty(); - } - @Override protected Optional customId(LegacyItemWrapper item) { Object id = item.getJavaTag(IdModifier.CRAFT_ENGINE_ID); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index b0af3b91c..7ec945e9c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -6,7 +6,6 @@ import io.papermc.paper.potion.PotionMix; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.bukkit.item.CloneableConstantItem; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; @@ -14,795 +13,211 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitExcepti import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.MaterialUtils; -import net.momirealms.craftengine.bukkit.util.RecipeUtils; -import net.momirealms.craftengine.core.item.*; +import net.momirealms.craftengine.core.item.BuildableItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.recipe.*; -import net.momirealms.craftengine.core.item.recipe.Recipe; -import net.momirealms.craftengine.core.item.recipe.vanilla.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.*; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.event.HandlerList; -import org.bukkit.inventory.*; -import org.bukkit.inventory.recipe.CookingBookCategory; -import org.bukkit.inventory.recipe.CraftingBookCategory; +import org.bukkit.inventory.ItemStack; +import java.io.IOException; import java.io.Reader; import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; +import java.sql.Ref; import java.util.*; -import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; +// todo 在folia上替换recipe map使其线程安全 public class BukkitRecipeManager extends AbstractRecipeManager { private static BukkitRecipeManager instance; - // TODO 需要重构整个 recipe manager - - private static final Object MINECRAFT_RECIPE_MANAGER; - private static final Object MINECRAFT_RECIPE_MAP; - - static { - try { - MINECRAFT_RECIPE_MANAGER = CoreReflections.method$MinecraftServer$getRecipeManager.invoke(CoreReflections.method$MinecraftServer$getServer.invoke(null)); - MINECRAFT_RECIPE_MAP = VersionHelper.isOrAbove1_21_2() ? CoreReflections.field$RecipeManager$recipes.get(MINECRAFT_RECIPE_MANAGER) : null; - } catch (ReflectiveOperationException e) { - throw new ReflectionInitException("Failed to initialize recipe manager", e); - } - } - private static final Consumer MINECRAFT_RECIPE_REMOVER = VersionHelper.isOrAbove1_21_2() ? (id -> { Object resourceKey = toRecipeResourceKey(id); - FastNMS.INSTANCE.method$RecipeMap$removeRecipe(MINECRAFT_RECIPE_MAP, resourceKey); + FastNMS.INSTANCE.method$RecipeMap$removeRecipe(FastNMS.INSTANCE.field$RecipeManager$recipes(minecraftRecipeManager()), resourceKey); }) : (id -> { Object resourceLocation = KeyUtils.toResourceLocation(id); - FastNMS.INSTANCE.method$RecipeManager$removeRecipe(MINECRAFT_RECIPE_MANAGER, resourceLocation); + FastNMS.INSTANCE.method$RecipeManager$removeRecipe(minecraftRecipeManager(), resourceLocation); }); - private static final BiConsumer MINECRAFT_RECIPE_ADDER = + private static final BiFunction MINECRAFT_RECIPE_ADDER = VersionHelper.isOrAbove1_21_2() ? (id, recipe) -> { Object resourceKey = toRecipeResourceKey(id); Object recipeHolder = FastNMS.INSTANCE.constructor$RecipeHolder(resourceKey, recipe); - FastNMS.INSTANCE.method$RecipeManager$addRecipe(MINECRAFT_RECIPE_MANAGER, recipeHolder); + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipeHolder); + return recipeHolder; } : VersionHelper.isOrAbove1_20_2() ? (id, recipe) -> { Object resourceLocation = KeyUtils.toResourceLocation(id); Object recipeHolder = FastNMS.INSTANCE.constructor$RecipeHolder(resourceLocation, recipe); - FastNMS.INSTANCE.method$RecipeManager$addRecipe(MINECRAFT_RECIPE_MANAGER, recipeHolder); + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipeHolder); + return recipeHolder; } : (id, recipe) -> { - FastNMS.INSTANCE.method$RecipeManager$addRecipe(MINECRAFT_RECIPE_MANAGER, recipe); + FastNMS.INSTANCE.method$RecipeManager$addRecipe(minecraftRecipeManager(), recipe); + return recipe; }; static { try { Key dyeRecipeId = Key.from("armor_dye"); MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); - MINECRAFT_RECIPE_ADDER.accept(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); + MINECRAFT_RECIPE_ADDER.apply(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId)); Key repairRecipeId = Key.from("repair_item"); MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId); - MINECRAFT_RECIPE_ADDER.accept(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); + MINECRAFT_RECIPE_ADDER.apply(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId)); Key fireworkStarFadeRecipeId = Key.from("firework_star_fade"); MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId); - MINECRAFT_RECIPE_ADDER.accept(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); + MINECRAFT_RECIPE_ADDER.apply(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId)); } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to inject special recipes", e); } } - private static final List injectedIngredients = new ArrayList<>(); - // 将自定义配方转为“广义”配方,接受更加宽容的输入 - // 部分过程借助bukkit完成,部分直接通过nms方法注册 - private static final Map>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); - private static final IdentityHashMap, Object> MINECRAFT_RECIPE_HOLDER_BY_RECIPE = new IdentityHashMap<>(); + private static final List MODIFIED_INGREDIENTS = new ArrayList<>(); + private static final Map, Object>> ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER = Map.of( + RecipeSerializers.SHAPED, recipe -> { + CustomShapedRecipe shapedRecipe = (CustomShapedRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createShapedRecipe(shapedRecipe); + modifyShapedRecipeIngredients(shapedRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SHAPELESS, recipe -> { + CustomShapelessRecipe shapelessRecipe = (CustomShapelessRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createShapelessRecipe(shapelessRecipe); + modifyShapelessRecipeIngredients(shapelessRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMELTING, recipe -> { + CustomSmeltingRecipe smeltingRecipe = (CustomSmeltingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createSmeltingRecipe(smeltingRecipe); + modifyCookingRecipeIngredient(smeltingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.BLASTING, recipe -> { + CustomBlastingRecipe blastingRecipe = (CustomBlastingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createBlastingRecipe(blastingRecipe); + modifyCookingRecipeIngredient(blastingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMOKING, recipe -> { + CustomSmokingRecipe smokingRecipe = (CustomSmokingRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createSmokingRecipe(smokingRecipe); + modifyCookingRecipeIngredient(smokingRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.CAMPFIRE_COOKING, recipe -> { + CustomCampfireRecipe campfireRecipe = (CustomCampfireRecipe) recipe; + Object mcRecipe = FastNMS.INSTANCE.createCampfireRecipe(campfireRecipe); + modifyCookingRecipeIngredient(campfireRecipe, mcRecipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.STONECUTTING, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createStonecuttingRecipe((CustomStoneCuttingRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMITHING_TRIM, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createSmithingTrimRecipe((CustomSmithingTrimRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + }, + RecipeSerializers.SMITHING_TRANSFORM, recipe -> { + Object mcRecipe = FastNMS.INSTANCE.createSmithingTransformRecipe((CustomSmithingTransformRecipe) recipe); + return MINECRAFT_RECIPE_ADDER.apply(recipe.id(), mcRecipe); + } + ); - public static Object toRecipeResourceKey(Key id) { - return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); - } - - private static void registerBukkitShapedRecipe(Object recipe) { + private static void modifyShapedRecipeIngredients(CustomShapedRecipe recipe, Object shapedRecipe) { try { - Object craftRecipe = CraftBukkitReflections.method$CraftShapedRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register shaped recipe", e); - } - } - - private static void registerBukkitShapelessRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftShapelessRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register shapeless recipe", e); - } - } - - private static void registerBukkitSmeltingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftFurnaceRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smelting recipe", e); - } - } - - private static void registerBukkitSmokingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftSmokingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smoking recipe", e); - } - } - - private static void registerBukkitBlastingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftBlastingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register blasting recipe", e); - } - } - - private static void registerBukkitCampfireRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftCampfireRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register campfire recipe", e); - } - } - - private static void registerBukkitStoneCuttingRecipe(Object recipe) { - try { - Object craftRecipe = CraftBukkitReflections.method$CraftStonecuttingRecipe$fromBukkitRecipe.invoke(null, recipe); - CraftBukkitReflections.method$CraftRecipe$addToCraftingManager.invoke(craftRecipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register stonecutting recipe", e); - } - } - - static { - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SMITHING_TRANSFORM, (BukkitRecipeConvertor>) (id, recipe) -> { - try { - Object nmsRecipe = createMinecraftSmithingTransformRecipe(recipe); - return () -> MINECRAFT_RECIPE_ADDER.accept(id, nmsRecipe); - } catch (InvalidRecipeIngredientException e) { - throw e; - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to convert smithing transform recipe", e); - return null; - } - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SMITHING_TRIM, (BukkitRecipeConvertor>) (id, recipe) -> { - try { - Object nmsRecipe = createMinecraftSmithingTrimRecipe(recipe); - return () -> MINECRAFT_RECIPE_ADDER.accept(id, nmsRecipe); - } catch (InvalidRecipeIngredientException e) { - throw e; - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to convert smithing trim recipe", e); - return null; - } - }); - // TODO DO NOT USE BUKKIT RECIPE AS BRIDGE IN FUTURE VERSIONS, WE SHOULD DIRECTLY CONSTRUCT THOSE NMS RECIPES - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SHAPED, (BukkitRecipeConvertor>) (id, recipe) -> { - ShapedRecipe shapedRecipe = new ShapedRecipe(new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY)); - if (recipe.group() != null) shapedRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) shapedRecipe.setCategory(CraftingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - shapedRecipe.shape(recipe.pattern().pattern()); - for (Map.Entry> entry : recipe.pattern().ingredients().entrySet()) { - shapedRecipe.setIngredient(entry.getKey(), ingredientToBukkitRecipeChoice(entry.getValue())); - } - return () -> { - registerBukkitShapedRecipe(shapedRecipe); - injectShapedRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SHAPELESS, (BukkitRecipeConvertor>) (id, recipe) -> { - ShapelessRecipe shapelessRecipe = new ShapelessRecipe(new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY)); - if (recipe.group() != null) shapelessRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) shapelessRecipe.setCategory(CraftingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - for (Ingredient ingredient : recipe.ingredientsInUse()) { - shapelessRecipe.addIngredient(ingredientToBukkitRecipeChoice(ingredient)); - } - return () -> { - registerBukkitShapelessRecipe(shapelessRecipe); - injectShapelessRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SMELTING, (BukkitRecipeConvertor>) (id, recipe) -> { - FurnaceRecipe furnaceRecipe = new FurnaceRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) furnaceRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) furnaceRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitSmeltingRecipe(furnaceRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.SMOKING, (BukkitRecipeConvertor>) (id, recipe) -> { - SmokingRecipe smokingRecipe = new SmokingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) smokingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) smokingRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitSmokingRecipe(smokingRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.BLASTING, (BukkitRecipeConvertor>) (id, recipe) -> { - BlastingRecipe blastingRecipe = new BlastingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) blastingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) blastingRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitBlastingRecipe(blastingRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.CAMPFIRE_COOKING, (BukkitRecipeConvertor>) (id, recipe) -> { - CampfireRecipe campfireRecipe = new CampfireRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - ingredientToBukkitRecipeChoice(recipe.ingredient()), - recipe.experience(), recipe.cookingTime() - ); - if (recipe.group() != null) campfireRecipe.setGroup(Objects.requireNonNull(recipe.group())); - if (recipe.category() != null) campfireRecipe.setCategory(CookingBookCategory.valueOf(Objects.requireNonNull(recipe.category()).name())); - return () -> { - registerBukkitCampfireRecipe(campfireRecipe); - injectCookingRecipe(id, recipe); - }; - }); - MIXED_RECIPE_CONVERTORS.put(RecipeSerializers.STONECUTTING, (BukkitRecipeConvertor>) (id, recipe) -> { - List itemStacks = new ArrayList<>(); - for (UniqueKey item : recipe.ingredient().items()) { - itemStacks.add(BukkitItemManager.instance().buildItemStack(item.key(), null)); - } - StonecuttingRecipe stonecuttingRecipe = new StonecuttingRecipe( - new NamespacedKey(id.namespace(), id.value()), recipe.result(ItemBuildContext.EMPTY), - new RecipeChoice.ExactChoice(itemStacks) - ); - if (recipe.group() != null) stonecuttingRecipe.setGroup(Objects.requireNonNull(recipe.group())); - return () -> { - registerBukkitStoneCuttingRecipe(stonecuttingRecipe); - }; - }); - } - - /* - * 注册全过程: - * - * 0.准备阶段偷取flag以减少注册的性能开销 - * 1.先读取用户配置自定义配方 - * 2.延迟加载中为自定义配方生成转换为nms配方的任务 - * 3.读取全部的数据包配方并转换为自定义配方,对必要的含有tag配方添加先移除后注册nms配方的任务 - * 4.主线程完成剩余任务 - * 5.归还flag - */ - - private final BukkitCraftEngine plugin; - private final RecipeEventListener recipeEventListener; - private final CrafterEventListener crafterEventListener; - // Some delayed tasks on main thread - private final List delayedTasksOnMainThread = new ArrayList<>(); - // To optimize recipes loading, will return the flag later - private Object stolenFeatureFlagSet; - - public BukkitRecipeManager(BukkitCraftEngine plugin) { - instance = this; - this.plugin = plugin; - this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); - this.crafterEventListener = VersionHelper.isOrAbove1_21() ? new CrafterEventListener(plugin, this, plugin.itemManager()) : null; - } - - public Object nmsRecipeHolderByRecipe(Recipe recipe) { - if (super.isReloading) return null; - return MINECRAFT_RECIPE_HOLDER_BY_RECIPE.get(recipe); - } - - public static Object minecraftRecipeManager() { - return MINECRAFT_RECIPE_MANAGER; - } - - public static BukkitRecipeManager instance() { - return instance; - } - - @Override - public void delayedInit() { - Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin()); - if (this.crafterEventListener != null) { - Bukkit.getPluginManager().registerEvents(this.crafterEventListener, this.plugin.javaPlugin()); - } - } - - @Override - public void load() { - if (!Config.enableRecipeSystem()) return; - super.isReloading = true; - if (VersionHelper.isOrAbove1_21_2()) { - try { - this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(MINECRAFT_RECIPE_MANAGER); - CoreReflections.field$RecipeManager$featureflagset.set(MINECRAFT_RECIPE_MANAGER, null); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to steal featureflagset", e); - } - } - } - - @Override - public void unload() { - if (!Config.enableRecipeSystem()) return; - // 安排卸载任务,这些任务会在load后执行。如果没有load说明服务器已经关闭了,那就不需要管卸载了。 - if (!Bukkit.isStopping()) { - for (Map.Entry> entry : this.byId.entrySet()) { - Key id = entry.getKey(); - if (isDataPackRecipe(id)) continue; - boolean isBrewingRecipe = entry.getValue() instanceof CustomBrewingRecipe; - this.delayedTasksOnMainThread.add(() -> this.unregisterPlatformRecipe(id, isBrewingRecipe)); - } - } - super.unload(); - } - - @Override - public void delayedLoad() { - if (!Config.enableRecipeSystem()) return; - this.injectDataPackRecipes(); - } - - @Override - public void disable() { - unload(); - MINECRAFT_RECIPE_HOLDER_BY_RECIPE.clear(); - // 不是服务器关闭造成disable,那么需要把配方卸载干净 - if (!Bukkit.isStopping()) { - for (Runnable task : this.delayedTasksOnMainThread) { - task.run(); - } - } - HandlerList.unregisterAll(this.recipeEventListener); - if (this.crafterEventListener != null) { - HandlerList.unregisterAll(this.crafterEventListener); - } - } - - @Override - protected void unregisterPlatformRecipe(Key key, boolean isBrewingRecipe) { - if (isBrewingRecipe) { - Bukkit.getPotionBrewer().removePotionMix(new NamespacedKey(key.namespace(), key.value())); - } else { - MINECRAFT_RECIPE_REMOVER.accept(key); - } - } - - @Override - protected void registerPlatformRecipe(Key id, Recipe recipe) { - if (recipe instanceof CustomBrewingRecipe brewingRecipe) { - if (!VersionHelper.isOrAbove1_20_2()) return; - PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()), - brewingRecipe.result(ItemBuildContext.EMPTY), - PotionMix.createPredicateChoice(container -> { - Item wrapped = this.plugin.itemManager().wrap(container); - return brewingRecipe.container().test(new UniqueIdItem<>(wrapped.recipeIngredientId(), wrapped)); - }), - PotionMix.createPredicateChoice(ingredient -> { - Item wrapped = this.plugin.itemManager().wrap(ingredient); - return brewingRecipe.ingredient().test(new UniqueIdItem<>(wrapped.recipeIngredientId(), wrapped)); - }) - ); - this.delayedTasksOnMainThread.add(() -> { - Bukkit.getPotionBrewer().addPotionMix(potionMix); - }); - } else { - try { - Runnable converted = findNMSRecipeConvertor(recipe).convert(id, recipe); - if (converted != null) { - this.delayedTasksOnMainThread.add(converted); - } - } catch (InvalidRecipeIngredientException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.invalid_ingredient", e.ingredient()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to convert recipe " + id, e); - } - } - } - - @SuppressWarnings("unchecked") - private > BukkitRecipeConvertor findNMSRecipeConvertor(T recipe) { - return (BukkitRecipeConvertor) MIXED_RECIPE_CONVERTORS.get(recipe.serializerType()); - } - - @SuppressWarnings("unchecked") - private void injectDataPackRecipes() { - try { - Object fileToIdConverter = CoreReflections.method$FileToIdConverter$json.invoke(null, VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"); - Object minecraftServer = CoreReflections.method$MinecraftServer$getServer.invoke(null); - Object packRepository = CoreReflections.method$MinecraftServer$getPackRepository.invoke(minecraftServer); - List selected = (List) CoreReflections.field$PackRepository$selected.get(packRepository); - List packResources = new ArrayList<>(); - for (Object pack : selected) { - packResources.add(CoreReflections.method$Pack$open.invoke(pack)); - } - - boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty(); - try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.constructor$MultiPackResourceManager.newInstance(CoreReflections.instance$PackType$SERVER_DATA, packResources)) { - Map scannedResources = (Map) CoreReflections.method$FileToIdConverter$listMatchingResources.invoke(fileToIdConverter, resourceManager); - for (Map.Entry entry : scannedResources.entrySet()) { - Key id = extractKeyFromResourceLocation(entry.getKey().toString()); - // now CraftEngine takes over everything -// // Maybe it's unregistered by other plugins -// if (Bukkit.getRecipe(new NamespacedKey(id.namespace(), id.value())) == null) { -// continue; -// } - if (Config.disableAllVanillaRecipes()) { - this.delayedTasksOnMainThread.add(() -> unregisterPlatformRecipe(id, false)); - continue; - } - if (hasDisabledAny && Config.disabledVanillaRecipes().contains(id)) { - this.delayedTasksOnMainThread.add(() -> unregisterPlatformRecipe(id, false)); - continue; - } - markAsDataPackRecipe(id); - Reader reader = (Reader) CoreReflections.method$Resource$openAsReader.invoke(entry.getValue()); - JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); - String type = jsonObject.get("type").getAsString(); - switch (type) { - case "minecraft:crafting_shaped" -> { - VanillaShapedRecipe recipe = this.recipeReader.readShaped(jsonObject); - handleDataPackShapedRecipe(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:crafting_shapeless" -> { - VanillaShapelessRecipe recipe = this.recipeReader.readShapeless(jsonObject); - handleDataPackShapelessRecipe(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smelting" -> { - VanillaSmeltingRecipe recipe = this.recipeReader.readSmelting(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomSmeltingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:blasting" -> { - VanillaBlastingRecipe recipe = this.recipeReader.readBlasting(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomBlastingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smoking" -> { - VanillaSmokingRecipe recipe = this.recipeReader.readSmoking(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomSmokingRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:campfire_cooking" -> { - VanillaCampfireRecipe recipe = this.recipeReader.readCampfire(jsonObject); - handleDataPackCookingRecipe(id, recipe, CustomCampfireRecipe::new, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smithing_transform" -> { - VanillaSmithingTransformRecipe recipe = this.recipeReader.readSmithingTransform(jsonObject); - handleDataPackSmithingTransform(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:smithing_trim" -> { - VanillaSmithingTrimRecipe recipe = this.recipeReader.readSmithingTrim(jsonObject); - handleDataPackSmithingTrim(id, recipe, (this.delayedTasksOnMainThread::add)); - } - case "minecraft:stonecutting" -> { - VanillaStoneCuttingRecipe recipe = this.recipeReader.readStoneCutting(jsonObject); - handleDataPackStoneCuttingRecipe(id, recipe); - } - } - } - } - } catch (Exception e) { - this.plugin.logger().warn("Failed to read data pack recipes", e); - } - } - - @Override - public void runDelayedSyncTasks() { - if (!Config.enableRecipeSystem()) return; - - try { - // run delayed tasks - for (Runnable r : this.delayedTasksOnMainThread) { - r.run(); - } - this.delayedTasksOnMainThread.clear(); - - // give flags back on 1.21.2+ - if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { - CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), this.stolenFeatureFlagSet); - this.stolenFeatureFlagSet = null; - } - - // refresh recipes + List> actualIngredients = recipe.parsedPattern().ingredients() + .stream() + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(minecraftRecipeManager()); + CoreReflections.field$ShapedRecipe$placementInfo.set(shapedRecipe, null); } - - // send to players - CoreReflections.method$DedicatedPlayerList$reloadRecipes.invoke(CraftBukkitReflections.field$CraftServer$playerList.get(Bukkit.getServer())); - - // now we need to remove the fake `exact` - if (VersionHelper.isOrAbove1_21_4()) { - for (Object ingredient : injectedIngredients) { - CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, null); - } - } else if (VersionHelper.isOrAbove1_21_2()) { - for (Object ingredient : injectedIngredients) { - CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, null); - } - } - - // clear cache - injectedIngredients.clear(); - - MINECRAFT_RECIPE_HOLDER_BY_RECIPE.clear(); - // create mappings - for (Map.Entry> entry : this.byId.entrySet()) { - Optional nmsRecipe = getOptionalNMSRecipe(entry.getKey()); - nmsRecipe.ifPresent(o -> MINECRAFT_RECIPE_HOLDER_BY_RECIPE.put(entry.getValue(), o)); - } - super.isReloading = false; - } catch (Exception e) { - this.plugin.logger().warn("Failed to run delayed recipe tasks", e); + List ingredients = getIngredientsFromShapedRecipe(shapedRecipe); + modifyIngredients(ingredients, actualIngredients); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e); } } - private void handleDataPackStoneCuttingRecipe(Key id, VanillaStoneCuttingRecipe recipe) { - ItemStack result = createDataPackResultStack(recipe.result()); - Set holders = new HashSet<>(); - for (String item : recipe.ingredient()) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - holders.addAll(this.plugin.itemManager().tagToItems(tag)); - } else { - holders.add(UniqueKey.create(Key.from(item))); - } - } - CustomStoneCuttingRecipe ceRecipe = new CustomStoneCuttingRecipe<>( - id, recipe.group(), Ingredient.of(holders), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer callback) { - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag = false; - List> ingredientList = new ArrayList<>(); - for (List list : recipe.ingredients()) { - Set holders = new HashSet<>(); - for (String item : list) { - if (item.charAt(0) == '#') { - Key tag = Key.of(item.substring(1)); - if (!hasCustomItemInTag) { - if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; + @SuppressWarnings("unchecked") + public static List getIngredientsFromShapedRecipe(Object recipe) { + List ingredients = new ArrayList<>(); + try { + if (VersionHelper.isOrAbove1_20_3()) { + Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe); + if (VersionHelper.isOrAbove1_21_2()) { + List> optionals = (List>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern); + for (Optional optional : optionals) { + optional.ifPresent(ingredients::add); + } + } else { + List objectList = (List) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern); + for (Object object : objectList) { + Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); + // is empty or not + if (values.length != 0) { + ingredients.add(object); } } - holders.addAll(plugin.itemManager().tagToItems(tag)); - } else { - holders.add(UniqueKey.create(Key.from(item))); - } - } - ingredientList.add(Ingredient.of(holders)); - } - CustomShapelessRecipe ceRecipe = new CustomShapelessRecipe<>( - id, recipe.category(), recipe.group(), ingredientList, - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - MINECRAFT_RECIPE_REMOVER.accept(id); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackShapedRecipe(Key id, VanillaShapedRecipe recipe, Consumer callback) { - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag = false; - Map> ingredients = new HashMap<>(); - for (Map.Entry> entry : recipe.ingredients().entrySet()) { - Set holders = new HashSet<>(); - for (String item : entry.getValue()) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - if (!hasCustomItemInTag) { - if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; - } - } - holders.addAll(plugin.itemManager().tagToItems(tag)); - } else { - holders.add(UniqueKey.create(Key.from(item))); - } - } - ingredients.put(entry.getKey(), Ingredient.of(holders)); - } - CustomShapedRecipe ceRecipe = new CustomShapedRecipe<>( - id, recipe.category(), recipe.group(), - new CustomShapedRecipe.Pattern<>(recipe.pattern(), ingredients), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - MINECRAFT_RECIPE_REMOVER.accept(id); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackCookingRecipe(Key id, - VanillaCookingRecipe recipe, - HeptaFunction, Integer, Float, CustomRecipeResult, CustomCookingRecipe> constructor2, - Consumer callback) { - ItemStack result = createDataPackResultStack(recipe.result()); - Set holders = new HashSet<>(); - boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), holders::add); - CustomCookingRecipe ceRecipe = constructor2.apply( - id, recipe.category(), recipe.group(), - Ingredient.of(holders), - recipe.cookingTime(), recipe.experience(), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null) - ); - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - MINECRAFT_RECIPE_REMOVER.accept(id); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer callback) { - ItemStack result = createDataPackResultStack(recipe.result()); - boolean hasCustomItemInTag; - - Set additionHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add); - Set templateHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.template(), templateHolders::add); - Set baseHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.base(), baseHolders::add); - - CustomSmithingTransformRecipe ceRecipe = new CustomSmithingTransformRecipe<>( - id, - baseHolders.isEmpty() ? null : Ingredient.of(baseHolders), - templateHolders.isEmpty() ? null : Ingredient.of(templateHolders), - additionHolders.isEmpty() ? null : Ingredient.of(additionHolders), - new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), BukkitItemManager.instance().wrap(result)), recipe.result().count(), null), - true, - List.of() - ); - - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - MINECRAFT_RECIPE_REMOVER.accept(id); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private void handleDataPackSmithingTrim(Key id, VanillaSmithingTrimRecipe recipe, Consumer callback) { - boolean hasCustomItemInTag; - Set additionHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add); - Set templateHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.template(), templateHolders::add); - Set baseHolders = new HashSet<>(); - hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.base(), baseHolders::add); - - CustomSmithingTrimRecipe ceRecipe = new CustomSmithingTrimRecipe<>( - id, - Ingredient.of(baseHolders), - Ingredient.of(templateHolders), - Ingredient.of(additionHolders), - Optional.ofNullable(recipe.pattern()).map(Key::of).orElse(null) - ); - - if (hasCustomItemInTag) { - Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); - callback.accept(() -> { - MINECRAFT_RECIPE_REMOVER.accept(id); - converted.run(); - }); - } - this.registerInternalRecipe(id, ceRecipe); - } - - private boolean readVanillaIngredients(boolean hasCustomItemInTag, List ingredients, Consumer holderConsumer) { - for (String item : ingredients) { - if (item.charAt(0) == '#') { - Key tag = Key.from(item.substring(1)); - if (!hasCustomItemInTag) { - if (!this.plugin.itemManager().tagToCustomItems(tag).isEmpty()) { - hasCustomItemInTag = true; - } - } - for (UniqueKey holder : this.plugin.itemManager().tagToItems(tag)) { - holderConsumer.accept(holder); } } else { - holderConsumer.accept(UniqueKey.create(Key.from(item))); + List objectList = (List) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe); + for (Object object : objectList) { + Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); + if (values.length != 0) { + ingredients.add(object); + } + } } + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e); } - return hasCustomItemInTag; + return ingredients; } - private ItemStack createDataPackResultStack(RecipeResult result) { - ItemStack itemStack; - if (result.components() == null) { - itemStack = new ItemStack(Objects.requireNonNull(MaterialUtils.getMaterial(result.id()))); - itemStack.setAmount(result.count()); - } else { - // 低版本无法应用nbt或组件,所以这里是1.20.5+ - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("id", result.id()); - jsonObject.addProperty("count", result.count()); - jsonObject.add("components", result.components()); - Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject) - .resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null); - if (nmsStack == null) { - return new ItemStack(Material.STONE); + private static void modifyShapelessRecipeIngredients(CustomShapelessRecipe recipe, Object shapelessRecipe) { + try { + List> actualIngredients = recipe.ingredientsInUse(); + if (VersionHelper.isOrAbove1_21_2()) { + CoreReflections.field$ShapelessRecipe$placementInfo.set(shapelessRecipe, null); } - try { - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack); - } catch (Exception e) { - this.plugin.logger().warn("Failed to create ItemStack mirror", e); - return new ItemStack(Material.STICK); + @SuppressWarnings("unchecked") + List ingredients = (List) CoreReflections.field$ShapelessRecipe$ingredients.get(shapelessRecipe); + modifyIngredients(ingredients, actualIngredients); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e); + } + } + + private static void modifyCookingRecipeIngredient(CustomCookingRecipe recipe, Object cookingRecipe) { + try { + Ingredient actualIngredient = recipe.ingredient(); + Object ingredient; + if (VersionHelper.isOrAbove1_21_2()) { + ingredient = CoreReflections.field$SingleItemRecipe$input.get(cookingRecipe); + } else { + ingredient = CoreReflections.field$AbstractCookingRecipe$input.get(cookingRecipe); } + modifyIngredients(List.of(ingredient), List.of(actualIngredient)); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e); } - return itemStack; - } - - private Key extractKeyFromResourceLocation(String input) { - int prefixEndIndex = input.indexOf(':'); - String prefix = input.substring(0, prefixEndIndex); - int lastSlashIndex = input.lastIndexOf('/'); - int lastDotIndex = input.lastIndexOf('.'); - String fileName = input.substring(lastSlashIndex + 1, lastDotIndex); - return Key.of(prefix, fileName); - } - - private static RecipeChoice ingredientToBukkitRecipeChoice(Ingredient ingredient) { - Set materials = new HashSet<>(); - for (UniqueKey holder : ingredient.items()) { - materials.add(getMaterialById(holder.key())); - } - return new RecipeChoice.MaterialChoice(new ArrayList<>(materials)); - } - - private static Material getMaterialById(Key key) { - Material material = MaterialUtils.getMaterial(key); - if (material != null) { - return material; - } - Optional> optionalItem = BukkitItemManager.instance().getCustomItem(key); - return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElseThrow(() -> new InvalidRecipeIngredientException(key.asString())); } private static List getIngredientLooks(List holders) { @@ -815,96 +230,14 @@ public class BukkitRecipeManager extends AbstractRecipeManager { itemStacks.add(nmsStack); } else { Item barrier = BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null); + assert barrier != null; barrier.customNameJson(AdventureHelper.componentToJson(Component.text(holder.key().asString()).color(NamedTextColor.RED))); } } return itemStacks; } - // 无论是什么注入什么配方类型的方法,其本质都是注入ingredient - private static void injectShapedRecipe(Key id, CustomShapedRecipe recipe) { - try { - List> actualIngredients = recipe.parsedPattern().ingredients() - .stream() - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - - Object shapedRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe); - } - - if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.field$ShapedRecipe$placementInfo.set(shapedRecipe, null); - } - - List ingredients = RecipeUtils.getIngredientsFromShapedRecipe(shapedRecipe); - injectIngredients(ingredients, actualIngredients); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e); - } - } - - private static void injectShapelessRecipe(Key id, CustomShapelessRecipe recipe) { - try { - List> actualIngredients = recipe.ingredientsInUse(); - - Object shapelessRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe); - } - - if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.field$ShapelessRecipe$placementInfo.set(shapelessRecipe, null); - } - @SuppressWarnings("unchecked") - List ingredients = (List) CoreReflections.field$ShapelessRecipe$ingredients.get(shapelessRecipe); - injectIngredients(ingredients, actualIngredients); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e); - } - } - - private static void injectCookingRecipe(Key id, CustomCookingRecipe recipe) { - try { - Ingredient actualIngredient = recipe.ingredient(); - Object smeltingRecipe = getOptionalNMSRecipe(id).get(); - if (VersionHelper.isOrAbove1_20_2()) { - smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe); - } - - Object ingredient; - if (VersionHelper.isOrAbove1_21_2()) { - ingredient = CoreReflections.field$SingleItemRecipe$input.get(smeltingRecipe); - } else { - ingredient = CoreReflections.field$AbstractCookingRecipe$input.get(smeltingRecipe); - } - injectIngredients(List.of(ingredient), List.of(actualIngredient)); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e); - } - } - - // 获取nms配方,请注意1.20.1获取配方本身,而1.20.2+获取的是配方的holder - // recipe on 1.20.1 and holder on 1.20.2+ - private static Optional getOptionalNMSRecipe(Key id) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_2()) { - Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); - @SuppressWarnings("unchecked") - Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(MINECRAFT_RECIPE_MANAGER, resourceKey); - return optional; - } else { - Object resourceLocation = KeyUtils.toResourceLocation(id); - @SuppressWarnings("unchecked") - Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(MINECRAFT_RECIPE_MANAGER, resourceLocation); - return optional; - } - } - - // 注入原料,这个方法受不同服务端fork和版本影响极大,需要每个版本测试 - // 此过程是为了避免自己处理“广义”配方与客户端的注册通讯 - private static void injectIngredients(List fakeIngredients, List> actualIngredients) throws ReflectiveOperationException { + private static void modifyIngredients(List fakeIngredients, List> actualIngredients) throws ReflectiveOperationException { if (fakeIngredients.size() != actualIngredients.size()) { throw new IllegalArgumentException("Ingredient count mismatch"); } @@ -923,100 +256,256 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } CoreReflections.field$Ingredient$itemStacks1_20_1.set(ingredient, itemStackArray); } - injectedIngredients.add(ingredient); + MODIFIED_INGREDIENTS.add(ingredient); } } - // 1.20-1.21.2 - private static Object toMinecraftIngredient(Ingredient ingredient) throws ReflectiveOperationException { - if (ingredient == null) { - return CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, null, true); - } else { - RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient); - return CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, choice, true); + public static Object toRecipeResourceKey(Key id) { + return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); + } + + /* + * 注册全过程: + * + * 0.准备阶段偷取flag以减少注册的性能开销 + * 1.先读取用户配置自定义配方 + * 2.延迟加载中为自定义配方生成转换为nms配方的任务 + * 3.读取全部的数据包配方并转换为自定义配方,对必要的含有tag配方添加先移除后注册nms配方的任务 + * 4.主线程完成剩余任务 + * 5.归还flag + */ + private final BukkitCraftEngine plugin; + private final RecipeEventListener recipeEventListener; + // 欺骗服务端使其以为自己处于启动阶段 + private Object stolenFeatureFlagSet; + // 需要在主线程卸载的配方 + private final List> recipesToUnregister = new ArrayList<>(); + // 已经被替换过的数据包配方 + private final Set replacedDatapackRecipes = new HashSet<>(); + // 换成的数据包配方 + private Map> lastDatapackRecipes = Map.of(); + private Object lastRecipeManager = null; + + public BukkitRecipeManager(BukkitCraftEngine plugin) { + instance = this; + this.plugin = plugin; + this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); + } + + public static Object minecraftRecipeManager() { + return FastNMS.INSTANCE.method$MinecraftServer$getRecipeManager(FastNMS.INSTANCE.method$MinecraftServer$getServer()); + } + + public static BukkitRecipeManager instance() { + return instance; + } + + @Override + public void delayedInit() { + Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin()); + } + + @Override + public void load() { + if (!Config.enableRecipeSystem()) return; + if (VersionHelper.isOrAbove1_21_2()) { + try { + this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(minecraftRecipeManager()); + CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), null); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to steal feature flag set", e); + } } } - // 1.21.2+ - private static Optional toOptionalMinecraftIngredient(Ingredient ingredient) throws ReflectiveOperationException { - if (ingredient == null) { - return Optional.empty(); + @Override + public void unload() { + if (!Config.enableRecipeSystem()) return; + // 安排卸载任务,这些任务会在load后执行。如果没有load说明服务器已经关闭了,那就不需要管卸载了。 + if (!Bukkit.isStopping()) { + for (Map.Entry> entry : this.byId.entrySet()) { + Key id = entry.getKey(); + // 不要卸载数据包配方,只记录自定义的配方 + if (isDataPackRecipe(id)) continue; + boolean isBrewingRecipe = entry.getValue() instanceof CustomBrewingRecipe; + this.recipesToUnregister.add(Pair.of(id, isBrewingRecipe)); + } + } + super.unload(); + } + + @Override + public void delayedLoad() { + if (!Config.enableRecipeSystem()) return; + this.loadDataPackRecipes(); + } + + @Override + public void disable() { + unload(); + HandlerList.unregisterAll(this.recipeEventListener); + } + + @Override + protected void unregisterPlatformRecipeMainThread(Key key, boolean isBrewingRecipe) { + if (isBrewingRecipe) { + Bukkit.getPotionBrewer().removePotionMix(new NamespacedKey(key.namespace(), key.value())); } else { - RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient); - Object mcIngredient = CraftBukkitReflections.method$CraftRecipe$toIngredient.invoke(null, choice, true); - return Optional.of(mcIngredient); + MINECRAFT_RECIPE_REMOVER.accept(key); } } - // 1.21.5+ - private static Object toTransmuteResult(ItemStack item) throws InvocationTargetException, IllegalAccessException, InstantiationException { - Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item); - Object nmsItem = CoreReflections.method$ItemStack$getItem.invoke(itemStack); - return CoreReflections.constructor$TransmuteResult.newInstance(nmsItem); - } - - // create nms smithing recipe for different versions - private static Object createMinecraftSmithingTransformRecipe(CustomSmithingTransformRecipe recipe) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_5()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()), - toTransmuteResult(recipe.result(ItemBuildContext.EMPTY)) - ); - } else if (VersionHelper.isOrAbove1_21_2()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toOptionalMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) - ); - } else if (VersionHelper.isOrAbove1_20_2()) { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) + @Override + protected void registerPlatformRecipeMainThread(Recipe recipe) { + Key id = recipe.id(); + if (recipe instanceof CustomBrewingRecipe brewingRecipe) { + if (!VersionHelper.isOrAbove1_20_2()) return; + PotionMix potionMix = new PotionMix(new NamespacedKey(id.namespace(), id.value()), + brewingRecipe.result(ItemBuildContext.EMPTY), + PotionMix.createPredicateChoice(container -> { + Item wrapped = this.plugin.itemManager().wrap(container); + return brewingRecipe.container().test(UniqueIdItem.of(wrapped)); + }), + PotionMix.createPredicateChoice(ingredient -> { + Item wrapped = this.plugin.itemManager().wrap(ingredient); + return brewingRecipe.ingredient().test(UniqueIdItem.of(wrapped)); + }) ); + Bukkit.getPotionBrewer().addPotionMix(potionMix); } else { - return CoreReflections.constructor$SmithingTransformRecipe.newInstance( - KeyUtils.toResourceLocation(recipe.id()), - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(recipe.result(ItemBuildContext.EMPTY)) - ); + // 如果是数据包配方 + if (isDataPackRecipe(id)) { + // 如果这个数据包配方已经被换成了注入配方,那么是否需要重新注册取决于其是否含有tag,且tag里有自定义物品 + if (!this.replacedDatapackRecipes.add(id)) { + outer: { + for (Ingredient ingredient : recipe.ingredientsInUse()) { + if (ingredient.hasCustomItem()) { + break outer; + } + } + // 没有自定义物品,且被注入过了,那么就不需要移除后重新注册 + return; + } + } + MINECRAFT_RECIPE_REMOVER.accept(id); + } + ADD_RECIPE_FOR_MINECRAFT_RECIPE_HOLDER.get(recipe.serializerType()).apply(recipe); } } - private static Object createMinecraftSmithingTrimRecipe(CustomSmithingTrimRecipe recipe) throws ReflectiveOperationException { - if (VersionHelper.isOrAbove1_21_5()) { - Object registry = FastNMS.INSTANCE.method$RegistryAccess$lookupOrThrow(FastNMS.INSTANCE.registryAccess(), MRegistries.TRIM_PATTERN); - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()), - FastNMS.INSTANCE.method$Registry$getHolderByResourceLocation(registry, KeyUtils.toResourceLocation(recipe.pattern())).orElseThrow(() -> new RuntimeException("Pattern " + recipe.pattern() + " doesn't exist.")) - ); - } else if (VersionHelper.isOrAbove1_21_2()) { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toOptionalMinecraftIngredient(recipe.template()), - toOptionalMinecraftIngredient(recipe.base()), - toOptionalMinecraftIngredient(recipe.addition()) - ); - } else if (VersionHelper.isOrAbove1_20_2()) { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()) - ); - } else { - return CoreReflections.constructor$SmithingTrimRecipe.newInstance( - KeyUtils.toResourceLocation(recipe.id()), - toMinecraftIngredient(recipe.template()), - toMinecraftIngredient(recipe.base()), - toMinecraftIngredient(recipe.addition()) - ); + private void loadDataPackRecipes() { + Object currentRecipeManager = minecraftRecipeManager(); + if (currentRecipeManager != this.lastRecipeManager) { + this.lastRecipeManager = currentRecipeManager; + this.replacedDatapackRecipes.clear(); + try { + this.lastDatapackRecipes = scanResources(); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to load datapack recipes", e); + } + } + for (Map.Entry> entry : this.lastDatapackRecipes.entrySet()) { + markAsDataPackRecipe(entry.getKey()); + registerInternalRecipe(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + private Map> scanResources() throws ReflectiveOperationException { + Object fileToIdConverter = CoreReflections.method$FileToIdConverter$json.invoke(null, VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"); + Object minecraftServer = FastNMS.INSTANCE.method$MinecraftServer$getServer(); + Object packRepository = CoreReflections.method$MinecraftServer$getPackRepository.invoke(minecraftServer); + List selected = (List) CoreReflections.field$PackRepository$selected.get(packRepository); + List packResources = new ArrayList<>(); + for (Object pack : selected) { + packResources.add(CoreReflections.method$Pack$open.invoke(pack)); + } + Map> recipes = new HashMap<>(); + boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty(); + try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.constructor$MultiPackResourceManager.newInstance(CoreReflections.instance$PackType$SERVER_DATA, packResources)) { + Map scannedResources = (Map) CoreReflections.method$FileToIdConverter$listMatchingResources.invoke(fileToIdConverter, resourceManager); + for (Map.Entry entry : scannedResources.entrySet()) { + Key id = extractKeyFromResourceLocation(entry.getKey().toString()); + if (Config.disableAllVanillaRecipes()) { + this.recipesToUnregister.add(new Pair<>(id, false)); + continue; + } + if (hasDisabledAny && Config.disabledVanillaRecipes().contains(id)) { + this.recipesToUnregister.add(new Pair<>(id, false)); + continue; + } + Reader reader = (Reader) CoreReflections.method$Resource$openAsReader.invoke(entry.getValue()); + JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); + Key serializerType = Key.of(jsonObject.get("type").getAsString()); + RecipeSerializer> serializer = (RecipeSerializer>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType); + if (serializer == null) { + continue; + } + try { + Recipe recipe = serializer.readJson(id, jsonObject); + recipes.put(id, recipe); + } catch (Exception e) { + this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e); + } + } + } catch (Exception e) { + this.plugin.logger().warn("Unknown error occurred when loading data pack recipes", e); + } + return recipes; + } + + private Key extractKeyFromResourceLocation(String input) { + int prefixEndIndex = input.indexOf(':'); + String prefix = input.substring(0, prefixEndIndex); + int lastSlashIndex = input.lastIndexOf('/'); + int lastDotIndex = input.lastIndexOf('.'); + String fileName = input.substring(lastSlashIndex + 1, lastDotIndex); + return Key.of(prefix, fileName); + } + + @Override + public void runDelayedSyncTasks() { + if (!Config.enableRecipeSystem()) return; + + // 卸载掉需要卸载的配方(禁用的原版配方+注册的自定义配方) + for (Pair pair : this.recipesToUnregister) { + unregisterPlatformRecipeMainThread(pair.left(), pair.right()); + } + // 注册新的配方 + for (Recipe recipe : this.byId.values()) { + registerPlatformRecipeMainThread(recipe); + } + + try { + // give flags back on 1.21.2+ + if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { + CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), this.stolenFeatureFlagSet); + this.stolenFeatureFlagSet = null; + } + + // refresh recipes + if (VersionHelper.isOrAbove1_21_2()) { + CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(minecraftRecipeManager()); + } + + // send to players + CoreReflections.method$DedicatedPlayerList$reloadRecipes.invoke(CraftBukkitReflections.field$CraftServer$playerList.get(Bukkit.getServer())); + + // now we need to remove the fake `exact` choices + if (VersionHelper.isOrAbove1_21_4()) { + for (Object ingredient : MODIFIED_INGREDIENTS) { + CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, null); + } + } else if (VersionHelper.isOrAbove1_21_2()) { + for (Object ingredient : MODIFIED_INGREDIENTS) { + CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, null); + } + } + + // clear cache + MODIFIED_INGREDIENTS.clear(); + } catch (Exception e) { + this.plugin.logger().warn("Failed to run delayed recipe tasks", e); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java deleted file mode 100644 index 6fb03e8ec..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/CrafterEventListener.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.momirealms.craftengine.bukkit.item.recipe; - -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.ItemStackUtils; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.ItemManager; -import net.momirealms.craftengine.core.item.recipe.Recipe; -import net.momirealms.craftengine.core.item.recipe.RecipeType; -import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; -import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; -import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.block.Crafter; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.CrafterCraftEvent; -import org.bukkit.inventory.CraftingRecipe; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.List; - -public class CrafterEventListener implements Listener { - private final ItemManager itemManager; - private final BukkitRecipeManager recipeManager; - private final BukkitCraftEngine plugin; - - public CrafterEventListener(BukkitCraftEngine plugin, BukkitRecipeManager recipeManager, ItemManager itemManager) { - this.itemManager = itemManager; - this.recipeManager = recipeManager; - this.plugin = plugin; - } - - @EventHandler - public void onCrafting(CrafterCraftEvent event) { - if (!Config.enableRecipeSystem()) return; - CraftingRecipe recipe = event.getRecipe(); - if (!(event.getBlock().getState() instanceof Crafter crafter)) { - return; - } - - Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { - return; - } - - Inventory inventory = crafter.getInventory(); - ItemStack[] ingredients = inventory.getStorageContents(); - - List> uniqueIdItems = new ArrayList<>(); - for (ItemStack itemStack : ingredients) { - if (ItemStackUtils.isEmpty(itemStack)) { - uniqueIdItems.add(this.itemManager.uniqueEmptyItem()); - } else { - Item wrappedItem = this.itemManager.wrap(itemStack); - uniqueIdItems.add(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - } - } - - CraftingInput input; - if (ingredients.length == 9) { - input = CraftingInput.of(3, 3, uniqueIdItems); - } else if (ingredients.length == 4) { - input = CraftingInput.of(2, 2, uniqueIdItems); - } else { - return; - } - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeType.CRAFTING, input); - if (ceRecipe != null) { - event.setResult(ceRecipe.assemble(input, ItemBuildContext.EMPTY)); - return; - } - // clear result if not met - event.setCancelled(true); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index ceb1036ee..8c2d8f75a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -4,12 +4,9 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.ComponentTypes; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.injector.RecipeInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.InventoryUtils; @@ -28,21 +25,14 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.util.*; import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Campfire; -import org.bukkit.block.Furnace; -import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.*; import org.bukkit.event.inventory.*; import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.*; import org.bukkit.inventory.view.AnvilView; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -74,7 +64,7 @@ public class RecipeEventListener implements Listener { ItemStack item = event.getCurrentItem(); if (ItemStackUtils.isEmpty(item)) return; if (ItemStackUtils.isEmpty(fuelStack)) { - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(item)); + SingleItemInput input = new SingleItemInput<>(ItemStackUtils.getUniqueIdItem(item)); RecipeType recipeType; if (furnaceInventory.getType() == InventoryType.FURNACE) { recipeType = RecipeType.SMELTING; @@ -258,166 +248,9 @@ public class RecipeEventListener implements Listener { } } - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onFurnaceInventoryOpen(InventoryOpenEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!(event.getInventory() instanceof FurnaceInventory furnaceInventory)) { - return; - } - Furnace furnace = furnaceInventory.getHolder(); - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - - // for 1.20.1-1.21.1 - @EventHandler(ignoreCancelled = true) - public void onBlockIgnite(BlockIgniteEvent event) { - if (!Config.enableRecipeSystem()) return; - if (VersionHelper.isOrAbove1_21_2()) return; - Block block = event.getBlock(); - Material material = block.getType(); - if (material == Material.CAMPFIRE) { - if (block.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPlaceBlock(BlockPlaceEvent event) { - if (!Config.enableRecipeSystem()) return; - Block block = event.getBlock(); - Material material = block.getType(); - if (material == Material.FURNACE || material == Material.BLAST_FURNACE || material == Material.SMOKER) { - if (block.getState() instanceof Furnace furnace) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(furnace); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } else if (!VersionHelper.isOrAbove1_21_2() && material == Material.CAMPFIRE) { - if (block.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - } - } - - // for 1.21.2+ - @EventHandler(ignoreCancelled = true) - public void onPutItemOnCampfire(PlayerInteractEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - Block clicked = event.getClickedBlock(); - if (clicked == null) return; - Material type = clicked.getType(); - if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; - if (clicked.getState() instanceof Campfire campfire) { - try { - Object blockEntity = CraftBukkitReflections.field$CraftBlockEntityState$tileEntity.get(campfire); - RecipeInjector.injectCookingBlockEntity(blockEntity); - } catch (Exception e) { - this.plugin.logger().warn("Failed to inject cooking block entity", e); - } - } - - ItemStack itemStack = event.getItem(); - if (ItemStackUtils.isEmpty(itemStack)) return; - try { - @SuppressWarnings("unchecked") - Optional optionalMCRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor( - BukkitRecipeManager.minecraftRecipeManager(), - MRecipeTypes.CAMPFIRE_COOKING, - CoreReflections.constructor$SingleRecipeInput.newInstance(FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)), - FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()), - null - ); - if (optionalMCRecipe.isEmpty()) { - return; - } - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeType.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setCancelled(true); - } - } catch (Exception e) { - this.plugin.logger().warn("Failed to handle interact campfire", e); - } - } - - // for 1.21.2+ - @SuppressWarnings("UnstableApiUsage") - @EventHandler(ignoreCancelled = true) - public void onCampfireCook(CampfireStartEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - CampfireRecipe recipe = event.getRecipe(); - Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - return; - } - - ItemStack itemStack = event.getSource(); - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeType.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setTotalCookTime(Integer.MAX_VALUE); - return; - } - - event.setTotalCookTime(ceRecipe.cookingTime()); - } - - // for 1.21.2+ - @EventHandler(ignoreCancelled = true) - public void onCampfireCook(BlockCookEvent event) { - if (!Config.enableRecipeSystem()) return; - if (!VersionHelper.isOrAbove1_21_2()) return; - Material type = event.getBlock().getType(); - if (type != Material.CAMPFIRE && type != Material.SOUL_CAMPFIRE) return; - CampfireRecipe recipe = (CampfireRecipe) event.getRecipe(); - if (recipe == null) return; - - Key recipeId = new Key(recipe.getKey().namespace(), recipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - return; - } - - ItemStack itemStack = event.getSource(); - SingleItemInput input = new SingleItemInput<>(getUniqueIdItem(itemStack)); - CustomCampfireRecipe ceRecipe = (CustomCampfireRecipe) this.recipeManager.recipeByInput(RecipeType.CAMPFIRE_COOKING, input); - if (ceRecipe == null) { - event.setCancelled(true); - return; - } - - event.setResult(ceRecipe.result(ItemBuildContext.EMPTY)); - } - // Paper only @EventHandler public void onPrepareResult(PrepareResultEvent event) { -// if (!ConfigManager.enableRecipeSystem()) return; if (event.getInventory() instanceof CartographyInventory cartographyInventory) { if (ItemStackUtils.hasCustomItem(cartographyInventory.getStorageContents())) { event.setResult(new ItemStack(Material.AIR)); @@ -745,98 +578,35 @@ public class RecipeEventListener implements Listener { return new Pair<>(first, second); } - // 不是完美的解决方案,仍然需要更多的探讨 - // TODO 生成类代理掉ResultSlot,并注入menu的slots对象,修改掉onTake方法 - // TODO 对于耐久度降低的配方,应该注册special recipe? - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onCraft(CraftItemEvent event) { - org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (!(recipe instanceof ShapelessRecipe) && !(recipe instanceof ShapedRecipe)) return; - HumanEntity humanEntity = event.getWhoClicked(); - if (!(humanEntity instanceof Player player)) return; - CraftingInventory inventory = event.getInventory(); - ItemStack result = inventory.getResult(); - if (result == null) return; - ItemStack[] usedItems = inventory.getMatrix(); - ItemStack[] replacements = new ItemStack[usedItems.length]; - boolean hasReplacement = false; - for (int i = 0; i < usedItems.length; i++) { - ItemStack usedItem = usedItems[i]; - if (ItemStackUtils.isEmpty(usedItem)) continue; - if (usedItem.getAmount() != 1) continue; - Item wrapped = BukkitItemManager.instance().wrap(usedItem); - if (ItemUtils.isEmpty(wrapped)) continue; - Optional> optionalCustomItem = wrapped.getCustomItem(); - if (optionalCustomItem.isPresent()) { - CustomItem customItem = optionalCustomItem.get(); - Key remainingItem = customItem.settings().craftRemainder(); - if (remainingItem != null) { - replacements[i] = BukkitItemManager.instance().buildItemStack(remainingItem, this.plugin.adapt(player)); - hasReplacement = true; - } - } - } - if (!hasReplacement) return; - Runnable delayedTask = () -> { - for (int i = 0; i < replacements.length; i++) { - if (replacements[i] == null) continue; - inventory.setItem(i + 1, replacements[i]); - } - }; - if (VersionHelper.isFolia()) { - player.getScheduler().run(this.plugin.javaPlugin(), (t) -> delayedTask.run(), () -> {}); - } else { - this.plugin.scheduler().sync().runDelayed(delayedTask); - } - } - @EventHandler(ignoreCancelled = true) public void onCraftingRecipe(PrepareItemCraftEvent event) { if (!Config.enableRecipeSystem()) return; org.bukkit.inventory.Recipe recipe = event.getRecipe(); - - // we only handle shaped and shapeless recipes - boolean shapeless = recipe instanceof ShapelessRecipe; - boolean shaped = recipe instanceof ShapedRecipe; - if (!shaped && !shapeless) return; - - CraftingRecipe craftingRecipe = (CraftingRecipe) recipe; + if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); - - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + // 也许是其他插件注册的配方,直接无视 + if (optionalRecipe.isEmpty()) { return; } - CraftingInventory inventory = event.getInventory(); + if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe craftingTableRecipe)) { + inventory.setResult(null); + return; + } CraftingInput input = getCraftingInput(inventory); if (input == null) return; - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = this.plugin.adapt(player); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeType.CRAFTING, input, recipeId); - if (ceRecipe != null) { - inventory.setResult(ceRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); - serverPlayer.setLastUsedRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(recipeId)) { - correctCraftingRecipeUsed(inventory, ceRecipe); - } - return; - } - // clear result if not met - inventory.setResult(null); + inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); } private CraftingInput getCraftingInput(CraftingInventory inventory) { ItemStack[] ingredients = inventory.getMatrix(); - List> uniqueIdItems = new ArrayList<>(); for (ItemStack itemStack : ingredients) { - uniqueIdItems.add(getUniqueIdItem(itemStack)); + uniqueIdItems.add(ItemStackUtils.getUniqueIdItem(itemStack)); } - CraftingInput input; if (ingredients.length == 9) { input = CraftingInput.of(3, 3, uniqueIdItems); @@ -848,17 +618,6 @@ public class RecipeEventListener implements Listener { return input; } - private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe recipe) { - Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); - if (holderOrRecipe == null) { - return; - } - Object resultInventory = FastNMS.INSTANCE.method$CraftInventoryCrafting$getResultInventory(inventory); - FastNMS.INSTANCE.method$ResultContainer$setRecipeUsed(resultInventory, holderOrRecipe); - Object matrixInventory = FastNMS.INSTANCE.method$CraftInventoryCrafting$getMatrixInventory(inventory); - FastNMS.INSTANCE.method$CraftingContainer$setCurrentRecipe(matrixInventory, holderOrRecipe); - } - @EventHandler(ignoreCancelled = true) public void onSmithingTrim(PrepareSmithingEvent event) { SmithingInventory inventory = event.getInventory(); @@ -880,31 +639,17 @@ public class RecipeEventListener implements Listener { } Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + if (optionalRecipe.isEmpty()) { return; } - - SmithingInput input = new SmithingInput<>( - getUniqueIdItem(inventory.getInputEquipment()), - getUniqueIdItem(inventory.getInputTemplate()), - getUniqueIdItem(inventory.getInputMineral()) - ); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeType.SMITHING, input, recipeId); - if (ceRecipe == null) { + if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe smithingTrimRecipe)) { event.setResult(null); return; } - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - CustomSmithingTrimRecipe trimRecipe = (CustomSmithingTrimRecipe) ceRecipe; - ItemStack result = trimRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); + ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); event.setResult(result); - if (!ceRecipe.id().equals(recipeId)) { - correctSmithingRecipeUsed(inventory, ceRecipe); - } } @EventHandler(ignoreCancelled = true) @@ -912,59 +657,25 @@ public class RecipeEventListener implements Listener { if (!Config.enableRecipeSystem()) return; SmithingInventory inventory = event.getInventory(); if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return; - Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); - boolean isCustom = this.recipeManager.isCustomRecipe(recipeId); - // Maybe it's recipe from other plugins, then we ignore it - if (!isCustom) { + Optional> optionalRecipe = this.recipeManager.recipeById(recipeId); + if (optionalRecipe.isEmpty()) { return; } - - ItemStack base = inventory.getInputEquipment(); - ItemStack template = inventory.getInputTemplate(); - ItemStack addition = inventory.getInputMineral(); - - SmithingInput input = new SmithingInput<>( - getUniqueIdItem(base), - getUniqueIdItem(template), - getUniqueIdItem(addition) - ); - - Recipe ceRecipe = this.recipeManager.recipeByInput(RecipeType.SMITHING, input, recipeId); - if (ceRecipe == null) { + if (!(optionalRecipe.get() instanceof CustomSmithingTransformRecipe smithingTransformRecipe)) { event.setResult(null); return; } - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - - CustomSmithingTransformRecipe transformRecipe = (CustomSmithingTransformRecipe) ceRecipe; - ItemStack processed = transformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); + ItemStack processed = smithingTransformRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY)); event.setResult(processed); - if (!ceRecipe.id().equals(recipeId)) { - correctSmithingRecipeUsed(inventory, ceRecipe); - } } - private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe recipe) { - Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); - if (holderOrRecipe == null) { - return; - } - try { - Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory); - CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); - } catch (ReflectiveOperationException e) { - this.plugin.logger().warn("Failed to correct used recipe", e); - } - } - - private UniqueIdItem getUniqueIdItem(@Nullable ItemStack itemStack) { - if (ItemStackUtils.isEmpty(itemStack)) { - return this.itemManager.uniqueEmptyItem(); - } else { - Item wrappedItem = this.itemManager.wrap(itemStack); - return new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem); - } + private SmithingInput getSmithingInput(SmithingInventory inventory) { + return new SmithingInput<>( + ItemStackUtils.getUniqueIdItem(inventory.getInputEquipment()), + ItemStackUtils.getUniqueIdItem(inventory.getInputTemplate()), + ItemStackUtils.getUniqueIdItem(inventory.getInputMineral()) + ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 7ae11cc4f..bad128c87 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -60,28 +60,12 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { @Override public void unload() { super.unload(); - if (ReloadCommand.RELOAD_PACK_FLAG) { - if (VersionHelper.isOrAbove1_20_2()) { - this.resetServerSettings(); - } - } } @Override public void disable() { super.disable(); HandlerList.unregisterAll(this); - this.resetServerSettings(); - } - - public void resetServerSettings() { - try { - Object settings = CoreReflections.field$DedicatedServer$settings.get(CoreReflections.method$MinecraftServer$getServer.invoke(null)); - Object properties = CoreReflections.field$DedicatedServerSettings$properties.get(settings); - CoreReflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to reset resource pack settings", e); - } } @EventHandler(priority = EventPriority.MONITOR) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java index 63ad89c4b..623bdf0e2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -6,32 +6,22 @@ import it.unimi.dsi.fastutil.ints.IntList; import net.bytebuddy.ByteBuddy; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; -import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.bind.annotation.AllArguments; import net.bytebuddy.implementation.bind.annotation.RuntimeType; -import net.bytebuddy.implementation.bind.annotation.This; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.ComponentTypes; -import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; import net.momirealms.craftengine.bukkit.util.ItemTags; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; -import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeType; -import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; -import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; import net.momirealms.craftengine.core.util.*; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -46,43 +36,13 @@ import java.util.function.Function; import java.util.function.Predicate; public class RecipeInjector { - private static Class clazz$InjectedCacheChecker; private static Class clazz$InjectedArmorDyeRecipe; private static Class clazz$InjectedRepairItemRecipe; private static Class clazz$InjectedFireworkStarFadeRecipe; public static void init() { ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); - clazz$InjectedCacheChecker = byteBuddy - .subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) - .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") - .implement(CoreReflections.clazz$RecipeManager$CachedCheck) - .implement(InjectedCacheCheck.class) - .defineField("recipeType", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("recipeType")) - .intercept(FieldAccessor.ofField("recipeType")) - .defineField("customRecipeType", RecipeType.class, Visibility.PUBLIC) - .method(ElementMatchers.named("customRecipeType")) - .intercept(FieldAccessor.ofField("customRecipeType")) - .defineField("lastRecipe", Object.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastRecipe")) - .intercept(FieldAccessor.ofField("lastRecipe")) - .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) - .method(ElementMatchers.named("lastCustomRecipe")) - .intercept(FieldAccessor.ofField("lastCustomRecipe")) - .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) - .intercept(MethodDelegation.to( - VersionHelper.isOrAbove1_21_2() ? - GetRecipeForMethodInterceptor1_21_2.INSTANCE : - (VersionHelper.isOrAbove1_21() ? - GetRecipeForMethodInterceptor1_21.INSTANCE : - VersionHelper.isOrAbove1_20_5() ? - GetRecipeForMethodInterceptor1_20_5.INSTANCE : - GetRecipeForMethodInterceptor1_20.INSTANCE) - )) - .make() - .load(RecipeInjector.class.getClassLoader()) - .getLoaded(); + ElementMatcher.Junction matches = (VersionHelper.isOrAbove1_21() ? ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$Level) : ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$Level) @@ -151,35 +111,6 @@ public class RecipeInjector { } } - public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException { - if (CoreReflections.clazz$AbstractFurnaceBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - if (recipeType == MRecipeTypes.SMELTING) { - injectedChecker.customRecipeType(RecipeType.SMELTING); - injectedChecker.recipeType(MRecipeTypes.SMELTING); - } else if (recipeType == MRecipeTypes.BLASTING) { - injectedChecker.customRecipeType(RecipeType.BLASTING); - injectedChecker.recipeType(MRecipeTypes.BLASTING); - } else if (recipeType == MRecipeTypes.SMOKING) { - injectedChecker.customRecipeType(RecipeType.SMOKING); - injectedChecker.recipeType(MRecipeTypes.SMOKING); - } else { - throw new IllegalStateException("RecipeType " + recipeType + " not supported"); - } - CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); - } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { - Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); - if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected - InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - injectedChecker.customRecipeType(RecipeType.CAMPFIRE_COOKING); - injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); - CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); - } - } - private static final Function INGREDIENT_SIZE_GETTER = VersionHelper.isOrAbove1_21() ? FastNMS.INSTANCE::method$CraftingInput$size : @@ -471,185 +402,4 @@ public class RecipeInjector { private static boolean isVanillaDyeItem(Item item) { return CoreReflections.clazz$DyeItem.isInstance(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject())); } - - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_20 { - public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - Pair resourceLocationAndRecipe = optionalRecipe.get(); - Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst(); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return Optional.of(resourceLocationAndRecipe.getSecond()); - } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( - injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? - FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : - FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) - ); - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); - } - } - - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_20_5 { - public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - Object rawRecipeHolder = optionalRecipe.get(); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( - injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? - FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : - FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) - ); - - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); - } - } - - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_21 { - public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - Object rawRecipeHolder = optionalRecipe.get(); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); - return optionalRecipe; - } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - if (ceRecipe == null) { - return Optional.empty(); - } - - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); - } - } - - @SuppressWarnings("DuplicatedCode") - public static class GetRecipeForMethodInterceptor1_21_2 { - public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); - - @SuppressWarnings("unchecked") - @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) { - InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey); - if (optionalRecipe.isEmpty()) { - return Optional.empty(); - } - - // 获取配方的基础信息 - Object recipeHolder = optionalRecipe.get(); - Object rawRecipeResourceKey = FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolder); - Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(rawRecipeResourceKey); - Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); - - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - // 来自其他插件注册的自定义配方 - boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); - if (!isCustom) { - injectedCacheCheck.lastRecipe(rawRecipeResourceKey); - return optionalRecipe; - } - - // 获取唯一内存地址id - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - SingleItemInput input = new SingleItemInput<>(new UniqueIdItem<>(wrappedItem.recipeIngredientId(), wrappedItem)); - CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); - // 这个ce配方并不存在,那么应该返回空 - if (ceRecipe == null) { - return Optional.empty(); - } - - // 记录上一次使用的配方(ce) - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // 更新上一次使用的配方(nms) - if (!ceRecipe.id().equals(rawRecipeKey)) { - injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id()))); - } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); - } - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index cfa1bf880..291e542c3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -128,7 +128,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.plugin.javaPlugin().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.javaPlugin(), MOD_CHANNEL); // Inject server channel try { - Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null); + Object server = FastNMS.INSTANCE.method$MinecraftServer$getServer(); Object serverConnection = CoreReflections.field$MinecraftServer$connection.get(server); @SuppressWarnings("unchecked") List channels = (List) CoreReflections.field$ServerConnectionListener$channels.get(serverConnection); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 125a9a8cf..4877f0aa3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -2849,10 +2849,6 @@ public final class CoreReflections { ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("server.MinecraftServer")) ); - public static final Method method$MinecraftServer$getServer = requireNonNull( - ReflectionUtils.getMethod(clazz$MinecraftServer, new String[] { "getServer" }) - ); - public static final Field field$MinecraftServer$registries = requireNonNull( ReflectionUtils.getDeclaredField(clazz$MinecraftServer, clazz$LayeredRegistryAccess, 0) ); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 2ef53cd94..fd6156d23 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -93,8 +93,6 @@ public class BukkitServerPlayer extends Player { // for client visual sync private int resentSoundTick; private int resentSwingTick; - // cache used recipe - private Key lastUsedRecipe = null; // has fabric client mod or not private boolean hasClientMod = false; // cache if player can break blocks @@ -885,14 +883,6 @@ public class BukkitServerPlayer extends Player { return resentSwingTick == gameTicks(); } - public Key lastUsedRecipe() { - return lastUsedRecipe; - } - - public void setLastUsedRecipe(Key lastUsedRecipe) { - this.lastUsedRecipe = lastUsedRecipe; - } - public boolean clientModEnabled() { return this.hasClientMod; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java index 2bbe31a7a..ec33bb09f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/InteractUtils.java @@ -129,15 +129,11 @@ public class InteractUtils { }); registerInteraction(BlockKeys.SOUL_CAMPFIRE, (player, item, blockState, result) -> { if (!Config.enableRecipeSystem()) return false; - return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>( - item.recipeIngredientId(), item - ))) != null; + return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null; }); registerInteraction(BlockKeys.CAMPFIRE, (player, item, blockState, result) -> { if (!Config.enableRecipeSystem()) return false; - return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(new UniqueIdItem<>( - item.recipeIngredientId(), item - ))) != null; + return BukkitRecipeManager.instance().recipeByInput(RecipeType.CAMPFIRE_COOKING, new SingleItemInput<>(UniqueIdItem.of(item))) != null; }); registerInteraction(BlockKeys.CHISELED_BOOKSHELF, (player, item, blockState, result) -> { if (!(blockState instanceof ChiseledBookshelf chiseledBookshelf)) return false; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java index 1ade133c7..bbebf2a97 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java @@ -3,11 +3,14 @@ package net.momirealms.craftengine.bukkit.util; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; -public class ItemStackUtils { +public final class ItemStackUtils { private ItemStackUtils() {} @@ -43,4 +46,9 @@ public class ItemStackUtils { return FastNMS.INSTANCE.method$CraftItemStack$asCraftCopy(itemStack); } } + + public static UniqueIdItem getUniqueIdItem(@Nullable ItemStack itemStack) { + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + return UniqueIdItem.of(wrappedItem); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java index 8fed8c247..ca67f6b6b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RecipeUtils.java @@ -1,50 +1,7 @@ package net.momirealms.craftengine.bukkit.util; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.VersionHelper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - public class RecipeUtils { private RecipeUtils() {} - @SuppressWarnings("unchecked") - public static List getIngredientsFromShapedRecipe(Object recipe) { - List ingredients = new ArrayList<>(); - try { - if (VersionHelper.isOrAbove1_20_3()) { - Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - List> optionals = (List>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern); - for (Optional optional : optionals) { - optional.ifPresent(ingredients::add); - } - } else { - List objectList = (List) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern); - for (Object object : objectList) { - Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); - // is empty or not - if (values.length != 0) { - ingredients.add(object); - } - } - } - } else { - List objectList = (List) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe); - for (Object object : objectList) { - Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object); - if (values.length != 0) { - ingredients.add(object); - } - } - } - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e); - } - return ingredients; - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java index bd04de0e4..e4d090bad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitWorldManager.java @@ -403,13 +403,6 @@ public class BukkitWorldManager implements WorldManager, Listener { (injected) -> sections[finalI] = injected); } } - if (Config.enableRecipeSystem()) { - @SuppressWarnings("unchecked") - Map blockEntities = (Map) FastNMS.INSTANCE.field$ChunkAccess$blockEntities(levelChunk); - for (Object blockEntity : blockEntities.values()) { - RecipeInjector.injectCookingBlockEntity(blockEntity); - } - } } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to restore chunk at " + chunk.getX() + " " + chunk.getZ(), e); return; diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 8f0f5a79a..8a428d817 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -143,6 +143,8 @@ warning.config.recipe.smithing_trim.missing_addition: "Issue found in fi warning.config.recipe.smithing_trim.missing_pattern: "Issue found in file - The smithing trim recipe '' is missing the required 'pattern' argument." warning.config.recipe.brewing.missing_container: "Issue found in file - The brewing recipe '' is missing the required 'container' argument." warning.config.recipe.brewing.missing_ingredient: "Issue found in file - The brewing recipe '' is missing the required 'ingredient' argument." +warning.config.recipe.result.post_processor.missing_type: "Issue found in file - The recipe '' is missing the required 'type' argument for result post processors." +warning.config.recipe.result.post_processor.invalid_type: "Issue found in file - The recipe '' is using an invalid result post processor type ''." warning.config.i18n.unknown_locale: "Issue found in file - Unknown locale ''." warning.config.template.duplicate: "Issue found in file - Duplicated template ''. Please check if there is the same configuration in other files." warning.config.template.invalid: "Issue found in file - The config '' is using an invalid template ''." diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java index 1cfb119f9..8abe2a871 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockSettings.java @@ -464,7 +464,7 @@ public class BlockSettings { LazyReference> correctTools = LazyReference.lazyReference(() -> { Set ids = new HashSet<>(); for (String tool : tools) { - if (tool.charAt(0) == '#') ids.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(tool.substring(1))).stream().map(UniqueKey::key).toList()); + if (tool.charAt(0) == '#') ids.addAll(CraftEngine.instance().itemManager().itemIdsByTag(Key.of(tool.substring(1))).stream().map(UniqueKey::key).toList()); else ids.add(Key.of(tool)); } return ids; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java index db56a096f..66e6c4d53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractCustomItem.java @@ -15,6 +15,7 @@ import java.util.Map; import java.util.Optional; public abstract class AbstractCustomItem implements CustomItem { + protected final boolean isVanillaItem; protected final UniqueKey id; protected final Key material; protected final Key clientBoundMaterial; @@ -25,12 +26,13 @@ public abstract class AbstractCustomItem implements CustomItem { protected final Map>> events; @SuppressWarnings("unchecked") - public AbstractCustomItem(UniqueKey id, Key material, Key clientBoundMaterial, + public AbstractCustomItem(boolean isVanillaItem, UniqueKey id, Key material, Key clientBoundMaterial, List behaviors, List> modifiers, List> clientBoundModifiers, ItemSettings settings, Map>> events) { + this.isVanillaItem = isVanillaItem; this.id = id; this.material = material; this.clientBoundMaterial = clientBoundMaterial; @@ -75,6 +77,11 @@ public abstract class AbstractCustomItem implements CustomItem { return this.modifiers; } + @Override + public boolean isVanillaItem() { + return isVanillaItem; + } + @Override public boolean hasClientBoundDataModifier() { return this.clientBoundModifiers.length != 0; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 6cf94134c..77d581dc1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -137,43 +137,31 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl Key id = customItem.id(); if (this.customItems.containsKey(id)) return false; this.customItems.put(id, customItem); - // cache command suggestions - this.cachedSuggestions.add(Suggestion.suggestion(id.toString())); - // totem animations - if (VersionHelper.isOrAbove1_21_2()) { - this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); - } else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) { - this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); - } - // tags - Set tags = customItem.settings().tags(); - for (Key tag : tags) { - this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); + if (!customItem.isVanillaItem()) { + // cache command suggestions + this.cachedSuggestions.add(Suggestion.suggestion(id.toString())); + // totem animations + if (VersionHelper.isOrAbove1_21_2()) { + this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); + } else if (customItem.material().equals(ItemKeys.TOTEM_OF_UNDYING)) { + this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); + } + // tags + Set tags = customItem.settings().tags(); + for (Key tag : tags) { + this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); + } } return true; } @Override - public List tagToItems(Key tag) { - List items = new ArrayList<>(); - List holders = VANILLA_ITEM_TAGS.get(tag); - if (holders != null) { - items.addAll(holders); - } - List customItems = this.customItemTags.get(tag); - if (customItems != null) { - items.addAll(customItems); - } - return items; - } - - @Override - public List tagToVanillaItems(Key tag) { + public List vanillaItemIdsByTag(Key tag) { return Collections.unmodifiableList(VANILLA_ITEM_TAGS.getOrDefault(tag, List.of())); } @Override - public List tagToCustomItems(Key tag) { + public List customItemIdsByTag(Key tag) { return Collections.unmodifiableList(this.customItemTags.getOrDefault(tag, List.of())); } @@ -415,6 +403,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 构建自定义物品 CustomItem customItem = itemBuilder + .isVanillaItem(isVanillaItem) .behaviors(behaviors) .settings(settings) .events(eventTriggerListMap) diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java new file mode 100644 index 000000000..639d8d137 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CloneableConstantItem.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.item; + +import net.momirealms.craftengine.core.util.Key; + +public class CloneableConstantItem implements BuildableItem { + private final Item item; + + private CloneableConstantItem(Item item) { + this.item = item; + } + + public static CloneableConstantItem of(Item item) { + return new CloneableConstantItem<>(item); + } + + @Override + public Key id() { + return this.item.id(); + } + + @Override + public Item buildItem(ItemBuildContext context, int count) { + return this.item.copyWithCount(count); + } + + @Override + public I buildItemStack(ItemBuildContext context, int count) { + return this.item.copyWithCount(count).getItem(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java index 2ab666cf9..5d0415a30 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/CustomItem.java @@ -14,6 +14,8 @@ import java.util.Map; public interface CustomItem extends BuildableItem { + boolean isVanillaItem(); + Key id(); UniqueKey uniqueId(); @@ -40,6 +42,8 @@ public interface CustomItem extends BuildableItem { List behaviors(); interface Builder { + Builder isVanillaItem(boolean isVanillaItem); + Builder id(UniqueKey id); Builder clientBoundMaterial(Key clientBoundMaterialKey); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 30c17b8b4..8e61ca095 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; @@ -37,10 +38,13 @@ public interface Item { boolean isBlockItem(); + @Nullable Key id(); + @Nullable Key vanillaId(); + @Nullable UniqueKey recipeIngredientId(); Optional customId(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index 9b82c4f3d..e1c18038d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.equipment.Equipment; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.ModernItemModel; @@ -53,10 +54,6 @@ public interface ItemManager extends Manageable, ModelGenerator { Collection items(); - Key itemId(T itemStack); - - Key customItemId(T itemStack); - ExternalItemSource getExternalItemSource(String name); boolean registerExternalItemSource(ExternalItemSource externalItemSource); @@ -81,11 +78,16 @@ public interface ItemManager extends Manageable, ModelGenerator { boolean addCustomItem(CustomItem customItem); - List tagToItems(Key tag); + default List itemIdsByTag(Key tag) { + List items = new ArrayList<>(); + items.addAll(vanillaItemIdsByTag(tag)); + items.addAll(customItemIdsByTag(tag)); + return items; + } - List tagToVanillaItems(Key tag); + List vanillaItemIdsByTag(Key tag); - List tagToCustomItems(Key tag); + List customItemIdsByTag(Key tag); int fuelTime(T itemStack); @@ -108,4 +110,6 @@ public interface ItemManager extends Manageable, ModelGenerator { UniqueIdItem uniqueEmptyItem(); Item applyTrim(Item base, Item addition, Item template, Key pattern); + + Item build(DatapackRecipeResult result); } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java index 9b71c93ce..9af70aabb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractGroupedRecipe.java @@ -10,7 +10,7 @@ public abstract class AbstractGroupedRecipe implements FixedResultRecipe { protected final CustomRecipeResult result; protected AbstractGroupedRecipe(Key id, String group, CustomRecipeResult result) { - this.group = group; + this.group = group == null ? "" : group; this.id = id; this.result = result; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java deleted file mode 100644 index 42f10cfae..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; - -import java.util.*; - -public abstract class AbstractRecipeFactory implements RecipeFactory { - - protected List ingredients(Map arguments) { - return MiscUtils.getAsStringList(getIngredientOrThrow(arguments)); - } - - protected Map ingredientMap(Map arguments) { - return MiscUtils.castToMap(getIngredientOrThrow(arguments), true); - } - - protected Set ingredientHolders(Map arguments) { - Set holders = new HashSet<>(); - for (String item : ingredients(arguments)) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - return holders; - } - - protected Object getIngredientOrThrow(Map arguments) { - Object ingredient = ResourceConfigUtils.get(arguments, "ingredient", "ingredients"); - if (ingredient == null) { - throw new LocalizedResourceConfigException("warning.config.recipe.missing_ingredient"); - } - return ingredient; - } - - protected CookingRecipeCategory cookingRecipeCategory(Map arguments) { - CookingRecipeCategory recipeCategory; - try { - recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; - } catch (IllegalArgumentException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.cooking.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CookingRecipeCategory.values())); - } - return recipeCategory; - } - - protected CraftingRecipeCategory craftingRecipeCategory(Map arguments) { - CraftingRecipeCategory recipeCategory; - try { - recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; - } catch (IllegalArgumentException e) { - throw new LocalizedResourceConfigException("warning.config.recipe.crafting.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CraftingRecipeCategory.values())); - } - return recipeCategory; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 5334d96ae..796c79e9f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -1,11 +1,6 @@ package net.momirealms.craftengine.core.item.recipe; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_20_5; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_21_2; -import net.momirealms.craftengine.core.item.recipe.vanilla.reader.VanillaRecipeReader1_21_5; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -14,24 +9,20 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.UniqueKey; -import net.momirealms.craftengine.core.util.VersionHelper; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.*; public abstract class AbstractRecipeManager implements RecipeManager { - protected final VanillaRecipeReader recipeReader; protected final Map>> byType = new HashMap<>(); protected final Map> byId = new HashMap<>(); protected final Map>> byResult = new HashMap<>(); protected final Map>> byIngredient = new HashMap<>(); protected final Set dataPackRecipes = new HashSet<>(); protected final RecipeParser recipeParser; - protected boolean isReloading; public AbstractRecipeManager() { - this.recipeReader = initVanillaRecipeReader(); this.recipeParser = new RecipeParser(); } @@ -40,18 +31,6 @@ public abstract class AbstractRecipeManager implements RecipeManager { return this.recipeParser; } - private VanillaRecipeReader initVanillaRecipeReader() { - if (VersionHelper.isOrAbove1_21_5()) { - return new VanillaRecipeReader1_21_5(); - } else if (VersionHelper.isOrAbove1_21_2()) { - return new VanillaRecipeReader1_21_2(); - } else if (VersionHelper.isOrAbove1_20_5()) { - return new VanillaRecipeReader1_20_5(); - } else { - return new VanillaRecipeReader1_20(); - } - } - @Override public void unload() { this.dataPackRecipes.clear(); @@ -67,44 +46,37 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Override public boolean isDataPackRecipe(Key key) { - if (this.isReloading) return false; return this.dataPackRecipes.contains(key); } @Override public boolean isCustomRecipe(Key key) { - if (this.isReloading) return false; return this.byId.containsKey(key); } @Override public Optional> recipeById(Key key) { - if (this.isReloading) return Optional.empty(); return Optional.ofNullable(this.byId.get(key)); } @Override public List> recipesByType(RecipeType type) { - if (this.isReloading) return List.of(); return this.byType.getOrDefault(type, List.of()); } @Override public List> recipeByResult(Key result) { - if (this.isReloading) return List.of(); return this.byResult.getOrDefault(result, List.of()); } @Override public List> recipeByIngredient(Key ingredient) { - if (this.isReloading) return List.of(); return this.byIngredient.getOrDefault(ingredient, List.of()); } @Nullable @Override public Recipe recipeByInput(RecipeType type, RecipeInput input) { - if (this.isReloading) return null; List> recipes = this.byType.get(type); if (recipes == null) return null; for (Recipe recipe : recipes) { @@ -118,7 +90,6 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Nullable @Override public Recipe recipeByInput(RecipeType type, RecipeInput input, Key lastRecipe) { - if (this.isReloading) return null; if (lastRecipe != null) { Recipe last = this.byId.get(lastRecipe); if (last != null && last.matches(input)) { @@ -128,7 +99,8 @@ public abstract class AbstractRecipeManager implements RecipeManager { return recipeByInput(type, input); } - protected void registerInternalRecipe(Key id, Recipe recipe) { + protected boolean registerInternalRecipe(Key id, Recipe recipe) { + if (this.byId.containsKey(id)) return false; this.byType.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe); this.byId.put(id, recipe); if (recipe instanceof FixedResultRecipe fixedResult) { @@ -143,6 +115,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } } } + return true; } public class RecipeParser implements ConfigParser { @@ -167,7 +140,6 @@ public abstract class AbstractRecipeManager implements RecipeManager { Recipe recipe = RecipeSerializers.fromMap(id, section); try { registerInternalRecipe(id, recipe); - registerPlatformRecipe(id, recipe); } catch (LocalizedResourceConfigException e) { throw e; } catch (Exception e) { @@ -176,7 +148,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { } } - protected abstract void unregisterPlatformRecipe(Key key, boolean isBrewingRecipe); + protected abstract void unregisterPlatformRecipeMainThread(Key key, boolean isBrewingRecipe); - protected abstract void registerPlatformRecipe(Key key, Recipe recipe); + protected abstract void registerPlatformRecipeMainThread(Recipe recipe); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java new file mode 100644 index 000000000..4c17b43a1 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java @@ -0,0 +1,119 @@ +package net.momirealms.craftengine.core.item.recipe; + +import net.momirealms.craftengine.core.item.CloneableConstantItem; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemManager; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_20; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_20_5; +import net.momirealms.craftengine.core.item.recipe.reader.VanillaRecipeReader1_21_2; +import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public abstract class AbstractRecipeSerializer> implements RecipeSerializer { + protected static final VanillaRecipeReader VANILLA_RECIPE_HELPER = + VersionHelper.isOrAbove1_21_2() ? + new VanillaRecipeReader1_21_2() : + VersionHelper.isOrAbove1_20_5() ? + new VanillaRecipeReader1_20_5() : + new VanillaRecipeReader1_20(); + + protected Ingredient singleInputIngredient(Map arguments) { + List ingredients = MiscUtils.getAsStringList(getIngredientOrThrow(arguments)); + return toIngredient(ingredients); + } + + // 不确定的类型 + protected Object getIngredientOrThrow(Map arguments) { + Object ingredient = ResourceConfigUtils.get(arguments, "ingredient", "ingredients"); + if (ingredient == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.missing_ingredient"); + } + return ingredient; + } + + protected CookingRecipeCategory cookingRecipeCategory(Map arguments) { + CookingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.cooking.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CookingRecipeCategory.values())); + } + return recipeCategory; + } + + protected CraftingRecipeCategory craftingRecipeCategory(Map arguments) { + CraftingRecipeCategory recipeCategory; + try { + recipeCategory = arguments.containsKey("category") ? CraftingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null; + } catch (IllegalArgumentException e) { + throw new LocalizedResourceConfigException("warning.config.recipe.crafting.invalid_category", e, arguments.get("category").toString(), EnumUtils.toString(CraftingRecipeCategory.values())); + } + return recipeCategory; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected CustomRecipeResult parseResult(Map arguments) { + Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); + if (resultMap == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); + } + String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); + int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); + List> processors = ResourceConfigUtils.parseConfigAsList(resultMap.get("post-processors"), CustomRecipeResult.PostProcessor::fromMap); + return new CustomRecipeResult( + CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)), + count, + processors.isEmpty() ? null : processors.toArray(new CustomRecipeResult.PostProcessor[0]) + ); + } + + @SuppressWarnings("unchecked") + protected CustomRecipeResult parseResult(DatapackRecipeResult recipeResult) { + Item result = (Item) CraftEngine.instance().itemManager().build(recipeResult); + return new CustomRecipeResult<>(CloneableConstantItem.of(result), recipeResult.count(), null); + } + + @Nullable + protected Ingredient toIngredient(String item) { + return toIngredient(List.of(item)); + } + + @Nullable + protected Ingredient toIngredient(List items) { + Set itemIds = new HashSet<>(); + Set minecraftItemIds = new HashSet<>(); + ItemManager itemManager = CraftEngine.instance().itemManager(); + for (String item : items) { + if (item.charAt(0) == '#') itemIds.addAll(itemManager.itemIdsByTag(Key.of(item.substring(1)))); + else { + Key itemId = Key.of(item); + if (itemManager.getBuildableItem(itemId).isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_ingredient", item); + } + itemIds.add(UniqueKey.create(itemId)); + } + } + boolean hasCustomItem = false; + for (UniqueKey holder : itemIds) { + Optional> optionalCustomItem = itemManager.getCustomItem(holder.key()); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + if (customItem.isVanillaItem()) { + minecraftItemIds.add(holder); + } else { + minecraftItemIds.add(UniqueKey.create(customItem.material())); + hasCustomItem = true; + } + } else { + minecraftItemIds.add(holder); + } + } + return itemIds.isEmpty() ? null : Ingredient.of(itemIds, minecraftItemIds, hasCustomItem); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java index 2ab483f74..3dcf683e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBlastingRecipe.java @@ -1,15 +1,14 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomBlastingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomBlastingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); @@ -25,16 +24,31 @@ public class CustomBlastingRecipe extends CustomCookingRecipe { return RecipeType.BLASTING; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomBlastingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomBlastingRecipe readMap(Key id, Map arguments) { + return new CustomBlastingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomBlastingRecipe readJson(Key id, JsonObject json) { + return new CustomBlastingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java index 012eefc78..994af19c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomBrewingRecipe.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.BrewingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; @@ -14,7 +15,7 @@ import java.util.List; import java.util.Map; public class CustomBrewingRecipe implements FixedResultRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final Ingredient container; private final Ingredient ingredient; @@ -81,10 +82,10 @@ public class CustomBrewingRecipe implements FixedResultRecipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomBrewingRecipe readMap(Key id, Map arguments) { List container = MiscUtils.getAsStringList(arguments.get("container")); if (container.isEmpty()) { throw new LocalizedResourceConfigException("warning.config.recipe.brewing.missing_container"); @@ -98,5 +99,10 @@ public class CustomBrewingRecipe implements FixedResultRecipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(ingredient), "warning.config.recipe.brewing.missing_ingredient"), parseResult(arguments)); } + + @Override + public CustomBrewingRecipe readJson(Key id, JsonObject json) { + throw new UnsupportedOperationException(); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java index 6f34328fa..de0afd5a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCampfireRecipe.java @@ -1,15 +1,14 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomCampfireRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomCampfireRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); @@ -25,16 +24,31 @@ public class CustomCampfireRecipe extends CustomCookingRecipe { return RecipeType.CAMPFIRE_COOKING; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomCampfireRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomCampfireRecipe readMap(Key id, Map arguments) { + return new CustomCampfireRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomCampfireRecipe readJson(Key id, JsonObject json) { + return new CustomCampfireRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java index 423470eb1..87847a545 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCookingRecipe.java @@ -20,7 +20,7 @@ public abstract class CustomCookingRecipe extends AbstractGroupedRecipe { float experience, CustomRecipeResult result) { super(id, group, result); - this.category = category; + this.category = category == null ? CookingRecipeCategory.MISC : category; this.ingredient = ingredient; this.experience = experience; this.cookingTime = cookingTime; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java index 5d2c241fe..9eb9153d4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java @@ -7,7 +7,7 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe protected CustomCraftingTableRecipe(Key id, CraftingRecipeCategory category, String group, CustomRecipeResult result) { super(id, group, result); - this.category = category; + this.category = category == null ? CraftingRecipeCategory.MISC : category; } public CraftingRecipeCategory category() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java index 0afc9ab64..aff047c53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomRecipeResult.java @@ -4,10 +4,12 @@ import net.momirealms.craftengine.core.item.BuildableItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; import java.util.ArrayList; @@ -19,19 +21,24 @@ import java.util.Optional; public record CustomRecipeResult(BuildableItem item, int count, PostProcessor[] postProcessors) { public T buildItemStack(ItemBuildContext context) { + return buildItem(context).getItem(); + } + + public Item buildItem(ItemBuildContext context) { Item builtItem = this.item.buildItem(context, count); if (this.postProcessors != null) { for (PostProcessor postProcessor : this.postProcessors) { builtItem = postProcessor.process(builtItem, context); } } - return builtItem.getItem(); + return builtItem; } static { registerPostProcessorType(Key.of("apply_data"), args -> { List> modifiers = new ArrayList<>(); - for (Map.Entry entry : args.entrySet()) { + Map data = ResourceConfigUtils.getAsMap(args.get("data"), "data"); + for (Map.Entry entry : data.entrySet()) { Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), "craftengine"))) .ifPresent(factory -> modifiers.add(factory.create(entry.getValue()))); } @@ -40,12 +47,23 @@ public record CustomRecipeResult(BuildableItem item, int count, PostProces } public static void registerPostProcessorType(Key id, PostProcessor.Type type) { - ((WritableRegistry>) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE).register(ResourceKey.create(Registries.RECIPE_POST_PROCESSOR_TYPE.location(), id), type); + ((WritableRegistry>) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE) + .register(ResourceKey.create(Registries.RECIPE_POST_PROCESSOR_TYPE.location(), id), type); } @FunctionalInterface public interface PostProcessor { + static PostProcessor fromMap(Map map) { + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.recipe.result.post_processor.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + PostProcessor.Type processor = (PostProcessor.Type) BuiltInRegistries.RECIPE_POST_PROCESSOR_TYPE.getValue(key); + if (processor == null) { + throw new LocalizedResourceConfigException("warning.config.recipe.result.post_processor.invalid_type", type); + } + return processor.create(map); + } + Item process(Item item, ItemBuildContext context); interface Type { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index 274a2e5a8..4e7d62a5d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -1,18 +1,19 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.common.collect.Maps; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.UniqueKey; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; import java.util.*; public class CustomShapedRecipe extends CustomCraftingTableRecipe { - public static final Factory FACTORY = new Factory>(); + public static final Serializer SERIALIZER = new Serializer>(); private final ParsedPattern parsedPattern; private final Pattern pattern; @@ -133,11 +134,11 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { } } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { + public CustomShapedRecipe readMap(Key id, Map arguments) { List pattern = MiscUtils.getAsStringList(arguments.get("pattern")); if (pattern.isEmpty()) { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.missing_pattern"); @@ -146,30 +147,37 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_pattern", pattern.toString()); } Object ingredientObj = getIngredientOrThrow(arguments); - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; Map> ingredients = new HashMap<>(); - for (Map.Entry entry : MiscUtils.castToMap(ingredientObj, false).entrySet()) { + for (Map.Entry entry : ResourceConfigUtils.getAsMap(ingredientObj, "ingredient").entrySet()) { String key = entry.getKey(); if (key.length() != 1) { throw new LocalizedResourceConfigException("warning.config.recipe.shaped.invalid_symbol", key); } char ch = key.charAt(0); List items = MiscUtils.getAsStringList(entry.getValue()); - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.put(ch, Ingredient.of(holders)); + ingredients.put(ch, toIngredient(items)); } - return new CustomShapedRecipe(id, craftingRecipeCategory(arguments), group, new Pattern<>(pattern.toArray(new String[0]), ingredients), parseResult(arguments)); + return new CustomShapedRecipe(id, + craftingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + new Pattern<>(pattern.toArray(new String[0]), ingredients), + parseResult(arguments) + ); + } + + @Override + public CustomShapedRecipe readJson(Key id, JsonObject json) { + Map> ingredients = Maps.transformValues(VANILLA_RECIPE_HELPER.shapedIngredientMap(json.getAsJsonObject("key")), this::toIngredient); + return new CustomShapedRecipe<>(id, + VANILLA_RECIPE_HELPER.craftingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))) + ); } private boolean validatePattern(List pattern) { - String first = pattern.get(0); + String first = pattern.getFirst(); int length = first.length(); for (String s : pattern) { if (s.length() != length) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index aae980768..9449ec83f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -1,17 +1,18 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; public class CustomShapelessRecipe extends CustomCraftingTableRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final List> ingredients; private final PlacementInfo placementInfo; @@ -51,61 +52,45 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { return RecipeSerializers.SHAPELESS; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; + public CustomShapelessRecipe readMap(Key id, Map arguments) { List> ingredients = new ArrayList<>(); Object ingredientsObject = getIngredientOrThrow(arguments); if (ingredientsObject instanceof Map map) { for (Map.Entry entry : (MiscUtils.castToMap(map, false)).entrySet()) { - List items = MiscUtils.getAsStringList(entry.getValue()); - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.add(Ingredient.of(holders)); + if (entry.getValue() == null) continue; + ingredients.add(toIngredient(MiscUtils.getAsStringList(entry.getValue()))); } } else if (ingredientsObject instanceof List list) { for (Object obj : list) { if (obj instanceof List inner) { - Set holders = new HashSet<>(); - for (String item : MiscUtils.getAsStringList(inner)) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(MiscUtils.getAsStringList(inner))); } else { String item = obj.toString(); - Set holders = new HashSet<>(); - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(item)); } } } else { - String item = ingredientsObject.toString(); - Set holders = new HashSet<>(); - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - ingredients.add(Ingredient.of(holders)); + ingredients.add(toIngredient(ingredientsObject.toString())); } - return new CustomShapelessRecipe(id, craftingRecipeCategory(arguments), group, ingredients, parseResult(arguments)); + return new CustomShapelessRecipe(id, + craftingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + ingredients, + parseResult(arguments)); + } + + @Override + public CustomShapelessRecipe readJson(Key id, JsonObject json) { + return new CustomShapelessRecipe<>(id, + VANILLA_RECIPE_HELPER.craftingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList(), + parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java index ef0dc9a6c..2b03dec6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmeltingRecipe.java @@ -1,15 +1,14 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomSmeltingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomSmeltingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); @@ -25,16 +24,31 @@ public class CustomSmeltingRecipe extends CustomCookingRecipe { return RecipeType.SMELTING; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomSmeltingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomSmeltingRecipe readMap(Key id, Map arguments) { + return new CustomSmeltingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomSmeltingRecipe readJson(Key id, JsonObject json) { + return new CustomSmeltingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 80bafeccf..f1134f90f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; @@ -16,9 +17,10 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class CustomSmithingTransformRecipe implements FixedResultRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final CustomRecipeResult result; private final Ingredient base; @@ -28,7 +30,7 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { private final List processors; public CustomSmithingTransformRecipe(Key id, - @Nullable Ingredient base, + @NotNull Ingredient base, @Nullable Ingredient template, @Nullable Ingredient addition, CustomRecipeResult result, @@ -118,7 +120,7 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { return this.result; } - @Nullable + @NotNull public Ingredient base() { return this.base; } @@ -134,26 +136,37 @@ public class CustomSmithingTransformRecipe implements FixedResultRecipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - if (base.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.missing_base"); - } - List addition = MiscUtils.getAsStringList(arguments.get("addition")); List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); @SuppressWarnings("unchecked") List> processors = (List>) arguments.getOrDefault("post-processors", List.of()); - return new CustomSmithingTransformRecipe<>( - id, - toIngredient(base), toIngredient(template),toIngredient(addition), parseResult(arguments), + return new CustomSmithingTransformRecipe<>(id, + ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_transform.missing_base"), + toIngredient(template), + toIngredient(addition), + parseResult(arguments), mergeComponents, ItemDataProcessors.fromMapList(processors) ); } + + @Override + public CustomSmithingTransformRecipe readJson(Key id, JsonObject json) { + return new CustomSmithingTransformRecipe<>(id, + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), + parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), + true, + null + ); + } } public static class ItemDataProcessors { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index 18d1b7062..51c2a5491 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -1,11 +1,11 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -16,9 +16,10 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; public class CustomSmithingTrimRecipe implements Recipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); private final Key id; private final Ingredient base; private final Ingredient template; @@ -60,14 +61,7 @@ public class CustomSmithingTrimRecipe implements Recipe { } private boolean checkIngredient(Ingredient ingredient, UniqueIdItem item) { - if (ingredient != null) { - if (item == null || item.isEmpty()) { - return false; - } - return ingredient.test(item); - } else { - return item == null || item.isEmpty(); - } + return ingredient.test(item); } @Override @@ -94,17 +88,17 @@ public class CustomSmithingTrimRecipe implements Recipe { return this.id; } - @Nullable + @NotNull public Ingredient base() { return this.base; } - @Nullable + @NotNull public Ingredient template() { return template; } - @Nullable + @NotNull public Ingredient addition() { return addition; } @@ -115,22 +109,13 @@ public class CustomSmithingTrimRecipe implements Recipe { } @SuppressWarnings({"DuplicatedCode"}) - public static class Factory implements RecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @Override - public Recipe create(Key id, Map arguments) { + public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - if (base.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_base"); - } - List addition = MiscUtils.getAsStringList(arguments.get("addition")); - if (addition.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_addition"); - } List template = MiscUtils.getAsStringList(arguments.get("template-type")); - if (template.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.recipe.smithing_trim.missing_template_type"); - } + List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_trim.missing_base"), @@ -138,5 +123,15 @@ public class CustomSmithingTrimRecipe implements Recipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(addition), "warning.config.recipe.smithing_trim.missing_addition"), pattern); } + + @Override + public CustomSmithingTrimRecipe readJson(Key id, JsonObject json) { + return new CustomSmithingTrimRecipe<>(id, + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template")))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition")))), + VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null + ); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java index 1ea3590af..e59d341de 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmokingRecipe.java @@ -1,15 +1,14 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.Map; -import java.util.Set; public class CustomSmokingRecipe extends CustomCookingRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); public CustomSmokingRecipe(Key id, CookingRecipeCategory category, String group, Ingredient ingredient, int cookingTime, float experience, CustomRecipeResult result) { super(id, category, group, ingredient, cookingTime, experience, result); @@ -25,16 +24,31 @@ public class CustomSmokingRecipe extends CustomCookingRecipe { return RecipeType.SMOKING; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { - String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - int cookingTime = ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"); - float experience = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"); - Set holders = ingredientHolders(arguments); - return new CustomSmokingRecipe(id, cookingRecipeCategory(arguments), group, Ingredient.of(holders), cookingTime, experience, parseResult(arguments)); + public CustomSmokingRecipe readMap(Key id, Map arguments) { + return new CustomSmokingRecipe(id, + cookingRecipeCategory(arguments), + arguments.containsKey("group") ? arguments.get("group").toString() : null, + singleInputIngredient(arguments), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("time", 80), "time"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("experience", 0.0f), "experience"), + parseResult(arguments) + ); + } + + @Override + public CustomSmokingRecipe readJson(Key id, JsonObject json) { + return new CustomSmokingRecipe<>(id, + VANILLA_RECIPE_HELPER.cookingCategory(json), + VANILLA_RECIPE_HELPER.readGroup(json), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), + VANILLA_RECIPE_HELPER.cookingTime(json), + VANILLA_RECIPE_HELPER.cookingExperience(json), + parseResult(VANILLA_RECIPE_HELPER.cookingResult(json.get("result"))) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java index 485dc6b74..3bc3a14e9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomStoneCuttingRecipe.java @@ -1,17 +1,16 @@ package net.momirealms.craftengine.core.item.recipe; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.UniqueKey; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; -import java.util.Set; public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { - public static final Factory FACTORY = new Factory<>(); + public static final Serializer SERIALIZER = new Serializer<>(); protected final Ingredient ingredient; public CustomStoneCuttingRecipe(Key id, String group, Ingredient ingredient, CustomRecipeResult result) { @@ -27,7 +26,7 @@ public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { @Override public List> ingredientsInUse() { - return List.of(ingredient); + return List.of(this.ingredient); } @Override @@ -41,17 +40,22 @@ public class CustomStoneCuttingRecipe extends AbstractGroupedRecipe { } public Ingredient ingredient() { - return ingredient; + return this.ingredient; } - public static class Factory extends AbstractRecipeFactory { + public static class Serializer extends AbstractRecipeSerializer> { @SuppressWarnings({"DuplicatedCode"}) @Override - public Recipe create(Key id, Map arguments) { + public CustomStoneCuttingRecipe readMap(Key id, Map arguments) { String group = arguments.containsKey("group") ? arguments.get("group").toString() : null; - Set holders = ingredientHolders(arguments); - return new CustomStoneCuttingRecipe<>(id, group, Ingredient.of(holders), parseResult(arguments)); + return new CustomStoneCuttingRecipe<>(id, group, singleInputIngredient(arguments), parseResult(arguments)); + } + + @Override + public CustomStoneCuttingRecipe readJson(Key id, JsonObject json) { + String group = VANILLA_RECIPE_HELPER.readGroup(json); + return new CustomStoneCuttingRecipe<>(id, group, toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("ingredient"))), parseResult(VANILLA_RECIPE_HELPER.stoneCuttingResult(json))); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java new file mode 100644 index 000000000..4f2f388e4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/DatapackRecipeResult.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.item.recipe; + +import com.google.gson.JsonObject; + +public record DatapackRecipeResult(String id, int count, JsonObject components) { + + public boolean isCustom() { + return components != null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java index db368e605..6d2f1ce02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Ingredient.java @@ -6,10 +6,17 @@ import java.util.*; import java.util.function.Predicate; public class Ingredient implements Predicate>, StackedContents.IngredientInfo { + // 自定义物品与原版物品混合的列表 private final List items; + // 自定义物品原版材质与原版物品混合的列表 + private final List vanillaItems; + // ingredient里是否含有自定义物品 + private final boolean hasCustomItem; - public Ingredient(List items) { - this.items = items; + private Ingredient(List items, List vanillaItems, boolean hasCustomItem) { + this.items = List.copyOf(items); + this.vanillaItems = List.copyOf(vanillaItems); + this.hasCustomItem = hasCustomItem; } public static boolean isInstance(Optional> optionalIngredient, UniqueIdItem stack) { @@ -17,12 +24,12 @@ public class Ingredient implements Predicate>, StackedContent .orElseGet(stack::isEmpty); } - public static Ingredient of(List items) { - return new Ingredient<>(items); + public static Ingredient of(Set items, Set minecraftItems, boolean hasCustomItem) { + return new Ingredient<>(new ArrayList<>(items), new ArrayList<>(minecraftItems), hasCustomItem); } - public static Ingredient of(Set items) { - return new Ingredient<>(new ArrayList<>(items)); + public boolean hasCustomItem() { + return hasCustomItem; } @Override @@ -39,6 +46,10 @@ public class Ingredient implements Predicate>, StackedContent return this.items; } + public List minecraftItems() { + return vanillaItems; + } + @Override public String toString() { StringJoiner joiner = new StringJoiner(", "); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java index 2068a735b..db3d2f675 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/Recipe.java @@ -21,4 +21,8 @@ public interface Recipe { RecipeType type(); Key id(); + + default boolean showNotification() { + return true; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java deleted file mode 100644 index 563b8ca1c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.UniqueKey; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public interface RecipeFactory { - - Recipe create(Key id, Map arguments); - - @SuppressWarnings({"unchecked", "rawtypes"}) - default CustomRecipeResult parseResult(Map arguments) { - Map resultMap = MiscUtils.castToMap(arguments.get("result"), true); - if (resultMap == null) { - throw new LocalizedResourceConfigException("warning.config.recipe.missing_result"); - } - String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id"); - int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count"); - return new CustomRecipeResult( - CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow( - () -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id)), - count, - null - ); - } - - default Ingredient toIngredient(List items) { - Set holders = new HashSet<>(); - for (String item : items) { - if (item.charAt(0) == '#') { - holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1)))); - } else { - holders.add(UniqueKey.create(Key.of(item))); - } - } - return holders.isEmpty() ? null : Ingredient.of(holders); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java new file mode 100644 index 000000000..f307bbb45 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializer.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.item.recipe; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.Key; + +import java.util.Map; + +public interface RecipeSerializer> { + + R readMap(Key id, Map arguments); + + R readJson(Key id, JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java index 4ba3385db..0d3fe7b12 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeSerializers.java @@ -23,31 +23,34 @@ public final class RecipeSerializers { public static final Key BREWING = Key.of("minecraft:brewing"); static { - register(SHAPED, CustomShapedRecipe.FACTORY); - register(SHAPELESS, CustomShapelessRecipe.FACTORY); - register(SMELTING, CustomSmeltingRecipe.FACTORY); - register(SMOKING, CustomSmokingRecipe.FACTORY); - register(BLASTING, CustomBlastingRecipe.FACTORY); - register(CAMPFIRE_COOKING, CustomCampfireRecipe.FACTORY); - register(STONECUTTING, CustomStoneCuttingRecipe.FACTORY); - register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.FACTORY); - register(SMITHING_TRIM, CustomSmithingTrimRecipe.FACTORY); - register(BREWING, CustomBrewingRecipe.FACTORY); + register(SHAPED, CustomShapedRecipe.SERIALIZER); + register(Key.of("crafting_shaped"), CustomShapedRecipe.SERIALIZER); + register(SHAPELESS, CustomShapelessRecipe.SERIALIZER); + register(Key.of("crafting_shapeless"), CustomShapelessRecipe.SERIALIZER); + register(SMELTING, CustomSmeltingRecipe.SERIALIZER); + register(SMOKING, CustomSmokingRecipe.SERIALIZER); + register(BLASTING, CustomBlastingRecipe.SERIALIZER); + register(CAMPFIRE_COOKING, CustomCampfireRecipe.SERIALIZER); + register(STONECUTTING, CustomStoneCuttingRecipe.SERIALIZER); + register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.SERIALIZER); + register(SMITHING_TRIM, CustomSmithingTrimRecipe.SERIALIZER); + register(BREWING, CustomBrewingRecipe.SERIALIZER); } - public static void register(Key key, RecipeFactory factory) { - ((WritableRegistry>) BuiltInRegistries.RECIPE_FACTORY) - .register(ResourceKey.create(Registries.RECIPE_FACTORY.location(), key), factory); + @SuppressWarnings({"unchecked", "rawtypes"}) + public static > void register(Key key, RecipeSerializer serializer) { + WritableRegistry> registry = (WritableRegistry) BuiltInRegistries.RECIPE_SERIALIZER; + registry.register(ResourceKey.create(Registries.RECIPE_FACTORY.location(), key), serializer); } @SuppressWarnings("unchecked") - public static Recipe fromMap(Key id, Map map) { + public static > Recipe fromMap(Key id, Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.recipe.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); - RecipeFactory factory = (RecipeFactory) BuiltInRegistries.RECIPE_FACTORY.getValue(key); + RecipeSerializer factory = (RecipeSerializer) BuiltInRegistries.RECIPE_SERIALIZER.getValue(key); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.recipe.invalid_type", type); } - return factory.create(id, map); + return factory.readMap(id, map); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java index 3166c9d00..7e60308d1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/UniqueIdItem.java @@ -8,9 +8,13 @@ public class UniqueIdItem { private final Item rawItem; private final UniqueKey uniqueId; - public UniqueIdItem(@NotNull UniqueKey uniqueId, @NotNull Item rawItem) { - this.uniqueId = uniqueId; + private UniqueIdItem(@NotNull Item rawItem) { this.rawItem = rawItem; + this.uniqueId = rawItem.recipeIngredientId(); + } + + public static UniqueIdItem of(Item rawItem) { + return new UniqueIdItem<>(rawItem); } @NotNull @@ -28,7 +32,7 @@ public class UniqueIdItem { } public boolean isEmpty() { - return this.uniqueId == UniqueKey.AIR; + return this.uniqueId == null; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java index ec1757f31..1ab863933 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/SmithingInput.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.item.recipe.input; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public final class SmithingInput implements RecipeInput { private final UniqueIdItem base; @@ -10,8 +9,8 @@ public final class SmithingInput implements RecipeInput { private final UniqueIdItem addition; public SmithingInput(@NotNull UniqueIdItem base, - @Nullable UniqueIdItem template, - @Nullable UniqueIdItem addition) { + @NotNull UniqueIdItem template, + @NotNull UniqueIdItem addition) { this.base = base; this.template = template; this.addition = addition; @@ -22,12 +21,12 @@ public final class SmithingInput implements RecipeInput { return base; } - @Nullable + @NotNull public UniqueIdItem template() { return template; } - @Nullable + @NotNull public UniqueIdItem addition() { return addition; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java new file mode 100644 index 000000000..4cf75aa23 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20.java @@ -0,0 +1,148 @@ +package net.momirealms.craftengine.core.item.recipe.reader; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class VanillaRecipeReader1_20 implements VanillaRecipeReader { + + @Override + public @NotNull DatapackRecipeResult cookingResult(JsonElement object) { + return new DatapackRecipeResult(object.getAsString(), 1, null); + } + + @Override + public @NotNull DatapackRecipeResult craftingResult(JsonObject object) { + String item = object.get("item").getAsString(); + int count = object.has("count") ? object.get("count").getAsInt() : 1; + return new DatapackRecipeResult(item, count, null); + } + + @Override + public @NotNull DatapackRecipeResult smithingResult(JsonObject object) { + String item = object.get("item").getAsString(); + return new DatapackRecipeResult(item, 1, null); + } + + @Override + public List> shapelessIngredients(JsonArray json) { + List> ingredients = new ArrayList<>(); + for (JsonElement element : json) { + if (element.isJsonObject()) { + JsonObject jsonObject = element.getAsJsonObject(); + if (jsonObject.has("item")) { + ingredients.add(List.of(jsonObject.get("item").getAsString())); + } else if (jsonObject.has("tag")) { + ingredients.add(List.of("#" + jsonObject.get("tag").getAsString())); + } + } else if (element.isJsonArray()) { + List ingredient = ingredientList((JsonArray) element); + ingredients.add(ingredient); + } + } + return ingredients; + } + + @Override + public Map> shapedIngredientMap(JsonObject json) { + Map> ingredients = new HashMap<>(); + for (Map.Entry entry : json.entrySet()) { + char c = entry.getKey().charAt(0); + if (entry.getValue().isJsonObject()) { + JsonObject argument = entry.getValue().getAsJsonObject(); + if (argument.has("item")) { + ingredients.put(c, List.of(argument.get("item").getAsString())); + } else if (argument.has("tag")) { + ingredients.put(c, List.of("#" + argument.get("tag").getAsString())); + } + } else if (entry.getValue().isJsonArray()) { + List items = ingredientList((JsonArray) entry.getValue()); + ingredients.put(c, items); + } + } + return ingredients; + } + + @Override + public @NotNull List ingredientList(JsonArray array) { + List items = new ArrayList<>(); + for (JsonElement element : array) { + if (element.isJsonObject()) { + JsonObject argument = element.getAsJsonObject(); + if (argument.has("item")) { + items.add(argument.get("item").getAsString()); + } else if (argument.has("tag")) { + items.add("#" + argument.get("tag").getAsString()); + } + } + } + return items; + } + + @Override + public String[] craftingShapedPattern(JsonObject object) { + JsonArray pattern = object.getAsJsonArray("pattern"); + List patternList = new ArrayList<>(); + for (JsonElement element : pattern) { + patternList.add(element.getAsString()); + } + return patternList.toArray(new String[0]); + } + + @Override + public @Nullable String readGroup(JsonObject object) { + return object.has("group") ? object.get("group").getAsString() : null; + } + + @Override + public @NotNull CraftingRecipeCategory craftingCategory(JsonObject object) { + return object.has("category") ? CraftingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : CraftingRecipeCategory.MISC; + } + + @Override + public @NotNull CookingRecipeCategory cookingCategory(JsonObject object) { + return object.has("category") ? CookingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : CookingRecipeCategory.MISC; + } + + @Override + public float cookingExperience(JsonObject object) { + return object.has("experience") ? object.get("experience").getAsFloat() : 0; + } + + @Override + public int cookingTime(JsonObject object) { + return object.has("cookingtime") ? object.get("cookingtime").getAsInt() : 200; + } + + @Override + public @NotNull DatapackRecipeResult stoneCuttingResult(JsonObject json) { + int count = json.has("count") ? json.get("count").getAsInt() : 1; + String result = json.get("result").getAsString(); + return new DatapackRecipeResult(result, count, null); + } + + @Override + public List singleIngredient(JsonElement json) { + List ingredients = new ArrayList<>(); + if (json.isJsonObject()) { + JsonObject argument = json.getAsJsonObject(); + if (argument.has("item")) { + ingredients.add(argument.get("item").getAsString()); + } else if (argument.has("tag")) { + ingredients.add("#" + argument.get("tag").getAsString()); + } + } else if (json.isJsonArray()) { + List items = ingredientList((JsonArray) json); + ingredients.addAll(items); + } + return ingredients; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java new file mode 100644 index 000000000..806766772 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_20_5.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.item.recipe.reader; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import org.jetbrains.annotations.NotNull; + +public class VanillaRecipeReader1_20_5 extends VanillaRecipeReader1_20 { + + @Override + public @NotNull DatapackRecipeResult craftingResult(JsonObject object) { + String item = object.get("id").getAsString(); + JsonObject components = object.has("components") ? object.getAsJsonObject("components") : null; + int count = object.has("count") ? object.get("count").getAsInt() : 1; + return new DatapackRecipeResult(item, count, components); + } + + @NotNull + @Override + public DatapackRecipeResult cookingResult(JsonElement object) { + return craftingResult(object.getAsJsonObject()); + } + + @NotNull + @Override + public DatapackRecipeResult stoneCuttingResult(JsonObject json) { + return craftingResult(json.getAsJsonObject("result")); + } + + @Override + public @NotNull DatapackRecipeResult smithingResult(JsonObject object) { + return craftingResult(object.getAsJsonObject()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java rename to core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java index eafec4a5a..bacb8037d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_2.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/reader/VanillaRecipeReader1_21_2.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; +package net.momirealms.craftengine.core.item.recipe.reader; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -12,7 +12,7 @@ import java.util.Map; public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { @Override - protected List readSingleIngredient(JsonElement json) { + public List singleIngredient(JsonElement json) { if (json.isJsonPrimitive()) { return List.of(json.getAsString()); } else { @@ -26,7 +26,7 @@ public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { } @Override - protected Map> readShapedIngredientMap(JsonObject json) { + public Map> shapedIngredientMap(JsonObject json) { Map> ingredients = new HashMap<>(); for (Map.Entry entry : json.entrySet()) { char c = entry.getKey().charAt(0); @@ -44,7 +44,7 @@ public class VanillaRecipeReader1_21_2 extends VanillaRecipeReader1_20_5 { } @Override - protected List> readShapelessIngredients(JsonArray json) { + public List> shapelessIngredients(JsonArray json) { List> ingredients = new ArrayList<>(); for (JsonElement element : json) { if (element.isJsonPrimitive()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java deleted file mode 100644 index a00730bb0..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/RecipeResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla; - -import com.google.gson.JsonObject; - -public record RecipeResult(String id, int count, JsonObject components) { - - public boolean isCustom() { - return components != null; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java index bacbaa854..9a81d0698 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaBlastingRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -8,7 +9,7 @@ import java.util.List; public class VanillaBlastingRecipe extends VanillaCookingRecipe { - public VanillaBlastingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { + public VanillaBlastingRecipe(CookingRecipeCategory category, String group, DatapackRecipeResult result, List ingredient, float experience, int cookingTime) { super(category, group, result, ingredient, experience, cookingTime); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java index 5a2b1624a..ae53346b0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCampfireRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -8,7 +9,7 @@ import java.util.List; public class VanillaCampfireRecipe extends VanillaCookingRecipe { - public VanillaCampfireRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { + public VanillaCampfireRecipe(CookingRecipeCategory category, String group, DatapackRecipeResult result, List ingredient, float experience, int cookingTime) { super(category, group, result, ingredient, experience, cookingTime); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java index 93d91f943..a87f385f7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCookingRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import java.util.List; @@ -10,7 +11,7 @@ public abstract class VanillaCookingRecipe extends VanillaGroupedRecipe { protected final float experience; protected final int cookingTime; - protected VanillaCookingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { + protected VanillaCookingRecipe(CookingRecipeCategory category, String group, DatapackRecipeResult result, List ingredient, float experience, int cookingTime) { super(group, result); this.ingredient = ingredient; this.experience = experience; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java index d8cd3bdb5..80b19b799 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaCraftingRecipe.java @@ -1,11 +1,12 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; public abstract class VanillaCraftingRecipe extends VanillaGroupedRecipe { protected final CraftingRecipeCategory category; - protected VanillaCraftingRecipe(CraftingRecipeCategory category, String group, RecipeResult result) { + protected VanillaCraftingRecipe(CraftingRecipeCategory category, String group, DatapackRecipeResult result) { super(group, result); this.category = category; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java index 99d4e9aed..0e3c3c81b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaGroupedRecipe.java @@ -1,10 +1,12 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; + public abstract class VanillaGroupedRecipe implements VanillaRecipe { protected final String group; - protected final RecipeResult result; + protected final DatapackRecipeResult result; - protected VanillaGroupedRecipe(String group, RecipeResult result) { + protected VanillaGroupedRecipe(String group, DatapackRecipeResult result) { this.group = group; this.result = result; } @@ -13,7 +15,7 @@ public abstract class VanillaGroupedRecipe implements VanillaRecipe { return group; } - public RecipeResult result() { + public DatapackRecipeResult result() { return result; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java index 343d9b06a..4b09b88c0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaRecipeReader.java @@ -1,24 +1,47 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; public interface VanillaRecipeReader { - VanillaShapedRecipe readShaped(JsonObject json); + @NotNull DatapackRecipeResult cookingResult(JsonElement object); - VanillaShapelessRecipe readShapeless(JsonObject json); + @NotNull DatapackRecipeResult craftingResult(JsonObject object); - VanillaBlastingRecipe readBlasting(JsonObject json); + @NotNull DatapackRecipeResult smithingResult(JsonObject object); - VanillaSmeltingRecipe readSmelting(JsonObject json); + List> shapelessIngredients(JsonArray json); - VanillaSmokingRecipe readSmoking(JsonObject json); + Map> shapedIngredientMap(JsonObject json); - VanillaCampfireRecipe readCampfire(JsonObject json); + @NotNull List ingredientList(JsonArray array); - VanillaStoneCuttingRecipe readStoneCutting(JsonObject json); + String[] craftingShapedPattern(JsonObject object); - VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json); + @Nullable + String readGroup(JsonObject object); - VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json); + @NotNull + CraftingRecipeCategory craftingCategory(JsonObject object); + + @NotNull + CookingRecipeCategory cookingCategory(JsonObject object); + + float cookingExperience(JsonObject object); + + int cookingTime(JsonObject object); + + @NotNull DatapackRecipeResult stoneCuttingResult(JsonObject json); + + List singleIngredient(JsonElement json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java index bedb18ef2..7b40adea4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapedRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -15,7 +16,7 @@ public class VanillaShapedRecipe extends VanillaCraftingRecipe { String group, Map> key, String[] pattern, - RecipeResult result) { + DatapackRecipeResult result) { super(category, group, result); this.key = key; this.pattern = pattern; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java index 1c2c912c3..54e9a01ba 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaShapelessRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -9,7 +10,7 @@ import java.util.List; public class VanillaShapelessRecipe extends VanillaCraftingRecipe { private final List> ingredients; - public VanillaShapelessRecipe(CraftingRecipeCategory category, String group, List> ingredients, RecipeResult result) { + public VanillaShapelessRecipe(CraftingRecipeCategory category, String group, List> ingredients, DatapackRecipeResult result) { super(category, group, result); this.ingredients = ingredients; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java index 34db78614..f5bca29dc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmeltingRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -8,7 +9,7 @@ import java.util.List; public class VanillaSmeltingRecipe extends VanillaCookingRecipe { - public VanillaSmeltingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { + public VanillaSmeltingRecipe(CookingRecipeCategory category, String group, DatapackRecipeResult result, List ingredient, float experience, int cookingTime) { super(category, group, result, ingredient, experience, cookingTime); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java index 213d352e8..8452b3f22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmithingTransformRecipe.java @@ -1,17 +1,18 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; import java.util.List; public class VanillaSmithingTransformRecipe implements VanillaRecipe { - private final RecipeResult result; + private final DatapackRecipeResult result; private final List base; private final List template; private final List addition; - public VanillaSmithingTransformRecipe(List base, List template, List addition, RecipeResult result) { + public VanillaSmithingTransformRecipe(List base, List template, List addition, DatapackRecipeResult result) { this.result = result; this.base = base; this.template = template; @@ -23,7 +24,7 @@ public class VanillaSmithingTransformRecipe implements VanillaRecipe { return RecipeSerializers.SMITHING_TRANSFORM; } - public RecipeResult result() { + public DatapackRecipeResult result() { return result; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java index fbb262398..cc22dea5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaSmokingRecipe.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -8,7 +9,7 @@ import java.util.List; public class VanillaSmokingRecipe extends VanillaCookingRecipe { - public VanillaSmokingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List ingredient, float experience, int cookingTime) { + public VanillaSmokingRecipe(CookingRecipeCategory category, String group, DatapackRecipeResult result, List ingredient, float experience, int cookingTime) { super(category, group, result, ingredient, experience, cookingTime); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java index 6770a90c9..a273f5e04 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/VanillaStoneCuttingRecipe.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item.recipe.vanilla; +import net.momirealms.craftengine.core.item.recipe.DatapackRecipeResult; import net.momirealms.craftengine.core.item.recipe.RecipeSerializers; import net.momirealms.craftengine.core.util.Key; @@ -8,7 +9,7 @@ import java.util.List; public class VanillaStoneCuttingRecipe extends VanillaGroupedRecipe { private final List ingredient; - public VanillaStoneCuttingRecipe(String group, RecipeResult result, List ingredient) { + public VanillaStoneCuttingRecipe(String group, DatapackRecipeResult result, List ingredient) { super(group, result); this.ingredient = ingredient; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java deleted file mode 100644 index 9422071a2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/AbstractRecipeReader.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaRecipeReader; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public abstract class AbstractRecipeReader implements VanillaRecipeReader { - - protected String[] readPattern(JsonObject object) { - JsonArray pattern = object.getAsJsonArray("pattern"); - List patternList = new ArrayList<>(); - for (JsonElement element : pattern) { - patternList.add(element.getAsString()); - } - return patternList.toArray(new String[0]); - } - - @Nullable - protected String readGroup(JsonObject object) { - return object.has("group") ? object.get("group").getAsString() : null; - } - - @Nullable - protected CraftingRecipeCategory readCraftingCategory(JsonObject object) { - return object.has("category") ? CraftingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : null; - } - - @Nullable - protected CookingRecipeCategory readCookingCategory(JsonObject object) { - return object.has("category") ? CookingRecipeCategory.valueOf(object.get("category").getAsString().toUpperCase(Locale.ENGLISH)) : null; - } - - protected float readExperience(JsonObject object) { - return object.has("experience") ? object.get("experience").getAsFloat() : 0; - } - - protected int readCookingTime(JsonObject object) { - return object.has("cookingtime") ? object.get("cookingtime").getAsInt() : 200; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java deleted file mode 100644 index d45c8e5a2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20.java +++ /dev/null @@ -1,206 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.*; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class VanillaRecipeReader1_20 extends AbstractRecipeReader { - - @Override - public VanillaShapedRecipe readShaped(JsonObject json) { - return new VanillaShapedRecipe( - readCraftingCategory(json), - readGroup(json), - readShapedIngredientMap(json.getAsJsonObject("key")), - readPattern(json), - readCraftingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaShapelessRecipe readShapeless(JsonObject json) { - return new VanillaShapelessRecipe( - readCraftingCategory(json), - readGroup(json), - readShapelessIngredients(json.getAsJsonArray("ingredients")), - readCraftingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaBlastingRecipe readBlasting(JsonObject json) { - return new VanillaBlastingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaSmeltingRecipe readSmelting(JsonObject json) { - return new VanillaSmeltingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaSmokingRecipe readSmoking(JsonObject json) { - return new VanillaSmokingRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaCampfireRecipe readCampfire(JsonObject json) { - return new VanillaCampfireRecipe( - readCookingCategory(json), - readGroup(json), - readCookingResult(json.get("result")), - readSingleIngredient(json.get("ingredient")), - readExperience(json), - readCookingTime(json) - ); - } - - @Override - public VanillaStoneCuttingRecipe readStoneCutting(JsonObject json) { - return new VanillaStoneCuttingRecipe( - readGroup(json), - readStoneCuttingResult(json), - readSingleIngredient(json.get("ingredient")) - ); - } - - @Override - public VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json) { - return new VanillaSmithingTransformRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - readSmithingResult(json.getAsJsonObject("result")) - ); - } - - @Override - public VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json) { - return new VanillaSmithingTrimRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - null - ); - } - - protected List readSingleIngredient(JsonElement json) { - List ingredients = new ArrayList<>(); - if (json.isJsonObject()) { - JsonObject argument = json.getAsJsonObject(); - if (argument.has("item")) { - ingredients.add(argument.get("item").getAsString()); - } else if (argument.has("tag")) { - ingredients.add("#" + argument.get("tag").getAsString()); - } - } else if (json.isJsonArray()) { - List items = readIngredientList((JsonArray) json); - ingredients.addAll(items); - } - return ingredients; - } - - @NotNull - protected RecipeResult readStoneCuttingResult(JsonObject json) { - int count = json.has("count") ? json.get("count").getAsInt() : 1; - String result = json.get("result").getAsString(); - return new RecipeResult(result, count, null); - } - - @NotNull - protected RecipeResult readCookingResult(JsonElement object) { - return new RecipeResult(object.getAsString(), 1, null); - } - - @NotNull - protected RecipeResult readCraftingResult(JsonObject object) { - String item = object.get("item").getAsString(); - int count = object.has("count") ? object.get("count").getAsInt() : 1; - return new RecipeResult(item, count, null); - } - - @NotNull - protected RecipeResult readSmithingResult(JsonObject object) { - String item = object.get("item").getAsString(); - return new RecipeResult(item, 1, null); - } - - protected List> readShapelessIngredients(JsonArray json) { - List> ingredients = new ArrayList<>(); - for (JsonElement element : json) { - if (element.isJsonObject()) { - JsonObject jsonObject = element.getAsJsonObject(); - if (jsonObject.has("item")) { - ingredients.add(List.of(jsonObject.get("item").getAsString())); - } else if (jsonObject.has("tag")) { - ingredients.add(List.of("#" + jsonObject.get("tag").getAsString())); - } - } else if (element.isJsonArray()) { - List ingredient = readIngredientList((JsonArray) element); - ingredients.add(ingredient); - } - } - return ingredients; - } - - protected Map> readShapedIngredientMap(JsonObject json) { - Map> ingredients = new HashMap<>(); - for (Map.Entry entry : json.entrySet()) { - char c = entry.getKey().charAt(0); - if (entry.getValue().isJsonObject()) { - JsonObject argument = entry.getValue().getAsJsonObject(); - if (argument.has("item")) { - ingredients.put(c, List.of(argument.get("item").getAsString())); - } else if (argument.has("tag")) { - ingredients.put(c, List.of("#" + argument.get("tag").getAsString())); - } - } else if (entry.getValue().isJsonArray()) { - List items = readIngredientList((JsonArray) entry.getValue()); - ingredients.put(c, items); - } - } - return ingredients; - } - - protected @NotNull List readIngredientList(JsonArray array) { - List items = new ArrayList<>(); - for (JsonElement element : array) { - if (element.isJsonObject()) { - JsonObject argument = element.getAsJsonObject(); - if (argument.has("item")) { - items.add(argument.get("item").getAsString()); - } else if (argument.has("tag")) { - items.add("#" + argument.get("tag").getAsString()); - } - } - } - return items; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java deleted file mode 100644 index 3bd399f96..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_20_5.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.RecipeResult; -import org.jetbrains.annotations.NotNull; - -public class VanillaRecipeReader1_20_5 extends VanillaRecipeReader1_20 { - - @Override - protected @NotNull RecipeResult readCraftingResult(JsonObject object) { - String item = object.get("id").getAsString(); - JsonObject components = object.has("components") ? object.getAsJsonObject("components") : null; - int count = object.has("count") ? object.get("count").getAsInt() : 1; - return new RecipeResult(item, count, components); - } - - @NotNull - @Override - protected RecipeResult readCookingResult(JsonElement object) { - return readCraftingResult(object.getAsJsonObject()); - } - - @NotNull - @Override - protected RecipeResult readStoneCuttingResult(JsonObject json) { - return readCraftingResult(json.getAsJsonObject("result")); - } - - @Override - protected @NotNull RecipeResult readSmithingResult(JsonObject object) { - return readCraftingResult(object.getAsJsonObject()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java deleted file mode 100644 index 49634d90e..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/vanilla/reader/VanillaRecipeReader1_21_5.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe.vanilla.reader; - -import com.google.gson.JsonObject; -import net.momirealms.craftengine.core.item.recipe.vanilla.VanillaSmithingTrimRecipe; - -public class VanillaRecipeReader1_21_5 extends VanillaRecipeReader1_21_2 { - - @Override - public VanillaSmithingTrimRecipe readSmithingTrim(JsonObject json) { - return new VanillaSmithingTrimRecipe( - readSingleIngredient(json.get("base")), - readSingleIngredient(json.get("template")), - readSingleIngredient(json.get("addition")), - json.get("pattern").getAsString() - ); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index a864ca257..fd0da1023 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -9,7 +9,8 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; import net.momirealms.craftengine.core.item.recipe.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeFactory; +import net.momirealms.craftengine.core.item.recipe.Recipe; +import net.momirealms.craftengine.core.item.recipe.RecipeSerializer; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipe; import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; import net.momirealms.craftengine.core.item.recipe.network.modern.display.slot.SlotDisplay; @@ -63,7 +64,7 @@ public class BuiltInRegistries { public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); - public static final Registry> RECIPE_FACTORY = createConstantBoundRegistry(Registries.RECIPE_FACTORY); + public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index fe447e488..e0845e4ff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -9,7 +9,8 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; import net.momirealms.craftengine.core.item.recipe.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; -import net.momirealms.craftengine.core.item.recipe.RecipeFactory; +import net.momirealms.craftengine.core.item.recipe.Recipe; +import net.momirealms.craftengine.core.item.recipe.RecipeSerializer; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipe; import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay; import net.momirealms.craftengine.core.item.recipe.network.modern.display.slot.SlotDisplay; @@ -65,7 +66,7 @@ public class Registries { public static final ResourceKey> CONDITION_PROPERTY_READER = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("condition_property_reader")); public static final ResourceKey> SELECT_PROPERTY_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_factory")); public static final ResourceKey> SELECT_PROPERTY_READER = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_reader")); - public static final ResourceKey>> RECIPE_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_factory")); + public static final ResourceKey>>> RECIPE_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_factory")); public static final ResourceKey> FORMULA_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("formula_factory")); public static final ResourceKey>> PATH_MATCHER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory")); public static final ResourceKey> RESOLUTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory")); diff --git a/gradle.properties b/gradle.properties index 837b678ab..5ac011f68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.60.10 +project_version=0.0.60.10.3 config_version=43 lang_version=23 project_group=net.momirealms @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.18 -nms_helper_version=1.0.48 +nms_helper_version=1.0.51 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23