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 e0296b3e2..7b2ded9df 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 @@ -13,7 +13,6 @@ import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.StringUtils; import net.momirealms.craftengine.core.util.UniqueKey; @@ -115,18 +114,7 @@ public abstract class BukkitItemFactory> extend return FastNMS.INSTANCE.method$ItemStack$is(literalObject, tag); } - @Override - protected boolean isDyeItem(W item) { - return CoreReflections.clazz$DyeItem.isInstance(FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject())); - } - @Override - protected Optional dyeColor(W item) { - Object itemStack = item.getLiteralObject(); - Object dyeItem = FastNMS.INSTANCE.method$ItemStack$getItem(itemStack); - if (!CoreReflections.clazz$DyeItem.isInstance(dyeItem)) return Optional.empty(); - return Optional.of(Color.fromDecimal(FastNMS.INSTANCE.method$DyeColor$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)))); - } @Override protected void setJavaComponent(W item, Object type, Object value) { 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 7538a9272..5ae08c405 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 @@ -9,6 +9,8 @@ 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; +import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; 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; @@ -36,25 +38,70 @@ import java.io.Reader; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.function.BiConsumer; import java.util.function.Consumer; 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); + }) : + (id -> { + Object resourceLocation = KeyUtils.toResourceLocation(id); + FastNMS.INSTANCE.method$RecipeManager$removeRecipe(MINECRAFT_RECIPE_MANAGER, resourceLocation); + }); + private static final BiConsumer 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); + } : + 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); + } : + (id, recipe) -> { + FastNMS.INSTANCE.method$RecipeManager$addRecipe(MINECRAFT_RECIPE_MANAGER, recipe); + }; + + static { + try { + Key dyeRecipeId = Key.from("armor_dye"); + MINECRAFT_RECIPE_REMOVER.accept(dyeRecipeId); + MINECRAFT_RECIPE_ADDER.accept(dyeRecipeId, RecipeInjector.createCustomDyeRecipe()); + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to inject dye recipes", e); + } + } + + private static final List injectedIngredients = new ArrayList<>(); // 将自定义配方转为“广义”配方,接受更加宽容的输入 // 部分过程借助bukkit完成,部分直接通过nms方法注册 private static final Map>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); - private static final List injectedIngredients = new ArrayList<>(); - private static final IdentityHashMap, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>(); - public static final CustomDyeRecipe DYE_RECIPE = new CustomDyeRecipe<>(); - private static Object nmsRecipeManager; + private static final IdentityHashMap, Object> MINECRAFT_RECIPE_HOLDER_BY_RECIPE = new IdentityHashMap<>(); - private static void registerNMSSmithingRecipe(Object recipe) { - try { - CoreReflections.method$RecipeManager$addRecipe.invoke(nmsRecipeManager(), recipe); - } catch (IllegalAccessException | InvocationTargetException e) { - CraftEngine.instance().logger().warn("Failed to register smithing recipe", e); - } + public static Object toRecipeResourceKey(Key id) { + return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); } private static void registerBukkitShapedRecipe(Object recipe) { @@ -124,17 +171,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRANSFORM, (BukkitRecipeConvertor>) (id, recipe) -> { try { Object nmsRecipe = createMinecraftSmithingTransformRecipe(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance( - CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())), nmsRecipe); - } else if (VersionHelper.isOrAbove1_20_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance(KeyUtils.toResourceLocation(id), nmsRecipe); - } else { - Object finalNmsRecipe0 = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe0); - } - Object finalNmsRecipe = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe); + return () -> MINECRAFT_RECIPE_ADDER.accept(id, nmsRecipe); } catch (InvalidRecipeIngredientException e) { throw e; } catch (Exception e) { @@ -145,17 +182,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRIM, (BukkitRecipeConvertor>) (id, recipe) -> { try { Object nmsRecipe = createMinecraftSmithingTrimRecipe(recipe); - if (VersionHelper.isOrAbove1_21_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance( - CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())), nmsRecipe); - } else if (VersionHelper.isOrAbove1_20_2()) { - nmsRecipe = CoreReflections.constructor$RecipeHolder.newInstance(KeyUtils.toResourceLocation(id), nmsRecipe); - } else { - Object finalNmsRecipe0 = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe0); - } - Object finalNmsRecipe = nmsRecipe; - return () -> registerNMSSmithingRecipe(finalNmsRecipe); + return () -> MINECRAFT_RECIPE_ADDER.accept(id, nmsRecipe); } catch (InvalidRecipeIngredientException e) { throw e; } catch (Exception e) { @@ -281,20 +308,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager { this.plugin = plugin; this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); this.crafterEventListener = VersionHelper.isOrAbove1_21() ? new CrafterEventListener(plugin, this, plugin.itemManager()) : null; - try { - nmsRecipeManager = CoreReflections.method$MinecraftServer$getRecipeManager.invoke(CoreReflections.method$MinecraftServer$getServer.invoke(null)); - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to get minecraft recipe manager", e); - } } public Object nmsRecipeHolderByRecipe(Recipe recipe) { if (super.isReloading) return null; - return CE_RECIPE_2_NMS_HOLDER.get(recipe); + return MINECRAFT_RECIPE_HOLDER_BY_RECIPE.get(recipe); } - public static Object nmsRecipeManager() { - return nmsRecipeManager; + public static Object minecraftRecipeManager() { + return MINECRAFT_RECIPE_MANAGER; } public static BukkitRecipeManager instance() { @@ -315,8 +337,8 @@ public class BukkitRecipeManager extends AbstractRecipeManager { super.isReloading = true; if (VersionHelper.isOrAbove1_21_2()) { try { - this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager); - CoreReflections.field$RecipeManager$featureflagset.set(nmsRecipeManager, null); + 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); } @@ -347,7 +369,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { @Override public void disable() { unload(); - CE_RECIPE_2_NMS_HOLDER.clear(); + MINECRAFT_RECIPE_HOLDER_BY_RECIPE.clear(); // 不是服务器关闭造成disable,那么需要把配方卸载干净 if (!Bukkit.isStopping()) { for (Runnable task : this.delayedTasksOnMainThread) { @@ -365,7 +387,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (isBrewingRecipe) { Bukkit.getPotionBrewer().removePotionMix(new NamespacedKey(key.namespace(), key.value())); } else { - unregisterNMSRecipe(new NamespacedKey(key.namespace(), key.value())); + MINECRAFT_RECIPE_REMOVER.accept(key); } } @@ -406,19 +428,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { return (BukkitRecipeConvertor) MIXED_RECIPE_CONVERTORS.get(recipe.type()); } - private void unregisterNMSRecipe(NamespacedKey key) { - try { - if (VersionHelper.isOrAbove1_21_2()) { - Object recipeMap = CoreReflections.field$RecipeManager$recipes.get(nmsRecipeManager); - CoreReflections.method$RecipeMap$removeRecipe.invoke(recipeMap, CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, key)); - } else { - Bukkit.removeRecipe(key); - } - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to unregister nms recipes", e); - } - } - @SuppressWarnings("unchecked") private void injectDataPackRecipes() { try { @@ -501,6 +510,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { @Override public void runDelayedSyncTasks() { if (!Config.enableRecipeSystem()) return; + try { // run delayed tasks for (Runnable r : this.delayedTasksOnMainThread) { @@ -510,13 +520,13 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // give flags back on 1.21.2+ if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) { - CoreReflections.field$RecipeManager$featureflagset.set(nmsRecipeManager(), this.stolenFeatureFlagSet); + CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), this.stolenFeatureFlagSet); this.stolenFeatureFlagSet = null; } // refresh recipes if (VersionHelper.isOrAbove1_21_2()) { - CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(nmsRecipeManager()); + CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(minecraftRecipeManager()); } // send to players @@ -536,13 +546,12 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // clear cache injectedIngredients.clear(); - CE_RECIPE_2_NMS_HOLDER.clear(); + MINECRAFT_RECIPE_HOLDER_BY_RECIPE.clear(); // create mappings for (Map.Entry> entry : this.byId.entrySet()) { Optional nmsRecipe = getOptionalNMSRecipe(entry.getKey()); - nmsRecipe.ifPresent(o -> CE_RECIPE_2_NMS_HOLDER.put(entry.getValue(), o)); + nmsRecipe.ifPresent(o -> MINECRAFT_RECIPE_HOLDER_BY_RECIPE.put(entry.getValue(), o)); } - CE_RECIPE_2_NMS_HOLDER.put(DYE_RECIPE, getOptionalNMSRecipe(CustomDyeRecipe.ID).orElseThrow(() -> new IllegalStateException("DyeRecipe not found"))); super.isReloading = false; } catch (Exception e) { this.plugin.logger().warn("Failed to run delayed recipe tasks", e); @@ -568,7 +577,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); ItemStack result = createDataPackResultStack(recipe.result()); boolean hasCustomItemInTag = false; List> ingredientList = new ArrayList<>(); @@ -596,7 +604,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (hasCustomItemInTag) { Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); callback.accept(() -> { - unregisterNMSRecipe(key); + MINECRAFT_RECIPE_REMOVER.accept(id); converted.run(); }); } @@ -604,7 +612,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } private void handleDataPackShapedRecipe(Key id, VanillaShapedRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); ItemStack result = createDataPackResultStack(recipe.result()); boolean hasCustomItemInTag = false; Map> ingredients = new HashMap<>(); @@ -633,7 +640,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (hasCustomItemInTag) { Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); callback.accept(() -> { - unregisterNMSRecipe(key); + MINECRAFT_RECIPE_REMOVER.accept(id); converted.run(); }); } @@ -644,7 +651,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { VanillaCookingRecipe recipe, HeptaFunction, Integer, Float, CustomRecipeResult, CustomCookingRecipe> constructor2, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); ItemStack result = createDataPackResultStack(recipe.result()); Set holders = new HashSet<>(); boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), holders::add); @@ -657,7 +663,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (hasCustomItemInTag) { Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); callback.accept(() -> { - unregisterNMSRecipe(key); + MINECRAFT_RECIPE_REMOVER.accept(id); converted.run(); }); } @@ -665,7 +671,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); ItemStack result = createDataPackResultStack(recipe.result()); boolean hasCustomItemInTag; @@ -689,7 +694,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (hasCustomItemInTag) { Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); callback.accept(() -> { - unregisterNMSRecipe(key); + MINECRAFT_RECIPE_REMOVER.accept(id); converted.run(); }); } @@ -697,8 +702,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } private void handleDataPackSmithingTrim(Key id, VanillaSmithingTrimRecipe recipe, Consumer callback) { - NamespacedKey key = new NamespacedKey(id.namespace(), id.value()); - boolean hasCustomItemInTag; Set additionHolders = new HashSet<>(); hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add); @@ -718,7 +721,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { if (hasCustomItemInTag) { Runnable converted = findNMSRecipeConvertor(ceRecipe).convert(id, ceRecipe); callback.accept(() -> { - unregisterNMSRecipe(key); + MINECRAFT_RECIPE_REMOVER.accept(id); converted.run(); }); } @@ -883,12 +886,12 @@ public class BukkitRecipeManager extends AbstractRecipeManager { 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(nmsRecipeManager, resourceKey); + 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(nmsRecipeManager, resourceLocation); + Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(MINECRAFT_RECIPE_MANAGER, resourceLocation); return optional; } } 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 4f5d13d3f..79e9a28f8 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 @@ -342,7 +342,7 @@ public class RecipeEventListener implements Listener { try { @SuppressWarnings("unchecked") Optional optionalMCRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor( - BukkitRecipeManager.nmsRecipeManager(), + BukkitRecipeManager.minecraftRecipeManager(), MRecipeTypes.CAMPFIRE_COOKING, CoreReflections.constructor$SingleRecipeInput.newInstance(FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()), @@ -699,7 +699,11 @@ public class RecipeEventListener implements Listener { } try { + // TODO 全部改注入 Object mcRecipe = CraftBukkitReflections.field$CraftComplexRecipe$recipe.get(complexRecipe); + if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe)) { + return; + } // Repair recipe if (CoreReflections.clazz$RepairItemRecipe.isInstance(mcRecipe)) { @@ -745,8 +749,6 @@ public class RecipeEventListener implements Listener { int newItemDamage = Math.max(0, newItem.maxDamage() - remainingDurability); newItem.damage(newItemDamage); inventory.setResult(newItem.getItem()); - } else if (CoreReflections.clazz$ArmorDyeRecipe.isInstance(mcRecipe)) { - handlePossibleDyeRecipe(event, false); } else if (CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) { ItemStack[] itemStacks = inventory.getMatrix(); for (ItemStack itemStack : itemStacks) { @@ -826,29 +828,6 @@ public class RecipeEventListener implements Listener { } } - @EventHandler(ignoreCancelled = true) - public void onDyeRecipe(PrepareItemCraftEvent event) { - org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (recipe != null) { - return; - } - handlePossibleDyeRecipe(event, true); - } - - private void handlePossibleDyeRecipe(PrepareItemCraftEvent event, boolean correct) { - // dye recipe - CraftingInventory inventory = event.getInventory(); - CraftingInput input = getCraftingInput(inventory); - if (input == null) return; - if (BukkitRecipeManager.DYE_RECIPE.matches(input)) { - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - event.getInventory().setResult(BukkitRecipeManager.DYE_RECIPE.assemble(input, ItemBuildContext.of(this.plugin.adapt(player)))); - if (correct) { - correctCraftingRecipeUsed(inventory, BukkitRecipeManager.DYE_RECIPE); - } - } - } - @EventHandler(ignoreCancelled = true) public void onCraftingRecipe(PrepareItemCraftEvent event) { if (!Config.enableRecipeSystem()) return; 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 7a8016ace..3298c5eba 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 @@ -17,22 +17,30 @@ 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.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.Key; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.*; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; public class RecipeInjector { private static Class clazz$InjectedCacheChecker; - + private static Class clazz$InjectedArmorDyeRecipe; + public static void init() { ByteBuddy byteBuddy = new ByteBuddy(ClassFileVersion.JAVA_V17); clazz$InjectedCacheChecker = byteBuddy @@ -70,6 +78,35 @@ public class RecipeInjector { .make() .load(RecipeInjector.class.getClassLoader()) .getLoaded(); + 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)) + .make() + .load(RecipeInjector.class.getClassLoader()) + .getLoaded(); + } + + public static Object createCustomDyeRecipe() throws ReflectiveOperationException { + if (VersionHelper.isOrAbove1_20_2()) { + Constructor constructor = ReflectionUtils.getConstructor(clazz$InjectedArmorDyeRecipe, 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); + } } public static void injectCookingBlockEntity(Object entity) throws ReflectiveOperationException { @@ -101,6 +138,139 @@ public class RecipeInjector { } } + private static final Function INGREDIENT_COUNT_CHECKER = + VersionHelper.isOrAbove1_21() ? + (input) -> FastNMS.INSTANCE.method$CraftingInput$ingredientCount(input) < 2 : + (container) -> false; + private static final Function INGREDIENT_COUNT_GETTER = + VersionHelper.isOrAbove1_21() ? + FastNMS.INSTANCE::method$CraftingInput$ingredientCount : + FastNMS.INSTANCE::method$Container$getContainerSize; + private static final BiFunction 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(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) { + Object input = args[0]; + if (INGREDIENT_COUNT_CHECKER.apply(input)) { + return false; + } + int size = INGREDIENT_COUNT_GETTER.apply(input); + Item itemToDye = null; + boolean hasDye = false; + for (int i = 0; i < size; i++) { + Object itemStack = INGREDIENT_GETTER.apply(input, i); + if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemStack)) { + continue; + } + Item wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isDyeable(wrapped)) { + if (itemToDye != null) { + return false; + } + itemToDye = wrapped; + } else { + if (!isDye(wrapped)) { + return false; + } + hasDye = true; + } + } + return hasDye && itemToDye != null; + } + } + + public static class AssembleInterceptor { + public static final AssembleInterceptor INSTANCE = new AssembleInterceptor(); + + @RuntimeType + public Object intercept(@AllArguments Object[] args) { + List colors = new ArrayList<>(); + Item itemToDye = null; + Object input = args[0]; + int size = INGREDIENT_COUNT_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 wrapped = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack)); + if (isDyeable(wrapped)) { + itemToDye = wrapped.copyWithCount(1); + } else { + Color dyeColor = getDyeColor(wrapped); + if (dyeColor != null) { + colors.add(dyeColor); + } else { + return CoreReflections.instance$ItemStack$EMPTY; + } + } + } + if (itemToDye == null || itemToDye.isEmpty() || colors.isEmpty()) { + return null; + } + return itemToDye.applyDyedColors(colors).getLiteralObject(); + } + } + + @Nullable + private static Color getDyeColor(final Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return Optional.ofNullable(customItem.settings().dyeColor()).orElseGet(() -> getVanillaDyeColor(dyeItem)); + } + return getVanillaDyeColor(dyeItem); + } + + private static final Predicate> IS_DYEABLE = + VersionHelper.isOrAbove1_20_5() ? + (item -> item.is(ItemTags.DYEABLE)) : + (item -> { + Object itemLike = FastNMS.INSTANCE.method$ItemStack$getItem(item.getLiteralObject()); + return CoreReflections.clazz$DyeableLeatherItem.isInstance(itemLike); + }); + + private static boolean isDyeable(final Item item) { + Optional> optionalCustomItem = item.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + if (customItem.settings().dyeable() == Tristate.FALSE) { + return false; + } + if (customItem.settings().dyeable() == Tristate.TRUE) { + return true; + } + } + return IS_DYEABLE.test(item); + } + + @Nullable + private static Color getVanillaDyeColor(final Item 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$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem))); + } + + private static boolean isDye(Item dyeItem) { + Optional> optionalCustomItem = dyeItem.getCustomItem(); + if (optionalCustomItem.isPresent()) { + CustomItem customItem = optionalCustomItem.get(); + return customItem.settings().dyeColor() != null || isVanillaDyeItem(dyeItem); + } + return isVanillaDyeItem(dyeItem); + } + + 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(); @@ -110,7 +280,7 @@ public class RecipeInjector { 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.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); if (optionalRecipe.isEmpty()) { return Optional.empty(); } @@ -156,7 +326,7 @@ public class RecipeInjector { 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.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); if (optionalRecipe.isEmpty()) { return Optional.empty(); } @@ -202,7 +372,7 @@ public class RecipeInjector { 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.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); if (optionalRecipe.isEmpty()) { return Optional.empty(); } @@ -243,7 +413,7 @@ public class RecipeInjector { 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.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey); if (optionalRecipe.isEmpty()) { return Optional.empty(); } 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 76514f5de..125a9a8cf 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 @@ -3903,4 +3903,67 @@ public final class CoreReflections { "world.item.DyeItem" ) ); + + public static final Method method$Recipe$matches = requireNonNull( + VersionHelper.isOrAbove1_21() ? + ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$RecipeInput, clazz$Level) : + ReflectionUtils.getMethod(clazz$Recipe, boolean.class, clazz$Container, clazz$Level) + ); + + public static final Method method$Recipe$assemble = requireNonNull( + VersionHelper.isOrAbove1_21() ? + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$RecipeInput, clazz$HolderLookup$Provider) : + VersionHelper.isOrAbove1_20_5() ? + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$HolderLookup$Provider) : + ReflectionUtils.getMethod(clazz$Recipe, clazz$ItemStack, clazz$Container, clazz$RegistryAccess) + ); + + public static final Class clazz$CraftingBookCategory = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.crafting.CraftingBookCategory", + "world.item.crafting.CraftingBookCategory" + ) + ); + + public static final Method method$CraftingBookCategory$values = requireNonNull( + ReflectionUtils.getStaticMethod(clazz$CraftingBookCategory, clazz$CraftingBookCategory.arrayType()) + ); + + public static final Object instance$CraftingBookCategory$BUILDING; + public static final Object instance$CraftingBookCategory$REDSTONE; + public static final Object instance$CraftingBookCategory$EQUIPMENT; + public static final Object instance$CraftingBookCategory$MISC; + + static { + try { + Object[] values = (Object[]) method$CraftingBookCategory$values.invoke(null); + instance$CraftingBookCategory$BUILDING = values[0]; + instance$CraftingBookCategory$REDSTONE = values[1]; + instance$CraftingBookCategory$EQUIPMENT = values[2]; + instance$CraftingBookCategory$MISC = values[3]; + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize CraftingBookCategory", e); + } + } + + public static final Class clazz$CraftingInput = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.crafting.CraftingInput", + "world.item.crafting.CraftingInput" + ), VersionHelper.isOrAbove1_21() + ); + + public static final Class clazz$CraftingContainer = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.inventory.InventoryCrafting", + "world.inventory.CraftingContainer" + ) + ); + + public static final Class clazz$DyeableLeatherItem = MiscUtils.requireNonNullIf( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.IDyeable", + "world.item.DyeableLeatherItem" + ), !VersionHelper.isOrAbove1_20_5() + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java index cfe080d81..258666205 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; public final class MItems { private MItems() {} @@ -9,11 +12,17 @@ public final class MItems { public static final Object WATER_BUCKET; public static final Object BARRIER; + @Nullable private static Object getById(String id) { Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id); return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, rl); } + @Nullable + public static Object getById(Key id) { + return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(id)); + } + static { AIR = getById("air"); WATER_BUCKET = getById("water_bucket"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java index 099653115..a2981f6be 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.JukeboxSong; import net.momirealms.craftengine.core.util.Key; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java index 1f35e3e90..e94bc4a32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java @@ -12,6 +12,7 @@ public class ItemTags { public static final Key AXES = Key.of("minecraft:axes"); public static final Key SWORDS = Key.of("minecraft:swords"); + public static final Key DYEABLE = Key.of("minecraft:dyeable"); private ItemTags() {} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java index 872aa411d..af97388f6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java @@ -481,14 +481,4 @@ public class AbstractItem, I> implements Item { public void shrink(int amount) { this.item.shrink(amount); } - - @Override - public boolean isDyeItem() { - return this.factory.isDyeItem(this.item); - } - - @Override - public Optional dyeColor() { - return this.factory.dyeColor(this.item); - } } 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 ee799b262..30c17b8b4 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 @@ -214,7 +214,33 @@ public interface Item { byte[] toByteArray(); - boolean isDyeItem(); - - Optional dyeColor(); + default Item applyDyedColors(List colors) { + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + int totalMaxComponent = 0; + int colorCount = 0; + Optional existingColor = dyedColor(); + existingColor.ifPresent(colors::add); + for (Color color : colors) { + int dyeRed = color.r(); + int dyeGreen = color.g(); + int dyeBlue = color.b(); + totalMaxComponent += Math.max(dyeRed, Math.max(dyeGreen, dyeBlue)); + totalRed += dyeRed; + totalGreen += dyeGreen; + totalBlue += dyeBlue; + ++colorCount; + } + int avgRed = totalRed / colorCount; + int avgGreen = totalGreen / colorCount; + int avgBlue = totalBlue / colorCount; + float avgMaxComponent = (float) totalMaxComponent / (float)colorCount; + float currentMaxComponent = (float) Math.max(avgRed, Math.max(avgGreen, avgBlue)); + avgRed = (int) ((float) avgRed * avgMaxComponent / currentMaxComponent); + avgGreen = (int) ((float) avgGreen * avgMaxComponent / currentMaxComponent); + avgBlue = (int) ((float) avgBlue * avgMaxComponent / currentMaxComponent); + Color finalColor = new Color(0, avgRed, avgGreen, avgBlue); + return dyedColor(finalColor); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java index f88830935..1391a199d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemFactory.java @@ -213,9 +213,5 @@ public abstract class ItemFactory, I> { protected abstract UniqueKey recipeIngredientID(W item); - protected abstract boolean isDyeItem(W item); - - protected abstract Optional dyeColor(W item); - protected abstract void attributeModifiers(W item, List modifiers); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomDyeRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomDyeRecipe.java deleted file mode 100644 index 0e0201af5..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomDyeRecipe.java +++ /dev/null @@ -1,159 +0,0 @@ -package net.momirealms.craftengine.core.item.recipe; - -import net.momirealms.craftengine.core.item.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemBuildContext; -import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; -import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; -import net.momirealms.craftengine.core.util.Color; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.Tristate; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public class CustomDyeRecipe implements Recipe { - public static final Key ID = Key.of("armor_dye"); - private static final Key DYEABLE = Key.of("dyeable"); - - public CustomDyeRecipe() { - } - - @SuppressWarnings("unchecked") - @Override - public T assemble(RecipeInput input, ItemBuildContext context) { - List colors = new ArrayList<>(); - CraftingInput craftingInput = (CraftingInput) input; - Item itemToDye = null; - for (UniqueIdItem uniqueIdItem : craftingInput) { - if (uniqueIdItem.isEmpty()) { - continue; - } - if (isDyeable(uniqueIdItem)) { - itemToDye = uniqueIdItem.item().copyWithCount(1); - } else { - Color dyeColor = getDyeColor(uniqueIdItem); - if (dyeColor != null) { - colors.add(dyeColor); - } else { - return null; - } - } - } - if (itemToDye == null || itemToDye.isEmpty() || colors.isEmpty()) { - return null; - } - return applyDyes(itemToDye, colors).getItem(); - } - - private Item applyDyes(Item item, List colors) { - int totalRed = 0; - int totalGreen = 0; - int totalBlue = 0; - int totalMaxComponent = 0; - int colorCount = 0; - Optional existingColor = item.dyedColor(); - existingColor.ifPresent(colors::add); - for (Color color : colors) { - int dyeRed = color.r(); - int dyeGreen = color.g(); - int dyeBlue = color.b(); - totalMaxComponent += Math.max(dyeRed, Math.max(dyeGreen, dyeBlue)); - totalRed += dyeRed; - totalGreen += dyeGreen; - totalBlue += dyeBlue; - ++colorCount; - } - int avgRed = totalRed / colorCount; - int avgGreen = totalGreen / colorCount; - int avgBlue = totalBlue / colorCount; - float avgMaxComponent = (float) totalMaxComponent / (float)colorCount; - float currentMaxComponent = (float) Math.max(avgRed, Math.max(avgGreen, avgBlue)); - avgRed = (int) ((float) avgRed * avgMaxComponent / currentMaxComponent); - avgGreen = (int) ((float) avgGreen * avgMaxComponent / currentMaxComponent); - avgBlue = (int) ((float) avgBlue * avgMaxComponent / currentMaxComponent); - Color finalColor = new Color(0, avgRed, avgGreen, avgBlue); - return item.dyedColor(finalColor); - } - - @SuppressWarnings("unchecked") - @Override - public boolean matches(RecipeInput input) { - CraftingInput craftingInput = (CraftingInput) input; - if (craftingInput.ingredientCount() < 2) { - return false; - } - boolean hasItemToDye = false; - boolean hasDye = false; - for(int i = 0; i < craftingInput.size(); ++i) { - UniqueIdItem item = craftingInput.getItem(i); - if (!item.isEmpty()) { - if (isDyeable(item)) { - if (hasItemToDye) { - return false; - } - hasItemToDye = true; - } else { - if (!isDye(item)) { - return false; - } - hasDye = true; - } - } - } - return hasDye && hasItemToDye; - } - - private boolean isDyeable(final UniqueIdItem item) { - Optional> optionalCustomItem = item.item().getCustomItem(); - if (optionalCustomItem.isPresent()) { - CustomItem customItem = optionalCustomItem.get(); - if (customItem.settings().dyeable() == Tristate.FALSE) { - return false; - } - if (customItem.settings().dyeable() == Tristate.TRUE) { - return true; - } - } - return item.item().is(DYEABLE); - } - - private boolean isDye(final UniqueIdItem item) { - Item dyeItem = item.item(); - Optional> optionalCustomItem = item.item().getCustomItem(); - if (optionalCustomItem.isPresent()) { - CustomItem customItem = optionalCustomItem.get(); - return customItem.settings().dyeColor() != null || dyeItem.isDyeItem(); - } - return dyeItem.isDyeItem(); - } - - @Nullable - private Color getDyeColor(final UniqueIdItem item) { - Item dyeItem = item.item(); - Optional> optionalCustomItem = item.item().getCustomItem(); - if (optionalCustomItem.isPresent()) { - CustomItem customItem = optionalCustomItem.get(); - return Optional.ofNullable(customItem.settings().dyeColor()).orElseGet(() -> dyeItem.dyeColor().orElse(null)); - } - return dyeItem.dyeColor().orElse(null); - } - - @Override - public List> ingredientsInUse() { - return List.of(); - } - - @Override - public @NotNull Key type() { - return RecipeTypes.SPECIAL; - } - - @Override - public Key id() { - return ID; - } -} diff --git a/gradle.properties b/gradle.properties index 9424ed38f..1d95c9947 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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.43 +nms_helper_version=1.0.46 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23