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-01 04:47:27 +08:00
parent 3e6855aa9e
commit 169f0c9973
13 changed files with 364 additions and 299 deletions

View File

@@ -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<W extends ItemWrapper<ItemStack>> 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<Color> 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) {

View File

@@ -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<ItemStack> {
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<Key> 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<Key, Object> 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<Object> injectedIngredients = new ArrayList<>();
// 将自定义配方转为“广义”配方,接受更加宽容的输入
// 部分过程借助bukkit完成部分直接通过nms方法注册
private static final Map<Key, BukkitRecipeConvertor<? extends Recipe<ItemStack>>> MIXED_RECIPE_CONVERTORS = new HashMap<>();
private static final List<Object> injectedIngredients = new ArrayList<>();
private static final IdentityHashMap<Recipe<ItemStack>, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>();
public static final CustomDyeRecipe<ItemStack> DYE_RECIPE = new CustomDyeRecipe<>();
private static Object nmsRecipeManager;
private static final IdentityHashMap<Recipe<ItemStack>, 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<ItemStack> {
MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRANSFORM, (BukkitRecipeConvertor<CustomSmithingTransformRecipe<ItemStack>>) (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<ItemStack> {
MIXED_RECIPE_CONVERTORS.put(RecipeTypes.SMITHING_TRIM, (BukkitRecipeConvertor<CustomSmithingTrimRecipe<ItemStack>>) (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<ItemStack> {
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<ItemStack> 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<ItemStack> {
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<ItemStack> {
@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<ItemStack> {
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<ItemStack> {
return (BukkitRecipeConvertor<T>) 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<ItemStack> {
@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<ItemStack> {
// 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<ItemStack> {
// clear cache
injectedIngredients.clear();
CE_RECIPE_2_NMS_HOLDER.clear();
MINECRAFT_RECIPE_HOLDER_BY_RECIPE.clear();
// create mappings
for (Map.Entry<Key, Recipe<ItemStack>> entry : this.byId.entrySet()) {
Optional<Object> 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<ItemStack> {
}
private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createDataPackResultStack(recipe.result());
boolean hasCustomItemInTag = false;
List<Ingredient<ItemStack>> ingredientList = new ArrayList<>();
@@ -596,7 +604,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
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<ItemStack> {
}
private void handleDataPackShapedRecipe(Key id, VanillaShapedRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createDataPackResultStack(recipe.result());
boolean hasCustomItemInTag = false;
Map<Character, Ingredient<ItemStack>> ingredients = new HashMap<>();
@@ -633,7 +640,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
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<ItemStack> {
VanillaCookingRecipe recipe,
HeptaFunction<Key, CookingRecipeCategory, String, Ingredient<ItemStack>, Integer, Float, CustomRecipeResult<ItemStack>, CustomCookingRecipe<ItemStack>> constructor2,
Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createDataPackResultStack(recipe.result());
Set<UniqueKey> holders = new HashSet<>();
boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), holders::add);
@@ -657,7 +663,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
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<ItemStack> {
}
private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer<Runnable> 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<ItemStack> {
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<ItemStack> {
}
private void handleDataPackSmithingTrim(Key id, VanillaSmithingTrimRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
boolean hasCustomItemInTag;
Set<UniqueKey> additionHolders = new HashSet<>();
hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionHolders::add);
@@ -718,7 +721,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
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<ItemStack> {
if (VersionHelper.isOrAbove1_21_2()) {
Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id));
@SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey);
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(MINECRAFT_RECIPE_MANAGER, resourceKey);
return optional;
} else {
Object resourceLocation = KeyUtils.toResourceLocation(id);
@SuppressWarnings("unchecked")
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation);
Optional<Object> optional = (Optional<Object>) CoreReflections.method$RecipeManager$byKey.invoke(MINECRAFT_RECIPE_MANAGER, resourceLocation);
return optional;
}
}

View File

@@ -342,7 +342,7 @@ public class RecipeEventListener implements Listener {
try {
@SuppressWarnings("unchecked")
Optional<Object> 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<ItemStack> 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;

View File

@@ -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<Object, Boolean> INGREDIENT_COUNT_CHECKER =
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$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();
@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<ItemStack> 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<ItemStack> 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<Color> colors = new ArrayList<>();
Item<ItemStack> 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<ItemStack> 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<ItemStack> dyeItem) {
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
return Optional.ofNullable(customItem.settings().dyeColor()).orElseGet(() -> getVanillaDyeColor(dyeItem));
}
return getVanillaDyeColor(dyeItem);
}
private static final Predicate<Item<ItemStack>> 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<ItemStack> item) {
Optional<CustomItem<ItemStack>> optionalCustomItem = item.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> 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<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$getTextureDiffuseColor(FastNMS.INSTANCE.method$DyeItem$getDyeColor(dyeItem)));
}
private static boolean isDye(Item<ItemStack> dyeItem) {
Optional<CustomItem<ItemStack>> optionalCustomItem = dyeItem.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
return customItem.settings().dyeColor() != 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()));
}
@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<Pair<Object, Object>> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Pair<Object, Object>> 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<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Object> optionalRecipe = (Optional<Object>) 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<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation);
Optional<Object> optionalRecipe = (Optional<Object>) 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<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey);
Optional<Object> optionalRecipe = (Optional<Object>) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.minecraftRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey);
if (optionalRecipe.isEmpty()) {
return Optional.empty();
}

View File

@@ -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()
);
}

View File

@@ -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");

View File

@@ -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;

View File

@@ -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() {}