From 4947b473103c06e64b6024425167710bbe514e02 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 30 Jul 2025 15:51:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=85=8D=E6=96=B9=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../furniture/BukkitFurnitureElement.java | 3 +- .../item/factory/BukkitItemFactory.java | 20 ++- .../factory/ComponentItemFactory1_20_5.java | 11 +- .../item/factory/UniversalItemFactory.java | 9 +- .../item/recipe/BukkitRecipeManager.java | 3 +- .../item/recipe/RecipeEventListener.java | 82 ++++++--- .../reflection/minecraft/CoreReflections.java | 7 + .../bukkit/util/InteractUtils.java | 5 +- .../entity/furniture/FurnitureExtraData.java | 9 +- .../craftengine/core/item/AbstractItem.java | 15 +- .../core/item/AbstractItemManager.java | 4 +- .../craftengine/core/item/Item.java | 10 +- .../craftengine/core/item/ItemFactory.java | 9 +- .../craftengine/core/item/ItemKeys.java | 18 +- .../craftengine/core/item/ItemSettings.java | 28 ++- .../core/item/modifier/DyedColorModifier.java | 5 +- .../core/item/recipe/CustomDyeRecipe.java | 159 ++++++++++++++++++ .../core/item/recipe/RecipeTypes.java | 1 + .../core/item/recipe/input/CraftingInput.java | 9 +- .../context/function/ParticleFunction.java | 10 +- .../craftengine/core/util/Color.java | 91 +++++++--- gradle.properties | 4 +- 22 files changed, 416 insertions(+), 96 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomDyeRecipe.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java index f108d5e2a..b82f164f0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java @@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureElement; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.Material; @@ -65,7 +66,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement { } } - private synchronized List getCachedValues(@Nullable Integer color, int @Nullable [] colors) { + private synchronized List getCachedValues(@Nullable Color color, int @Nullable [] colors) { List cachedValues = new ArrayList<>(this.commonValues); Item item = BukkitItemManager.instance().createWrappedItem(item(), null); if (item == null) { 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 0c5ee3b3c..e0296b3e2 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,6 +13,7 @@ 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; @@ -111,11 +112,20 @@ public abstract class BukkitItemFactory> extend protected boolean is(W item, Key itemTag) { Object literalObject = item.getLiteralObject(); Object tag = ItemTags.getOrCreate(itemTag); - try { - return (boolean) CoreReflections.method$ItemStack$isTag.invoke(literalObject, tag); - } catch (ReflectiveOperationException e) { - return false; - } + 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 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 9abbd8a16..ae6cf7e17 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 @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.Trim; 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.MiscUtils; import net.momirealms.sparrow.nbt.CompoundTag; @@ -386,23 +387,23 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory dyedColor(ComponentItemWrapper item) { + protected Optional dyedColor(ComponentItemWrapper item) { if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty(); Object javaObj = getJavaComponent(item, ComponentTypes.DYED_COLOR); if (javaObj instanceof Integer integer) { - return Optional.of(integer); + return Optional.of(Color.fromDecimal(integer)); } else if (javaObj instanceof Map map) { - return Optional.of((int) map.get("rgb")); + return Optional.of(Color.fromDecimal((int) map.get("rgb"))); } return Optional.empty(); } @Override - protected void dyedColor(ComponentItemWrapper item, Integer color) { + protected void dyedColor(ComponentItemWrapper item, Color color) { if (color == null) { item.resetComponent(ComponentTypes.DYED_COLOR); } else { - item.setJavaComponent(ComponentTypes.DYED_COLOR, color); + item.setJavaComponent(ComponentTypes.DYED_COLOR, color.color()); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 1d77a5ef4..c8437d089 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.modifier.IdModifier; 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.SkullUtils; import net.momirealms.sparrow.nbt.Tag; @@ -169,17 +170,17 @@ public class UniversalItemFactory extends BukkitItemFactory { } @Override - protected Optional dyedColor(LegacyItemWrapper item) { + protected Optional dyedColor(LegacyItemWrapper item) { if (!item.hasTag("display", "color")) return Optional.empty(); - return Optional.of(item.getJavaTag("display", "color")); + return Optional.of(Color.fromDecimal(item.getJavaTag("display", "color"))); } @Override - protected void dyedColor(LegacyItemWrapper item, Integer color) { + protected void dyedColor(LegacyItemWrapper item, Color color) { if (color == null) { item.remove("display", "color"); } else { - item.setTag(color, "display", "color"); + item.setTag(color.color(), "display", "color"); } } 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 953f220fe..7538a9272 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 @@ -46,6 +46,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { 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 void registerNMSSmithingRecipe(Object recipe) { @@ -541,7 +542,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { Optional nmsRecipe = getOptionalNMSRecipe(entry.getKey()); nmsRecipe.ifPresent(o -> CE_RECIPE_2_NMS_HOLDER.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); 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 2f1da0cb7..4f5d13d3f 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 @@ -745,13 +745,15 @@ 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) || CoreReflections.clazz$FireworkStarFadeRecipe.isInstance(mcRecipe)) { + } 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) { if (itemStack == null) continue; Item item = plugin.itemManager().wrap(itemStack); Optional> optionalCustomItem = item.getCustomItem(); - if (optionalCustomItem.isPresent() && !optionalCustomItem.get().settings().dyeable()) { + if (optionalCustomItem.isPresent() && optionalCustomItem.get().settings().dyeable() == Tristate.FALSE) { inventory.setResult(null); return; } @@ -824,16 +826,37 @@ 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; org.bukkit.inventory.Recipe recipe = event.getRecipe(); - if (recipe == null) - return; // we only handle shaped and shapeless recipes - boolean shapeless = event.getRecipe() instanceof ShapelessRecipe; - boolean shaped = event.getRecipe() instanceof ShapedRecipe; + boolean shapeless = recipe instanceof ShapelessRecipe; + boolean shaped = recipe instanceof ShapedRecipe; if (!shaped && !shapeless) return; CraftingRecipe craftingRecipe = (CraftingRecipe) recipe; @@ -846,24 +869,10 @@ public class RecipeEventListener implements Listener { } CraftingInventory inventory = event.getInventory(); - ItemStack[] ingredients = inventory.getMatrix(); - - List> uniqueIdItems = new ArrayList<>(); - for (ItemStack itemStack : ingredients) { - uniqueIdItems.add(getUniqueIdItem(itemStack)); - } - - 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; - } + CraftingInput input = getCraftingInput(inventory); + if (input == null) return; Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - BukkitServerPlayer serverPlayer = this.plugin.adapt(player); Key lastRecipe = serverPlayer.lastUsedRecipe(); @@ -889,17 +898,34 @@ public class RecipeEventListener implements Listener { inventory.setResult(null); } + private CraftingInput getCraftingInput(CraftingInventory inventory) { + ItemStack[] ingredients = inventory.getMatrix(); + + List> uniqueIdItems = new ArrayList<>(); + for (ItemStack itemStack : ingredients) { + uniqueIdItems.add(getUniqueIdItem(itemStack)); + } + + 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 null; + } + return input; + } + private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe recipe) { Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); if (holderOrRecipe == null) { return; } - try { - Object resultInventory = CraftBukkitReflections.field$CraftInventoryCrafting$resultInventory.get(inventory); - CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); - } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to correct used recipe", e); - } + 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) 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 d0eb6fa8c..76514f5de 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 @@ -3896,4 +3896,11 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to initialize SnowLayerBlock$LAYERS", e); } } + + public static final Class clazz$DyeItem = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.item.ItemDye", + "world.item.DyeItem" + ) + ); } 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 c63b370d0..b9f6c5556 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 @@ -14,7 +14,10 @@ import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.QuadFunction; +import net.momirealms.craftengine.core.util.TriFunction; import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.GameMode; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java index c7c5704e9..72587f2a1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; @@ -49,8 +50,8 @@ public class FurnitureExtraData { return Optional.empty(); } - public Optional dyedColor() { - if (this.data.containsKey(DYED_COLOR)) return Optional.of(this.data.getInt(DYED_COLOR)); + public Optional dyedColor() { + if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR))); return Optional.empty(); } @@ -92,9 +93,9 @@ public class FurnitureExtraData { return this; } - public Builder dyedColor(Integer color) { + public Builder dyedColor(Color color) { if (color == null) return this; - this.data.putInt(DYED_COLOR, color); + this.data.putInt(DYED_COLOR, color.color()); return this; } 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 74f5d96a3..33170ded2 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 @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.setting.EquipmentData; +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; @@ -107,13 +108,13 @@ public class AbstractItem, I> implements Item { } @Override - public Item dyedColor(Integer data) { + public Item dyedColor(Color data) { this.factory.dyedColor(this.item, data); return this; } @Override - public Optional dyedColor() { + public Optional dyedColor() { return this.factory.dyedColor(this.item); } @@ -473,4 +474,14 @@ 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/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 2635e6d7d..79ee3ad6a 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 @@ -548,10 +548,10 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl }, "dynamic-lore"); registerDataType((obj) -> { if (obj instanceof Integer integer) { - return new DyedColorModifier<>(integer); + return new DyedColorModifier<>(Color.fromDecimal(integer)); } else { Vector3f vector3f = MiscUtils.getAsVector3f(obj, "dyed-color"); - return new DyedColorModifier<>(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vector3f.x) << 16 | MCUtils.fastFloor(vector3f.y) << 8 | MCUtils.fastFloor(vector3f.z)); + return new DyedColorModifier<>(Color.fromVector3f(vector3f)); } }, "dyed-color"); registerDataType((obj) -> { 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 86b4c4729..f1692ece7 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 @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.setting.EquipmentData; +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; @@ -69,9 +70,10 @@ public interface Item { int maxDamage(); - Item dyedColor(Integer data); + // todo 考虑部分版本的show in tooltip保留 + Item dyedColor(Color data); - Optional dyedColor(); + Optional dyedColor(); Item fireworkExplosion(FireworkExplosion explosion); @@ -208,4 +210,8 @@ public interface Item { } byte[] toByteArray(); + + boolean isDyeItem(); + + Optional dyeColor(); } 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 365582553..70d734922 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 @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.AdventureHelper; +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; @@ -135,9 +136,9 @@ public abstract class ItemFactory, I> { protected abstract void damage(W item, Integer damage); - protected abstract Optional dyedColor(W item); + protected abstract Optional dyedColor(W item); - protected abstract void dyedColor(W item, Integer color); + protected abstract void dyedColor(W item, Color color); protected abstract int maxDamage(W item); @@ -210,4 +211,8 @@ public abstract class ItemFactory, I> { protected abstract boolean isEmpty(W item); protected abstract UniqueKey recipeIngredientID(W item); + + protected abstract boolean isDyeItem(W item); + + protected abstract Optional dyeColor(W item); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 05b0bc297..841487680 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.util.Key; -public class ItemKeys { +public final class ItemKeys { public static final Key AIR = Key.of("minecraft:air"); public static final Key FLINT_AND_STEEL = Key.of("minecraft:flint_and_steel"); public static final Key STONE = Key.of("minecraft:stone"); @@ -44,6 +44,22 @@ public class ItemKeys { public static final Key GLOWSTONE = Key.of("minecraft:glowstone"); public static final Key SADDLE = Key.of("minecraft:saddle"); public static final Key HARNESS = Key.of("minecraft:harness"); + public static final Key WHITE_DYE = Key.of("minecraft:white_dye"); + public static final Key ORANGE_DYE = Key.of("minecraft:orange_dye"); + public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye"); + public static final Key LIGHT_BLUE_DYE = Key.of("minecraft:light_blue_dye"); + public static final Key YELLOW_DYE = Key.of("minecraft:yellow_dye"); + public static final Key LIME_DYE = Key.of("minecraft:lime_dye"); + public static final Key PINK_DYE = Key.of("minecraft:pink_dye"); + public static final Key GRAY_DYE = Key.of("minecraft:gray_dye"); + public static final Key LIGHT_GRAY_DYE = Key.of("minecraft:light_gray_dye"); + public static final Key CYAN_DYE = Key.of("minecraft:cyan_dye"); + public static final Key PURPLE_DYE = Key.of("minecraft:purple_dye"); + public static final Key BLUE_DYE = Key.of("minecraft:blue_dye"); + public static final Key BROWN_DYE = Key.of("minecraft:brown_dye"); + public static final Key GREEN_DYE = Key.of("minecraft:green_dye"); + public static final Key RED_DYE = Key.of("minecraft:red_dye"); + public static final Key BLACK_DYE = Key.of("minecraft:black_dye"); public static final Key[] AXES = new Key[] { WOODEN_AXE, STONE_AXE, IRON_AXE, GOLDEN_AXE, DIAMOND_AXE, NETHERITE_AXE diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index b55cc6de9..526f00969 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -30,7 +30,7 @@ public class ItemSettings { boolean renameable = true; boolean canPlaceRelatedVanillaBlock = false; ProjectileMeta projectileMeta; - boolean dyeable = true; + Tristate dyeable = Tristate.UNDEFINED; Helmet helmet = null; FoodData foodData = null; Key consumeReplacement = null; @@ -41,6 +41,8 @@ public class ItemSettings { boolean respectRepairableComponent = false; @Nullable ItemEquipment equipment; + @Nullable + Color dyeColor; private ItemSettings() {} @@ -99,6 +101,7 @@ public class ItemSettings { newSettings.canEnchant = settings.canEnchant; newSettings.compostProbability = settings.compostProbability; newSettings.respectRepairableComponent = settings.respectRepairableComponent; + newSettings.dyeColor = settings.dyeColor; return newSettings; } @@ -138,7 +141,7 @@ public class ItemSettings { return tags; } - public boolean dyeable() { + public Tristate dyeable() { return dyeable; } @@ -179,6 +182,11 @@ public class ItemSettings { return equipment; } + @Nullable + public Color dyeColor() { + return dyeColor; + } + public List invulnerable() { return invulnerable; } @@ -187,6 +195,11 @@ public class ItemSettings { return compostProbability; } + public ItemSettings dyeColor(Color color) { + this.dyeColor = color; + return this; + } + public ItemSettings repairItems(List items) { this.anvilRepairItems = items; return this; @@ -252,7 +265,7 @@ public class ItemSettings { return this; } - public ItemSettings dyeable(boolean bool) { + public ItemSettings dyeable(Tristate bool) { this.dyeable = bool; return this; } @@ -390,12 +403,19 @@ public class ItemSettings { })); registerFactory("dyeable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "dyeable"); - return settings -> settings.dyeable(bool); + return settings -> settings.dyeable(bool ? Tristate.TRUE : Tristate.FALSE); })); registerFactory("respect-repairable-component", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "respect-repairable-component"); return settings -> settings.respectRepairableComponent(bool); })); + registerFactory("dye-color", (value -> { + if (value instanceof Integer i) { + return settings -> settings.dyeColor(Color.fromDecimal(i)); + } else { + return settings -> settings.dyeColor(Color.fromVector3f(MiscUtils.getAsVector3f(value, "dye-color"))); + } + })); registerFactory("food", (value -> { Map args = MiscUtils.castToMap(value, false); FoodData data = new FoodData( diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java index a93bbf284..f96536406 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DyedColorModifier.java @@ -4,14 +4,15 @@ import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; public class DyedColorModifier implements ItemDataModifier { - private final int color; + private final Color color; - public DyedColorModifier(int color) { + public DyedColorModifier(Color color) { this.color = color; } 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 new file mode 100644 index 000000000..0e0201af5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomDyeRecipe.java @@ -0,0 +1,159 @@ +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/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java index 603c76840..d4b7ee61a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/RecipeTypes.java @@ -21,6 +21,7 @@ public class RecipeTypes { public static final Key SMITHING_TRANSFORM = Key.of("minecraft:smithing_transform"); public static final Key SMITHING_TRIM = Key.of("minecraft:smithing_trim"); public static final Key BREWING = Key.of("minecraft:brewing"); + public static final Key SPECIAL = Key.of("minecraft:special"); static { register(SHAPED, CustomShapedRecipe.FACTORY); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java index e07cac354..c08a45f86 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/input/CraftingInput.java @@ -2,12 +2,14 @@ package net.momirealms.craftengine.core.item.recipe.input; import net.momirealms.craftengine.core.item.recipe.RecipeFinder; import net.momirealms.craftengine.core.item.recipe.UniqueIdItem; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; -public final class CraftingInput implements RecipeInput { +public final class CraftingInput implements RecipeInput, Iterable> { private final int width; private final int height; private final List> items; @@ -99,4 +101,9 @@ public final class CraftingInput implements RecipeInput { public UniqueIdItem getItem(int index) { return this.items.get(index); } + + @Override + public @NotNull Iterator> iterator() { + return this.items.iterator(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java index bc65826b4..2ab73db03 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ParticleFunction.java @@ -38,7 +38,7 @@ public class ParticleFunction extends AbstractConditionalFu })), ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER); registerParticleData(map -> new ColorData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(","))), ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES); registerParticleData(map -> new JavaTypeData( ResourceConfigUtils.getAsFloat(map.get("charge"), "charge")), @@ -47,12 +47,12 @@ public class ParticleFunction extends AbstractConditionalFu ResourceConfigUtils.getAsInt(map.get("shriek"), "shriek")), ParticleTypes.SHRIEK); registerParticleData(map -> new DustData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), ParticleTypes.DUST); registerParticleData(map -> new DustTransitionData( - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("from"), "warning.config.function.particle.missing_from").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("to"), "warning.config.function.particle.missing_to").split(",")), ResourceConfigUtils.getAsFloat(map.getOrDefault("scale", 1), "scale")), ParticleTypes.DUST_COLOR_TRANSITION); registerParticleData(map -> new ItemStackData( @@ -75,7 +75,7 @@ public class ParticleFunction extends AbstractConditionalFu NumberProviders.fromObject(map.getOrDefault("target-x", 0)), NumberProviders.fromObject(map.getOrDefault("target-y", 0)), NumberProviders.fromObject(map.getOrDefault("target-z", 0)), - Color.fromString(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), + Color.fromStrings(ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("color"), "warning.config.function.particle.missing_color").split(",")), NumberProviders.fromObject(map.getOrDefault("duration", 10))), ParticleTypes.TRAIL); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java index 0e47529c1..6884afedd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java @@ -1,52 +1,95 @@ package net.momirealms.craftengine.core.util; +import org.joml.Vector3f; + import java.util.Arrays; public class Color { + private static final int BIT_MASK = 0xff; private static final byte DEFAULT_ALPHA = (byte) 255; - private final byte r; - private final byte g; - private final byte b; - private final byte a; + private final int color; - public Color(byte r, byte g, byte b, byte a) { - this.b = b; - this.g = g; - this.r = r; - this.a = a; + public Color(int color) { + this.color = color; } - public Color(byte r, byte g, byte b) { - this(r, g, b, DEFAULT_ALPHA); + public Color(int r, int g, int b) { + this(DEFAULT_ALPHA, r, g, b); } - public int toDecimal() { - return DEFAULT_ALPHA << 24 | (r << 16) | (g << 8) | b; + public Color(int a, int r, int g, int b) { + this(toDecimal(a, r, g, b)); } - public static Color fromString(String[] strings) { + public int color() { + return color; + } + + public static int toDecimal(int a, int r, int g, int b) { + return a << 24 | r << 16 | g << 8 | b; + } + + public static int toDecimal(int r, int g, int b) { + return DEFAULT_ALPHA << 24 | r << 16 | g << 8 | b; + } + + public static Color fromDecimal(int decimal) { + return new Color(decimal); + } + + public static Color fromVector3f(Vector3f vec) { + return new Color(0 << 24 /*不可省略*/ | MCUtils.fastFloor(vec.x) << 16 | MCUtils.fastFloor(vec.y) << 8 | MCUtils.fastFloor(vec.z)); + } + + public static int opaque(int color) { + return color | -16777216; + } + + public static int transparent(int color) { + return color & 16777215; + } + + public static int alpha(int color) { + return color >>> 24; + } + + public static int red(int color) { + return color >> 16 & BIT_MASK; + } + + public static int green(int color) { + return color >> 8 & BIT_MASK; + } + + public static int blue(int color) { + return color & BIT_MASK; + } + + public static Color fromStrings(String[] strings) { if (strings.length == 3) { - return new Color(Byte.parseByte(strings[0]), Byte.parseByte(strings[1]), Byte.parseByte(strings[2])); + // rgb + return fromDecimal(toDecimal(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2]))); } else if (strings.length == 4) { - return new Color(Byte.parseByte(strings[0]), Byte.parseByte(strings[1]), Byte.parseByte(strings[2]), Byte.parseByte(strings[3])); + // argb + return fromDecimal(toDecimal(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2]), Integer.parseInt(strings[3]))); } else { throw new IllegalArgumentException("Invalid color format: " + Arrays.toString(strings)); } } - public byte a() { - return a; + public int a() { + return alpha(color); } - public byte b() { - return b; + public int b() { + return blue(color); } - public byte g() { - return g; + public int g() { + return green(color); } - public byte r() { - return r; + public int r() { + return red(color); } } diff --git a/gradle.properties b/gradle.properties index 9026ef29a..829302ea9 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.7 +project_version=0.0.60.8 config_version=43 lang_version=22 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.39 +nms_helper_version=1.0.43 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23