9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-30 04:19:27 +00:00

重构特殊配方

This commit is contained in:
XiaoMoMi
2025-08-02 00:41:55 +08:00
parent 169f0c9973
commit f1da4dc4a1
8 changed files with 317 additions and 121 deletions

View File

@@ -468,7 +468,7 @@ public class ItemEventListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) {
if (event.getEntity() instanceof org.bukkit.entity.Item item) {
Optional.ofNullable(this.plugin.itemManager().wrap(item.getItemStack()))
Optional.of(this.plugin.itemManager().wrap(item.getItemStack()))
.flatMap(Item::getCustomItem)
.ifPresent(it -> {
if (it.settings().invulnerable().contains(DamageCauseUtils.fromBukkit(event.getCause()))) {

View File

@@ -88,9 +88,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try {
Key dyeRecipeId = Key.from("armor_dye");
MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId);
MINECRAFT_RECIPE_ADDER.accept(dyeRecipeId, RecipeInjector.createCustomDyeRecipe());
MINECRAFT_RECIPE_ADDER.accept(dyeRecipeId, RecipeInjector.createCustomDyeRecipe(dyeRecipeId));
Key repairRecipeId = Key.from("repair_item");
MINECRAFT_RECIPE_REMOVER.accept(repairRecipeId);
MINECRAFT_RECIPE_ADDER.accept(repairRecipeId, RecipeInjector.createRepairItemRecipe(repairRecipeId));
Key fireworkStarFadeRecipeId = Key.from("firework_star_fade");
MINECRAFT_RECIPE_REMOVER.accept(fireworkStarFadeRecipeId);
MINECRAFT_RECIPE_ADDER.accept(fireworkStarFadeRecipeId, RecipeInjector.createFireworkStarFadeRecipe(fireworkStarFadeRecipeId));
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to inject dye recipes", e);
throw new ReflectionInitException("Failed to inject special recipes", e);
}
}

View File

@@ -481,7 +481,7 @@ public class RecipeEventListener implements Listener {
// 如果禁止在铁砧使用两个相同物品修复
firstCustom.ifPresent(it -> {
if (!it.settings().canRepair()) {
if (it.settings().canRepair() == Tristate.FALSE) {
event.setResult(null);
}
});
@@ -522,7 +522,7 @@ public class RecipeEventListener implements Listener {
Key firstId = wrappedFirst.id();
Optional<CustomItem<ItemStack>> optionalCustomTool = wrappedFirst.getCustomItem();
// 物品无法被修复
if (optionalCustomTool.isPresent() && !optionalCustomTool.get().settings().canRepair()) {
if (optionalCustomTool.isPresent() && optionalCustomTool.get().settings().canRepair() == Tristate.FALSE) {
return;
}
@@ -679,93 +679,52 @@ public class RecipeEventListener implements Listener {
}
// only handle repair items for the moment
@EventHandler(ignoreCancelled = true)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onSpecialRecipe(PrepareItemCraftEvent event) {
// if (!ConfigManager.enableRecipeSystem()) return;
org.bukkit.inventory.Recipe recipe = event.getRecipe();
if (recipe == null)
return;
if (!(recipe instanceof ComplexRecipe complexRecipe))
return;
CraftingInventory inventory = event.getInventory();
ItemStack result = inventory.getResult();
if (ItemStackUtils.isEmpty(result))
return;
boolean hasCustomItem = ItemStackUtils.hasCustomItem(inventory.getMatrix());
if (!hasCustomItem) {
if (!hasCustomItem)
return;
}
if (!CraftBukkitReflections.clazz$CraftComplexRecipe.isInstance(complexRecipe)) {
inventory.setResult(null);
return;
}
try {
// TODO 全部改注入
Object mcRecipe = CraftBukkitReflections.field$CraftComplexRecipe$recipe.get(complexRecipe);
if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe)) {
if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) {
return;
}
// Repair recipe
// 处理修复配方,在此处理才能使用玩家参数构建物品
if (CoreReflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) {
// repair item
ItemStack[] itemStacks = inventory.getMatrix();
Pair<ItemStack, ItemStack> onlyTwoItems = getTheOnlyTwoItem(itemStacks);
if (onlyTwoItems.left() == null || onlyTwoItems.right() == null) {
inventory.setResult(null);
return;
}
Item<ItemStack> left = plugin.itemManager().wrap(onlyTwoItems.left());
Item<ItemStack> right = plugin.itemManager().wrap(onlyTwoItems.right());
if (!left.id().equals(right.id())) {
inventory.setResult(null);
return;
}
int totalDamage = right.damage().orElse(0) + left.damage().orElse(0);
int totalMaxDamage = left.maxDamage() + right.maxDamage();
// should be impossible, but take care
if (totalDamage >= totalMaxDamage) {
inventory.setResult(null);
return;
}
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(left.id());
Pair<ItemStack, ItemStack> theOnlyTwoItem = getTheOnlyTwoItem(inventory.getMatrix());
if (theOnlyTwoItem == null) return;
Item<ItemStack> first = BukkitItemManager.instance().wrap(theOnlyTwoItem.left());
Item<ItemStack> right = BukkitItemManager.instance().wrap(theOnlyTwoItem.right());
int max = Math.max(first.maxDamage(), right.maxDamage());
int durability1 = first.maxDamage() - first.damage().orElse(0);
int durability2 = right.maxDamage() - right.damage().orElse(0);
int finalDurability = durability1 + durability2 + max * 5 / 100;
Optional<CustomItem<ItemStack>> customItemOptional = plugin.itemManager().getCustomItem(first.id());
if (customItemOptional.isEmpty()) {
inventory.setResult(null);
return;
}
CustomItem<ItemStack> customItem = customItemOptional.get();
if (!customItem.settings().canRepair()) {
inventory.setResult(null);
return;
}
Item<ItemStack> newItem = customItem.buildItem(ItemBuildContext.of(plugin.adapt(player)));
int remainingDurability = totalMaxDamage - totalDamage;
int newItemDamage = Math.max(0, newItem.maxDamage() - remainingDurability);
newItem.damage(newItemDamage);
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
Item<ItemStack> newItem = customItemOptional.get().buildItem(plugin.adapt(player));
newItem.maxDamage(max);
newItem.damage(Math.max(max - finalDurability, 0));
inventory.setResult(newItem.getItem());
} else if (CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) {
ItemStack[] itemStacks = inventory.getMatrix();
for (ItemStack itemStack : itemStacks) {
if (itemStack == null) continue;
Item<ItemStack> item = plugin.itemManager().wrap(itemStack);
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
if (optionalCustomItem.isPresent() && optionalCustomItem.get().settings().dyeable() == Tristate.FALSE) {
inventory.setResult(null);
return;
}
}
} else {
inventory.setResult(null);
return;
}
// 其他配方不允许使用自定义物品
inventory.setResult(null);
} catch (Exception e) {
this.plugin.logger().warn("Failed to handle minecraft custom recipe", e);
this.plugin.logger().warn("Failed to handle custom recipe", e);
}
}
@@ -776,7 +735,10 @@ public class RecipeEventListener implements Listener {
if (itemStack == null) continue;
if (first == null) {
first = itemStack;
} else if (second == null) {
} else {
if (second != null) {
return null;
}
second = itemStack;
}
}

View File

@@ -1,8 +1,11 @@
package net.momirealms.craftengine.bukkit.plugin.injector;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.ints.IntArrayList;
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;
@@ -10,8 +13,10 @@ 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;
@@ -21,12 +26,15 @@ 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.RecipeTypes;
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;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
@@ -40,6 +48,8 @@ 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);
@@ -48,64 +58,96 @@ public class RecipeInjector {
.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", Key.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)
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<MethodDescription> matches = (VersionHelper.isOrAbove1_21() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$Level) :
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$Level)
).and(ElementMatchers.returns(boolean.class));
ElementMatcher.Junction<MethodDescription> assemble = (
VersionHelper.isOrAbove1_21() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$HolderLookup$Provider) :
VersionHelper.isOrAbove1_20_5() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$HolderLookup$Provider) :
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$RegistryAccess)
).and(ElementMatchers.returns(CoreReflections.clazz$ItemStack));
clazz$InjectedArmorDyeRecipe = byteBuddy
.subclass(CoreReflections.clazz$ArmorDyeRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
.name("net.momirealms.craftengine.bukkit.item.recipe.ArmorDyeRecipe")
.method((VersionHelper.isOrAbove1_21() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$Level) :
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$Level)
).and(ElementMatchers.returns(boolean.class)))
.intercept(MethodDelegation.to(MatchesInterceptor.INSTANCE))
.method((
VersionHelper.isOrAbove1_21() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingInput, CoreReflections.clazz$HolderLookup$Provider) :
VersionHelper.isOrAbove1_20_5() ?
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$HolderLookup$Provider) :
ElementMatchers.takesArguments(CoreReflections.clazz$CraftingContainer, CoreReflections.clazz$RegistryAccess)
).and(ElementMatchers.returns(CoreReflections.clazz$ItemStack)))
.intercept(MethodDelegation.to(AssembleInterceptor.INSTANCE))
.method(matches)
.intercept(MethodDelegation.to(DyeMatchesInterceptor.INSTANCE))
.method(assemble)
.intercept(MethodDelegation.to(DyeAssembleInterceptor.INSTANCE))
.make()
.load(RecipeInjector.class.getClassLoader())
.getLoaded();
clazz$InjectedFireworkStarFadeRecipe = byteBuddy
.subclass(CoreReflections.clazz$FireworkStarFadeRecipe)
.name("net.momirealms.craftengine.bukkit.item.recipe.FireworkStarFadeRecipe")
.method(matches)
.intercept(MethodDelegation.to(FireworkStarFadeMatchesInterceptor.INSTANCE))
.method(assemble)
.intercept(MethodDelegation.to(FireworkStarFadeAssembleInterceptor.INSTANCE))
.make()
.load(RecipeInjector.class.getClassLoader())
.getLoaded();
clazz$InjectedRepairItemRecipe = byteBuddy
.subclass(CoreReflections.clazz$RepairItemRecipe, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
.name("net.momirealms.craftengine.bukkit.item.recipe.RepairItemRecipe")
// 只修改match逻辑合并需要在事件里处理否则无法应用变量
.method(matches)
.intercept(MethodDelegation.to(RepairMatchesInterceptor.INSTANCE))
.make()
.load(RecipeInjector.class.getClassLoader())
.getLoaded();
}
public static Object createCustomDyeRecipe() throws ReflectiveOperationException {
public static Object createRepairItemRecipe(Key id) throws ReflectiveOperationException {
return createSpecialRecipe(id, clazz$InjectedRepairItemRecipe);
}
public static Object createCustomDyeRecipe(Key id) throws ReflectiveOperationException {
return createSpecialRecipe(id, clazz$InjectedArmorDyeRecipe);
}
public static Object createFireworkStarFadeRecipe(Key id) throws ReflectiveOperationException {
return createSpecialRecipe(id, clazz$InjectedFireworkStarFadeRecipe);
}
@NotNull
private static Object createSpecialRecipe(Key id, Class<?> clazz$InjectedRepairItemRecipe) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
if (VersionHelper.isOrAbove1_20_2()) {
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedArmorDyeRecipe, CoreReflections.clazz$CraftingBookCategory);
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$CraftingBookCategory);
return constructor.newInstance(CoreReflections.instance$CraftingBookCategory$MISC);
} else {
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedArmorDyeRecipe, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$CraftingBookCategory);
return constructor.newInstance(KeyUtils.toResourceLocation(Key.of("armor_dye")), CoreReflections.instance$CraftingBookCategory$MISC);
Constructor<?> constructor = ReflectionUtils.getConstructor(clazz$InjectedRepairItemRecipe, CoreReflections.clazz$ResourceLocation, CoreReflections.clazz$CraftingBookCategory);
return constructor.newInstance(KeyUtils.toResourceLocation(id), CoreReflections.instance$CraftingBookCategory$MISC);
}
}
@@ -138,29 +180,161 @@ public class RecipeInjector {
}
}
private static final Function<Object, Boolean> INGREDIENT_COUNT_CHECKER =
private static final Function<Object, Integer> INGREDIENT_SIZE_GETTER =
VersionHelper.isOrAbove1_21() ?
(input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) < 2 :
(container) -> false;
private static final Function<Object, Integer> INGREDIENT_COUNT_GETTER =
VersionHelper.isOrAbove1_21() ?
FastNMS.INSTANCE::method$CraftingInput$ingredientCount :
FastNMS.INSTANCE::method$CraftingInput$size :
FastNMS.INSTANCE::method$Container$getContainerSize;
private static final BiFunction<Object, Integer, Object> INGREDIENT_GETTER =
VersionHelper.isOrAbove1_21() ?
FastNMS.INSTANCE::method$CraftingInput$getItem :
FastNMS.INSTANCE::method$Container$getItem;
public static class MatchesInterceptor {
public static final MatchesInterceptor INSTANCE = new MatchesInterceptor();
private static final Function<Object, Boolean> REPAIR_INGREDIENT_COUNT_CHECKER =
VersionHelper.isOrAbove1_21() ?
(input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) != 2 :
(container) -> false;
public static class FireworkStarFadeMatchesInterceptor {
public static final FireworkStarFadeMatchesInterceptor INSTANCE = new FireworkStarFadeMatchesInterceptor();
@RuntimeType
public Object intercept(@AllArguments Object[] args) {
Object input = args[0];
if (INGREDIENT_COUNT_CHECKER.apply(input)) {
if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) {
return false;
}
int size = INGREDIENT_COUNT_GETTER.apply(input);
boolean hasDye = false;
boolean hasFireworkStar = false;
int size = INGREDIENT_SIZE_GETTER.apply(input);
for (int i = 0; i < size; i++) {
Object itemStack = INGREDIENT_GETTER.apply(input, i);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
continue;
}
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
if (isFireworkDye(wrapped)) {
hasDye = true;
} else {
if (!wrapped.id().equals(ItemKeys.FIREWORK_STAR)) {
return false;
}
if (hasFireworkStar) {
return false;
}
hasFireworkStar = true;
}
}
return hasDye && hasFireworkStar;
}
}
public static class FireworkStarFadeAssembleInterceptor {
public static final FireworkStarFadeAssembleInterceptor INSTANCE = new FireworkStarFadeAssembleInterceptor();
@RuntimeType
public Object intercept(@AllArguments Object[] args) {
IntList colors = new IntArrayList();
Item<ItemStack> starItem = null;
Object input = args[0];
int size = INGREDIENT_SIZE_GETTER.apply(input);
for (int i = 0; i < size; i++) {
Object itemStack = INGREDIENT_GETTER.apply(input, i);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
continue;
}
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
if (isFireworkDye(wrapped)) {
Color color = getFireworkColor(wrapped);
if (color == null) {
return CoreReflections.instance$ItemStack$EMPTY;
}
colors.add(color.color());
} else if (wrapped.id().equals(ItemKeys.FIREWORK_STAR)) {
starItem = wrapped.copyWithCount(1);
}
}
if (starItem == null || colors.isEmpty()) {
return CoreReflections.instance$ItemStack$EMPTY;
}
FireworkExplosion explosion = starItem.fireworkExplosion().orElse(FireworkExplosion.DEFAULT);
starItem.fireworkExplosion(explosion.withFadeColors(colors));
return starItem.getLiteralObject();
}
}
public static class RepairMatchesInterceptor {
public static final RepairMatchesInterceptor INSTANCE = new RepairMatchesInterceptor();
@RuntimeType
public Object intercept(@AllArguments Object[] args) {
Object input = args[0];
if (REPAIR_INGREDIENT_COUNT_CHECKER.apply(input)) {
return false;
}
return getItemsToCombine(input) != null;
}
}
@Nullable
private static Pair<Item<ItemStack>, Item<ItemStack>> getItemsToCombine(Object input) {
Item<ItemStack> item1 = null;
Item<ItemStack> item2 = null;
int size = INGREDIENT_SIZE_GETTER.apply(input);
for (int i = 0; i < size; i++) {
Object itemStack = INGREDIENT_GETTER.apply(input, i);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
continue;
}
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack));
if (item1 == null) {
item1 = wrapped;
} else {
if (item2 != null) {
return null;
}
item2 = wrapped;
}
}
if (item1 == null || item2 == null) {
return null;
}
if (!canCombine(item1, item2)) {
return null;
}
return new Pair<>(item1, item2);
}
private static boolean canCombine(Item<ItemStack> input1, Item<ItemStack> input2) {
if (input1.count() != 1 || !isDamageableItem(input1)) return false;
if (input2.count() != 1 || !isDamageableItem(input2)) return false;
if (!input1.id().equals(input2.id())) return false;
Optional<CustomItem<ItemStack>> customItem = input1.getCustomItem();
return customItem.isEmpty() || customItem.get().settings().canRepair() != Tristate.FALSE;
}
private static boolean isDamageableItem(Item<ItemStack> item) {
if (VersionHelper.isOrAbove1_20_5()) {
return item.hasComponent(ComponentTypes.MAX_DAMAGE) && item.hasComponent(ComponentTypes.DAMAGE);
} else {
return FastNMS.INSTANCE.method$Item$canBeDepleted(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()));
}
}
private static final Function<Object, Boolean> DYE_INGREDIENT_COUNT_CHECKER =
VersionHelper.isOrAbove1_21() ?
(input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) < 2 :
(container) -> false;
public static class DyeMatchesInterceptor {
public static final DyeMatchesInterceptor INSTANCE = new DyeMatchesInterceptor();
@RuntimeType
public Object intercept(@AllArguments Object[] args) {
Object input = args[0];
if (DYE_INGREDIENT_COUNT_CHECKER.apply(input)) {
return false;
}
int size = INGREDIENT_SIZE_GETTER.apply(input);
Item<ItemStack> itemToDye = null;
boolean hasDye = false;
for (int i = 0; i < size; i++) {
@@ -175,7 +349,7 @@ public class RecipeInjector {
}
itemToDye = wrapped;
} else {
if (!isDye(wrapped)) {
if (!isArmorDye(wrapped)) {
return false;
}
hasDye = true;
@@ -185,15 +359,15 @@ public class RecipeInjector {
}
}
public static class AssembleInterceptor {
public static final AssembleInterceptor INSTANCE = new AssembleInterceptor();
public static class DyeAssembleInterceptor {
public static final DyeAssembleInterceptor INSTANCE = new DyeAssembleInterceptor();
@RuntimeType
public Object intercept(@AllArguments Object[] args) {
List<Color> colors = new ArrayList<>();
Item<ItemStack> itemToDye = null;
Object input = args[0];
int size = INGREDIENT_COUNT_GETTER.apply(input);
int size = INGREDIENT_SIZE_GETTER.apply(input);
for (int i = 0; i < size; i++) {
Object itemStack = INGREDIENT_GETTER.apply(input, i);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) {
@@ -212,7 +386,7 @@ public class RecipeInjector {
}
}
if (itemToDye == null || itemToDye.isEmpty() || colors.isEmpty()) {
return null;
return CoreReflections.instance$ItemStack$EMPTY;
}
return itemToDye.applyDyedColors(colors).getLiteralObject();
}
@@ -228,6 +402,16 @@ public class RecipeInjector {
return getVanillaDyeColor(dyeItem);
}
@Nullable
private static Color getFireworkColor(final Item<ItemStack> dyeItem) {
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
return Optional.ofNullable(customItem.settings().fireworkColor()).orElseGet(() -> getVanillaFireworkColor(dyeItem));
}
return getVanillaFireworkColor(dyeItem);
}
private static final Predicate<Item<ItemStack>> IS_DYEABLE =
VersionHelper.isOrAbove1_20_5() ?
(item -> item.is(ItemTags.DYEABLE)) :
@@ -258,7 +442,15 @@ public class RecipeInjector {
return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)));
}
private static boolean isDye(Item<ItemStack> dyeItem) {
@Nullable
private static Color getVanillaFireworkColor(final Item<ItemStack> item) {
Object itemStack = item.getLiteralObject();
Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack);
if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return null;
return Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getFireworkColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)));
}
private static boolean isArmorDye(Item<ItemStack> dyeItem) {
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
@@ -267,6 +459,15 @@ public class RecipeInjector {
return isVanillaDyeItem(dyeItem);
}
private static boolean isFireworkDye(Item<ItemStack> dyeItem) {
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
return customItem.settings().fireworkColor() != null || isVanillaDyeItem(dyeItem);
}
return isVanillaDyeItem(dyeItem);
}
private static boolean isVanillaDyeItem(Item<ItemStack> item) {
return CoreReflections.clazz$DyeItem.isInstance(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()));
}